如何减少MediaCodec视频/ avc解码的延迟

Pet*_*ran 4 android h.264 textureview mediacodec mediaextractor

我在Nexus 5上运行的Grafika MediaCodec示例代码中执行了一些简单的MoviePlayer.java计时.我在这些位置放了一个日志语句:

在前一行的203行

decoder.queueInputBuffer
Run Code Online (Sandbox Code Playgroud)

在第244行之后

decoder.dequeueOutputBuffer
Run Code Online (Sandbox Code Playgroud)

我使用了日志语句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
Run Code Online (Sandbox Code Playgroud)

我发现从第一个输入缓冲区排队到相应输出缓冲区出列的时间差是50毫秒.这似乎是硬件加速解码的大量时间.

有没有办法减少这种延迟?

fad*_*den 5

我想你会看到第一帧独有的一些效果.我重复了你的实验,doRender = false在244行周围进一步增加了强制,以避免用于管理输出帧速率的睡眠调用.我知道了:

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
Run Code Online (Sandbox Code Playgroud)

(为清楚起见,删除了外部线条.)这证实了您的结果.请注意,虽然pts = 0的输入和输出之间有50ms的延迟,但后续的输出帧几乎可以立即获得.我使用的视频是"camera-test.mp4"(720p摄像头输出).

要了解发生这种情况的原因,请查看日志中的其他内容以及它出现的位置.从第一个queueInputBuffer日志行开始,计算它与第一dequeueOutputBuffer行之间出现的日志数.我在OMX-VDEC-1080P上计算了大约60行输出.现在计算输出缓冲区开始出现后出现的OMX-VDEC线数.直到视频结束,我才看到.

在数据可用之前,视频解码器显然推迟了一些昂贵的初始化.那么接下来的问题是......它需要多少数据?提交第二帧后,我增加了500ms的睡眠时间(pts == 66633).结果:两个框提交,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
Run Code Online (Sandbox Code Playgroud)

通过快速馈送初始帧,我们将初始延迟从50ms减少到30ms.

(请注意所有时间戳如何以'2'结尾?用于记录时间的计时器似乎四舍五入到最接近的10ms,因此实际时间增量可能略有不同.)

我们缓慢地输入初始帧的原因是我们在提交每个输入缓冲区后试图从解码器中排出输出,等待10ms以便从未出现输出.我最初的想法是,我们想要等待任何一个 dequeueInputBuffer() dequeueOutputBuffer()两个超时,但不是两个 - 可能首先使用输入超时和输出的快速轮询,然后当我们用完时切换到输出超时输入到Feed.(就此而言,输入的初始超时可能为-1,因为我们知道在第一个输入缓冲区排队之前不会发生任何事情.)

我不知道是否有办法进一步减少延迟.