3

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

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

有两种方法可以在 AVSampleBufferDisplayLayer 上对缓冲区进行排队:每当缓冲区准备好时通过调用 -[AVSampleBufferDisplayLayer enqueueSampleBuffer:] 来“约束”,或者通过调用 -[AVSampleBufferDisplayLayer requestMediaDataWhenReadyOnQueue:usingBlock:] 并将缓冲区排队在该块中的“不受约束”。我已经尝试了这两种方法,但即使是第二种方法也尽可能快地显示缓冲区 - 例如,如果我在接收端排队了 300 帧,那么第一次执行上述方法中的块时,无论有多少,readyForMoreMediaData 都保持为真缓冲区被排入队列,并且它们都在很短的时间内显示出来。

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

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

4

3 回答 3

2

时基需要设置为您打算解码的第一帧的演示时间戳 (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 中有非常简短的介绍。

于 2016-01-14T22:01:45.567 回答
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(); 在第一帧

于 2017-08-22T12:30:35.507 回答
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);
于 2020-01-03T07:30:45.843 回答