写点什么

ULP Fec 与 Flex FEC 概述

用户头像
糖米唐爹
关注
发布于: 2 小时前
ULP Fec与 Flex FEC 概述

一,对抗网络丢包的主要技术


在介绍 fec 之前我先根据个人经验介绍下对抗网络丢包的一些技术,如有错误欢迎指正。

  • 多路传输

同一份数据使用多个传输通道进行传输,当数据包在某个传输通道丢失,接收端可以从其他通道接收数据,有些 sd-wan 用到了该技术。

  • FEC

前向纠错,发送端根据配置的冗余度将源数据包通过异或(XOR)生成对应数量的冗余包连同源数据包一起发送出去,接收端根据冗余包和源数据包通过再通过异或(XOR)恢复丢失的包,基础原理参考这篇文章

  • ARQ

维基百科解释的非常明白,链接

  • NACK

后向纠错,当接收端检查到报文丢失,发送 rtcp nack 请求源端重新发送丢失的包。

  • ......

二,FEC 介绍


1. Fec VS Nack

webrtc 对抗网络丢包主要使用了两种技术 fec 和 nack,两者使用的场景如下

  • 当 rtt <= 20ms ,fec 功能是不启用。我们对 gcc 稍作修改,视频大概可以支持 30%的丢包。

  • 当 rtt >20ms,fec 功能开启,webrtc 维护了一个 FEC Rate 一维的冗余表,其实相当一个二维表 kFecRateTable[rate_i][loss_j], 行代表单帧码率, 列代表丢包率 loss_ratio * 256, 理论上丢包率是能支持的最大值 50%,效果我后面测试再给。

static const int kFecRateTableSize = 6450;static const unsigned char kFecRateTable[kFecRateTableSize] = {//码率区间10,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,0,   0,   0,   11,  11,  11,  11,  11,  11,  11,  11,  11,  11,  11,  11,11,  11,  11,  39,  39,  39,  39,  39,  39,  39,  39,  39,  39,  39,  39,39,  39,  39,  39,  39,  39,  39,  39,  39,  39,  39,  39,  39,  39,  39,39,  39,  39,  39,  39,  39,  39,  39,  39,  39,  39,  39,  39,  39,  39,39,  39,  39,  39,  39,  39,  51,  51,  51,  51,  51,  51,  51,  51,  51,51,  51,  51,  51,  51,  51,  51,  51,  51,  51,  51,  51,  51,  51,  51,51,  51,  51,  51,  51,  51,  51,  51,  51,  
//码率区间20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,8, 8, 8, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,30, 30, 30, 56, 56, 56, 56, 56, 56, 56, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,65, 65, 65, 65, 65, 65, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 78, 78, 78, 78, 78, 78, 78, 78, 78, ```}
复制代码


当 rtt 过大,使用 nack 势必会引入延时,如果使用 fec,通过冗余包+源数据包能恢复丢失的包,延时问题是可以解决的。俗话说天下没有免费的午餐,当带宽受限 fec 包会挤占大量的带宽,这势必会导致发送端降帧率,码率或者分辨率。


2. Ulp FEC


ULP(RFC5109)是 Uneven Level Protection 的缩写,不均等保护,根据数据包重要程度使用不同级别的保护策略,webrtc 针对 I 帧和 P 帧有两个冗余度,这个冗余度是根据上述 kFecRateTable 表查询得到。

2.1) fec 包结构


FEC Header 固定为 10 个字节,FEC Header 后面可以跟着多个 Level,每个 Level 保护着不同的数据。


2.2) FEC Header 格式


E: 保留的扩展标位,默认为 0

L:长掩码标志位。当 L 等于 0 时,ulp Level header 的 mask 长度为 16bits,当 L 设置为 1 时,ulp Level header 的 mask 长度为 48bits。

P、X、CC、PT:由所保护的 RTP 包的对应值通过 XOR 运算得到。

SN base: fec 包所保护的 RTP 包中的最小序列号。

TS recovery: 所保护的 RTP 包的 timestamps XOR 运算得到。

Length recovery:所保护的 RTP 包的长度通过 XOR 运算得到。


2.3) Ulp Level header 格式



Protection Length:所保护的数据的长度

mask:根据 FEC Header 标志位 L 取值不同而改变,当 L 等于 0 时,ulp Level header 的 mask 长度为 16bits,当 L 设置为 1 时,ulp Level header 的 mask 长度为 48bits,举个例子:当 FEC Header SN base 为 100,mask 值为 0x5A(‭01011010‬), 得知该 fec 包保护的 rtp 包的序列号有 101,103,104,106.


备注:89 版本 ulp 和 red 包是绑定到一起的,必须同时启用或者不启用,58 版本 red 包可以单独存在。

3. Flex fec

Flexible Forward Error Correction 目前还在草案阶段(RFC),相对 ULP 它有两个优势

  • 自由选择对行,还是列来生成 fec 包

  • 去除了 Ulp Level header 对 mask 的限制,ULP 最大支持 48bit

3.1) Flex fec header


