1、定义解码延迟
视频解码延迟是个老生常谈的问题:
- 刚开始接触FFmpeg的同学,会问:为什么avcodec_receive_frame返回AVERROR(EAGAIN)?
- 有秒开和低延迟需要的同学会问,怎么让解码器更快输出首帧?
首先要定义下描述首帧延迟的方式:
输入第N个视频packet,开始输出第1帧图像,延迟为N – 1
如果输入第1个packet,输出第1个frame,延迟为0。输入第5个packet,才输出第1个frame,延迟为4。
这种定义方式的好处是:对解码性能(解码速度)和延迟做了解耦,是解码的固有延迟。首帧的输出时间问题,耦合了解码固有延迟、解码性能、送数据速度、系统调度等多个维度因素。
必须注意,这种定义方式对FFmpeg软件解码有效,因为软件解码延迟是确定性的,但对硬件解码不一定有效:
- 如果软件解码avcodec_receive_frame返回了AVERROR(EAGAIN),你再循环调用avcodec_receive_frame 10000次也是返回AVERROR(EAGAIN),等一个小时再avcodec_receive_frame 也是返回AVERROR(EAGAIN)
- 硬件解码avcodec_receive_frame返回了AVERROR(EAGAIN),再调用一次可能返回成功。硬件解码有个最低延迟,实际延迟可能上下浮动,不固定
2、解码延迟计算方式
FFmpeg H.264/H.265软件解码延迟为:
延迟 = max_num_reorder_pics + 帧级并行线程数 – 1
对H.264来说,max_num_reorder_pics可能直接包含在SPS中,也可能是推导出来的。H.265 max_num_reorder_pics包含在VPS/SPS里。例如一个4层分层参考的H.265码流,其最大max_num_reorder_pics是4。
![视频解码延迟](https://www.nxrte.com/wp-content/themes/justnews/themer/assets/images/lazy.png)
当FFmpeg采用16线程并行解码时,延迟为延迟帧数 = 4 + 16 – 1 = 19即送入20个packet,输出第1帧图像。如果采用单线程解码,延迟为4,送入第5个packet,输出第1帧图像。
由此可见,当线程数比较多时,多线程带来的延迟占大头。FFmpeg H.264/H.265/H.266帧级并行默认线程数为min(cpu_core,16)。PC端和server端,CPU核数大于16很普遍,带来的结果是解码延迟高。当主动控制解码线程数,比如使用单线程解码时,码流的编码特性,即max_num_reorder_pics变成了决定性因素。
3、解码延迟的来源和降低延迟的手段
max_num_reorder_pics代表按照解码序填满max_num_reorder_pics个图像buffer之后,再来一帧图像,可从这max_num_reorder_pics + 1个图像中选出POC(picture order count)最小的帧作为输出。
对图像队列做重排序后再输出,这一点容易理解。很多人的疑问可能在于:首帧(IDR帧)也要参与排序吗?首帧能不能直接输出呢?
答案是:首帧也要参与排序,解码序首帧不一定是显示序首帧。例如下面一个例子:
![视频解码延迟](https://www.nxrte.com/wp-content/themes/justnews/themer/assets/images/lazy.png)
IDR帧的POC是0,第二个解码序的帧POC是-2,是显示序的首帧。参考结构如下所示:
![视频解码延迟](https://www.nxrte.com/wp-content/themes/justnews/themer/assets/images/lazy.png)
所以,IDR帧直接输出可能导致显示乱序,应当排序之后再输出。只是IDR帧延后显示的参考结构用的比较少,多数编码器的IDR帧确实为显示序第一帧。
降低码流固有延迟,即降低max_num_reorder_pics,减小重排序队列。对于H.264之后的编码格式来说,并不是IPPP才是低延迟结构,IBBB也可以实现0延迟。
帧级并行解码延迟的来源是帧级并行算法。多线程帧级并行基本原理如下:
![视频解码延迟](https://www.nxrte.com/wp-content/themes/justnews/themer/assets/images/lazy.png)
注意这是简化的示意图。当一个线程在解码第M帧时,另一个线程解码第M+1帧,多线程之间有进度(progress)同步,用于控制参考补偿。帧级并行启动时,要为N个线程准备好数据(N个packet),再从“最老”的线程开始回收frame,所以每增加一个线程,增加一帧延迟。
降低帧级并行带来的延迟,可以通过减少帧级并行线程数、采用多slice并行、WPP并行等方式实现。FFmpeg H.266解码器和dav1d解码器采用了更现代的架构设计,可以实现更低的解码延迟和加速效果。
作者:quink
来源:Fun With FFmpeg
原文:https://mp.weixin.qq.com/s/BOxX5-6gimLJOCSk8iVp0w
版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。