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