[英]Problem setting video frame rate using AVAssetWriter/AVAssetReader
[英]How to control video frame rate with AVAssetReader and AVAssetWriter?
我们试图了解如何控制/指定我们使用AVAssetReader
和AVAssetWriter
编码的视频的帧速率。 具体来说,我们正在使用AVAssetReader
和AVAssetWriter
来转码/编码/压缩我们从照片/视频库访问过的视频。 我们能够控制比特率,宽高比变化等等,但无法弄清楚如何控制帧速率。 具体而言,我们希望能够将长达5分钟的30 FPS视频作为输入,并以15 FPS发出5分钟视频。
我们处理样本缓冲区的当前循环是:
[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
以及除上面代码示例中的其他方法之外的其他方法,但细节尚不清楚。
根据您合成帧的方式,您可能只需要设置movieTimeScale
。
或者,您需要使用CMTime
在将每个帧添加到CMTime
时设置每个帧的时间。
CMTime time = CMTimeMake(0, 30); // (time, time_scale)
这将以每秒30帧的帧速率创建第一帧的时间。 将第二个参数设置为所需的帧速率,不要更改它。 为添加到编写器的每个帧增加第一个。
编辑:
有许多不同的方法可以处理传入和传出的数据。 因此,有多种选择可以如何/需要指定时间。 通常,上述内容适用于使用AVAssetWriterInputPixelBufferAdaptor
(如果您正在编辑视频帧)。
根据您更新的代码,您正在进行更“简单”的传递,您可能需要使用CMSampleBufferCreateCopyWithNewTiming
来生成从阅读器接收的sampleBuffer
的副本。 奇怪的是,我认为,这使得时间更加复杂。 根据您尝试使用编辑实现的内容,您可能需要创建可用于所有帧的新单个CMSampleTimingInfo
,或者使用CMSampleBufferGetSampleTimingInfoArray
从样本缓冲区获取现有时序信息,然后创建其编辑版本。 有点像:
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
决定了你会得到什么结果。
在此之前,我使用dispatch_block_wait在delta时间执行块以再次调用整个函数。 但是一旦我意识到它有一天会成为一个有缺陷的东西,我使用dispatch_source_t作为计时器来执行块作为FPS的控制。
创建一个你想做的事情的块:
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)
如果您正在寻找获取缓冲区的真实案例参考,我已经在https://github.com/matthewlui/FSVideoView上创建了它。 *添加传入的时间间隔是纳秒秒数= 1 / 1,000,000,000秒。 将您的愿望加速到下一帧。
更好的方法是相应地设置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);
应该明白为什么这是所有其他方式的首选方式。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.