关于RTP打包可以阅读WebRTC之RTP包
关于RTCP可以阅读WebRTC之RTCP
由于受到MTU限制,为了保证我们的数据包不会被路由器分段传输,所以我们单次发送的包大小不能超过1500字节。
想要知道这个协议存在的意义先思考三个问题:
对于这三种情况我们的做法应该都是拆帧/组帧/单帧发送,但是每家的协议可能都不一样,所以它的目的就很明显了,要不大家都只能自己玩自己的。
缩写 | 全拼 |
---|---|
DON | Decoding Order Number |
DONB | Decoding Order Number Base |
DOND | Decoding Order Number Difference |
FU | Fragmentation Unit |
IDR | Instantaneous Decoding Refresh |
MTAP | Multi-Time Aggregation Packet |
MTAP16 | MTAP with 16-bit timestamp offset |
MTAP24 | MTAP with 24-bit timestamp offset |
NAL | Network Abstraction Layer |
NALU | Network Abstraction Layer Unit |
SAR | Sample Aspect Ratio |
SEI | Supplemental Enhancement Information |
STAP | Single-Time Aggregation Packet |
STAP-A | STAP type A |
STAP-B | STAP type B |
VCL | Video Coding Layer |
VUI | Video Usability Information |
协议定义了3中打包模式(Single NAL Unit Mode/Non-Interleaved Mode/Interleaved Mode)和7种负载结构(NAL unit/STAP-A/STAP-B/MTAP16/MTAP24/FU-A/FU-B),下面表格指明了不同的打包模式可以使用那几种负载结构。我们只会简单介绍几种WebRTC中用到的负载结构。
Summary of allowed NAL unit types for each packetization mode (yes = allowed, no = disallowed, ig = ignore)
Payload Type | Packet Type | Single NAL Unit Mode | Non-Interleaved Mode | Interleaved Mode |
---|---|---|---|---|
0 | reserved | ig | ig | ig |
1-23 | NAL unit | yes | yes | no |
24 | STAP-A | no | yes | no |
25 | STAP-B | no | no | yes |
26 | MTAP16 | no | no | yes |
27 | MTAP24 | no | no | yes |
28 | FU-A | no | yes | yes |
29 | FU-B | no | no | yes |
30-31 | reserved | ig | ig | ig |
打包模式简单介绍
负载结构简单介绍
每一个NAL单元的第一个字节是NAL单元的头部,头部格式如下:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
标志位 | 说明 |
---|---|
F | forbidden_zero_bit,禁止为0 |
NRI | 不同的负载类型对应不同的值,具体看下面的表格 |
Type | NAL Unit的负载类型,具体看下面的表格 |
NAL Unit Type | Content of NAL Unit | NRI(binary) |
---|---|---|
0 | Unspecified | - |
1 | Coded slice of a non-IDR picture | 10 |
2 | Coded slice data partition A | 10 |
3 | Coded slice data partition B | 01 |
4 | Coded slice data partition C | 01 |
5 | Coded slice of an IDR picture | 11 |
6 | Supplemental enhancement information (SEI) | 00 |
7 | Sequence parameter set (SPS) | 11 |
8 | Picture parameter set (PPS) | 11 |
9 | Access unit delimiter | 00 |
10 | End of sequence | 00 |
11 | End of stream | 00 |
12 | Filler data | 00 |
13-23 | Reserved | - |
24-31 | Unspecified | - |
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|NRI| Type | |
+-+-+-+-+-+-+-+-+ |
| |
| Bytes 2..n of a single NAL unit |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Payload format for STAP-A
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: |
+-+-+-+-+-+-+-+-+ |
| |
| single-time aggregation units |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Payload format for STAP-B
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: decoding order number (DON) | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| |
| single-time aggregation units |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: NAL unit size | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| |
| NAL unit |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| RTP Header |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|STAP-A NAL HDR | NALU 1 Size | NALU 1 HDR |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NALU 1 Data |
: :
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | NALU 2 Size | NALU 2 HDR |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NALU 2 Data |
: :
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: decoding order number base | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| |
| multi-time aggregation units |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Multi-time aggregation unit for MTAP16
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: NAL unit size | DOND | TS offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| TS offset | |
+-+-+-+-+-+-+-+-+ NAL unit |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Multi-time aggregation unit for MTAP24
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: NAL unit size | DOND | TS offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| TS offset | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| NAL unit |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| RTP Header |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|MTAP16 NAL HDR | decoding order number base | NALU 1 Size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NALU 1 Size | NALU 1 DOND | NALU 1 TS offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NALU 1 HDR | NALU 1 DATA |
+-+-+-+-+-+-+-+-+ +
: :
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | NALU 2 SIZE | NALU 2 DOND |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NALU 2 TS offset | NALU 2 HDR | NALU 2 DATA |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
: :
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
FU适用于一个NAL unit过大,需要拆分为多个RTP包的情况,这种情况下多个RTP包的时间戳相同,序号不同,一帧最后一个RTP包的Mark位为1
FU-A
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FU indicator | FU header | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| |
| FU payload |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
FU-B
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FU indicator | FU header | DON |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
| |
| FU payload |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E|R| Type |
+---------------+
标志位 | 说明 |
---|---|
S | 表示一帧的开始部分 |
E | 表示一帧的结束部分 |
R | 保留位,必须是0 |
Type | NAL unit的负载类型 |
有了上面的基础以后再看WebRTC这部分代码感觉就很容易理解了,WebRTC的结构写得挺好的。
直接看这两个枚举类型就知道WebRTC支持的打包模式以及打包模式对应支持的打包结构了,注释都说得蛮清楚的。
enum H264PacketizationTypes {
kH264SingleNalu, // This packet contains a single NAL unit.
kH264StapA, // This packet contains STAP-A (single time aggregation) packets. If this packet has an associated NAL unit type, it'll be for the first such aggregated packet.
kH264FuA, // This packet contains a FU-A (fragmentation unit) packet, meaning it is a part of a frame that was too large to fit into a single packet.
};
enum class H264PacketizationMode {
NonInterleaved = 0, // Mode 1 - STAP-A, FU-A is allowed
SingleNalUnit // Mode 0 - only single NALU allowed
};
// payload_data编码后的裸数据;payload_size裸数据大小;fragmentation裸数据有几个片,偏移和大小分别是多少,有些编码器每个I帧都会带sps和pps,也就是这种样子的sps|pps|nal unit
size_t RtpPacketizerH264::SetPayloadData(const uint8_t* payload_data, size_t payload_size, const RTPFragmentationHeader* fragmentation) {
for (int i = 0; i < fragmentation->fragmentationVectorSize; ++i) {
// 依次取出NAL单元
const uint8_t* buffer = &payload_data[fragmentation->fragmentationOffset[i]];
size_t length = fragmentation->fragmentationLength[i];
bool updated_sps = false;
H264::NaluType nalu_type = H264::ParseNaluType(buffer[0]);
// 当判断是sps的时候,判断sps是否需要重写,具体目的看代码,不影响我们理解打包
if (nalu_type == H264::NaluType::kSps) {
rtc::Optional<webrtc::SpsParser::SpsState> sps;
std::unique_ptr<rtc::Buffer> output_buffer(new rtc::Buffer());
output_buffer->AppendData(buffer[0]);
webrtc::SpsVuiRewriter::ParseResult result = webrtc::SpsVuiRewriter::ParseAndRewriteSps(buffer + webrtc::H264::kNaluTypeSize, length - H264::kNaluTypeSize, &sps, output_buffer.get());
switch (result) {
case SpsVuiRewriter::ParseResult::kVuiRewritten:
input_fragments_.push_back(Fragment(output_buffer->data(), output_buffer->size()));
input_fragments_.rbegin()->tmp_buffer = std::move(output_buffer);
updated_sps = true;
break;
}
} // end for
// 用Fragment结构封装每一个NAL单元
if (!updated_sps)
input_fragments_.push_back(Fragment(buffer, length));
}
// Fragment结构转为PacketUnit结构
GeneratePackets();
return num_packets_left_;
}
void RtpPacketizerH264::GeneratePackets() {
for (size_t i = 0; i < input_fragments_.size();) {
// 选择打包模式
switch (packetization_mode_) {
case webrtc::H264PacketizationMode::SingleNalUnit:
PacketizeSingleNalu(i);
++i;
break;
case webrtc::H264PacketizationMode::NonInterleaved:
size_t fragment_len = input_fragments_[i].length;
if (i + 1 == input_fragments_.size()) {
fragment_len += last_packet_reduction_len_;
}
// 当NAL单元过大的时候选择FU-A结构,否则选择STAP-A结构
if (fragment_len > max_payload_len_) {
PacketizeFuA(i);
++i;
} else {
i = PacketizeStapA(i);
}
break;
}
}
}
// 填充RtpPacketToSend
bool RtpPacketizerH264::NextPacket(RtpPacketToSend* rtp_packet) {
if (packets_.empty()) {
return false;
}
webrtc::RtpPacketizerH264::PacketUnit packet = packets_.front();
if (packet.first_fragment && packet.last_fragment) {
// Single NAL unit packet.
size_t bytes_to_send = packet.source_fragment.length;
uint8_t* buffer = rtp_packet->AllocatePayload(bytes_to_send);
memcpy(buffer, packet.source_fragment.buffer, bytes_to_send);
packets_.pop();
input_fragments_.pop_front();
} else if (packet.aggregated) {
RTC_CHECK_EQ(H264PacketizationMode::NonInterleaved, packetization_mode_);
bool is_last_packet = num_packets_left_ == 1;
NextAggregatePacket(rtp_packet, is_last_packet);
} else {
RTC_CHECK_EQ(H264PacketizationMode::NonInterleaved, packetization_mode_);
NextFragmentPacket(rtp_packet);
}
RTC_DCHECK_LE(rtp_packet->payload_size(), max_payload_len_);
if (packets_.empty()) {
RTC_DCHECK_LE(rtp_packet->payload_size(), max_payload_len_ - last_packet_reduction_len_);
}
rtp_packet->SetMarker(packets_.empty());
--num_packets_left_;
return true;
}