如何控制AVAssetWriter以正确的FPS写入

让我看看我是否理解正确。

在目前最先进的硬件,iOS允许我logging下面的帧数:30,60,120和240。

但是这些fps的行为有所不同。 如果我以30或60 fps的速度拍摄,我预计这些fps拍摄的video文件分别以30 fps和60 fps的速度播放。

但是,如果我以120或240 fps的速度拍摄,我预计在这些fps下拍摄的video文件可以以30 fps的速度播放,否则我将看不到慢镜头。

几个问题:

  1. 我对吗?
  2. 有没有办法以120或240 fps的速度拍摄,分别以120和240 fps的速度拍摄? 我的意思是在fps上播放video是在没有慢镜头的情况下拍摄的?
  3. 我在写文件时如何控制帧速率?

我正在创build这样的AVAssetWriterinput…

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方法非常简单。 我从filter中获取图像,并使用以下命令将其写入文件:

 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 

如果你在它们之间进行一些计算,你将会看到帧率大约是240fps。 所以帧被存储在正确的时间。

但是当我观看video时,运动不是慢动作,快速的时候说video是30 fps。

注意:这个应用程序从相机抓取帧,帧进入CIFilters和这些filter的结果被转换回存储到文件并显示在屏幕上的样本缓冲区。

我到达这里,但是我认为这是你出错的地方。 把你的video捕获视为一个stream水线。

 (1) Capture buffer -> (2) Do Something With buffer -> (3) Write buffer as frames in video. 

听起来像你已经成功完成(1)和(2),你得到的缓冲区足够快,你正在处理它们,所以你可以出售他们作为框架。

问题几乎可以肯定在(3)写video帧。

https://developer.apple.com/reference/avfoundation/avmutablevideocomposition

查看AVMutableComposition中的frameDuration设置,你需要像CMTime(1,60)// 60FPS或者CMTime(1,240)// 240FPS这样的东西来获得你想要的东西(告诉video写这么多帧并以此速率进行编码)。

使用AVAssetWriter,原理完全相同,但是您可以将AVAssetWriterInput outputSettings中的帧速率设置为AVVideoExpectedSourceFrameRateKey中添加的属性。

 NSDictionary *videoCompressionSettings = @{AVVideoCodecKey : AVVideoCodecH264, AVVideoWidthKey : @(videoWidth), AVVideoHeightKey : @(videoHeight), AVVideoExpectedSourceFrameRateKey : @(60), AVVideoCompressionPropertiesKey : @{ AVVideoAverageBitRateKey : @(bitsPerSecond), AVVideoMaxKeyFrameIntervalKey : @(1)} }; 

要扩大一点 – 你不能严格控制或同步你的相机捕获精确的输出/回放速度,时间只是不这样工作,并不是那么确切,当然,处理stream水线增加了开销。 当您捕捉帧时,您已经看到了时间戳,但是在写入/压缩阶段,它仅使用需要的帧来产生指定的合成输出。

它可以两种方式,你可以捕获只有30 FPS和240 FPS写,video将显示正常,你只是有很多帧“遗漏”,并由algorithm填写。 你甚至可以每秒只售出1帧,并以30FPS的速度播放,两者是彼此分开的(我每秒捕获多less帧和多less帧)

至于如何以不同的速度播放,只需要调整播放速度 – 根据需要减慢播放速度。

如果你已经正确设置了时间基准(frameDuration),那么它将总是回放“正常” – 你告诉它“回放是X帧每秒”,当然,你的眼睛可能会注意到一个差异低FPS和高FPS),并且屏幕可能不会刷新那么高(60FPS以上),但是无论video的时基是“正常的”1X速度。 通过减慢video,如果我的时间基准是120,而我减慢到0.5倍,我知道有效地看到60FPS和1秒的播放需要两秒钟。

您可以通过设置AVPlayer的https://developer.apple.com/reference/avfoundation/avplayer上的rate属性来控制播放速&#x5EA6;

iOS屏幕刷新被locking在60fps,所以要“看”额外的帧的唯一方法是,如你所说,减慢播放速度,也就是慢动作。

所以

  1. 你是对的
  2. 屏幕刷新率(也许人类视觉系统的限制,假设你是人类?)意味着你不能感知120和240fps的帧率。 您可以通过下采样到屏幕刷新率以正常速度播放它们。 这当然是AVPlayer已经做了,虽然我不知道这是你正在寻找的答案。
  3. 当您使用CMSampleBuffer演示时间戳写入文件时,您可以控制文件的帧率。 如果帧来自相机,那么您可能会直接通过时间戳,在这种情况下,请检查是否确实获得了您所要求的帧率(捕获callback中的日志语句应该足以validation这一点)。 如果您正在程序化地创build框架,那么您select演示时间戳,以使它们的间隔为1.0 / desiredFrameRate秒!

是不是为你工作?

您可以放弃和忽略AVVideoMaxKeyFrameIntervalKey – 这是一个质量设置,并与播放帧率无关。