1、NAL全称Network Abstract Layer, 即网络抽象层。
在H.264/AVC视频编码标准中,整个系统框架被分为了两个层面:视频编码层面(VCL)和网络抽象层面(NAL)。其中,前者负责有效表示视频数据的内容,而后者则负责格式化数据并提供头信息,以保证数据适合各种信道和存储介质上的传输。因此我们平时的每帧数据就是一个NAL单元(SPS与PPS除外)。在实际的H264数据帧中,往往帧前面带有00 00 00 01 或 00 00 01分隔符,一般来说编码器编出的首帧数据为PPS与SPS,接着为I帧……
如下图:

2、如何判断帧类型(是图像参考帧还是I、P帧等)?
NALU类型是我们判断帧类型的利器,从官方文档中得出如下图:

我们还是接着看最上面图的码流对应的数据来层层分析,以00 00 00 01分割之后的下一个字节就是NALU类型,将其转为二进制数据后,解读顺序为从左往右算,如下:
(1)第1位禁止位,值为1表示语法出错
(2)第2~3位为参考级别
(3)第4~8为是nal单元类型
例如上面00000001后有67,68以及65,41
其中0x67的二进制码为:
0110 0111
4-8为00111,转为十进制7,参考第二幅图:7对应序列参数集SPS
其中0x68的二进制码为:
0110 1000
4-8为01000,转为十进制8,参考第二幅图:8对应图像参数集PPS
其中0x65的二进制码为:
0110 0101
4-8为00101,转为十进制5,参考第二幅图:5对应IDR图像中的片(I帧)
其中0x41的二进制码为:
0100 0001
4-8为00001,转为十进制1,参考第二幅图:根据上图可知道这段码流是【不分区、非IDR图像的片】,在baseline的档次中就是P帧,因为baseline没有B帧。
所以判断是否为I帧的算法为: (NALU类型 & 0001 1111) = 5 即 NALU类型 & 31 = 5
比如0x65 & 31 = 5
3.以上区分不出B帧和P帧
我们看到常用naltype 像sps= 0x07 pps= 0x08 sei = 0x06 I/P/B= 0x01/0x05 也就是说只判断naltype = 0x01/0x05是判断不出来I/P/B帧类型的,需要到slice层去判断用到“熵编码”具体的“熵编码”内容请看:“H.264官方中文版.pdf”.
下面是扣的ffmpeg的源码判断I/P/B帧类型的实现:
- int GetFrameType(NALU_t * nal)
- {
- bs_t s;
- int frame_type = 0;
- unsigned char * OneFrameBuf_H264 = NULL ;
- if ((OneFrameBuf_H264 = (unsigned char *)calloc(nal->len + 4,sizeof(unsigned char))) == NULL)
- {
- printf("Error malloc OneFrameBuf_H264\n");
- return getchar();
- }
- if (nal->startcodeprefix_len == 3)
- {
- OneFrameBuf_H264[0] = 0x00;
- OneFrameBuf_H264[1] = 0x00;
- OneFrameBuf_H264[2] = 0x01;
- memcpy(OneFrameBuf_H264 + 3,nal->buf,nal->len);
- }
- else if (nal->startcodeprefix_len == 4)
- {
- OneFrameBuf_H264[0] = 0x00;
- OneFrameBuf_H264[1] = 0x00;
- OneFrameBuf_H264[2] = 0x00;
- OneFrameBuf_H264[3] = 0x01;
- memcpy(OneFrameBuf_H264 + 4,nal->buf,nal->len);
- }
- else
- {
- printf("H264读取错误!\n");
- }
- bs_init( &s,OneFrameBuf_H264 + nal->startcodeprefix_len + 1 ,nal->len - 1 );
-
-
- if (nal->nal_unit_type == NAL_SLICE || nal->nal_unit_type == NAL_SLICE_IDR )
- {
-
- bs_read_ue( &s );
-
- frame_type = bs_read_ue( &s );
- switch(frame_type)
- {
- case 0: case 5:
- nal->Frametype = FRAME_P;
- break;
- case 1: case 6:
- nal->Frametype = FRAME_B;
- break;
- case 3: case 8:
- nal->Frametype = FRAME_P;
- break;
- case 2: case 7:
- nal->Frametype = FRAME_I;
- I_Frame_Num ++;
- break;
- case 4: case 9:
- nal->Frametype = FRAME_I;
- break;
- }
- }
- else if (nal->nal_unit_type == NAL_SEI)
- {
- nal->Frametype = NAL_SEI;
- }
- else if(nal->nal_unit_type == NAL_SPS)
- {
- nal->Frametype = NAL_SPS;
- }
- else if(nal->nal_unit_type == NAL_PPS)
- {
- nal->Frametype = NAL_PPS;
- }
- if (OneFrameBuf_H264)
- {
- free(OneFrameBuf_H264);
- OneFrameBuf_H264 = NULL;
- }
- return 1;
- }
-
- typedef struct Tag_NALU_t
- {
- unsigned char forbidden_bit;
- unsigned char nal_reference_idc;
- unsigned char nal_unit_type;
- unsigned int startcodeprefix_len;
- unsigned int len;
- unsigned int max_size;
- unsigned char * buf;
- unsigned char Frametype;
- unsigned int lost_packets;
- } NALU_t;
-
-
- enum nal_unit_type_e
- {
- NAL_UNKNOWN = 0,
- NAL_SLICE = 1,
- NAL_SLICE_DPA = 2,
- NAL_SLICE_DPB = 3,
- NAL_SLICE_DPC = 4,
- NAL_SLICE_IDR = 5,
- NAL_SEI = 6,
- NAL_SPS = 7,
- NAL_PPS = 8
-
- };
-
-
- enum Frametype_e
- {
- FRAME_I = 15,
- FRAME_P = 16,
- FRAME_B = 17
- };
-
- #pragma once
-
- #include "Information.h"
-
-
- typedef struct Tag_bs_t
- {
- unsigned char *p_start;
- unsigned char *p;
- unsigned char *p_end;
- int i_left;
- }bs_t;
-
-
-
-
-
-
-
-
-
-
-
- void bs_init( bs_t *s, void *p_data, int i_data );
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- int bs_read( bs_t *s, int i_count );
-
-
-
-
-
-
-
-
-
-
-
- int bs_read1( bs_t *s );
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- int bs_read_ue( bs_t *s );
-
- #include "Mybs.h"
-
- void bs_init( bs_t *s, void *p_data, int i_data )
- {
- s->p_start = (unsigned char *)p_data;
- s->p = (unsigned char *)p_data;
- s->p_end = s->p + i_data;
- s->i_left = 8;
- }
-
-
- int bs_read( bs_t *s, int i_count )
- {
- static int i_mask[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};
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- int i_shr;
- int i_result = 0;
-
- while( i_count > 0 )
- {
- if( s->p >= s->p_end )
- {
- break;
- }
-
- if( ( i_shr = s->i_left - i_count ) >= 0 )
- {
-
-
-
- i_result |= ( *s->p >> i_shr )&i_mask[i_count];
-
-
-
-
- s->i_left -= i_count;
- if( s->i_left == 0 )
- {
- s->p++;
- s->i_left = 8;
- }
- return( i_result );
- }
- else
- {
-
-
-
-
- i_result |= (*s->p&i_mask[s->i_left]) << -i_shr;
-
-
-
- i_count -= s->i_left;
- s->p++;
- s->i_left = 8;
- }
- }
-
- return( i_result );
- }
-
- int bs_read1( bs_t *s )
- {
-
- if( s->p < s->p_end )
- {
- unsigned int i_result;
-
- s->i_left--;
- i_result = ( *s->p >> s->i_left )&0x01;
- if( s->i_left == 0 )
- {
- s->p++;
- s->i_left = 8;
- }
- return i_result;
- }
-
- return 0;
- }
-
- int bs_read_ue( bs_t *s )
- {
- int i = 0;
-
- while( bs_read1( s ) == 0 && s->p < s->p_end && i < 32 )
- {
- i++;
- }
- return( ( 1 << i) - 1 + bs_read( s, i ) );
- }