如何使用AVAssetReader和AVAssetWriter控制视频帧率? [英] How to control video frame rate with AVAssetReader and AVAssetWriter?

查看:719
本文介绍了如何使用AVAssetReader和AVAssetWriter控制视频帧率?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们试图了解如何控制/指定使用AVAssetReaderAVAssetWriter编码的视频的帧频.具体来说,我们正在使用AVAssetReaderAVAssetWriter对我们从照片/视频库访问的视频进行转码/编码/压缩.我们能够控制比特率,宽高比变化等,但无法弄清楚如何控制帧频.具体来说,我们希望能够将30分钟(5分钟长)的30 FPS视频作为输入,并以15 FPS的速度发出5分钟的视频.

We are trying to understand how to control/specify the frame rate for videos that we are encoding with AVAssetReader and AVAssetWriter. Specifically, we are are using AVAssetReader and AVAssetWriter to transcode/encode/compress a video that we have accessed from the photo/video gallery. We are able to control things like bit rate, aspect ratio changes, etc., but cannot figure out how to control the frame rate. To be specific, we'd like to be able to take as input a 30 FPS video that's 5 minutes long and emit a 5 minute video at 15 FPS.

我们当前处理样本缓冲区的循环是:

Our current loop that processes sample buffers is:

[writer startWriting];
[writer startSessionAtSourceTime:kCMTimeZero];
[videoReader startReading];

[videoWriterInput requestMediaDataWhenReadyOnQueue:videoEncoderQueue usingBlock:
 ^{         
    while ([videoWriterInput isReadyForMoreMediaData]) {
        CMSampleBufferRef sampleBuffer;

        if ([videoReader status] == AVAssetReaderStatusReading 
            && (sampleBuffer = [videoReaderTrackOutput copyNextSampleBuffer])) {
            if (sampleBuffer) {
                BOOL result = [videoWriterInput appendSampleBuffer:sampleBuffer];
                CFRelease(sampleBuffer);

                if (!result) {
                    [videoReader cancelReading];
                    break;
                }
            }
        } else {
            // deal with status other than AVAssetReaderStatusReading
            [videoWriterInput markAsFinished];
            // [...]
            break;
        }
    }
 }];

我们如何增加或更改它,以便我们可以控制所创建视频的帧频?我们似乎无法在SO或其他任何地方找到可以清楚地说明如何执行此操作的示例.我认为我们应该使用CMTime以及其他可能与上面代码示例中的方法不同的方法,但是细节尚不清楚.

How do we augment or change this so that we could control the frame rate of the created video? We cannot seem to find a sample in SO or anywhere else that clearly explains how to do this. I think we're supposed to use CMTime and probably some other methods other than the ones in the code sample above, but the details aren't clear.

推荐答案

取决于您如何组合框架,您可能只需要设置movieTimeScale.

Depending on how you're compositing the frames, you may just need to set the movieTimeScale.

或者,您需要使用CMTime设置将每一帧添加到写入器中的时间.

Alternately, you need to use CMTime to set the time of each frame as you add it to the writer.

CMTime time = CMTimeMake(0, 30); // (time, time_scale)

这将以每秒30帧的帧速率创建第一帧的时间.将第二个参数设置为所需的帧速率,不要更改它.为您添加到编写器的每个帧增加第一个帧.

This would create the time for the first frame at a frame rate of 30 frames per second. Set the second parameter to your desired frame rate and don't change it. Increment the first for each frame you add to the writer.

您可以使用许多不同的方式来处理传入和传出数据.因此,有很多关于如何/需要指定时间的选择.通常,在使用AVAssetWriterInputPixelBufferAdaptor(如果您正在编辑视频帧)的情况下,上面的方法是合适的.

There are many different ways in which you can process the incoming and outgoing data. Hence there are many options for how the timing can / needs to be specified. Generally, the above is suitable when using a AVAssetWriterInputPixelBufferAdaptor (if you were editing the video frames).

根据更新后的代码,您正在执行更简单"的传递,您可能需要使用CMSampleBufferCreateCopyWithNewTiming生成从读者那里收到的sampleBuffer的副本.我认为,奇怪的是,这使时间安排变得更加复杂.根据您要通过编辑尝试实现的目标,您可能需要创建一个新的CMSampleTimingInfo,可将其用于所有帧,或者使用CMSampleBufferGetSampleTimingInfoArray从示例缓冲区中获取现有的计时信息,然后创建一个版本.类似于以下内容:

Based on your updated code, you're doing a more 'simple' pass through, you probably need to use CMSampleBufferCreateCopyWithNewTiming to generate a copy of the sampleBuffer you receive from the reader. Strangely, I think, this makes the timing more complex. Depending on what you're trying to achieve with the edits you may want to create a new single CMSampleTimingInfo which can be used for all frames, or get the existing timing info from the sample buffer with CMSampleBufferGetSampleTimingInfoArray and then create an edited version of that. Something along the lines of:

CMItemCount count;
CMTime newTimeStamp = CMTimeMake(...);
CMSampleBufferGetSampleTimingInfoArray(sampleBuffer, 0, nil, &count);
CMSampleTimingInfo *timingInfo = malloc(sizeof(CMSampleTimingInfo) * count);
CMSampleBufferGetSampleTimingInfoArray(sampleBuffer, count, timingInfo, &count);

for (CMItemCount i = 0; i < count; i++)
{
    timingInfo[i].decodeTimeStamp = kCMTimeInvalid;
    timingInfo[i].presentationTimeStamp = newTimeStamp;
}

CMSampleBufferRef completedSampleBuffer;
CMSampleBufferCreateCopyWithNewTiming(kCFAllocatorDefault, sampleBuffer, count, timingInfo, &completedSampleBuffer);
free(timingInfo);

您如何选择newTimeStamp决定了您将获得什么结果.

How you choose your newTimeStamp dictates what results you'll get.

这篇关于如何使用AVAssetReader和AVAssetWriter控制视频帧率?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