1.
NALU 头由一个字节组成, 它的语法如下:
,以00 00 00 01分割之后的下一个字节就是NALU类型,将其转为二进制数据后,
解读顺序为从左往右算,如下:
(1)第1位禁止位,值为1表示语法出错
(2)第2~3位为参考级别
(3)第4~8为是nal单元类型
例如上面00000001后有67,68以及65
其中0x67的二进制码为:
0110 0111
4-8为00111,转为十进制7,参考第二幅图:7对应序列参数集SPS
其中0x68的二进制码为:
0110 1000
4-8为01000,转为十进制8,参考第二幅图:8对应图像参数集PPS
其中0x65的二进制码为:
011 00101
4-8位为00101,转为十进制5,参考第二幅图:5对应IDR图像中的片(I帧)
所以判断是否为I帧的算法为:
(NALU类型 & 0001 1111) = 5 即 (NALU类型 & 31) = 5
比如0x65 & 31 = 5
rtp
对于 NALU 的长度小于 MTU 大小的包, 一般采用单一 NAL 单元模式.
对于一个原始的 H.264 NALU 单元常由[Start Code] [NALU Header] [NALU Payload]三部分组成, 其中 Start Code 用于标示这是一个
NALU 单元的开始, 必须是 “00 00 00 01” 或 “00 00 01”, NALU 头仅一个字节, 其后都是 NALU 单元内容.
打包时去除 “00 00 01” 或 “00 00 00 01” 的开始码, 把其他数据封包的 RTP 包即可.
如有一个 H.264 的 NALU 是这样的:
[00 00 00 01 67 42 A0 1E 23 56 0E 2F … ]
这是一个序列参数集 NAL 单元. [00 00 00 01] 是四个字节的开始码, 67 是 NALU 头, 42 开始的数据是 NALU 内容.
封装成 RTP 包将如下:
[ RTP Header ] [ 67 42 A0 1E 23 56 0E 2F ]
即只要去掉 4 个字节的开始码就可以了.
nal
FU indicator有以下格式:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
FU指示字节的类型域的28,29表示FU-A和FU-B。NRI域的值必须根据分片NAL单元的NRI域的值设置。(此处Type就是rtp分片类型) 见下表
.Type Packet Type name
---------------------------------------------------------
0 undefined -
1-23 NAL unit Single NAL unit packet per H.264
24 STAP-A Single-time aggregation packet
25 STAP-B Single-time aggregation packet
26 MTAP16 Multi-time aggregation packet
27 MTAP24 Multi-time aggregation packet
28 FU-A Fragmentation unit
29 FU-B Fragmentation unit
30-31 undefined
#define RTP_HEADLEN 12 bool UnpackRTPH264( void * bufIn, int len, void ** pBufOut, int * pOutLen) { *pOutLen = 0 ; if (len < RTP_HEADLEN) { return false ; } unsigned char * src = (unsigned char* )bufIn + RTP_HEADLEN; unsigned char head1 = * src; // 获取第一个字节 unsigned char head2 = * (src + 1 ); // 获取第二个字节 unsigned char nal = head1 & 0x1f; // 获取FUindicator的类型域, unsigned char flag = head2 & 0xe0 ; // 获取FU header的前三位,判断当前是分包的开始、中间或结束 unsigned char nal_fua = (head1 & 0xe0 ) | (head2 & 0x1f); // FU_A nal bool bFinishFrame = false ; if (nal == 0x1c ) // 判断NAL的类型为0x1c=28,说明是FU-A分片 {// fu-a if (flag== 0x80 ) // 开始 { * pBufOut = src - 3 ; * (( int * )( * pBufOut)) = 0x01000000 ; // zyf:大模式会有问题 * ((char * )( * pBufOut) + 4) = nal_fua; * pOutLen = len - RTP_HEADLEN + 3 ; } else if (flag == 0x40 ) // 结束 { * pBufOut = src + 2 ; * pOutLen = len - RTP_HEADLEN - 2 ; } else // 中间 { * pBufOut = src + 2 ; * pOutLen = len - RTP_HEADLEN - 2 ; } } else // 单包数据 { * pBufOut = src - 4 ; * (( int * )( * pBufOut)) = 0x01000000 ; // zyf:大模式会有问题 * pOutLen = len - RTP_HEADLEN + 4 ; } unsigned char * bufTmp = (unsigned char* )bufIn; if (bufTmp[ 1 ] & 0x80 ) { bFinishFrame = true ; // rtpmark } else { bFinishFrame = false ; } return bFinishFrame; }