Сжатие AVAsset (в основном AVMutableComposition)

У меня есть видео с этими характеристиками

  • Формат: H.264, 1280x544
  • ФПС: 25
  • Размер данных: 26 МБ
  • Продолжительность : 3:00
  • Скорость передачи данных: 1,17 Мбит/с

Во время эксперимента я выполнил removeTimeRange(range : CMTimeRange) для каждого другого кадра (всего кадров = 4225). Это приводит к тому, что видео становится в 2 раза быстрее , поэтому продолжительность становится 1:30.

Однако, когда я экспортирую видео, оно становится в 12 раз больше по размеру, т.е. 325 МБ. Это имеет смысл, поскольку этот метод разбивает видео примерно на 2112 фрагментов и склеивает их вместе. . По-видимому, при этом теряется сжатие между отдельными кадрами, что приводит к огромному размеру.

Это вызывает заикание в видео при воспроизведении с AVPlayer и, следовательно, низкую производительность.

Вопрос. Как можно применить какое-либо сжатие при склеивании кадров, чтобы видео могло воспроизводиться плавно и иметь меньший размер?

Я только хочу точку в правильном направлении. Спасибо!

КОД

1) Создание AVMutableComposition из актива и его настройка

func configureAssets(){

let options =    [AVURLAssetPreferPreciseDurationAndTimingKey : "true"]
let videoAsset = AVURLAsset(url: Bundle.main.url(forResource: "Push", withExtension: "mp4")! , options : options)

let videoAssetSourceTrack = videoAsset.tracks(withMediaType: AVMediaTypeVideo).first! as AVAssetTrack

let comp = AVMutableComposition()
let videoCompositionTrack = comp.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)

do {

    try videoCompositionTrack.insertTimeRange(
        CMTimeRangeMake(kCMTimeZero, videoAsset.duration),
        of: videoAssetSourceTrack,
        at: kCMTimeZero)


    deleteSomeFrames(from: comp)

    videoCompositionTrack.preferredTransform = videoAssetSourceTrack.preferredTransform

}catch { print(error) }

asset = comp   }

2) Удаление каждого второго кадра.

   func deleteSomeFrames(from asset : AVMutableComposition){

let fps =               Int32(asset.tracks(withMediaType: AVMediaTypeVideo).first!.nominalFrameRate)
let sumTime =           Int32(asset.duration.value)  /  asset.duration.timescale;
let totalFrames =       sumTime * fps
let totalTime =         Float(CMTimeGetSeconds(asset.duration))
let frameDuration =     Double(totalTime / Float(totalFrames))
let frameTime =         CMTime(seconds: frameDuration, preferredTimescale: 1000)


for frame in Swift.stride(from: 0, to: totalFrames, by: 2){

    let timing =    CMTimeMultiplyByFloat64(frameTime, Float64(frame))

    print("Asset Duration = \(CMTimeGetSeconds(asset.duration))")
    print("")

    let timeRange = CMTimeRange(start: timing, duration : frameTime)
    asset.removeTimeRange(timeRange)
}

print("duration after time removed = \(CMTimeGetSeconds(asset.duration))")
}

3) Сохранение файла

  func createFileFromAsset(_ asset: AVAsset){

let documentsDirectory =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as URL
let filePath =            documentsDirectory.appendingPathComponent("rendered-vid.mp4")

if let exportSession =    AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality){

    exportSession.outputURL = filePath
    exportSession.shouldOptimizeForNetworkUse = true
    exportSession.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration)
    exportSession.outputFileType = AVFileTypeQuickTimeMovie
    exportSession.exportAsynchronously {
        print("finished: \(filePath) :  \(exportSession.status.rawValue) ")

        if exportSession.status.rawValue == 4{

            print("Export failed -> Reason: \(exportSession.error!.localizedDescription))")
            print(exportSession.error!)

        }
 }}}

4) Наконец, обновите ViewController, чтобы воспроизвести новую композицию!

override func viewDidLoad() {
super.viewDidLoad()

//  Create the AVPlayer and play the composition

assetConfig.configureAssets()
let snapshot : AVComposition =  assetConfig.asset  as! AVComposition
let playerItem =                AVPlayerItem(asset : snapshot)
player =                        AVPlayer(playerItem: playerItem)
let playerLayer =               AVPlayerLayer(player: player)
playerLayer.frame =             CGRect(x : 0, y : 0, width : self.view.frame.width , height : self.view.frame.height)

self.view.layer.addSublayer(playerLayer)
player?.play()

  }

person Community    schedule 23.02.2017    source источник
comment
Можете ли вы поделиться своим кодом для создания отредактированного видео? Вы используете AVMutableComposition?   -  person Dave Weston    schedule 06.03.2017
comment
Я использую AVMutableComposition . Я обновил вопрос с некоторым кодом. Большое спасибо!   -  person    schedule 06.03.2017


