解析 H.264 NAL Unit 帧类型
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解析 H.264 NAL Unit 帧类型相关的知识,希望对你有一定的参考价值。
解析 H.264 NAL Unit 帧类型的代码:
1 ////////////////////////////////////////////////////////////////////////// 2 // 功能: 从 Nal Unit 数据中获取帧类型 3 // 读取字节结构体 4 typedef struct bs_t_T 5 { 6 unsigned char *pucStart; // 缓冲区首地址 7 unsigned char *pucCurrent; // 缓冲区当前的读写指针:当前字节的地址(这个会不断的 ++),每次 ++ 进入一个新的字节 8 unsigned char *pucEnd; // 缓冲区尾地址 9 int iLeft; // pucCurrent 所指字节当前还有多少"位" 可读写 10 }bs_t; 11 // nal 类型 12 enum 13 { 14 NAL_UNKNOWN = 0, 15 NAL_SLICE = 1, 16 NAL_SLICE_DPA = 2, 17 NAL_SLICE_DPB = 3, 18 NAL_SLICE_DPC = 4, 19 NAL_SLICE_IDR = 5, /* ref_idc != 0 */ 20 NAL_SEI = 6, /* ref_idc == 0 */ 21 NAL_SPS = 7, 22 NAL_PPS = 8 23 /* ref_idc == 0 for 6,9,10,11,12 */ 24 }; 25 typedef enum NalFrameType_E 26 { 27 NAL_FRAME_TYPE_NULL, 28 NAL_FRAME_TYPE_P, 29 NAL_FRAME_TYPE_B, 30 NAL_FRAME_TYPE_SP, 31 NAL_FRAME_TYPE_I, 32 NAL_FRAME_TYPE_SI, 33 }NalFrameType; 34 void bsInit(bs_t *s,void *pData,int iDataLen) 35 { 36 s->pucStart = (unsigned char *)pData; 37 s->pucCurrent = (unsigned char *)pData; 38 s->pucEnd = s->pucCurrent + iDataLen; 39 s->iLeft = 8; 40 } 41 int bsRead2(bs_t *s) 42 { 43 if(s->pucCurrent < s->pucEnd) 44 { 45 unsigned int uiResult; 46 s->iLeft--; 47 // 把要读的比特移到当前字节最右,然后与 0x01:00000001 进行逻辑与操作 48 // 因为要读的只是一个比特,这个比特不是 0 就是 1,所以与 0000 0001 按位与 49 uiResult = (*s->pucCurrent >> s->iLeft) & 0x01; 50 if(0 == s->iLeft) // 如果当前字节剩余未读位数是0,即是说当前字节全读过了 51 { 52 s->pucCurrent++; // 指针 s->p 移到下一字节 53 s->iLeft = 8; // 新字节中,未读位数当然是 8 位 54 } 55 return uiResult; 56 } 57 return 0; 58 } 59 int bsRead(bs_t *s, int iCount) 60 { 61 static int iMaskArray[33] = 62 { 63 0x00, 64 0x01, 0x03, 0x07, 0x0f, 65 0x1f, 0x3f, 0x7f, 0xff, 66 0x1ff, 0x3ff, 0x7ff, 0xfff, 67 0x1fff, 0x3fff, 0x7fff, 0xffff, 68 0x1ffff, 0x3ffff, 0x7ffff, 0xfffff, 69 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff, 70 0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff, 71 0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff 72 }; 73 /* 74 数组中的元素用二进制表示如下: 75 假设:初始为0,已写入为+,已读取为- 76 77 字节: 1 2 3 4 78 00000000 00000000 00000000 00000000 下标 79 0x00: 00000000 x[0] 80 0x01: 00000001 x[1] 81 0x03: 00000011 x[2] 82 0x07: 00000111 x[3] 83 0x0f: 00001111 x[4] 84 0x1f: 00011111 x[5] 85 0x3f: 00111111 x[6] 86 0x7f: 01111111 x[7] 87 0xff: 11111111 x[8] 1字节 88 0x1ff: 0001 11111111 x[9] 89 0x3ff: 0011 11111111 x[10] iMaskArray[s->iLeft] 90 0x7ff: 0111 11111111 x[11] 91 0xfff: 1111 11111111 x[12] 1.5字节 92 0x1fff: 00011111 11111111 x[13] 93 0x3fff: 00111111 11111111 x[14] 94 0x7fff: 01111111 11111111 x[15] 95 0xffff: 11111111 11111111 x[16] 2字节 96 0x1ffff: 0001 11111111 11111111 x[17] 97 0x3ffff: 0011 11111111 11111111 x[18] 98 0x7ffff: 0111 11111111 11111111 x[19] 99 0xfffff: 1111 11111111 11111111 x[20] 2.5字节 100 0x1fffff: 00011111 11111111 11111111 x[21] 101 0x3fffff: 00111111 11111111 11111111 x[22] 102 0x7fffff: 01111111 11111111 11111111 x[23] 103 0xffffff: 11111111 11111111 11111111 x[24] 3字节 104 0x1ffffff: 0001 11111111 11111111 11111111 x[25] 105 0x3ffffff: 0011 11111111 11111111 11111111 x[26] 106 0x7ffffff: 0111 11111111 11111111 11111111 x[27] 107 0xfffffff: 1111 11111111 11111111 11111111 x[28] 3.5字节 108 0x1fffffff:00011111 11111111 11111111 11111111 x[29] 109 0x3fffffff:00111111 11111111 11111111 11111111 x[30] 110 0x7fffffff:01111111 11111111 11111111 11111111 x[31] 111 0xffffffff:11111111 11111111 11111111 11111111 x[32] 4字节 112 */ 113 int iShr; 114 int iResult = 0; // 用来存放读取到的的结果 115 while(iCount > 0) // 要读取的比特数 116 { 117 if(s->pucCurrent >= s->pucEnd) 118 { 119 break; 120 } 121 if((iShr = s->iLeft - iCount) >= 0) // 当前字节剩余的未读位数,比要读取的位数多,或者相等 122 { 123 iResult |= (*s->pucCurrent >> iShr) & iMaskArray[iCount]; 124 /* 读取后,更新结构体里的字段值 */ 125 s->iLeft -= iCount; // 当前字节剩余的未读位数 126 if(0 == s->iLeft) // 当前字节读完了,就要开始下一个字节 127 { 128 s->pucCurrent++; // 移动指针,所以 pucCurrent 好象是以字节为步长移动指针的 129 s->iLeft = 8; // 当前字节剩余的未读位数,就是 8 比特 130 } 131 return iResult; // 可能的返回值之一为: 00000000 00000000 00000000 00000001 (4字节长) 132 } 133 else /* iShr < 0,跨字节的情况 */ 134 { 135 iResult |= (*s->pucCurrent & iMaskArray[s->iLeft]) << -iShr; // "-iShr" 相当于取了绝对值 136 // |= 和 << 都是位操作符,优先级相同,所以从左往右顺序执行 137 // iLeft 最大是 8,最小是 0,取值范围是 [0,8] 138 iCount -= s->iLeft; // 待读取的比特数 139 s->pucCurrent++; // 定位到下一个新的字节 140 s->iLeft = 8; // 对一个新字节来说,未读过的位数当然是 8,即本字节所有位都没读取过 141 } 142 } 143 return iResult; // 可能的返回值之一为: 00000000 00000000 00000000 00000001 (4字节长) 144 } 145 int bsReadUE(bs_t *s) 146 { 147 int i = 0; 148 // 条件为: 读到的当前比特 =0,指针未越界,最多只能读 32 比特 149 while(0 == bsRead2(s) && s->pucCurrent < s->pucEnd && i < 32) 150 { 151 i++; 152 } 153 return ((1 << i) - 1 + bsRead(s, i)); 154 } 155 /* 156 * 功能: 从 Nal Unit 中获取帧类型 157 * 返回值: 帧类型 158 */ 159 int GetFrameType(NALUnit *nal) 160 { 161 bs_t s; 162 int iFrameType = 0; 163 NalFrameType FrameType = NAL_FRAME_TYPE_NULL; 164 ZeroMemory(&s,sizeof(bs_t)); 165 bsInit(&s,nal->pcNaluBuf + 1, nal->uiLength - 1); 166 if(NAL_SLICE == nal->iNalUnitType || NAL_SLICE_IDR == nal->iNalUnitType) 167 { 168 /* i_first_mb */ 169 bsReadUE(&s); 170 /* picture type */ 171 iFrameType = bsReadUE(&s); 172 switch(iFrameType) 173 { 174 case 0: 175 case 5: /* P */ 176 FrameType = NAL_FRAME_TYPE_P; 177 printf("当前帧是 P 帧!\n"); 178 break; 179 case 1: 180 case 6: /* B */ 181 FrameType = NAL_FRAME_TYPE_B; 182 printf("当前帧是 B 帧!\n"); 183 break; 184 case 3: 185 case 8: /* SP */ 186 FrameType = NAL_FRAME_TYPE_SP; 187 printf("当前帧是 SP 帧!\n"); 188 break; 189 case 2: 190 case 7: /* I */ 191 FrameType = NAL_FRAME_TYPE_I; 192 printf("当前帧是 I 帧!\n"); 193 break; 194 case 4: 195 case 9: /* SI */ 196 FrameType = NAL_FRAME_TYPE_SI; 197 printf("当前帧是 SI 帧!\n"); 198 break; 199 default: 200 break; 201 } 202 } 203 return FrameType; 204 }
以上是关于解析 H.264 NAL Unit 帧类型的主要内容,如果未能解决你的问题,请参考以下文章
H.264 NAL unit start code and NAL types