R: 1 重传包,0 修复包,目前 webrtc89 版本不支持重传包

F: 1 packets indicated by offset M and N ;0: 变长 mask

  • 当 F 为 0,使用 flexible mask(可变长度 mask),结构如下

  • 当 F 为 1 ,使用 packets indicated by offset M and N(固定长度 mask),结构:


M/N 含义

If M>0, N=0, is Row FEC, and no column FEC will follow

Hence, FEC = SN, SN+1, SN+2, ... , SN+(M-1), SN+M.

If M>0, N=1, is Row FEC, and column FEC will follow.

Hence, FEC = SN, SN+1, SN+2, ... , SN+(M-1), SN+M.

and more to come

If M>0, N>1, indicates column FEC of every M packet

in a group of N packets starting at SN base.

Hence, FEC = SN+(Mx0), SN+(Mx1), ... , SN+(MxN).


P/X/CC/M/PT/Length recovery/TS recovery/SSRC_i/Base_i: 参考 上述 ulp 的介绍

SSRC count: 被保护的 SSRC 数量,0:非法,目前 webrtc89 版本只支持一个

Reserved: 保留的扩展标位,默认为 0


flex fec sdp 示例

a=rtpmap:37 flexfec-03/90000a=rtcp-fb:37 goog-remba=rtcp-fb:37 transport-cca=fmtp:37 repair-window=10000000// 源视频流 ssrc 与nack a=ssrc-group:FID 2846053279 1498637190//源视频流ssrc(2846053279),fec ssrc(3987402777),通过FEC-FR 绑定到一起。a=ssrc-group:FEC-FR 2846053279 3987402777a=ssrc:2846053279 cname:54lpx0Ngoiw3OrbZa=ssrc:2846053279 msid:1075052545 video-defaulta=ssrc:2846053279 mslabel:1075052545a=ssrc:2846053279 label:video-defaulta=ssrc:1498637190 cname:54lpx0Ngoiw3OrbZa=ssrc:1498637190 msid:1075052545 video-defaulta=ssrc:1498637190 mslabel:1075052545a=ssrc:1498637190 label:video-defaulta=ssrc:3987402777 cname:54lpx0Ngoiw3OrbZa=ssrc:3987402777 msid:1075052545 video-defaulta=ssrc:3987402777 mslabel:1075052545a=ssrc:3987402777 label:video-default
复制代码


FEC-FR: 源视频流与 FEC 修复流 ssrc 绑一起。

repair-window: “The time that spans the source packets and the corresponding repair packets. The size of the repair window is specified in microseconds.”摘自rfc 5.1.1节我的理解是

源数据包与 fec 包的时间跨度,webrtc 注释必须要有,但是它还没有实现相关的代码。见函数:

template <class T>std::vector<VideoCodec> GetPayloadTypesAndDefaultCodecs(....) {  if (.....) {    webrtc::SdpVideoFormat flexfec_format(kFlexfecCodecName);    // This value is currently arbitrarily set to 10 seconds. (The unit    // is microseconds.) This parameter MUST be present in the SDP, but    // we never use the actual value anywhere in our code however.    // TODO(brandtr): Consider honouring this value in the sender and receiver.    flexfec_format.parameters = {{kFlexfecFmtpRepairWindow, "10000000"}};    supported_formats.push_back(flexfec_format);  }
复制代码


flex fec 测试结果记录

由于目前 89 版本的 webrtc flex fec 看起来还不是很完整,不能自由选择行/列来生成 fec 包的模式,只能使用 flexible mask, 而且我测试发现在 25ms + 30%丢包效果很差,个人觉得这一块还有很长的路要走。

bool FlexfecHeaderReader::ReadFecHeader(    ForwardErrorCorrection::ReceivedFecPacket* fec_packet) const {  uint8_t* const data = fec_packet->pkt->data.MutableData();  bool r_bit = (data[0] & 0x80) != 0;  // r_bit = 1 直接丢弃该包,意味着不支持重传包。  if (r_bit) {    RTC_LOG(LS_INFO)        << "FlexFEC packet with retransmission bit set. We do not yet "           "support this, thus discarding the packet.";    return false;  }    // 当f_bit = 1 时,才能支持行/列生成fec 包,它在这里直接返回了,  // 所以是不支持的根据行/列来生成fec 包的模式。  bool f_bit = (data[0] & 0x40) != 0;  if (f_bit) {    RTC_LOG(LS_INFO)        << "FlexFEC packet with inflexible generator matrix. We do "           "not yet support this, thus discarding packet.";    return false;  }
复制代码

参考:

https://zhuanlan.zhihu.com/p/143077304

https://datatracker.ietf.org/doc/html/draft-ietf-payload-flexible-fec-scheme-03

https://datatracker.ietf.org/doc/html/rfc5109

https://blog.csdn.net/qq_16135205/article/details/89924537

https://www.cnblogs.com/x_wukong/p/8193290.html

用户头像

糖米唐爹

关注

还未添加个人签名 2020.08.12 加入

还未添加个人简介

评论

发布
暂无评论
ULP Fec与 Flex FEC 概述