解析 H.264 NAL Unit 帧类型

解析 H.264 NAL Unit 帧类型的代码:


//////////////////////////////////////////////////////////////////////////
// 功能: 从 Nal Unit 数据中获取帧类型
// 读取字节结构体
typedef struct bs_t_T
{
  unsigned char *pucStart;               // 缓冲区首地址
  unsigned char *pucCurrent;             // 缓冲区当前的读写指针:当前字节的地址(这个会不断的 ++),每次 ++ 进入一个新的字节
  unsigned char *pucEnd;                 // 缓冲区尾地址
  int iLeft;                   // pucCurrent 所指字节当前还有多少"位" 可读写
}bs_t;
// nal 类型
enum
{
  NAL_UNKNOWN     = 0,
  NAL_SLICE       = 1,
  NAL_SLICE_DPA   = 2,
  NAL_SLICE_DPB   = 3,
  NAL_SLICE_DPC   = 4,
  NAL_SLICE_IDR   = 5,    /* ref_idc != 0 */
  NAL_SEI         = 6,    /* ref_idc == 0 */
  NAL_SPS         = 7,
  NAL_PPS         = 8
  /* ref_idc == 0 for 6,9,10,11,12 */
};
typedef enum NalFrameType_E
{
  NAL_FRAME_TYPE_NULL,
  NAL_FRAME_TYPE_P,
  NAL_FRAME_TYPE_B,
  NAL_FRAME_TYPE_SP,
  NAL_FRAME_TYPE_I,
  NAL_FRAME_TYPE_SI,
}NalFrameType;
void bsInit(bs_t *s,void *pData,int iDataLen)
{
  s->pucStart = (unsigned char *)pData;
  s->pucCurrent = (unsigned char *)pData;
  s->pucEnd = s->pucCurrent + iDataLen;
  s->iLeft = 8;
}
int bsRead2(bs_t *s)
{
  if(s->pucCurrent < s->pucEnd) 
  {
    unsigned int uiResult;
    s->iLeft--;
    // 把要读的比特移到当前字节最右,然后与 0x01:00000001 进行逻辑与操作
    // 因为要读的只是一个比特,这个比特不是 0 就是 1,所以与 0000 0001 按位与
    uiResult = (*s->pucCurrent >> s->iLeft) & 0x01;
    if(0 == s->iLeft)                // 如果当前字节剩余未读位数是0,即是说当前字节全读过了
    {
      s->pucCurrent++;             // 指针 s->p 移到下一字节
      s->iLeft = 8;                // 新字节中,未读位数当然是 8 位
    }
    return uiResult;
  }
  return 0;
}
int bsRead(bs_t *s, int iCount)
{
  static int iMaskArray[33] =
  {
    0x00,
    0x01,      0x03,      0x07,      0x0f,
    0x1f,      0x3f,      0x7f,      0xff,
    0x1ff,     0x3ff,     0x7ff,     0xfff,
    0x1fff,    0x3fff,    0x7fff,    0xffff,
    0x1ffff,   0x3ffff,   0x7ffff,   0xfffff,
    0x1fffff,  0x3fffff,  0x7fffff,  0xffffff,
    0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff,
    0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff
  };
  /*
        数组中的元素用二进制表示如下:
        假设:初始为0,已写入为+,已读取为-
        
        字节:   1   2   3   4
           00000000 00000000 00000000 00000000    下标
        0x00:               00000000    x[0]
        0x01:               00000001    x[1]
        0x03:               00000011    x[2]
        0x07:               00000111    x[3]
        0x0f:               00001111    x[4]
        0x1f:               00011111    x[5]
        0x3f:               00111111    x[6]
        0x7f:               01111111    x[7]
        0xff:               11111111    x[8]  1字节
       0x1ff:            0001 11111111    x[9]
       0x3ff:            0011 11111111    x[10] iMaskArray[s->iLeft]
       0x7ff:            0111 11111111    x[11]
       0xfff:            1111 11111111    x[12] 1.5字节
      0x1fff:          00011111 11111111    x[13]
      0x3fff:          00111111 11111111    x[14]
      0x7fff:          01111111 11111111    x[15]
      0xffff:          11111111 11111111    x[16] 2字节
       0x1ffff:       0001 11111111 11111111    x[17]
       0x3ffff:       0011 11111111 11111111    x[18]
       0x7ffff:       0111 11111111 11111111    x[19]
       0xfffff:       1111 11111111 11111111    x[20] 2.5字节
      0x1fffff:     00011111 11111111 11111111    x[21]
      0x3fffff:     00111111 11111111 11111111    x[22]
      0x7fffff:     01111111 11111111 11111111    x[23]
      0xffffff:     11111111 11111111 11111111    x[24] 3字节
     0x1ffffff:    0001 11111111 11111111 11111111    x[25]
     0x3ffffff:    0011 11111111 11111111 11111111    x[26]
     0x7ffffff:    0111 11111111 11111111 11111111    x[27]
     0xfffffff:    1111 11111111 11111111 11111111    x[28] 3.5字节
    0x1fffffff:00011111 11111111 11111111 11111111    x[29]
    0x3fffffff:00111111 11111111 11111111 11111111    x[30]
    0x7fffffff:01111111 11111111 11111111 11111111    x[31]
    0xffffffff:11111111 11111111 11111111 11111111    x[32] 4字节
   */
    int iShr;
    int iResult = 0;        // 用来存放读取到的的结果
    while(iCount > 0)       // 要读取的比特数
    {
        if(s->pucCurrent >= s->pucEnd)
        {
            break;
        }
        if((iShr = s->iLeft - iCount) >= 0)    // 当前字节剩余的未读位数,比要读取的位数多,或者相等
        {
            iResult |= (*s->pucCurrent >> iShr) & iMaskArray[iCount];
      /* 读取后,更新结构体里的字段值 */
            s->iLeft -= iCount;    // 当前字节剩余的未读位数
            if(0 == s->iLeft)      // 当前字节读完了,就要开始下一个字节
            {
                s->pucCurrent++;   // 移动指针,所以 pucCurrent 好象是以字节为步长移动指针的
                s->iLeft = 8;      // 当前字节剩余的未读位数,就是 8 比特
            }
            return iResult;        // 可能的返回值之一为: 00000000 00000000 00000000 00000001 (4字节长)
        }
        else  /* iShr < 0,跨字节的情况 */
        {
      iResult |= (*s->pucCurrent & iMaskArray[s->iLeft]) << -iShr;  // "-iShr" 相当于取了绝对值
                                    // |= 和 << 都是位操作符,优先级相同,所以从左往右顺序执行
                                    // iLeft 最大是 8,最小是 0,取值范围是 [0,8]
      iCount -= s->iLeft;     // 待读取的比特数
      s->pucCurrent++;        // 定位到下一个新的字节
      s->iLeft = 8;           // 对一个新字节来说,未读过的位数当然是 8,即本字节所有位都没读取过
        }
    }
    return iResult;                 // 可能的返回值之一为: 00000000 00000000 00000000 00000001 (4字节长)
}
int bsReadUE(bs_t *s)
{
  int i = 0;
  // 条件为: 读到的当前比特 =0,指针未越界,最多只能读 32 比特
  while(0 == bsRead2(s) && s->pucCurrent < s->pucEnd && i < 32)
  {
    i++;
  }
  return ((1 << i) - 1 + bsRead(s, i));
}
/*
 * 功能: 从 Nal Unit 中获取帧类型
 * 返回值: 帧类型
*/
int GetFrameType(NALUnit *nal)
{
  bs_t s;
  int iFrameType = 0;
  NalFrameType FrameType = NAL_FRAME_TYPE_NULL;
  ZeroMemory(&s,sizeof(bs_t));
  bsInit(&s,nal->pcNaluBuf + 1, nal->uiLength - 1);
  if(NAL_SLICE == nal->iNalUnitType || NAL_SLICE_IDR == nal->iNalUnitType)
  {
    /* i_first_mb */
    bsReadUE(&s);
    /* picture type */
    iFrameType = bsReadUE(&s);
    switch(iFrameType)
    {
    case 0: 
    case 5: /* P */
      FrameType = NAL_FRAME_TYPE_P;
      printf("当前帧是 P 帧!\n");
      break;
    case 1: 
    case 6: /* B */
      FrameType = NAL_FRAME_TYPE_B;
      printf("当前帧是 B 帧!\n");
      break;
    case 3: 
    case 8: /* SP */
      FrameType = NAL_FRAME_TYPE_SP;
      printf("当前帧是 SP 帧!\n");
      break;
    case 2: 
    case 7: /* I */
      FrameType = NAL_FRAME_TYPE_I;
      printf("当前帧是 I 帧!\n");
      break;
    case 4: 
    case 9: /* SI */
      FrameType = NAL_FRAME_TYPE_SI;
      printf("当前帧是 SI 帧!\n");
      break;
    default:
      break;
    }
  }
  return FrameType;
}


你可能感兴趣的:(解析 H.264 NAL Unit 帧类型)