Ответы (1)


Если вы используете AVMutableComposition, вы заметите, что каждая композиция может содержать один или несколько AVCompositionTrack(or AVMutableCompositionTrack), и лучший способ редактирования вашей композиции — работать с каждой дорожкой, а не со всей композицией.

но если ваша цель состоит в том, чтобы ускорить скорость вашего видео, редактирование дорожек не потребуется.

так что я постараюсь рассказать вам, что я знаю о вашем вопросе

О заикании видео при воспроизведении

Возможная причина заикания

обратите внимание, что вы используете метод removeTimeRange(range: CMTimeRange), этот метод удалит timeRange в композиции да, но НЕ автоматически заполнит пустое место каждого временного диапазона

Visualize Example
[F stand for Frame,E stand for Empty]

org_video    -->  F-F-F-F-F-F-F-F...
after remove time range, the composition will be like this
after_video  -->  F-E-F-E-F-E-F-E...
and you might think that the video will be like this
target_video -->  F-F-F-F...

это наиболее вероятная причина заикания во время воспроизведения.

Предлагаемое решение

Поэтому, если вы хотите укоротить свое видео, сделать его более быстрым/медленным, возможно, вам нужно использовать метод scaleTimeRange:(CMTimeRange) toDuration:(CMTime)

Example
AVMutableComposition * project;//if this video is 200s

//Scale
project.scaleTimeRange:CMTimeRangeMake(kCMTimeZero, project.duration) toDuration:CMTimeMakeWithSeconds(100,kCMTimeMaxTimescale)

этот метод заключается в том, чтобы сделать видео быстрее/медленнее.

О размере файла

размер видеофайла может зависеть от скорости передачи данных и типа формата. Если вы используете H.264, наиболее вероятной причиной увеличения размера будет скорость передачи данных. сильный>.

в вашем коде вы используете AVAssetExportSession

AVAssetExportSession(asset: asset, presetName:     AVAssetExportPresetHighestQuality

вы указали предустановку AVAssetExportPresetHighestQuality в моем собственном проекте приложения, после того, как я использовал эту предустановку, битрейт видео будет 20–30 Мбит/с, независимо от битрейта вашего исходного видео. и, ну, использование предустановки Apple не позволит вам установить битрейт вручную, так что.

Возможное решение

Существует инструмент третьей части, который называется SDAVAssetExportSession. Этот сеанс позволит вам полностью настроить сеанс экспорта. возможно, вы захотите попробовать изучить этот код о предустановке пользовательского сеанса экспорта.

вот что я могу сказать вам прямо сейчас. желание может помочь :›

person R. JA    schedule 13.03.2017
comment
Спасибо, что нашли время ответить на мой вопрос. Я использовал removeTimeRange(..) намеренно, так как хотел уменьшить общее количество кадров в видео. Кроме того, вычисление frameTime в пределах for loop, как вы могли заметить, позволяет мне манипулировать видео на кадровой основе. Я заметил, что scaleTimeRange(..) на самом деле не изменяет само видео, а просто метаданные. Это видно по битрейту видео, когда timeRange укорачивается. К сожалению, видео можно укоротить только до 1/9 ~ 240 кадров в секунду, поскольку все, что выше этого, слишком много для аппаратного обеспечения. - person ; 13.03.2017
comment
Еще одна вещь, проблема сохраняется, даже когда AVCompositionTrack используется вместо AVMutableComposition. Возможно, я что-то упускаю, потому что внутри цикла for любая модификация timeRange вызовет крошечное заикание / пустой кадр или другое несоответствие в видео. - person ; 13.03.2017
comment
Эээ... а вы пробовали использовать kCMTimeMaxTimeScale во время удаления временного диапазона? заикание было вызвано другой шкалой времени? мое приложение было своего рода видеоредактором, и я устанавливал всю шкалу времени на kCMTimeMaxTimeScale, когда я воспроизводил и экспортировал свою композицию, никаких заиканий не было замечено. Вы думали, что это имеет какой-то смысл? - person R. JA; 14.03.2017
comment
да, вы попытаетесь использовать CMTimeMaxScale в этой строке? let frameTime = CMTime(seconds: frameDuration, preferredTimescale: CMTimeMaxTimeScale это в твоем for loop - person R. JA; 14.03.2017
comment
К сожалению, CMTimeMaxTimeScale не смог исправить ситуацию. Но я заметил, что когда я экспортирую файл AVAsset и воспроизводлю его с помощью QuickTime на своем Mac, он работает гладко. То же самое экспортированное видео не работает гладко в приложении Фото симулятора. Я не могу понять, что случилось. - person ; 14.03.2017