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

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

在目前最先进的硬件上,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.

注意:此应用程序从相机抓取帧,帧进入CIF过滤器,这些过滤器的结果将转换回存储到文件显示在屏幕上的样本缓冲区.

解决方法

我到达这里,但我认为这是你出错的地方.将您的视频捕获视为管道.
(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属性来控制播放速度

相关文章

UITabBarController 是 iOS 中用于管理和显示选项卡界面的一...
UITableView的重用机制避免了频繁创建和销毁单元格的开销,使...
Objective-C中,类的实例变量(instance variables)和属性(...
从内存管理的角度来看,block可以作为方法的传入参数是因为b...
WKWebView 是 iOS 开发中用于显示网页内容的组件,它是在 iO...
OC中常用的多线程编程技术: 1. NSThread NSThread是Objecti...