设置AVSampleBufferDisplayLayer呈现样本缓冲区的速率

时间:2015-09-13 22:00:26

标签: ios video-streaming cmsamplebufferref video-toolbox cmsamplebuffer

我正在使用AVSampleBufferDisplayLayer来显示以h.264格式通过网络连接的CMSampleBuffers。视频播放流畅且工作正常,但我似乎无法控制帧速率。具体来说,如果我在AVSampleBufferDisplayLayer中每秒排队60帧,它会显示这60帧,即使视频以30 FPS记录。

创建样本缓冲区时,可以通过将定时信息数组传递给CMSampleBufferCreate来设置显示时间戳(时间信息不在h.264流中,但可以以容器格式计算或传递)。我设置的演示时间戳大约相隔0.033秒,持续时间为0.033,但显示层仍然每秒显示尽可能多的帧数。

有两种方法可以在AVSampleBufferDisplayLayer上排队缓冲区:"约束"通过调用 - [AVSampleBufferDisplayLayer enqueueSampleBuffer:],只要缓冲区准备好,或者#34;不受约束"通过调用 - [AVSampleBufferDisplayLayer requestMediaDataWhenReadyOnQueue:usingBlock:]并将该块中的缓冲区排入队列。我已经尝试了两种方法,但即便是第二种方法也能尽可能快地显示缓冲区 - 例如,如果我在接收端排队了300帧,那么第一次执行上述方法中的块readyForMoreMediaData仍然是真的无论如何入队的缓冲区数量,以及它们都会在很短的时间内显示出来。

此行为类似于在CMSampleBuffer上设置kCMSampleAttachmentKey_DisplayImmediately附件时所期望的行为,但是当前未设置此行为(默认值为false)。

我尝试设置图层controlTimeBase,但它似乎没有任何效果。我无法尝试其他东西,也无法在网上找到例子。有谁知道如何控制AVSampleBufferDisplayLayer显示帧的帧率?

3 个答案:

答案 0 :(得分:1)

需要将时基设置为要解码的第一帧的显示时间戳(pts)。我通过从所有后续pts中减去初始pts并将Timebase设置为0来将第一帧的pts索引为0.无论出于何种原因,这都不起作用。

你想要这样的东西(在调用解码之前称为):

CMTimebaseRef controlTimebase;
CMTimebaseCreateWithMasterClock( CFAllocatorGetDefault(), CMClockGetHostTimeClock(), &controlTimebase );

displayLayer.controlTimebase = controlTimebase;

// Set the timebase to the initial pts here
CMTimebaseSetTime(displayLayer.controlTimebase, CMTimeMake(ptsInitial, 1));
CMTimebaseSetRate(displayLayer.controlTimebase, 1.0);

设置CMSampleBuffer的PTS ...

CMSampleBufferSetOutputPresentationTimeStamp(sampleBuffer, presentationTimeStamp);

也许确保不立即显示......

CFDictionarySetValue(dict, kCMSampleAttachmentKey_DisplayImmediately, kCFBooleanFalse);

WWDC 2014 Session 513中对此进行了简要介绍。

答案 1 :(得分:0)

遇到同样的问题,设法在CMSampleBufferCreate创建中一个接一个地播放几个流而没有延迟跟随时间

CMSampleTimingInfo timingdata ={
 .presentationTimeStamp = CMTimeMakeWithSeconds(self.frame0time+(1/self.frameFPS)*self.frameActive, 1000),
 .duration =  CMTimeMakeWithSeconds(1/self.frameFPS, 1000),
 .decodeTimeStamp = kCMTimeInvalid
};

不需要使用kCMSampleAttachmentKey_DisplayImmediately使用这种方法,你只需要在每个Iframe和BFrame上使用self.frameActive ++并使self.frame0time = CACurrentMediaTime();在第一帧

答案 2 :(得分:0)

在将其排队到AVSampleBufferDisplayLayer之前,在示例缓冲区上设置输出演示时间戳记。对于FPS = 30:

CMSampleBufferSetOutputPresentationTimeStamp(sampleBuffer,    CMTimeAdd(kCMTimeZero, CMTimeMake(1 * _frameCount, 30)));

将第一个缓冲区的时间戳设置为0(kCMTimeZero),然后将后续缓冲区的时间戳增加1/30秒。

此外,需要在AVSampleBufferDisplayLayer实例上设置时基,以便首先显示演示时间戳设置为0的缓冲区。

CMTimebaseRef controlTimebase;
CMTimebaseCreateWithMasterClock(CFAllocatorGetDefault(),    CMClockGetHostTimeClock(), &controlTimebase);

_videoLayer.controlTimebase = controlTimebase;
CMTimebaseSetTime(_videoLayer.controlTimebase, kCMTimeZero);
CMTimebaseSetRate(_videoLayer.controlTimebase, 1.0);