解析 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 }  
时间: 2024-10-10 02:07:33

解析 H.264 NAL Unit 帧类型的相关文章

H.264 NAL unit start code and NAL types

H.264 NAL unit start code and NAL types 在H.264/AVC视频编码标准中,整个系统框架被分为了两个层面:视频编码层面(VCL)和网络抽象层面(NAL).其中,前者负责有效表示视频数据的内容,而后者则负责格式化数据并提供头信息,以保证数据适合各种信道和存储介质上的传输.NAL占一个字节. NAL单元(NALU):NAL的基本语法结构,它包含一个字节的头信息和一系列来自VCL的称为原始字节序列载荷(RBSP)的字节流. 数据流是储存在介质上时: 每个NALU

H.264视频的RTP荷载格式

Status of This Memo This document specifies an Internet standards track protocol for the   Internet community, and requests discussion and suggestions for   improvements.  Please refer to the current edition of the "Internet   Official Protocol Stand

FFmpeg的H.264解码器源代码简单分析:解析器(Parser)部分

本文继续分析FFmpeg中libavcodec的H.264解码器(H.264 Decoder).上篇文章概述了FFmpeg中H.264解码器的结构:从这篇文章开始,具体研究H.264解码器的源代码.本文分析H.264解码器中解析器(Parser)部分的源代码.这部分的代码用于分割H.264的NALU,并且解析SPS.PPS.SEI等信息.解析H.264码流(对应AVCodecParser结构体中的函数)和解码H.264码流(对应AVCodec结构体中的函数)的时候都会调用该部分的代码完成相应的功

H.264中NAL、Slice与frame意思及相互关系

NAL nal_unit_type中的1(非IDR图像的编码条带).2(编码条带数据分割块A).3(编码条带数据分割块B).4(编码条带数据分割块C).5(IDR图像的编码条带)种类型 与 Slice种的三种编码模式:I_slice.P_slice.B_slice 还有frame的3种类型:I frame.P frame. B frame之间有什么映射关系么? 最后,NAL nal_unit_type中的6(SEI).7(SPS).8(PPS)属于什么帧呢? 不好意思,文档看得头晕晕的了,问题比

H.264 RTPpayload 格式------ H.264 视频 RTP 负载格式(包含AAC部分解析)

H.264 RTPpayload 格式------ H.264 视频 RTP 负载格式 1. 网络抽象层单元类型 (NALU) NALU 头由一个字节组成, 它的语法如下: +---------------+      |0|1|2|3|4|5|6|7|      +-+-+-+-+-+-+-+-+      |F|NRI|  Type   |      +---------------+ F: 1 个比特(禁止位).  forbidden_zero_bit. 在 H.264 规范中规定了这一位

H.264码流与帧结构

参考连接:http://blog.csdn.net/dxpqxb/article/details/7631304 H264以NALU(NAL unit)为单位来支持编码数据在基于分组交换技术网络中传输. NALU定义了可用于基于分组和基于比特流系统的基本格式,同时给出头信息,从而提供了视频编码和外部世界的接口. H264编码过程中的三种不同的数据形式: SODB 数据比特串-->最原始的编码数据,即VCL数据: RBSP 原始字节序列载荷-->在SODB的后面填加了结尾比特(RBSP trai

FFmpeg的H.264解码器源代码简单分析:宏块解码(Decode)部分-帧内宏块(Intra)

本文分析FFmpeg的H.264解码器的宏块解码(Decode)部分的源代码.FFmpeg的H.264解码器调用decode_slice()函数完成了解码工作.这些解码工作可以大体上分为3个步骤:熵解码,宏块解码以及环路滤波.本文分析这3个步骤中的第2个步骤.由于宏块解码部分的内容比较多,因此将本部分内容拆分成两篇文章:一篇文章记录帧内预测宏块(Intra)的宏块解码,另一篇文章记录帧间预测宏块(Inter)的宏块解码. 函数调用关系图 宏块解码(Decode)部分的源代码在整个H.264解码器

【视频编解码&#183;学习笔记】5. NAL Unit 结构分析

在上一节中通过一个小程序,可以提取NAL Unit所包含的的字节数据.H.264码流中的每一个NAL Unit的作用并不是相同的,而是根据不同的类型起不同的作用.下面将对NAL Unit中的数据进行解析. 一.NAL Unit结构 一个NAL Unit都是由一个NAL Header和一个NAL Body组成.对于基本版本的H.264标准(不考虑SVC和MVC扩展),一个NAL Header的长度固定为1,即8bit.这8bit的含义分别为: forbidden_zero_bit:每一个NAL H

基于RTP的h.264视频传输系统设计(一)

一.H.264 的层次介绍 H.264 定义三个层次,每个层次支持一组特定的编码功能,并且依照各个层次指定所指定的功能.基础层次(baselineprofile)支持I 帧和 P 帧[1]的帧内和帧间编码,支持自适应的可变长度的熵编码(CAVLC).主要层次(main profile)支持隔行扫描视频,B帧[2]的帧内编码,使用加权预测的帧内编码和使用上下文的算术编码(CABAV).扩展层次(extendedprofile)不支持隔行扫描视频和CABAC,但增加了码流之间高效的转化模式(SP 和