WebRTC 的FEC(前向纠错编码) 是其QoS的重要组成部分, 用于网络丢包的时候恢复原始数据包, 减少重传次数, 减少延时, 改善视频质量. 它是RFC 5109标准的实现. 下文, 我们将深入剖析其原理.
要理解WebRTC中的FEC, 首先需要先了解Red Packet. 所谓Red Packet, 就是Redundant Coding 产生的包. 其定义非常简单, 下面来看Block 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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |F| block PT | timestamp offset | block length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
其定义如下:
F: 1 bit, 1 表示后面跟着其他的Block Header, 0 表示这个Block Header就是最后一个.
Block PT: 7 bits, 是这个block payload的 payload type
Timestamp Offset: 14 bits, 无符号数, 是RTP 的Timestamp 到这个Data block的时间偏移
Block Lenth: 10 bits, 是Data Block 长度.
另一种Primary Data Header, 之需要1byte
|F| Block PT|
用来描述最后的一个Data Block, 其TimeStamp 就是RTP Header的Timestap.
WebRTC的Red Packet, 编码时, 用一个rtp packet 产生 1个red packet, 所以使用了 Primary Data Header来描述的
如何协商 数据和Rad Channel 的绑定关系呢?
m=audio 12345 RTP/AVP 121 0 5 a=rtpmap:121 red/8000/1
这段SDP 表示, RTP 音频 Session, 它的Payload Type 是121, 0, 5, Codec Red payload type 是121.
下面来看一个例子:
0 12 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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |V=2|P|X| CC=0 |M| PT | sequence number of primary | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | timestamp of primary encoding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | synchronization source (SSRC) identifier | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| block PT=7 | timestamp offset | block length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| block PT=5 | | +-+-+-+-+-+-+-+-+ + | | + LPC encoded redundant data (PT=7) + | (14 bytes) | + +---------------+ | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + | | + + | | + + | DVI4 encoded primary data (PT=5) | + (84 bytes, not to scale) + / / + + | | + + | | + +---------------+ | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
WebRTC中的实现:
// 生成Rad Packet RedPacket* ProducerFec::BuildRedPacket(const uint8_t* data_buffer, size_t payload_length, size_t rtp_header_length, int red_pl_type) { RedPacket* red_packet = new RedPacket( payload_length + kREDForFECHeaderLength + rtp_header_length); int pl_type = data_buffer[1] & 0x7f; red_packet->CreateHeader(data_buffer, rtp_header_length, red_pl_type, pl_type); red_packet->AssignPayload(data_buffer + rtp_header_length, payload_length); return red_packet; }
// 生成RedPacket Header void RedPacket::CreateHeader(const uint8_t* rtp_header, size_t header_length,int red_pl_type, int pl_type) { memcpy(data_, rtp_header, header_length); // Replace payload type. data_[1] &= 0x80; data_[1] += red_pl_type; // Add RED header // f-bit always 0 data_[header_length] = static_cast(pl_type); header_length_ = header_length + kREDForFECHeaderLength; }
// 填上Payload void RedPacket::AssignPayload(const uint8_t* payload, size_t length) { memcpy(data_ + header_length_, payload, length); }
Reference:
1. rfc2198 - RTP Payload for Redundant Audio Data
2. rfc5109 - RTP Payload Format for Generic Forward Error Correction