解析 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; }