NALU解包
程序员文章站
2022-07-07 12:28:34
...
原文链接
RTP
荷载H264
码流荷载格式定义三个不同的基本荷载结构,接收者可以通过RTP
荷载的第一个字节后5
位识别荷载结构。
FU Indicator
& FU header
1) 单个NAL
单元包:荷载中只包含一个NAL
单元。NAL
头类型域等于原始 NAL
单元类型,即在范围1
到23
之间
2) 聚合包:本类型用于聚合多个NAL
单元到单个RTP
荷载中。
本包有四种版本,单时间聚合包类型A
(STAP-A
),单时间聚合包类型B
(STAP-B
),多时间聚合包类型(MTAP
)16
位位移(MTAP16
), 多时间聚合包类型(MTAP
)24
位位移(MTAP24
)。赋予STAP-A
,STAP-B
,MTAP16
,MTAP24
的NAL
单元类型号分别是 24,25, 26, 27
3) 分片单元:用于分片单个NAL
单元到多个RTP
包。现存两个版本FU-A
,FU-B
,用NAL
单元类型28,29
标识
单个NAL单元包
分片单元(FU_A)
S: 1 bit 当设置成1,开始位指示分片NAL单元的开始。当跟随的FU荷载不是分片NAL单元荷载的开始,开始位设为0。
E: 1 bit 当设置成1, 结束位指示分片NAL单元的结束,即, 荷载的最后字节也是分片NAL单元的最后一个字节。当跟随的 FU荷载不是分片NAL单元的最后分片,结束位设置为0。
R: 1 bit 保留位必须设置为0,接收者必须忽略该位
打包时,原始的NAL头的前三位为FU indicator的前三位,原始的NAL头的后五位为FU header的后五位。
取一段码流分析如下:
80 60 01 0f 00 0e 10 00 00 0000 00 7c 85 88 82 €`..........|???
00 0a 7f ca 94 05 3b7f 3e 7f fe 14 2b 27 26 f8 ...??.;.>.?.+'&?
89 88 dd 85 62 e1 6dfc 33 01 38 1a 10 35 f2 14 ????b?m?3.8..5?.
84 6e 21 24 8f 72 62f0 51 7e 10 5f 0d 42 71 12 ?n!$?rb?Q~._.Bq.
17 65 62 a1 f1 44 dc df 4b 4a 38 aa 96 b7 dd 24 .eb??D??KJ8????$
7c是FU indicator 85是FU Header
FU indicator(0x7C)和FU Header(0x85)换成二进制如下
0111 1100 1000 0101
按顺序解析如下:
0 是F
11 是NRI
11100 是FU Type,这里是28,即FU-A
1 是S,Start,说明是分片的第一包
0 是E,End,如果是分片的最后一包,设置为1,这里不是
0 是R,Remain,保留位,总是0
00101 是NAl Type,这里是5,说明是关键帧
打包时,FUindicator的F、NRI是NAL Header中的F、NRI,Type是28;FU Header的S、E、R分别按照分片起始位置设置,Type是NAL Header中的Type。
解包时,取FU indicator的前三位和FU Header的后五位,即0110 0101(0x65)为NAL类型。
判断码流类型为H.264
else if ((inBuf[0] & 0x1f) <= 24 || (inBuf[0] & 0x1f) == 28)
{
printf("GetStreamType:STREAM_TYPE_VIDEO_H264 ---[%d][%d]\n", inBuf[0], (inBuf[0] & 0x1f));
return STREAM_TYPE_VIDEO_H264;
}
else
{
printf("RTP GetStreamType: %x %x %x %x %x %x %x %x %x %x %x %x \n",
inBuf[0], inBuf[1], inBuf[2], inBuf[3],
inBuf[4], inBuf[5], inBuf[6], inBuf[7],
inBuf[8], inBuf[9], inBuf[10], inBuf[11]);
return STREAM_TYPE_UNDEF;
}
H264加头
pack_type = srcbuf[0] & 0x1f;
if (pack_type == RTP_DEF_H264_FU_A) // FRAGMENT UNIT(一个 NALU 分成多个 rtp 包)的传输形式
{
if ((srcbuf[1] & 0x80) == 0x80)//start of fu_a
{
if (((srcbuf[0] & 0xe0) | (srcbuf[1] & 0x1f)) == 0x00)
{
// 可能是异常的打包方式:打包时没有去掉起始码
if (srcbuf[2] == 0x00 && srcbuf[3] == 0x00 && srcbuf[4] == 0x01)
{
len_tmp = 0;
}
else
{
return HIK_RTP_UNPACK_LIB_E_STM_ERR;
}
}
else
{
len_tmp = add_avc_annexb_startcode(dstbuf);
}
dstbuf[len_tmp] = (srcbuf[0] & 0xe0) | (srcbuf[1] & 0x1f);
prg->frame_len += len_tmp + 1;
dstbuf = &dstbuf[len_tmp + 1];
}
if (prg->frame_len + length > prc->outbuf_size)
{
return HIK_RTP_UNPACK_LIB_E_MEM_OVER;
}
memcpy(dstbuf, &srcbuf[2], length - 2);
prg->frame_len += (length-2);
/*if ((srcbuf[1] & 0x40) != 0x40)//end of fu_a
{
prc->outbuf_len = 0;
printf("end of fu_a\n");
return HIK_RTP_UNPACK_LIB_S_FAIL;
}
prg->unit_end = 1;
*/
if((srcbuf[1] & 0x40) == 0x40)
{
prg->unit_end = 1;
}
}
判断NALU是单片还是分包
pack_type = srcbuf[0] & 0x1f;
if (pack_type == RTP_DEF_H264_FU_A) // FRAGMENT UNIT(一个 NALU 分成多个 rtp 包)的传输形式
#define RTP_DEF_H264_FU_A 0x1c /* Fragmentation unit */
判断是否为首包
if ((srcbuf[1] & 0x80) == 0x80)//start of fu_a
判断是否为结束包
if((srcbuf[1] & 0x40) == 0x40)
{
prg->unit_end = 1;
}
解析NALU
nal_unit_type
65为I帧 67为SPS 68为PPS
// sps
case 0x7:
ret = RtpDemH264SPS(chan);
if(ret)
{
return;
}
break;
// pps
case 0x8:
if ((param->flgNal & NAL_FLG_SPS) != NAL_FLG_SPS)
{
return;
}
param->flgNal |= NAL_FLG_PPS;
if (param->marker_bit)
{
param->marker_bit = 0;
}
param->ppsLen = param->outbuf_len;
break;
// I帧
case 0x5:
ret = RtpDemH264IFrame(chan);
if(ret)
{
return;
}
break;