【网络通信 -- WebRTC】FlexFec 基本知识点总结概述
【1】FlexFec 的保护方案
假设存在一组源数据包(D × L),其序列号从 1 开始运行到 D × L
- 一维非交错行 FEC(1-D Non-interleaved Row FEC) : 一种连续的源数据包进行保护的方案,可用于恢复按行分组的源数据包中一个丢失的数据包
- 每 L 个连续的源数据包通过 "异或" 计算生成一个修复数据包,最终可生成 D 个修复数据包
- 一维交错列 FEC(1-D Interleaved Column FEC) : 一种对交错的源数据包进行操作的保护方案,可用于恢复按列分组的源数据包中一个丢失的数据包
- 对序列号间隔 L 的一组源数据包进行 "异或" 计算生成一个修复数据包,最终可生成 L 个修复数据包
- 二维 FEC(2-D FEC) : 融合行和列的保护方案
- 同时对 D × L 个源数据包进行上述两种操作以生成对应的修复数据包
说明 :在对应分组的源数据包中仅存在一个数据包丢失时,可以对丢失的数据包进行恢复处理,换言之若对应分组的源数据包的丢包数量大于 1 则恢复处理会失败
【2】FlexFec 保护失败的场景
- 一维非交错行 FEC(1-D Non-interleaved Row FEC)
在同一行的分组的源数据包中出现两个以上的丢包则恢复失败
- 一维交错列 FEC(1-D Interleaved Column FEC)
在同一列分组的源数据包中出现两个以上的丢包则恢复失败
- 二维 FEC(2-D FEC)
至少存在两个按行分组的源数据包/FEC数据包丢包两个以上,并且丢失的数据包在列维度上是对齐的,则恢复失败
至少存在两个按列分组的源数据包/FEC数据包丢包两个以上,并且丢失的数据包在行维度上是对齐的,则恢复失败
【3】FlexFec 数据包格式
【3.1】FlexFec 数据包 -- 整体构成
FlexFec 数据包的整体构成如下图,包含 RTP 数据包头 + RTP 数据包载荷(FEC 数据包头 + 修复载荷)
【3.2】FlexFec 数据包 -- FEC 数据包头
- R,F : 用于决定 FlexFec 数据包头的格式
- Padding(P) bit : 表示数据包是否包含可选的填充字节
- Extension(X) bit : 表述数据包是否包含头部扩展
- CSRC Count(CC) 4 bit : CSRC 列表中元素个数
- Marker(M) bit : 标志位,对当前载荷无效,发送者应设置为 0,接收者可以忽视
- PT recovery : 载荷类型
【3.2.1】弹性 FEC 掩码标识源数据包 -- R = 0,F = 0
- length recovery (16 bits) : 恢复数据包的长度,该长度包括除了固定 12 字节的 RTP 头之外的长度
- TS recovery (32 bits) : 恢复数据包时间戳
- CSRC_i (32 bits) [包含于 RTP 头中] : 描述该 FEC 数据包保护的源数据包的 SSRC 信息,若该 FEC 数据包保护多个 SSRC,则将会存在多个 SN base 以及 Mask
- SN base_i (16 bits) : 由该 FEC 数据包保护的对应 SSRC 的一系列源数据包的最小序列号
- Mask : 掩码,标识由该 FEC 保护的数据包,若第 j 位置 1 表示保护序列号为 SN base_i + j 的源数据包
- k : 若 k = 1 表示后面跟随其他掩码,k = 0 表示最后一个掩码
- Repair Payload : 根据固定 12 字节的 RTP 头之后的数据计算得到
【3.2.2】固定 FEC 行/列(L/D)标识源数据包 -- R = 0,F = 1
- length recovery (16 bits) : 恢复数据包的长度,该长度包括除了固定 12 字节的 RTP 头之外的长度
- TS recovery (32 bits) : 恢复数据包时间戳
- CSRC_i (32 bits) [包含于 RTP 头中] : 描述该 FEC 数据包保护的源数据包的 SSRC 信息,若该 FEC 数据包保护多个 SSRC,则将会存在多个 SN base 以及 Mask
- SN base_i (16 bits) : 由该 FEC 数据包保护的对应 SSRC 的一系列源数据包的最小序列号
- L/D 取值说明
- L = 0,D = 0 : 保留
- L > 0,D = 0 : 行 FEC 不跟随列 FEC (1D)
- 每行保护的源数据包 SN,SN + 1,...,SN + (L - 1)
- L > 0,D = 1 : 行 FEC 跟随列 FEC (2D)
- 每行保护的源数据包 SN,SN + 1,...,SN + (L - 1)
- 每列保护的源数据包 SN,SN + L,...,SN + (D - 1) * L
- 当行 FEC 数据包发送完毕再发送列 FEC 数据包
- L > 0,D > 1 : 列 FEC 数据包重复 D 次
- 每列保护的源数据包 SN,SN + L,...,SN + (D - 1) * L
注意 :上图中的描述有些不太理解,D=1时应该不会存在按列保护的源数据包吧
【4】FlexFec 的 SDP 协商
【4.1】SDP 信令交互基本要求
- 决定是否发送一个/多个 RTP 流
- 决定是否发送一个/多个修复 RTP 流
- 明确源流与修复流 SSRC 的关联关系
- 明确 SSRC 与源流的关系
- 明确修复数据包与源流的关系
- 确保使用纠错包恢复特定 SSRC 相关的源数据
【4.2】SDP 交互示例
下图为 FlexFec SDP 交互的示例
表明存在一个视频流(ssrc:1234)以及一个 FlexFec 流(ssrc:2345),源流与修复流通过不同的 ssrc 复用,修复窗口设置为 200ms
【5】FlexFec 保护与恢复流程概述
【5.1】构造修复数据包一般流程概述
-
FEC 位串的生成过程
- 对由该特定纠错包保护的一系列独立的源数据包的位串进行异或计算,获得对应的 FEC 包头以及有效载荷信息
- 对生成的 FEC 包头以及有效载荷信息的位串进行奇偶校验
-
每个源数据包的位串的组成结构
- 16 bit : RTP 数据包头的前 16 bit
- 16 bit : 无符号网络字节序,描述源数据包长度 - 12 字节(RTP 数据包头的固定长度)
- 32 bit : 描述 RTP 数据包头的时间戳
- 其他 : 固定 12 字节 RTP 数据包头之后的所有字节
-
根据 FEC 位串生成 FEC 数据包头以及载荷的过程
- FEC Header 2 bit : 根据格式选择适当的 R/F 的值,FEC 位串 2 bit 跳过
- FEC Header P recovery field : FEC 位串 1 bit
- FEC Header X recovery field : FEC 位串 1 bit
- FEC Header CC recovery field : FEC 位串 4 bit
- FEC Header M recovery field : FEC 位串 1 bit
- FEC Header PT recovery field : FEC 位串 7 bit
- FEC Header length recovery field : FEC 位串 16 bit
- FEC Header TS recovery field : FEC 位串 32 bit
- FEC Header SN base_i field : 由当前 FEC 数据包保护的对应 SSRC 的一系列源数据包中的最小序列号
- FEC Header Mask 或者 L/D field : 根据 FEC 头部相关配置变量以及 FEC 与对应 SSRC 的源数据包保护关系设置
- FEC 载荷 : FEC 位串剩余部分(包含源数据包除了 12 字节固定 RTP 数据包头的数据)
注意 : 若源数据包系列中的数据包长度不等,则短的数据包通过在末尾填充字节 0 的方式与最长的数据包补齐
【5.2】重构源数据包一般流程概述
-
关联源数据包与纠错数据包一般流程概述
- 使用掩码关联
-
- FEC 数据包头部前两个 bit 必须为 R = 0,F = 0
-
- 若掩码中第 i 个 bit 位为 1 则表示序列号为 N + i 的源数据包被该 FEC 纠错数据包保护,其中 N 为基准序列号(标识在 FEC 头部对应字段中)
-
- 使用 L/D
-
- FEC 数据包头部前两个 bit 必须为 R = 0,F = 1
-
- 给定 FEC 纠错包 P* 其对应的源数据包判定方式如下(SN 基准的序列号)
- 若 D <= 1 则源数据包对应行,SN,SN + 1,...,SN + (L - 1)
- 若 D > 1 则源数据包对应列,SN,SN + L,...,SN + (D - 1) * L
-
- 使用掩码关联
-
恢复 RTP 头部
- 给定集合 T 则恢复其中丢失的数据包头部(数据包序列号为 SEQNUM) 一般过程概述
- 1. 给定集合 T 中所有收到的每个源数据包生成 80 bit 的位串(RTP 数据包头 : 64 bit + lengthMinus12 : 16 bit 无符号网络字节序),记为 RTP_Head_Bit_String_i
- 2. 给定集合 T 中的纠错数据包取其 FEC 数据包头的前 80 bit,记为 FEC_Head_Bit_String
- 3. 计算恢复的位串,Recovery_Head_Bit_String (针对 RTP_Head_Bit_String_i 以及 FEC_Head_Bit_String 进行异或计算)
-
- 创建新的数据包 RTP_Recovery 包含 12 字节 RTP 数据包头,不含载荷
-
- RTP_Recovery 2 bit : 设置为 2,Recovery_Head_Bit_String 2 bit 跳过
-
- RTP_Recovery P : Recovery_Head_Bit_String 1 bit
-
- RTP_Recovery X : Recovery_Head_Bit_String 1 bit
-
- RTP_Recovery CC : Recovery_Head_Bit_String 4 bit
-
- RTP_Recovery M : Recovery_Head_Bit_String 1 bit
-
- RTP_Recovery PT : Recovery_Head_Bit_String 7
-
- RTP_Recovery SN : SEQNUM
-
- Y : Recovery_Head_Bit_String 16 bit,转换为主机字节序,标识 RTP_Recovery 的长度 - 12
-
- RTP_Recovery TS : Recovery_Head_Bit_String 32 bit
-
- RTP_Recovery SSRC : 丢失的 RTP 数据包的 SSRC
- 给定集合 T 则恢复其中丢失的数据包头部(数据包序列号为 SEQNUM) 一般过程概述
说明 : 标红部分的文字说明有些不清楚,结合 webrtc 源码分析总结如下
-
- 给定集合 T 中的纠错数据包取其 FEC 数据包头部前 96 bit (RTP 数据包头固定长度 12 字节) 为 FEC_Head_Bit_String
- 其中
- FEC_Head_Bit_String(bit16 : bit31) 为 FEC Header length recovery field,该数据段之后需要被 SEQNUM 覆盖
- FEC_Head_Bit_String(bit64 : bit95) 该数据段之后需要被 SSRC 覆盖
-
- 给定集合 T 中所有收到的每个源数据包,RTP_Head_Bit_String_i 为 RTP 数据包头(64 bit),lengthMinus12_i 为每个源数据包大小 - 12
-
- FEC_Head_Bit_String(bit0 : bit15) 与 RTP_Head_Bit_String_i(bit0 : bit15) 做异或运算以恢复前 16 个 bit
-
- FEC_Head_Bit_String(bit16 : bit31) 与 lengthMinus12_i 做异或运算以恢复丢失数据包的大小(减去 12 个字节),记为 Y
-
- FEC_Head_Bit_String(bit32 : bit63) 与 RTP_Head_Bit_String_i(bit32 : bit63) 做异或运算以恢复时间戳
-
恢复 RTP 载荷(此处载荷标识除了 RTP 数据包头 12 字节之后的所有数据)
- 给定集合 T 则恢复其中丢失的数据包头部(数据包序列号为 SEQNUM) 一般过程概述
-
- 分配 Y 字节大小的空间
-
- 给定集合 T 中所有收到的每个源数据包,从第 13 字节开始,长度为 Y,组成位串 RTP_PayLoad_Bit_String_i
- 注意 : 若根据源数据包生成的位串长度小于 Y 则通过在末尾补充 0 的方式将长度补充到 Y
-
- 给定集合 T 中纠错数据包,获取 FEC 数据包头之后,长度为 Y,组成位串 FEC_PayLoad_Bit_String
-
- 计算恢复的位串,Recovery_PayLoad_Bit_String (针对 RTP_PayLoad_Bit_String_i 以及 FEC_PayLoad_Bit_String 进行异或计算)
-
- 将 Recovery_PayLoad_Bit_String 设置到 RTP_Recovery 中
-
- 给定集合 T 则恢复其中丢失的数据包头部(数据包序列号为 SEQNUM) 一般过程概述
-
针对二维 FEC 保护的迭代解码算法
- 一般过程概述
-
- num_recovered_until_this_iteration = 0
-
- num_recovered_so_far = 0
-
- 尽可能根据非交错 FEC 恢复丢失的数据包并记录 num_recovered_so_far = 当前已经恢复的数据包
-
- 尽可能根据交错 FEC 恢复丢失的数据包并记录 num_recovered_so_far = 当前已经恢复的数据包
-
- 若 num_recovered_so_far > num_recovered_until_this_iteration 则 num_recovered_until_this_iteration = num_recovered_so_far,执行步骤 3 否则执行完毕
-
- 一般过程概述
参考致谢
本博客为博主的学习实践总结,并参考了众多博主的博文,在此表示感谢,博主若有不足之处,请批评指正。
【1】RFC8627