编码RTP over H264的大致过程
1.判断H264的NALU数据大小和MTU(Maximum Transmission Unit[normal 1500bytes])的关系
小于MTU:发送整个NALU
大于MTU:分片发送NALU
2.添加RTP12字节的头
3.case小于MTU:添加payload(NALU数据)
4.case大于MTU:根据NALU的第一个字节添加FUindicator,FUheader两个字节,最后添加payload(NALU数据移除第一字节)
NALU结构
NALU由NAL头(占1byte)+RBSP(Raw Byte Sequence Payload)组成。RBSP的结构为原始编码数据的后面填充1 bit'1'和n bit'0',为了字节对齐。NALU头如下格式。
+-------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-
|F|NRI| Type |
+-------------+
F:forbidden_zero_bit,1位,如果有语法冲突,则为1。当网络识别此单元存在比特错误时,可将其设为 1,以便接收方丢掉该单元。
NRI:nal_ref_idc,2位,用来指示该NALU的重要性等级。值越大,表示当前NALU越重要。
Type:5位,NALU的类型。Type值为7和8的NALU分别为序列参数集sps和图像参数集pps。参数集是一组很少改变的,为大量VCL NALU 提供解码信息的数据。其中序列参数集作用于一系列连续的编码图像,而图像参数集作用于编码视频序列中一个或多个独立的图像。如果解码器没能正确接收到这两个参数集,那么其他NALU也是无法解码的。因此它们一般在发送其它NALU之前发送,并且使用不同的信道或者更加可靠的传输协议TCP进行传输,也可以重复传输。
Type取值和含义如下表:
0 | 没有定义 | |
1 | SLICE | 不分区、非IDR图像的片 |
2 | DPA | 片分区A |
3 | DPB | 片分区B |
4 | DPC | 片分区C |
5 | IDR_SLICE | IDR图像中的片 |
6 | SEI | 补充增强信息单元 |
7 | SPS | 序列参数集 |
8 | PPS | 图像参数集 |
9 | AUD | 分界符 |
10 | END_SEQUENCE | 序列结束 |
11 | END_STREAM | 码流结束 |
12 | FILLER_DATA | 填充 |
24 | STAP-A | 单一时间的组合包 |
25 | STAP-B | 单一时间的组合包 |
26 | MTAP16 | 多个时间的组合包 |
27 | MTAP24 | 多个时间的组合包 |
28 | FU-A | 分片的单元 |
29 | FU-B | 分片的单元 |
30 | 没有定义 | |
31 | 没有定义 |
RTP打包图-case小于MTU
RTP打包图-case大于MTU
伪代码
void rtp_send_h264(char *data, int len){
if(len <= MTU){
rtp_send_h264_nalu(data, len);
}else{
rtp_send_h264_nalu_fu(data, len);
}
}
void rtp_send_h264_nalu(char *data, int len){
char buf[MTU];
// fix buf[0~11] for rtp head
memcpy(&buf[0], &rtp_head, 12);
// fix nalu data
memcpy(&buf[12], data, len);
rtp_send(buf, len + 12);
}
void rtp_send_h264_nalu_fu(char *data, int len){
char buf[MTU];
// fix buf[0~11] for rtp head
memcpy(&buf[0], &rtp_head, 12);
// fix FUindicator(1 byte)
// 指示indicator的Type=28为FU-A
// indicator结构
// +---------------+
// |0|1|2|3|4|5|6|7|
// +-+-+-+-+-+-+-+-+
// |F|NRI| Type |
// +---------------+
char fu_indicator = ((data[0] & 0b11100000) | 28);
// fix FUheader(1 byte)
// 指示header的Type为原NALU的Type
// iheader结构
// +---------------+
// |0|1|2|3|4|5|6|7|
// +-+-+-+-+-+-+-+-+
// |S|E|R| Type |
// +---------------+
// S=1,E=0表示NALU分片的开始
// S=0,E=1表示NALU分片的结束
// R为保留位
char fu_header = (data[0] & 0b00011111);
// skip raw NALU head(1 byte)
data += 1;
len -= 1;
// fill fu_indicator and fu_header
buf[12] = fu_indicator;
buf[13] = fu_header;
// start send:S=1 E=0 R=0
// middle send:S=0 E=0 R=0
// end send:S=0 E=1 R=0
while(1){
if(MTU < len){
// fix payload
memcpy(&buf[14], data, MTU);
rtp_head.mark = 0;
rtp_send(buf, MTU + 14);
len -= MTU;
data += MTU;
}else{ // it's end
// fix last payload and set rtp mark the frame end
memcpy(&buf[14], data, len);
rtp_head.mark = 1;
rtp_send(buf, len + 14);
break;
}
}
}
RTSP->SDP
v=0
o=username 1291468791 1 IN IP4
s=h264
c=IN IP4 0.0.0.0
t=0 0
a=range:npt=0-
a=sendonly
a=control:*
m=video 0 RTP/AVP 96
a=rtpmap:96 H264/90000
a=control:track0