RTP载荷H264视频

音视频应用开发系列文章目录

编码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

发布了131 篇原创文章 · 获赞 195 · 访问量 38万+

猜你喜欢

转载自blog.csdn.net/KayChanGEEK/article/details/102488701