WebRTC::FEC
Tags: WebRTC FEC
- WebRTC對FEC進行恢復處理的動作對RTCP的統計是透明的.
- WebRTC對FEC的冗余度計算是動態的, 會根據丟包情況和網絡帶寬估計(BWE)的結果動態調整冗余度,
內部會維護一個靜態的冗余度表. 冗余度范圍: 0-255.(255相當於100%冗余度)
ULPFEC
ULPFEC: Uneven Level Protection FEC.
將需要保護的媒體流按照重要性分成若干區域(section),
不同的區域使用不同的保護級別(levels),每個ulpfec可以攜帶多個級別的保護區域。
Packet A #####################
: :
Packet B ############### :
: :
ULP FEC Packet #1 @@@@@ :
: :
Packet C ########### :
: :
Packet D ###################################
: :
ULP FEC Packet #2 @@@@@@@@@@@@@@
: : :
:<-L0->:<--L1-->:
Figure 1: Unequal Level Protection
Payload packet # | ULP FEC packet that protects at level
| L0 L1
---------------------+---------------------------------------
A | #1 #2
B | #1 #2
C | #2 #2
D | #2 #2
FEC Packet Structure
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| RTP Header (12 octets or more) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FEC Header (10 octets) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FEC Level 0 Header |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FEC Level 0 Payload |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FEC Level 1 Header |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FEC Level 1 Payload |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Cont. |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 2: FEC Packet Structure
FEC Header
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|E|L|P|X| CC |M| PT recovery | SN base |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| TS recovery |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| length recovery |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
E(Extension):0
L(Long Mask):使用使用long mask,0:16 bits mask、1:48 bits mask
P、X、CC、M、PT (Recovery field):對主流的RTP header使用保護算法計算后得到
SN base:被保護的主流的RTP包中最小的序號,通過結合 level header中的mask來表示該fec保護的主流的包的序號
TS recovery:對主流的RTP header中的 TS使用保護算法計算后得到
Length recovery:用於驗證恢復的包的payload長度,由被保護的rtp包的長度使用保護算法計算后得到
ULP Level Header:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Protection Length | mask |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| mask cont. (present only when L = 1) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
fec level header長度為 4字節(L=0)或8字節(L=1)
Proctection Length:fec level 負載的長度
Mask:bit i = 1 則該fec保護的包的序號包括 SN base + i,mask決定了該levle 保護了主流的哪些包。
設置原則:
- 一個媒體包只能被大於0的level 保護一次,可以被level 0保護多次。(多重保護的開銷太大)
- 一個媒體包被 level p 保護,那么肯定的也會被 level p-1 保護,且 levle p 與 p-1 可能不是同一個fec包。(含有不同levle 的fec包)
- 一個fec包如果有 levle p 保護區,那么也肯定有 levle =p-1 且 level p 與 p-1 可能保護着不同的packet(變長媒體包)
FEC的兩種傳輸方式:
(1) 以分開的一路流傳輸
使用另外一路 M 行來表示 fec, 並使用 mid 和 group 來綁定 FEC 流和被保護的流
v=0
o=adam 289083124 289083124 IN IP4 host.example.com
s=ULP FEC Seminar
t=0 0
c=IN IP4 224.2.17.12/127
a=group:FEC 1 2
a=group:FEC 3 4
m=audio 30000 RTP/AVP 0
a=mid:1
m=application 30002 RTP/AVP 100
a=rtpmap:100 ulpfec/8000
a=mid:2
m=video 30004 RTP/AVP 31
a=mid:3
m=application 30004 RTP/AVP 101
c=IN IP4 224.2.17.13/127
a=rtpmap:101 ulpfec/8000
a=mid:4
(2) FEC 作為冗余編碼傳輸(Chrome)
使用RED封裝 被保護的主流和 FEC 流.
m=audio 12345 RTP/AVP 121 0 5 100
a=rtpmap:121 red/8000/1
a=rtpmap:100 ulpfec/8000
a=fmtp:121 0/5/100
平時的主流使用 RED+RTP封裝, 當產生FEC時可使用 RED + FEC封裝格式傳輸FEC流. FEC 流跟在Marker后面, 使用跟Marker一樣的TS.
FLEXFEC
flexible 主要體現在可以自由選擇對行還是列(RTP包組成的數組)來生成fec包, 也可以選擇直接重傳某個包.
也體現在保護的源RTP包個數沒有ulpfec中的 Long Mask = 1 (48bit)的限制, flexfec可以使用 f bit來表示使用變長的mask bit,還是固定長度mask
(即使是固定長度mask bit, 也是可自定義 M(column), N(raw))
1-D Parity FEC protection
非交錯 1-D 失敗的情況(連續丟包):
+---+ +---+ +===+
| 1 | X X | 4 | |R_1|
+---+ +---+ +===+
+---+ +---+ +---+ +---+ +===+
| 5 | | 6 | | 7 | | 8 | |R_2|
+---+ +---+ +---+ +---+ +===+
+---+ +---+ +---+ +---+ +===+
| 9 | | 10| | 11| | 12| |R_3|
+---+ +---+ +---+ +---+ +===+
交錯 1-D 失敗的情況(定時丟包):
+---+ +---+ +---+
| 1 | X | 3 | | 4 |
+---+ +---+ +---+
+---+ +---+ +---+
| 5 | X | 7 | | 8 |
+---+ +---+ +---+
+---+ +---+ +---+ +---+
| 9 | | 10| | 11| | 12|
+---+ +---+ +---+ +---+
+===+ +===+ +===+ +===+
|C_1| |C_2| |C_3| |C_4|
+===+ +===+ +===+ +===+
2-D Parity FEC protection:
+---+ +---+ +===+
X X | 3 | | 4 | |R_1|
+---+ +---+ +===+
+---+ +---+ +---+ +---+ +===+
| 5 | | 6 | | 7 | | 8 | |R_2|
+---+ +---+ +---+ +---+ +===+
+---+ +---+ +===+
| 9 | X X | 12| |R_3|
+---+ +---+ +===+
+===+ +===+ +===+ +===+
|C_1| |C_2| |C_3| |C_4|
+===+ +===+ +===+ +===+
2-D 失敗的情況(特定定時連續丟包)
+---+ +---+ +===+
| 1 | X X | 4 | |R_1|
+---+ +---+ +===+
+---+ +---+ +---+ +---+ +===+
| 5 | | 6 | | 7 | | 8 | |R_2|
+---+ +---+ +---+ +---+ +===+
+---+ +---+ +===+
| 9 | X X | 12| |R_3|
+---+ +---+ +===+
+===+ +===+ +===+ +===+
|C_1| |C_2| |C_3| |C_4|
+===+ +===+ +===+ +===+
FlexFEC Header
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|R|F| P|X| CC |M| PT recovery | length recovery |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| TS recovery |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRCCount | reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC_i |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SN base_i |k| Mask [0-14] |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|k| Mask [15-45] (optional) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|k| |
+-+ Mask [46-108] (optional) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... next in SSRC_i ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
R:0:repair packet、1:retransmision
F:0:flexible mask、1:offset M and N
P、X、CC、M、PT (Recovery field):對主流的RTP header使用保護算法計算后得到
Length recovery:用於驗證恢復的包的payload長度,由被保護的rtp包的長度使用保護算法計算后得到、TS recovery:對主流的RTP header中的 TS使用保護算法計算后得到
SSRCCount:被fec保護的SSRC 個數,0:非法
Reserved: 0
SSRC_i、SN base_i: 分別描述fec 所保護的包的 SSRC、SN base
如果 F = 1 則使用固定長度描述被保護的包信息
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|1|0| P|X| CC |M| PT recovery | length recovery |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| TS recovery |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRCCount | reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC_i |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SN base_i | M (columns) | N (rows) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... next in SSRC_i ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
M > 0 and N = 0 Row Fec
M > 0 and N = 1 Row Fec + colum FEC follow
M > 0 and N > 1 Column FEC
如果 R=1 、F=1 則表示使用 Retransmision
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|1|1| P|X| CC |M| PT recovery | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Retransmission |
: payload :
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
FlexFEC in Offer、Answer
multiplexed on different SSRCs
Required
rate: RTP timestamp clock rate
repair-window: 主流與fec包的時間跨度
Optional:
L: columns
D:rows
ToP: 0:1-D interleaved FEC 1:1-D non-interleaved FEC 2:2-D parity FEC 3:reserved
v=0
o=ali 1122334455 1122334466 IN IP4 fec.example.com
s=2-D Parity FEC with no in band signalling Example
t=0 0
m=video 30000 RTP/AVP 100 110
c=IN IP4 233.252.0.1/127
a=rtpmap:100 MP2T/90000
a=rtpmap:110 flexfec/90000
a=fmtp:110 L:5; D:10; ToP:2; repair-window:200000
a=ssrc:1234
a=ssrc:2345
a=ssrc-group:FEC-FR 1234 2345
ssrc-group:FEC-FR
定義在 RFC 5956 (FEC grouping semantics in the SDP)
用於描述 source 和 repair 的對應關系.
Encode ulpfec
modules/rtp_rtcp/source/ulpfec_genrator.h
class UlpfecGenerator
中定義了ulpfec的編碼實現
設置 fec相關參數:
WebRTC 中對視頻幀類型為 KeyFrame 和 DeltaFrame 分開設置 FEC 相關參數, 均使用 FecProtectionParams 結構
// Struct containing forward error correction settings.
struct FecProtectionParams {
int fec_rate;//fec的冗余度, The range is between 0 and 255,
//where 255 corresponds to 100% overhead
int max_fec_frames;//fec保護的碼流的最大幀數
//(比如視頻時max_fec_frames = 30表示在30幀時必須生成fec包)
FecMaskType fec_mask_type;//fec的mask bitflag 使用模式, 分為隨機和突發模式.
};
編碼FEC包
使用 AddRtpPacketAndGenerateFec
傳入需要保護的packet.
然后使用emphasized text
獲取生成的 fec包數量
然后使用 GetUlpfecPacketsAsRed
可獲取 RED封裝的FEC包
內部細節:
傳入的 RTP 的包數量最多為ulpfec的最大bitflag長度(48), 內部會檢測RTP的Marker位用於檢測保護的流的幀數
生成fec的條件:
1) marker = 1
2) 保護的幀數達到了 params中的max_fec_frames 或者 fec的開銷和最小媒體包數量達到要求
生成的 fec 存放在 generated_fec_packets_ , 使用 NumAvailableFecPackets 可獲取其數量
PS:
ulpfecGenerator
內部使用的是ForwarErrorCorrection
對象 fec_ 來實現 ulpfecmodules\rtp_rtcp\source\forward_error_correction.h 這里是fec更底層的實現,
fec_.EnbcodeFec 可以指定更多的編碼參數。
-
fec_factor, 也就是創建Generator時指定的 fec_rate(0-255), 255表示100%冗余度, 即多少個包就用多少個fec
-
num_important_packets, 用於指定這一幀中重要的包的個數(前面多少個包較重要),
當使用UEP(Unequal Protection)時使用, 這些包會使用level比較高的保護
-
use_unequal_protection, 是否使用 UEP(Generator中就默認不使用UEP)
-
fec_mask_type, (這里同Generato中的 mask type)
Decode ulpfec
class UlpfecReceiver 定義了 ulpfec 接收和恢復相關接口, 具體實現在子類 calss UlpfecReceiverImpl 中.
modules\rtp_rtcp\include\ulpfec_receiver.h
modules\rtp_rtcp\source\ulpfec_receiver_impl.h
基本步驟
- 創建ulpfec receiver,
UlpfecReceiver::Create()
這里可以指定一個 基類為RtpData
(defined in rtp_rtcp_defines.h)的對象, 內部定義了回調接口OnRecoveredPacket
當媒體包得到恢復時會回調上來. - 將收到的 RED 包(包括媒體包和FEC包)傳入
UlpfecReceiver::AddReceivedRedPacket
- 使用
UlpfecReceiver::ProcessReceivedFec()
進行恢復處理, 然后使用UlpfecReceiver::GetPacketCounter
可獲取 receiver 中的媒體包/fec/恢復的包 的個數.恢復的包會使用創建 Receiver 時指定的 RtpData的回調到上層
動態 FEC 冗余度
詳見 VideoCoding::protection_bitrate_calculator 用於動態計算 FEC / NACK 可使用的發送 bitrate
WebRTC 中將 NACK 和 FEC 的動態發送bitrate 處理邏輯放在一起實現, 相關類包括: ProtectionBitrateCalculator
為了動態得到FEC/NACK的發送bitrate, 跟其相關的參數一共包括如下. 這些參數有些是直接從對端信令得到, 比如丟包率, RTT等信息,有些是自己根據本端實際發送數據得到, 比如實際發送 framerate, BitRatePerFrame, PacektNumPerFrame 等等.
1.struct VCMProtectionParameters {
2. int64_t rtt;
3. float lossPr;
4. float bitRate;
5. float packetsPerFrame;
6. float packetsPerFrameKey;
7. float frameRate;
8. float keyFrameSize;
9. uint8_t fecRateDelta;
10. uint8_t fecRateKey;
11. uint16_t codecWidth;
12. uint16_t codecHeight;
13. int numLayers;
14.};
當使用 Nack 和 Fec 混合模式時有如下邏輯:
1.// Thresholds for hybrid NACK/FEC
2.// common to media optimization and the jitter buffer.
3.const int64_t kLowRttNackMs = 20;
當前 RTT < kLowRttNackMs = 20ms, 使用 NACK, 此時FEC僅用於保護 關鍵幀.
當前 RTT > kHightRttNackMs 時僅使用 FEC, 關閉 NACK ( WebRTC 暫時未啟用 )
當前 RTT > kLowRttNackMs =20ms, 混合使用 NACK 和 FEC.
當前每幀畫面平均發送的BitRate 太低則會關閉 FEC, 此時不關心當前丟包率. 相關的幾個閾值如下:
1. enum { kUpperLimitFramesFec = 6 };
2. // Thresholds values for the bytes/frame and round trip time, below which we
3. // may turn off FEC, depending on |_numLayers| and |_maxFramesFec|.
4. // Max bytes/frame for VGA, corresponds to ~140k at 25fps.
5. enum { kMaxBytesPerFrameForFec = 700 };
6. // Max bytes/frame for CIF and lower: corresponds to ~80k at 25fps.
7. enum { kMaxBytesPerFrameForFecLow = 400 };
8. // Max bytes/frame for frame size larger than VGA, ~200k at 25fps.
9. enum { kMaxBytesPerFrameForFecHigh = 1000 };
單位為 kb/s, 並且這個 每幀平均bitrate 有自己的計算方法.
關鍵幀和非關鍵幀使用分開的 FEC 冗余度, 並且根據 effective rate(bits/frame) 每幀平均bitrate 和 丟包率 一起決定查表索引,靜態表定義如下:
1.// Table for Protection factor (code rate) of delta frames, for the XOR FEC.
2.// Input is the packet loss and an effective rate (bits/frame).
3.// Output is array kFecRateTable[k], where k = rate_i*129 + loss_j;
4.// loss_j = 0,1,..128, and rate_i varies over some range.
5.
6.static const int kFecRateTableSize = 6450;
7.static const unsigned char kFecRateTable[kFecRateTableSize]
表中最大冗余度為 127, 相當於 WebRTC 最大FEC冗余為 50%, 255相當於100%冗余度.
關鍵幀的 FEC 冗余度還與 關鍵幀的每幀平均包數 跟 非關鍵幀的每幀平均包數的比值相關,
會重新計算查表索引得到自己的冗余度
由於每個參數都會經過一些數學算法計算, 但是本人無法看出這么處理的意義, 就不詳細講其處理算法了.
上述邏輯源碼位置:
modules\video_coding\protection_bitrate_calculator.cc
modules\video_coding\media_opt_util.cc
FlexFEC in WebRTC
FlexFEC 跟 UlpFEC實現有較大區別.
ulpfec 是在
VideoReceiveStream
中解析RED后判斷 PT 再 將 RTP 包添加到UlpfecReceiver 中,
處理完再回調回來(分別使用AddReceivedPacket
OnRecoveredPackt
).FlexFEC 是新建一個類似VideoReceiveStream的流: FlexfecReceiveStream. 處理完成后將恢復的包再通過VideoReceiveStream::OnRtpPacket進行處理.
創建 FlexfecReceiveStream
FlexfecReceiveStream* Call::CreateFlexfecReceiveStream(const FlexfecReceiveStream::Config& config)
receive_stream = new FlexfecReceiveStreamImpl(
&video_receiver_controller_, config, recovered_packet_receiver,
call_stats_->rtcp_rtt_stats(), module_process_thread_.get());
RecoveredPacketReceiver* recovered_packet_receiver = this;
在 webrtcvideoengine.cc 中判斷flexfec是否開啟后再創建並啟動 FlexfecReceiveStream
recovered_packet_receiver
用於傳入 FlexfecReceiveStreamImpl 中, 某個包恢復之后用於回調到 Call 這層.
創建 FlexfecReceiver
在 FlexfecReceiveStream 中會創建 FlexfecReceiver(屬於RtpRtcp模塊), 也會創建自己的 RtpRtcp 實例.
FlexfecReceiver 用於處理處理收到的 flexfec 包.
增加 Sink 到 RtpStreamReceiverController
會使用 RtpStreamReceiverController
創建 Receiver, 同時將 this( FlexfecReceiveStream::OnPacket)作為
Sink 傳入 controller, 在sink中收到 RTP 包后傳入 FlexfecReceiver 中處理.
controller 增加sink時也會傳入 SSRC, 可以收取指定ssrc的 rtp 包.
在FlexfecReceiver 中處理 flexfec 包
通過 Sink::OnPacket 傳入到 FlexfecReceiver::OnPacket后在
在 FlexfecReceiver::AddReceivedPacket 中判斷包的負載長度要求以及ssrc
flexfec負載要求最小長度為 20byte.
最后使用 FlexfecReceiver::ProcessReceivedPackets進行flexfec 解碼獲取恢復出來的包
然后使用 recovered_packet_receiver::OnRecoveredPacket 回調到上層(Call).