Проблема с настройкой частоты кадров видео с помощью AVAssetWriter/AVAssetReader

Ситуация:

Я пытаюсь экспортировать видео с некоторыми параметрами, такими как скорость передачи видео, скорость передачи звука, частота кадров, изменение разрешения видео и т.д. Обратите внимание, что я разрешаю пользователю устанавливать частоту кадров видео; Как пользователь может установить частоту кадров видео скажем, 23,98.

Я использую AVAssetWriter и AVAssetReader для этой операции. Я использую AVAssetWriterInputPixelBufferAdaptor для записи примеров буферов.

Все остальное работает просто отлично, кроме: частота кадров видео.

Что я пробовал:

  1. Установка AVAssetWriter.movieTimeScale, как предлагается здесь. Что меняет частоту кадров видео, но также делает видео вялым. (суть здесь)

  1. Установка AVVideoExpectedSourceFrameRateKey. Который не помогает. (суть здесь)

  1. Установка AVAssetWriterInput.mediaTimeScale. Опять же, он изменяет частоту кадров видео, но делает видео вялым, как это делает AVAssetWriter.movieTimeScale. Видео показывает другой кадр в какой-то момент, а иногда оно снова залипает и возобновляется. (суть здесь)

  1. Использование AVAssetReaderVideoCompositionOutput и настройка AVMutableVideoComposition.frameDuration; так же, как это делает SDAVAssetExportSession. По иронии судьбы с кодом SDAVAssetExportSession видео экспортируется только с нужной частотой кадров, что мне нужно, но оно просто не работает в моем коде. суть здесь

Я не уверен, почему это не будет работать с моим кодом. Проблема этого подхода в том, что он всегда возвращает nil из AVAssetReaderVideoCompositionOutput.copyNextSampleBuffer().


  1. Вручную измените метку времени кадров с помощью 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 кадров в секунду), как мне решить, какой кадр отбрасывать, а если целевая частота кадров выше, какой кадр дублировать? Напоминание: частота кадров может быть в долях.