简体   繁体   English

如何使用AVAssetReader和AVAssetWriter控制视频帧速率?

[英]How to control video frame rate with AVAssetReader and AVAssetWriter?

We are trying to understand how to control/specify the frame rate for videos that we are encoding with AVAssetReader and AVAssetWriter . 我们试图了解如何控制/指定我们使用AVAssetReaderAVAssetWriter编码的视频的帧速率。 Specifically, we are are using AVAssetReader and AVAssetWriter to transcode/encode/compress a video that we have accessed from the photo/video gallery. 具体来说,我们正在使用AVAssetReaderAVAssetWriter来转码/编码/压缩我们从照片/视频库访问过的视频。 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. 具体而言,我们希望能够将长达5分钟的30 FPS视频作为输入,并以15 FPS发出5分钟视频。

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;
        }
    }
 }];

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. 我们似乎无法在SO或其他任何明确解释如何执行此操作的地方找到样本。 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. 我认为我们应该使用CMTime以及除上面代码示例中的其他方法之外的其他方法,但细节尚不清楚。

Depending on how you're compositing the frames, you may just need to set the movieTimeScale . 根据您合成帧的方式,您可能只需要设置movieTimeScale

Alternately, you need to use CMTime to set the time of each frame as you add it to the writer. 或者,您需要使用CMTime在将每个帧添加到CMTime时设置每个帧的时间。

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

This would create the time for the first frame at a frame rate of 30 frames per second. 这将以每秒30帧的帧速率创建第一帧的时间。 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. 为添加到编写器的每个帧增加第一个。

Edit: 编辑:

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). 通常,上述内容适用于使用AVAssetWriterInputPixelBufferAdaptor (如果您正在编辑视频帧)。

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. 根据您更新的代码,您正在进行更“简单”的传递,您可能需要使用CMSampleBufferCreateCopyWithNewTiming来生成从阅读器接收的sampleBuffer的副本。 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. 根据您尝试使用编辑实现的内容,您可能需要创建可用于所有帧的新单个CMSampleTimingInfo ,或者使用CMSampleBufferGetSampleTimingInfoArray从样本缓冲区获取现有时序信息,然后创建其编辑版本。 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);

How you choose your newTimeStamp dictates what results you'll get. 你如何选择你的newTimeStamp决定了你会得到什么结果。

Before, I use dispatch_block_wait to perform block at delta time to call the whole function again. 在此之前,我使用dispatch_block_wait在delta时间执行块以再次调用整个函数。 But once I realise it will someday become a buggy stuff, I use a dispatch_source_t as a timer to perform block as the control of the FPS instead. 但是一旦我意识到它有一天会成为一个有缺陷的东西,我使用dispatch_source_t作为计时器来执行块作为FPS的控制。

create a block of what you want to do: 创建一个你想做的事情的块:

var block = dispatch_block_create(...)
var queue = dispatch_queue_create(...)
var source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue) 
dispatch_set_timer(source,STARTTIME,INTERVAL,0)
dispatch_source_set_event_handler(source,block)
dispatch_resume(source)

If you are looking for real case reference of grab the buffers, I've made it on https://github.com/matthewlui/FSVideoView . 如果您正在寻找获取缓冲区的真实案例参考,我已经在https://github.com/matthewlui/FSVideoView上创建了它。 *Added The timeinterval to pass in is count in nano second = 1/1,000,000,000 second. *添加传入的时间间隔是纳秒秒数= 1 / 1,000,000,000秒。 Times it with your desire delta to next frame. 将您的愿望加速到下一帧。

A better way is to the set the timebase property of the AVSampleBufferDisplayLayer accordingly: 更好的方法是相应地设置AVSampleBufferDisplayLayer的timebase属性:

CMTimebaseRef timebase;
OSStatus timebaseResult;
timebaseResult = CMTimebaseCreateWithMasterClock(NULL, CMClockGetHostTimeClock(), &timebase);
if (timebaseResult != 0)
{
    NSLog(@"ERROR: could not create timebase");
} else {
    CMTimebaseSetTime(timebase, CMTimeMake(1, 3000));
    CMTimebaseSetRate(timebase, 1.0f);
}

[(AVSampleBufferDisplayLayer *)self.layer setControlTimebase:timebase];
CFRelease(timebase);

It should be obvious why this is the preferred means over all others. 应该明白为什么这是所有其他方式的首选方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用 AVAssetWriter/AVAssetReader 设置视频帧率时出现问题 - Problem setting video frame rate using AVAssetWriter/AVAssetReader 当前帧的AVAssetReader / AVAssetWriter预览 - AVAssetReader/AVAssetWriter preview of current frame 如何同时将 AVAssetReader 和 AVAssetWriter 用于多个轨道(音频和视频)? - How to use AVAssetReader and AVAssetWriter for multiple tracks (audio and video) simultaneously? AVAssetWriter-设置自定义帧率 - AVAssetWriter - Set Custom frame rate AVAssetWriter AVVideoExpectedSourceFrameRateKey(帧频)已忽略 - AVAssetWriter AVVideoExpectedSourceFrameRateKey (frame rate) ignored 如何使用AVAssetReader和AVAssetWriter创建AAC文件? - How can I use AVAssetReader and AVAssetWriter to create a AAC file? 使用AVAssetWriter降低视频帧速率,同时保持相同的长度/持续时间 - Reduce Video Frame Rate While Keeping Same Length/Duration Using AVAssetWriter AVAssetWriter / AVAssetWriterInputPixelBufferAdaptor - 黑帧和帧速率 - AVAssetWriter / AVAssetWriterInputPixelBufferAdaptor - black frames and frame rate 使用AVAssetWriter重新编码H264 mov文件-如何设置帧频? - Using AVAssetWriter to re-encode H264 mov file - how to set frame-rate? 使用AVAssetWriter和AVAssetReader进行音频录制和播放 - Using AVAssetWriter and AVAssetReader for Audio Recording and Playback
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM