H.264结构基本介绍

H264比特流 = Start_Code_Prefix + NALU + Start_Code_Prefix + NALU + …

由此可见,H264由一个起始码将各个NALU( Network Abstract Layer,即网络抽象层 U单元 ),其中,起始码分为3Byte和4Byte的0x000001和0x00000001。如果以Slice为一帧开始就用0x00000001,否则用0x000001。在起始码后面的第一个字节,就是NALU的类型。详细

类型值 解释
1 非IDR图像的编码条带,为P或B帧。P帧,前向预测编码帧,表示该帧与前一帧的差别(I或P),在I中找 出P某点的预测值和运动矢量,一起传送。接收端根据运动矢量从I中找出P某点的预测值并与差值相加得到样值。 1P帧在I帧后面1~2帧的位置 2压缩率比较高 3采用运动补偿传送差值和运动矢量 4可造成解码错误扩散 B帧,双向预测编码帧,通过前后帧与本帧数据叠加取得最终画面。先找出B帧中某点的预测值和两个运动矢量,传送预测值和运动矢量。接收端根据运动矢量在两个帧中找出预测值并求和。 1B帧由前面的I或者P和后面的P帧来计算 2压缩率最高,只反映帧间运动主体的变化 3不会造成解码错误扩散
2 编码条带数据分割块A
3 编码条带数据分割块B
4 编码条带数据分割块C
5 I帧,IDR图像的编码条带,俗称关键帧,也叫帧内编码帧。1全帧JPEG压缩编码 2描述了图像背景和运动主体的详情 3是P和B帧的参考帧 4一组只有一个
6 辅助增强信息 (SEI)
7 SPS(Sequence Parameter Set)序列参数集。包括编码所用的profile,level,图像的宽和高,deblock滤波器等
8 PPS(Picture Parameter Set)图像参数集。包括编码所用的profile,level,图像的宽和高,deblock滤波器等
9 访问单元分隔符
10 序列结尾
11 流结尾
12 填充数据
13 序列参数集扩展
19 未分割的辅助编码图像的编码条带

源码解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
typedef enum {
NALU_TYPE_SLICE = 1,
NALU_TYPE_DPA = 2,
NALU_TYPE_DPB = 3,
NALU_TYPE_DPC = 4,
NALU_TYPE_IDR = 5,
NALU_TYPE_SEI = 6,
NALU_TYPE_SPS = 7,
NALU_TYPE_PPS = 8,
NALU_TYPE_AUD = 9,
NALU_TYPE_EOSEQ = 10,
NALU_TYPE_EOSTREAM = 11,
NALU_TYPE_FILL = 12,
} NaluType;

typedef enum {
NALU_PRIORITY_DISPOSABLE = 0,
NALU_PRIRITY_LOW = 1,
NALU_PRIORITY_HIGH = 2,
NALU_PRIORITY_HIGHEST = 3
} NaluPriority;

typedef struct
{
int startcodeprefix_len; //slice表示4位 否则3位
unsigned len; //NALU的长度 不包括起始码
unsigned max_size; //NALU缓冲区大小
int forbidden_bit; //FALSE
int nal_reference_idc; //NALU_PRIORITY_xxxx
int nal_unit_type; //NALU_TYPE_xxxx
char *buf; //包含EBSP后面的第一个字节
} NALU_t;

FILE *h264bitstream;

int info2=0, info3=0;

static int FindStartCode2 (unsigned char *Buf){
if(Buf[0]!=0 || Buf[1]!=0 || Buf[2] !=1) return 0; //0x000001?
else return 1;
}

static int FindStartCode3 (unsigned char *Buf){
if(Buf[0]!=0 || Buf[1]!=0 || Buf[2] !=0 || Buf[3] !=1) return 0;//0x00000001?
else return 1;
}

int GetAnnexbNALU(NALU_t *nalu){
int pos = 0;
int StartCodeFound, rewind;
unsigned char *Buf;

if ((Buf = (unsigned char*)calloc (nalu->max_size , sizeof(char))) == NULL)
printf ("GetAnnexbNALU: Could not allocate Buf memory\n");

nalu->startcodeprefix_len=3;//假设是0x000001

if (3 != fread (Buf, 1, 3, h264bitstream)){
free(Buf);
return 0;
}
info2 = FindStartCode2 (Buf);
if(info2 != 1) {//不是0x000001
if(1 != fread(Buf+3, 1, 1, h264bitstream)){//多读取一个
free(Buf);
return 0;
}
info3 = FindStartCode3 (Buf);//是不是0x00000001
if (info3 != 1){
free(Buf);
return -1;
}
else {
pos = 4;
nalu->startcodeprefix_len = 4;
}
}
else{
nalu->startcodeprefix_len = 3;
pos = 3;
}
StartCodeFound = 0;
info2 = 0;
info3 = 0;

while (!StartCodeFound){
if (feof (h264bitstream)){//读到文件尾
nalu->len = (pos-1)-nalu->startcodeprefix_len;
memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len);
nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit 10000000
nalu->nal_reference_idc = nalu->buf[0] & 0x60; // 2 bit 01100000
nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;// 5 bit 00011111
free(Buf);
return pos-1;
}
Buf[pos++] = fgetc (h264bitstream);

//判断本段是否结束
info3 = FindStartCode3(&Buf[pos-4]);
if(info3 != 1)
info2 = FindStartCode2(&Buf[pos-3]);
StartCodeFound = (info2 == 1 || info3 == 1);
}

// Here, we have found another start code (and read length of startcode bytes more than we should
// have. Hence, go back in the file
rewind = (info3 == 1)? -4 : -3;

if (0 != fseek (h264bitstream, rewind, SEEK_CUR)){
free(Buf);
printf("GetAnnexbNALU: Cannot fseek in the bit stream file");
}

// Here the Start code, the complete NALU, and the next start code is in the Buf.
// The size of Buf is pos, pos+rewind are the number of bytes excluding the next
// start code, and (pos+rewind)-startcodeprefix_len is the size of the NALU excluding the start code

nalu->len = (pos+rewind)-nalu->startcodeprefix_len;
memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len);//
nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit
nalu->nal_reference_idc = nalu->buf[0] & 0x60; // 2 bit
nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;// 5 bit
free(Buf);

return (pos+rewind);
}

int h264Parser(char *url){

int data_offset=0;
int nal_num=0;
NALU_t *n=(NALU_t*)calloc(1,sizeof(NALU_t));
n->max_size=100000;
n->buf = (char*)calloc (buffersize, sizeof (char));

h264bitstream=fopen(url, "rb+");
while(!feof(h264bitstream))
{
int data_lenth;
data_lenth=GetAnnexbNALU(n);

char type_str[20]={0};
switch(n->nal_unit_type){
case NALU_TYPE_SLICE:sprintf(type_str,"SLICE");break;
case NALU_TYPE_DPA:sprintf(type_str,"DPA");break;
case NALU_TYPE_DPB:sprintf(type_str,"DPB");break;
case NALU_TYPE_DPC:sprintf(type_str,"DPC");break;
case NALU_TYPE_IDR:sprintf(type_str,"IDR");break;
case NALU_TYPE_SEI:sprintf(type_str,"SEI");break;
case NALU_TYPE_SPS:sprintf(type_str,"SPS");break;
case NALU_TYPE_PPS:sprintf(type_str,"PPS");break;
case NALU_TYPE_AUD:sprintf(type_str,"AUD");break;
case NALU_TYPE_EOSEQ:sprintf(type_str,"EOSEQ");break;
case NALU_TYPE_EOSTREAM:sprintf(type_str,"EOSTREAM");break;
case NALU_TYPE_FILL:sprintf(type_str,"FILL");break;
}
char idc_str[20]={0};
switch(n->nal_reference_idc>>5){
case NALU_PRIORITY_DISPOSABLE:sprintf(idc_str,"DISPOS");break;
case NALU_PRIRITY_LOW:sprintf(idc_str,"LOW");break;
case NALU_PRIORITY_HIGH:sprintf(idc_str,"HIGH");break;
case NALU_PRIORITY_HIGHEST:sprintf(idc_str,"HIGHEST");break;
}

fprintf(myout,"%5d| %8d| %7s| %6s| %8d|\n",nal_num,data_offset,idc_str,type_str,n->len);

data_offset=data_offset+data_lenth;

nal_num++;
}
if (n){
if (n->buf){
free(n->buf);
n->buf=NULL;
}
free (n);
}
return 0;
}