解析 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 帧类型的主要内容,如果未能解决你的问题,请参考以下文章

视频编解码·学习笔记5. NAL Unit 结构分析

H.264 NAL unit start code and NAL types

低延时高RTSP兼容的EasyPlayer-RTSP-win解决H.264一帧多个nal单元录像花屏问题方案

如何用C语言取出H.264ES文件里的nal信息

H264解析——切片头检测

H264 NAL解析