在目前最先进的硬件上,iOS允许我以下列fps进行录制:30,60,120和240.
但这些fps表现不同.如果我以30或60 fps拍摄,我希望通过这些fps拍摄创建的视频文件分别以30和60 fps播放.
但是如果我以120或240 fps的速度拍摄,我希望以这些fps拍摄的视频文件以30 fps的速度播放,否则我将看不到慢动作.
几个问题:
>我是对的吗?
>有没有办法以120或240 fps的速度分别以120和240 fps的速度进行拍摄?我的意思是在没有慢动作的情况下拍摄视频的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,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.
解决方法
(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,它的原理完全相同,但您将帧速率设置为AVAsetWriterInput outputSettings中添加AVVideoExpectedSourceFrameRateKey的属性.
NSDictionary *videoCompressionSettings = @{AVVideoCodecKey : AVVideoCodecH264,AVVideoExpectedSourceFrameRateKey : @(60),AVVideoMaxKeyFrameIntervalKey : @(1)} };
要进一步扩展 – 你不能严格控制或同步你的摄像头捕获精确到输出/播放速率,时间只是不这样工作,并不是那么精确,当然处理管道增加了开销.当您捕获帧时,它们会被标记为时间戳,但是在写入/压缩阶段,它只使用它所需的帧来生成为合成指定的输出.
它有两种方式,你只能捕获30 FPS并以240 FPS写出,视频显示正常,你只需要很多帧“丢失”并被算法填充.你甚至可以每秒只卖1帧,然后以30FPS回放,两者相互分开(我多快捕捉到多少帧和我每秒出现的帧数)
至于如何以不同的速度播放它,你只需要调整播放速度 – 根据需要减慢播放速度.
如果你已经正确设置时基(frameDuration),它将始终播放“正常” – 你告诉它“回放是每秒X帧”,当然,你的眼睛可能会注意到差异(几乎可以肯定)低FPS和高FPS),屏幕可能无法刷新到那么高(60FPS以上),但无论视频的时基是多少都是“正常”的1倍速.通过减慢视频速度,如果我的时基为120,我将其减慢到.5x,我知道有效地看到60FPS,一秒钟的播放需要两秒钟.
您可以通过在AVPlayer https://developer.apple.com/reference/avfoundation/avplayer上设置rate属性来控制播放速度