Как управлять AVAssetWriter для записи в правильном FPS
давайте посмотрим, правильно ли я понял.
в настоящее время самое современное оборудование, iOS позволяет мне записывать на следующих fps: 30, 60, 120 и 240.
но эти fps ведут себя по-разному. Если я снимаю на 30 или 60 fps, я ожидаю, что видеофайлы, созданные из съемки на этих fps, будут воспроизводиться на 30 и 60 fps соответственно.
но если я стреляю на 120 или 240 fps, я ожидаю, что видеофайлы, создаваемые из съемки на этих fps, будут играть на 30 fps, или я буду не вижу замедленной съемки.
несколько вопросов:
- Я прав?
- есть ли способ стрелять на 120 или 240 кадров в секунду и играть на 120 и 240 кадров в секунду соответственно? Я имею в виду игру в fps, видео были сняты без slo-mo?
- как я могу контролировать эту частоту кадров, когда я пишу файл?
Я создаю вход AVAssetWriter, как это...
NSDictionary *videoCompressionSettings = @{AVVideoCodecKey : AVVideoCodecH264,
AVVideoWidthKey : @(videoWidth),
AVVideoHeightKey : @(videoHeight),
AVVideoCompressionPropertiesKey : @{ AVVideoAverageBitRateKey : @(bitsPerSecond),
AVVideoMaxKeyFrameIntervalKey : @(1)}
};
_assetWriterVideoInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoCompressionSettings];
и нет никакого очевидного способа контролировать что.
примечание: Я пробовал разные номера, где это 1
есть. Я пробовал 1.0/fps
, Я пробовал fps
и я удалил ключ. Никакая разница.
вот как я настраиваю " AVAssetWriter:
AVAssetWriter *newAssetWriter = [[AVAssetWriter alloc] initWithURL:_movieURL fileType:AVFileTypeQuickTimeMovie
error:&error];
_assetWriter = newAssetWriter;
_assetWriter.shouldOptimizeForNetworkUse = NO;
CGFloat videoWidth = size.width;
CGFloat videoHeight = size.height;
NSUInteger numPixels = videoWidth * videoHeight;
NSUInteger bitsPerSecond;
// Assume that lower-than-SD resolutions are intended for streaming, and use a lower bitrate
// if ( numPixels < (640 * 480) )
// bitsPerPixel = 4.05; // This bitrate matches the quality produced by AVCaptureSessionPresetMedium or Low.
// else
NSUInteger bitsPerPixel = 11.4; // This bitrate matches the quality produced by AVCaptureSessionPresetHigh.
bitsPerSecond = numPixels * bitsPerPixel;
NSDictionary *videoCompressionSettings = @{AVVideoCodecKey : AVVideoCodecH264,
AVVideoWidthKey : @(videoWidth),
AVVideoHeightKey : @(videoHeight),
AVVideoCompressionPropertiesKey : @{ AVVideoAverageBitRateKey : @(bitsPerSecond)}
};
if (![_assetWriter canApplyOutputSettings:videoCompressionSettings forMediaType:AVMediaTypeVideo]) {
NSLog(@"Couldn't add asset writer video input.");
return;
}
_assetWriterVideoInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo
outputSettings:videoCompressionSettings
sourceFormatHint:formatDescription];
_assetWriterVideoInput.expectsMediaDataInRealTime = YES;
NSDictionary *adaptorDict = @{
(id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA),
(id)kCVPixelBufferWidthKey : @(videoWidth),
(id)kCVPixelBufferHeightKey : @(videoHeight)
};
_pixelBufferAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc]
initWithAssetWriterInput:_assetWriterVideoInput
sourcePixelBufferAttributes:adaptorDict];
// Add asset writer input to asset writer
if (![_assetWriter canAddInput:_assetWriterVideoInput]) {
return;
}
[_assetWriter addInput:_assetWriterVideoInput];
captureOutput
метод очень прост. Я получаю изображение из фильтра и записываю его в файл, используя:
if (videoJustStartWriting)
[_assetWriter startSessionAtSourceTime:presentationTime];
CVPixelBufferRef renderedOutputPixelBuffer = NULL;
OSStatus err = CVPixelBufferPoolCreatePixelBuffer(nil,
_pixelBufferAdaptor.pixelBufferPool,
&renderedOutputPixelBuffer);
if (err) return; // NSLog(@"Cannot obtain a pixel buffer from the buffer pool");
//_ciContext is a metal context
[_ciContext render:finalImage
toCVPixelBuffer:renderedOutputPixelBuffer
bounds:[finalImage extent]
colorSpace:_sDeviceRgbColorSpace];
[self writeVideoPixelBuffer:renderedOutputPixelBuffer
withInitialTime:presentationTime];
- (void)writeVideoPixelBuffer:(CVPixelBufferRef)pixelBuffer withInitialTime:(CMTime)presentationTime
{
if ( _assetWriter.status == AVAssetWriterStatusUnknown ) {
// If the asset writer status is unknown, implies writing hasn't started yet, hence start writing with start time as the buffer's presentation timestamp
if ([_assetWriter startWriting]) {
[_assetWriter startSessionAtSourceTime:presentationTime];
}
}
if ( _assetWriter.status == AVAssetWriterStatusWriting ) {
// If the asset writer status is writing, append sample buffer to its corresponding asset writer input
if (_assetWriterVideoInput.readyForMoreMediaData) {
if (![_pixelBufferAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:presentationTime]) {
NSLog(@"error", [_assetWriter.error localizedFailureReason]);
}
}
}
if ( _assetWriter.status == AVAssetWriterStatusFailed ) {
NSLog(@"failed");
}
}
я поставил все это, чтобы стрелять на 240 кадров в секунду. Это время представления добавляемых кадров.
time ======= 113594.311510508
time ======= 113594.324011508
time ======= 113594.328178716
time ======= 113594.340679424
time ======= 113594.344846383
если вы сделать некоторые расчеты между ними, вы увидите, что частота кадров составляет около 240 кадров в секунду. Таким образом, кадры сохраняются с правильным временем.
но когда я смотрю видео, движение не замедляется, и быстрое время говорит, что видео 30 кадров в секунду.
Примечание: это приложение захватывает кадры из камеры, кадры переходит в Сифилтеры и результат этих фильтров преобразуется обратно в буфер образца, который хранится в файл и отображается на экране.
2 ответов
я добираюсь сюда, но я думаю, что здесь вы ошибаетесь. Считайте видеозахват конвейером.
(1) Capture buffer -> (2) Do Something With buffer -> (3) Write buffer as frames in video.
похоже, что вы успешно завершили (1) и (2), Вы получаете буфер достаточно быстро, и вы обрабатываете их, чтобы вы могли отправить их в качестве кадров.
проблема почти наверняка в (3) записи видеокадров.
https://developer.apple.com/reference/avfoundation/avmutablevideocomposition
Проверьте настройку frameDuration в AVMutableComposition, вам понадобится что-то вроде CMTime(1, 60) //60FPS или CMTime(1, 240) // 240FPS, чтобы получить то, что вам нужно (говоря видео, чтобы написать это много кадров и кодировать с такой скоростью).
используя AVAssetWriter, это точно такой же принцип, но вы устанавливаете частоту кадров как свойство в AVAssetWriterInput outputSettings добавление в AVVideoExpectedSourceFrameRateKey.
NSDictionary *videoCompressionSettings = @{AVVideoCodecKey : AVVideoCodecH264,
AVVideoWidthKey : @(videoWidth),
AVVideoHeightKey : @(videoHeight),
AVVideoExpectedSourceFrameRateKey : @(60),
AVVideoCompressionPropertiesKey : @{ AVVideoAverageBitRateKey : @(bitsPerSecond),
AVVideoMaxKeyFrameIntervalKey : @(1)}
};
чтобы расширить немного больше - вы не можете строго контролировать или синхронизировать захват камеры точно со скоростью вывода / воспроизведения, время просто не работает таким образом и не так точно, и, конечно, конвейер обработки добавляет накладные расходы. Когда вы захватываете кадры, они помечены временем, которое вы видели, но на этапе записи / сжатия он использует только кадры, которые ему нужно создать выход, указанный для композиции.
это идет в обе стороны, вы можете захватить только 30 кадров в секунду и записать на 240 кадров в секунду, видео будет отображаться нормально, у вас просто будет много кадров "отсутствует" и заполняется алгоритмом. Вы даже можете vend только 1 кадр в секунду и воспроизводить на 30FPS, два отдельно друг от друга (как быстро я захватываю против сколько кадров и что я представляю в секунду)
о том, как воспроизвести его на разной скорости, вам просто нужно настроить скорость воспроизведения - замедлять его по мере необходимости.
Если вы правильно установили время база (frameDuration), он будет всегда проигрывать "нормально" - ты говоришь это "воспроизвести х кадров в секунду", конечно, ваш глаз может заметить разницу (почти наверняка низкий фпс и высокий FPS), а экран не может обновить, что высокий (выше 60), но независимо от того, видео будет в "нормальной" 1Х скорость его развертки. Замедляя видео, Если моя временная база 120, и я замедляю его к.5x я знаю, что эффективно вижу 60FPS, и одна секунда воспроизведения занимает две секунды.
вы контролируете скорость воспроизведения, устанавливая свойство rate на AVPlayer https://developer.apple.com/reference/avfoundation/avplayer
обновление экрана iOS заблокировано на 60fps, поэтому единственный способ "увидеть" дополнительные кадры-это, как вы говорите, замедлить скорость воспроизведения, a.к. медленное движение.
Так
- да, вы правы
- частота обновления экрана (и, возможно, ограничения зрительной системы человека, предполагая, что вы человек?) означает, что вы не можете воспринимать частоту кадров 120 & 240fps. Вы can играть их на нормальной скорости путем downsampling к частоте обновления экрана. Конечно, это
AVPlayer
уже делает, хотя я не уверен, что это ответ, который вы ищете. - вы контролируете частоту кадров файла при его записи с помощью
CMSampleBuffer
метки времени презентации. Если ваши кадры поступают с камеры, вы, вероятно, передаете метки времени прямо через, и в этом случае проверьте, что вы действительно получаете частоту кадров, которую вы просили (оператор журнала в обратном вызове захвата должен быть достаточным для проверки этого). Если вы процедурно создание рам, тогда вы выберете презентации метки так, что они разнесены 1.0/desiredFrameRate секунды!
Is 3. не работаешь на тебя?
p.s. вы можете отбросить & игнорировать AVVideoMaxKeyFrameIntervalKey
- это настройка качества и не имеет ничего общего с частотой кадров воспроизведения.