Ситуация:
Я пытаюсь экспортировать видео с некоторыми параметрами, такими как скорость передачи видео, скорость передачи звука, частота кадров, изменение разрешения видео и т.д. Обратите внимание, что я разрешаю пользователю устанавливать частоту кадров видео; Как пользователь может установить частоту кадров видео скажем, 23,98.
Я использую AVAssetWriter и AVAssetReader для этой операции. Я использую AVAssetWriterInputPixelBufferAdaptor для записи примеров буферов.
Все остальное работает просто отлично, кроме: частота кадров видео.
Что я пробовал:
- Установка AVAssetWriter.movieTimeScale, как предлагается здесь. Что меняет частоту кадров видео, но также делает видео вялым. (суть здесь)
- Установка AVVideoExpectedSourceFrameRateKey. Который не помогает. (суть здесь)
- Установка AVAssetWriterInput.mediaTimeScale. Опять же, он изменяет частоту кадров видео, но делает видео вялым, как это делает AVAssetWriter.movieTimeScale. Видео показывает другой кадр в какой-то момент, а иногда оно снова залипает и возобновляется. (суть здесь)
- Использование AVAssetReaderVideoCompositionOutput и настройка AVMutableVideoComposition.frameDuration; так же, как это делает SDAVAssetExportSession. По иронии судьбы с кодом SDAVAssetExportSession видео экспортируется только с нужной частотой кадров, что мне нужно, но оно просто не работает в моем коде. суть здесь
Я не уверен, почему это не будет работать с моим кодом. Проблема этого подхода в том, что он всегда возвращает nil из AVAssetReaderVideoCompositionOutput.copyNextSampleBuffer().
- Вручную измените метку времени кадров с помощью CMSampleTimingInfo, как предлагается здесь.
var sampleTimingInfo = CMSampleTimingInfo()
var sampleBufferToWrite: CMSampleBuffer?
CMSampleBufferGetSampleTimingInfo(vBuffer, at: 0, timingInfoOut: &sampleTimingInfo)
sampleTimingInfo.duration = CMTimeMake(value: 100, timescale: Int32(videoConfig.videoFrameRate * 100))
sampleTimingInfo.presentationTimeStamp = CMTimeAdd(previousPresentationTimeStamp, sampleTimingInfo.duration)
previousPresentationTimeStamp = sampleTimingInfo.presentationTimeStamp
let status = CMSampleBufferCreateCopyWithNewTiming(allocator: kCFAllocatorDefault, sampleBuffer: vBuffer,sampleTimingEntryCount: 1, sampleTimingArray: &sampleTimingInfo, sampleBufferOut: &sampleBufferToWrite)
При таком подходе я действительно правильно устанавливаю частоту кадров, но это увеличивает продолжительность видео (как уже упоминалось в комментарии к ответу на этот вопрос). Я думаю, что в какой-то момент мне, возможно, придется отказаться от некоторых кадров (если целевая частота кадров ниже; в большинстве случаев мне нужно снизить частоту кадров).
Если я знаю, что если я хочу 30 кадров в секунду, а моя текущая частота кадров составляет 60 кадров в секунду, то просто отбрасывать каждый второй кадр и соответственно устанавливать время SampleBuffer.
Если я пойду с этим подходом (то есть установлю 23,98 кадров в секунду), как мне решить, какой кадр отбрасывать, а если целевая частота кадров выше, какой кадр дублировать? Напоминание: частота кадров может быть в долях.