欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

NALU解包

程序员文章站 2022-07-07 12:28:34
...

原文链接 

NALU解包

 

RTP荷载H264码流荷载格式定义三个不同的基本荷载结构,接收者可以通过RTP荷载的第一个字节后5位识别荷载结构。

FU IndicatorFU headerNALU解包

1) 单个NAL单元包:荷载中只包含一个NAL单元。NAL头类型域等于原始 NAL单元类型,即在范围123之间
2) 聚合包:本类型用于聚合多个NAL单元到单个RTP荷载中。
本包有四种版本,单时间聚合包类型A (STAP-A),单时间聚合包类型B (STAP-B),多时间聚合包类型(MTAP)16位位移(MTAP16), 多时间聚合包类型(MTAP)24位位移(MTAP24)。赋予STAP-A,STAP-B,MTAP16,MTAP24NAL单元类型号分别是 24,25, 26, 27
3) 分片单元:用于分片单个NAL单元到多个RTP包。现存两个版本FU-AFU-B,用NAL单元类型28,29标识

单个NAL单元包

NALU解包

分片单元(FU_A)

NALU解包

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类型。

NALU解包

判断码流类型为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_typeNALU解包

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;

 

相关标签: 编码解码