webRTC 框架下的视频主动丢帧

用户头像
fumingwang
关注
发布于: 2020 年 07 月 31 日
webRTC框架下的视频主动丢帧

互联网之上的视频传输经常会遇到网络不稳定,终端性能不足等问题。将有限资源的能力最大化,提供良好的视频体验,是视频传输方案一直追逐的目标。除了不断优化软硬件的处理能力,可以采用的方法不外乎调整视频清晰度(分辨率),丢帧,调整编码码率等体验损伤性手段,来提供降级的服务。本文重点关注在丢帧的使用上,以webRTC m79基线h264编解码为参考探讨一下丢帧手段的使用。

视频传输关键路径

要了解丢帧的发生最好先了解视频传输的全路径,之前写了视频传输的关键路径,可以先行阅读https://xie.infoq.cn/article/738b8293dce86f7c8748e2629。这里借用关键路径的图放在这里,以供参考。



主动丢帧点

丢帧可以发生在关键路径的任何一个地方,网络传输丢包导致的丢帧不在讨论范围之内。

摄像头丢帧

摄像头的硬件丢帧主要跟成像的曝光时间和对焦控制有关。过暗的光线条件下摄像头需要增加曝光的时间来补偿,帧率就会降低。具有自动对焦功能的摄像头在调整焦距时也会影响摄像头输出帧率。如果要确定保持帧率,可以考虑设置设备的曝光和对焦相关参数来调整帧率。

编码前丢帧(capture&adpater)

采集帧进入编码阶段前会做一系列检查,看当前状态是否适合编码当前帧,丢帧可能有这几类情况。

  1. 非法帧(kSource)

设置摄像头输出旋转一定角度(如90/180/270度),摄像头未来得及反应前输出的未旋转的非法帧会丢弃。摄像头输出帧到达时间过晚,根据帧率的设置,每个视频帧的到来要满足时间间隔的要求,来晚了丢帧。

  1. 超出分辨率-最小码率限制

编码器可以有这样的设置:每一级的分辨率可以设置最小的码率,当前评估出可分配给编码器的带宽如果小于该码率,则可以丢帧,不在将采集的视频帧送到编码器。比如320 * 240档下,当前可用带宽<300kbps则丢帧。

  1. 编码器暂停工作或超负荷(kEncoderQueue)

编码器超负荷工作一直疲于奔命,造成queue中有累积多个帧,则把时间上比较早的帧丢弃,最新的帧进入编码。导致编码器暂停编码又会有如下原因

  • 无可用网络

  • 网络发包器累积过多待发送包(一般在网络拥塞时,评估带宽比较低时发生)

  1. 编码器的输出码率超出对其的限制(kDroppedByMediaOptimizations)

编码器工作在目标码率的限制下,编码器会一直努力尝试控制在这个码率上下。由于种种原因(比如画面突然变得复杂,突然画面运动)编码器并不能完美的控制码率,webRTC在编码器之外设计了丢帧逻辑来追踪当前的码率设置和编码器的真实输出,如果编码真实输出明显超出限制,webRTC会主动丢帧。

编码器丢帧(endcoder)

进入编码器后,编码器会做一系列的合法检查,如果检查失败会中止该帧编码,等同于丢帧。正常情况下合法性检查都会通过,同时编码器内部维护了一个流控的模块来控制码率的输出。流控丢帧的四种条件如下:

/* 4 cases for frame skipping
1:skipping when buffer size larger than target threshold and current continual skip frames is allowed
2:skipping when MaxBr buffer size + predict frame size - remaining bits in time window < 0 and current continual skip frames is allowed
3:if in last ODD_TIME_WINDOW the MAX Br is overflowed, make more strict skipping conditions
4:such as case 3 in the other window
*/

这部分的代码实现看起来比较费劲,根据自己的理解姑且总结一下。target_bitrate/max_bitrate分别设置了编码器的目标码率和最大码率,编码器尽自己的最大能力输出目标码率的码流,在视频帧真正编码前会对当前的统计量与target_bitrate/max_bitrate做算法比较,超出条件限制就会丢帧。比较规则就是上面的四条,结合代码略掉一些细节简化解释一下:

累积超出目标码率值:

累积超出最大码率值:

上面两个统计量如果超过阈值,则丢帧。比如BufferFullnessSkip每帧编码都做检查,如果超过(target_bitrate*50%),则丢帧。BufferMaxBRFullness的判断逻辑更晦涩也更严厉些,具体就不讲了。总体而言,target_bitrate/max_bitrate是对编码器的期望和限制,编码器努力工作尽力满足这个要求,实际上总是会出现超出要求的情况,丢帧就是一种码率调整的手段。注意,丢帧只针对P帧,I帧不会丢掉。

网络发包器(pacer,未丢帧)

直觉上此处会有丢帧的逻辑,webRTC实际没有丢帧逻辑。极限情况下,网络带宽突然变的比较低,网络发包器有机会聚积很多待发包。但此时已经是编码后且封装好的RTP packets,实现丢帧的逻辑不太实际。

服务器丢帧

服务器侧丢帧一般仅存在于特定的场景下,有的路径不适合增加丢帧逻辑。比如服务器端只是做了包的路由,则没办法做丢帧。如果服务器有transcoding转码或者混流功能,会把发送端的码流解码再编码,服务器端可以根据发送链路情况做丢帧逻辑。

帧缓存丢帧(Frame queue)
  • 插入缓存前丢弃:

  1. webRTC不支持B帧,参考帧id如果比当前帧大,则丢弃。

  2. 当前缓存>=kMaxFramesBuffered(800),如果当前帧是P帧则丢弃,如果是I帧则清掉过往历史并且I帧入列。

  3. 当前帧id比上个解码的帧id小,但当前帧是I帧且时间戳比较新(一般是发送端的编码器重置了),则清空历史缓存,重新从当前帧开始处理。

  4. 当前帧id比上个解码的帧id小,且不满足3的条件则丢弃当前帧。(当前帧已过期)

  5. 重复帧丢弃

注: 帧id是标识一帧画面的唯一标识,具有时间递增性。

  • 取出后丢弃:

从缓存的起始到最新的连续帧搜索,找出第下一个待解码帧。一个GOP内,没有收到I帧前所有的P帧全跳过。跳过的帧会被丢弃,不再渲染。

解码器丢帧

webrtc使用ffmpeg做为解码器,非异常情况下解码器不做主动丢帧的动作。

渲染丢帧

这部分不在webrtc的范畴内,一般不做主动丢帧,可以根据当前资源使用情况如CPU过高等增加丢帧策略。

总结

综上可以看出,丢帧主要发生在发送侧的摄像头采集、编码前适配和编码过程中,根据经验最经常发生的是网络问题、码率控制、客户端CPU过高等原因。而接收侧的丢帧主要还是应对网络环境导致的一些帧传输异常。



发布于: 2020 年 07 月 31 日 阅读数: 144
用户头像

fumingwang

关注

还未添加个人签名 2020.07.16 加入

还未添加个人简介

评论

发布
暂无评论
webRTC框架下的视频主动丢帧