如何降低MediaCodec视频/avc解码的延迟

5
我在 Nexus 5 上运行 Grafika MediaCodec 示例代码,并对 MoviePlayer.java 进行了一些简单的计时。我在以下位置添加了日志记录语句:

203 行之前

请注意,需要保留原文中的 标记。
decoder.queueInputBuffer

在244行之后
decoder.dequeueOutputBuffer

我使用presentationTimeUs相关联日志语句。
以下是来自logcat的摘录:
01-29 10:56:43.295: I/Grafika(21286): queueInputBuffer index/pts, 2,0
01-29 10:56:43.305: I/Grafika(21286): queueInputBuffer index/pts, 0,33100
01-29 10:56:43.315: I/Grafika(21286): queueInputBuffer index/pts, 3,66466
01-29 10:56:43.325: I/Grafika(21286): queueInputBuffer index/pts, 1,99833
01-29 10:56:43.325: I/Grafika(21286): queueInputBuffer index/pts, 2,133200
01-29 10:56:43.335: I/Grafika(21286): queueInputBuffer index/pts, 0,166566
01-29 10:56:43.345: I/ATSParser(21286): discontinuity on stream pid 0x1011
01-29 10:56:43.345: I/ATSParser(21286): discontinuity on stream pid 0x1100
01-29 10:56:43.345: I/Grafika(21286): queueInputBuffer index/pts, 3,199933
01-29 10:56:43.345: I/Grafika(21286): dequeueOutputBuffer index/pts, 7,0
01-29 10:56:43.345: I/Grafika(21286): queueInputBuffer index/pts, 1,300033
01-29 10:56:43.355: I/Grafika(21286): dequeueOutputBuffer index/pts, 6,33100
01-29 10:56:43.385: I/Grafika(21286): queueInputBuffer index/pts, 2,333400
01-29 10:56:43.385: I/Grafika(21286): dequeueOutputBuffer index/pts, 5,66466
01-29 10:56:43.415: I/Grafika(21286): queueInputBuffer index/pts, 0,366766
01-29 10:56:43.415: I/Grafika(21286): dequeueOutputBuffer index/pts, 4,99833
01-29 10:56:43.445: I/Grafika(21286): queueInputBuffer index/pts, 3,400133
01-29 10:56:43.445: I/Grafika(21286): dequeueOutputBuffer index/pts, 3,133200

我发现第一个输入缓冲区排队到相应的输出缓冲区出列的时间差为50毫秒。这对于硬件加速解码来说似乎是很长的时间。 有没有办法减少这种延迟?

我注意到@fadden在这个答案中提到Surface使用BufferQueue:https://dev59.com/questions/P3fZa4cB1Zd3GeqPVcCq#19260131。我还需要配置其他缓冲吗? - Peter Tran
1个回答

5

我认为你看到的是第一帧独特的效果。我重复了你的实验,并在第244行周围进一步添加了强制doRender = false,以避免管理输出帧速率所使用的睡眠调用。我看到:

01-29 14:05:36.552  9115  9224 I Grafika : queueInputBuffer index/pts, 2,0
01-29 14:05:36.562  9115  9224 I Grafika : queueInputBuffer index/pts, 0,66655
01-29 14:05:36.572  9115  9224 I Grafika : queueInputBuffer index/pts, 3,133288
01-29 14:05:36.582  9115  9224 I Grafika : queueInputBuffer index/pts, 1,199955

01-29 14:05:36.602  9115  9224 I Grafika : dequeueOutputBuffer index/pts, 4,0
01-29 14:05:36.602  9115  9224 I Grafika : dequeueOutputBuffer index/pts, 3,66655
01-29 14:05:36.602  9115  9224 I Grafika : dequeueOutputBuffer index/pts, 2,133288
01-29 14:05:36.612  9115  9224 I Grafika : dequeueOutputBuffer index/pts, 4,199955

(为了清晰起见,已删除多余的行。)这证实了你的结果。请注意,虽然在pts=0时输入和输出之间有50ms的延迟,但随后的输出帧几乎是即时可用的。我使用的视频是“camera-test.mp4”(720p相机输出)。
要了解这是为什么,请查看日志中的其他内容以及它们出现的位置。从第一个queueInputBuffer日志行开始,计算出该行与第一个dequeueOutputBuffer行之间出现的日志数量。我在我的日志中数到了大约60行OMX-VDEC-1080P的输出。现在数一下在输出缓冲区开始出现之后出现了多少个OMX-VDEC行。直到视频结束,我都没有看到任何东西。
很明显,视频解码器会推迟一些昂贵的初始化操作,直到有数据可用。那么下一个问题是……它需要多少数据?在提交第二帧(pts==66633)后,我添加了500毫秒的休眠。结果:提交了两个帧,暂停500毫秒,提交了两个帧,OMX-VDEC日志的堆积。因此,解码器似乎需要几个帧才能开始。
这表明我们可以通过快速提供前几帧来减少启动延迟。为了测试这一点,我将TIMEOUT_USEC更改为零,这样它会快速响应但会消耗CPU。新的日志输出(您的日志,没有休眠,也没有渲染):
01-29 14:29:04.542 10560 10599 I Grafika : queueInputBuffer index/pts, 0,0
01-29 14:29:04.542 10560 10599 I Grafika : queueInputBuffer index/pts, 2,66633
01-29 14:29:04.542 10560 10599 I Grafika : queueInputBuffer index/pts, 3,133288
...
01-29 14:29:04.572 10560 10599 I Grafika : dequeueOutputBuffer index/pts, 4,0
01-29 14:29:04.572 10560 10599 I Grafika : dequeueOutputBuffer index/pts, 3,66633
01-29 14:29:04.572 10560 10599 I Grafika : dequeueOutputBuffer index/pts, 2,133288

通过快速输入初始帧,我们将初始延迟从50毫秒减少到30毫秒。(请注意,所有时间戳的结尾都是“2”吗?日志时间使用的计时器似乎舍入到最接近的10毫秒,因此实际时间差可能略有不同。)
我们之所以缓慢输入初始帧,是因为我们正在尝试在提交每个输入缓冲区后从解码器中排空输出,等待10毫秒却没有输出。我的初步想法是,我们要等待dequeueInputBuffer()dequeueOutputBuffer()任一一个超时,但不是两者都等待——也许一开始使用输入超时和快速轮询输出,然后在没有输入要供给时切换到输出超时。(说起来,对于输入,初始超时可能是-1,因为在第一个输入缓冲区入队之前,什么都不会发生。)
我不知道是否有办法进一步降低延迟。

你认为基于 Choreographer.FrameCallback 渲染怎么样?Chromium 项目在一个很好的类中封装了它,叫做 VSyncMonitor。所以,不是先调用dequeue然后睡眠来平衡输出速度,我会首先做出决定是否要进行渲染,然后再调用dequeue。你有什么想法? - Peter Tran
你应该把这个放在一个新的问题里。顺便说一下,Grafika的“使用FBO记录GL应用程序”活动是由Choreographer驱动的。 - fadden

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接