如何控制AVAssetWriter以正确的FPS编写
让我看看我是否理解正确。
在目前最先进的硬件上,iOS允许我以下面的帧数记录:30,60,120和240。
但这些fps行为有所不同。 如果我以30或60 fps的速度拍摄,我预计在这些fps下拍摄的视频文件分别以30 fps和60 fps的速度播放。
但是如果我以120或240 fps的速度拍摄,我希望从这些fps拍摄的视频文件以30 fps的速度拍摄,否则我将看不到慢镜头。
几个问题:
我正在创建像这样的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 fps的速度拍摄下来。 这些是附加帧的呈现时间。
time ======= 113594.311510508
time ======= 113594.324011508
time ======= 113594.328178716
time ======= 113594.340679424
time ======= 113594.344846383
如果你在它们之间进行一些计算,你会发现帧率大约是240 fps。 所以帧被存储在正确的时间。
但是当我观看视频时,运动不是慢动作,而快速时间表示视频速度为30 fps。
注意:此应用程序从相机抓取帧,帧将进入CIFilter,并将这些滤镜的结果转换回存储到文件并显示在屏幕上的样本缓冲区。
我到达这里,但我认为这是你出错的地方。 将您的视频捕获视为管道。
(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
查看AVMutableComposition中的frameDuration设置,您需要像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帧FPS并以240帧/秒的速度输出,视频将显示正常,只会有很多帧“遗漏”并被算法填充。 你甚至可以每秒仅售出1帧,并以30FPS的速度播放,两者是彼此分开的(我以多快的速度捕捉Vs多少帧以及我每秒呈现的内容)
至于如何以不同的速度播放它,您只需调整播放速度 - 根据需要减慢播放速度。
如果你已经正确设置了时间基准(frameDuration),它将总是回放“正常” - 你告诉它“回放是每秒X帧”,当然,你的眼睛可能会注意到一个差异低FPS和高FPS),并且屏幕可能不会刷新那么高(60FPS以上),但无论视频的时基是“正常”1倍速。 通过减慢视频,如果我的时间基准是120,并且我将它减慢到0.5x我知道有效地看到60FPS和一秒钟的回放需要两秒钟。
您可以通过设置AVPlayer的https://developer.apple.com/reference/avfoundation/avplayer上的rate属性来控制播放速度
iOS屏幕刷新被锁定在60fps,因此,如您所说,“看”多余帧的唯一方法是放慢播放速度,也就是慢动作。
所以
AVPlayer
已经做的事情,尽管我不确定这是否是您要找的答案。 CMSampleBuffer
演示时间戳写入文件时,您可以控制文件的帧率。 如果帧是来自相机,那么您可能会直接通过时间戳,在这种情况下,请检查您是否确实获得了您要求的帧率(捕获回调中的日志语句应该足以验证该帧)。 如果您正在程序化地创建框架,那么您选择演示时间戳,以便它们的间隔为1.0 / desiredFrameRate秒! 3.不适合你吗?
ps你可以放弃并忽略AVVideoMaxKeyFrameIntervalKey
- 这是一个质量设置,与播放帧率无关。
上一篇: How do I control AVAssetWriter to write at the correct FPS