在我们的高清视频传输中,一般都需要对视频信息进行指定协议的打包,RTP是比较常用的一种视频打包负载传输方式,那么具体是怎么打包的呢?今天就简单介绍一下。
1.H265的结构
在H265中,每一个前缀码00000001后面跟随的前两个字节为NALU的语法元素,总体结构如下:
这两个字节字段分布如下,F(forbidden_zero_bit)通常为0,LayId(nuh_layer_id)通常为0,Tid(nuh_temporal_id_plus1)通常为1,所以对一段码流来说第二字节通常都是1。
1. 当收到NALU单元长度小于MTU时,直接把00000001/000001前缀去掉,加上RTPHead即可
2.当收到NALU单元长度大于MTU时,需要做FU分包处理,先把00000001前缀+2B类型去掉,然后对剩下的数据分包处理。分包的每一段数据前加上FUHead,FUHead长度是3字节。FUHead[0],FUHead[1]的结构和H265NULU类型2B结构相同,只是Type取值49即可。FUHead[2]结构如下,S置1表示分包开始,E置1分包结束,中间包都不置1,后面6bit就是H265的类型字段。本篇介绍一下H265怎么打包RTP,在H265中,每一个前缀码00000001后面跟随的前两个字节为NALU的语法元素,总体结构如下:
H265类型定义如下:
/*** Table 7-1: NAL unit type codes*/
enum HEVCNALUnitType {HEVC_NAL_TRAIL_N = 0,HEVC_NAL_TRAIL_R = 1,HEVC_NAL_TSA_N = 2,HEVC_NAL_TSA_R = 3,HEVC_NAL_STSA_N = 4,HEVC_NAL_STSA_R = 5,HEVC_NAL_RADL_N = 6,HEVC_NAL_RADL_R = 7,HEVC_NAL_RASL_N = 8,HEVC_NAL_RASL_R = 9,HEVC_NAL_VCL_N10 = 10,HEVC_NAL_VCL_R11 = 11,HEVC_NAL_VCL_N12 = 12,HEVC_NAL_VCL_R13 = 13,HEVC_NAL_VCL_N14 = 14,HEVC_NAL_VCL_R15 = 15,HEVC_NAL_BLA_W_LP = 16,HEVC_NAL_BLA_W_RADL = 17,HEVC_NAL_BLA_N_LP = 18,HEVC_NAL_IDR_W_RADL = 19,HEVC_NAL_IDR_N_LP = 20,HEVC_NAL_CRA_NUT = 21,HEVC_NAL_IRAP_VCL22 = 22,HEVC_NAL_IRAP_VCL23 = 23,HEVC_NAL_RSV_VCL24 = 24,HEVC_NAL_RSV_VCL25 = 25,HEVC_NAL_RSV_VCL26 = 26,HEVC_NAL_RSV_VCL27 = 27,HEVC_NAL_RSV_VCL28 = 28,HEVC_NAL_RSV_VCL29 = 29,HEVC_NAL_RSV_VCL30 = 30,HEVC_NAL_RSV_VCL31 = 31,HEVC_NAL_VPS = 32,HEVC_NAL_SPS = 33,HEVC_NAL_PPS = 34,HEVC_NAL_AUD = 35,HEVC_NAL_EOS_NUT = 36,HEVC_NAL_EOB_NUT = 37,HEVC_NAL_FD_NUT = 38,HEVC_NAL_SEI_PREFIX = 39,HEVC_NAL_SEI_SUFFIX = 40,};
来看一段FFmpeg的源码:
static void nal_send(AVFormatContext *ctx, const uint8_t *buf, int len, int last_packet_of_frame){RTPMuxContext *rtp_ctx &#61; ctx->priv_data;int rtp_payload_size &#61; rtp_ctx->max_payload_size - RTP_HEVC_HEADERS_SIZE;int nal_type &#61; (buf[0] >> 1) & 0x3F;//长度max_payload_size) {/* use the original NAL unit buffer and transmit it as RTP payload */ff_rtp_send_data(ctx, buf, len, last_packet_of_frame);} else {/*create the HEVC payload header and transmit the buffer as fragmentation units (FU)0 10 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5&#43;-&#43;-&#43;-&#43;-&#43;-&#43;-&#43;-&#43;-&#43;-&#43;-&#43;-&#43;-&#43;-&#43;-&#43;-&#43;-&#43;|F| Type | LayerId | TID |&#43;-------------&#43;-----------------&#43;F &#61; 0Type &#61; 49 (fragmentation unit (FU))LayerId &#61; 0TID &#61; 1*///分包先赋值RTP头三个字节//类型49表示该包是FU分包rtp_ctx->buf[0] &#61; 49 <<1;//保持原来H265类型字段第二个字段 一般都是1rtp_ctx->buf[1] &#61; 1;/*create the FU header0 1 2 3 4 5 6 7&#43;-&#43;-&#43;-&#43;-&#43;-&#43;-&#43;-&#43;-&#43;|S|E| FuType |&#43;---------------&#43;S &#61; variableE &#61; variableFuType &#61; NAL unit type*///保持原来H265类型字段类型字段 rtp_ctx->buf[2] &#61; nal_type;/* set the S bit: mark as start fragment *///设置开始标志rtp_ctx->buf[2] |&#61; 1 <<7;/* pass the original NAL header *///去掉H264头类型长度buf &#43;&#61; 2;len -&#61; 2;while (len > rtp_payload_size) {/* complete and send current RTP packet */memcpy(&rtp_ctx->buf[RTP_HEVC_HEADERS_SIZE], buf, rtp_payload_size);ff_rtp_send_data(ctx, rtp_ctx->buf, rtp_ctx->max_payload_size, 0);buf &#43;&#61; rtp_payload_size;len -&#61; rtp_payload_size;/* reset the S bit *///去掉开始结束标志rtp_ctx->buf[2] &&#61; ~(1 <<7);}/* set the E bit: mark as last fragment *///设置结束标志rtp_ctx->buf[2] |&#61; 1 <<6;/* complete and send last RTP packet */memcpy(&rtp_ctx->buf[RTP_HEVC_HEADERS_SIZE], buf, len);ff_rtp_send_data(ctx, rtp_ctx->buf, len &#43; 2, last_packet_of_frame);}}