当前位置: 移动技术网 > IT编程>开发语言>C/C++ > h264文件分析(纯c解析代码)

h264文件分析(纯c解析代码)

2019年03月11日  | 移动技术网IT编程  | 我要评论

搜狐焦点枣庄站,网络速度测试,红粉兵团

参考链接:1. 解析h264的sps信息 https://blog.csdn.net/lizhijian21/article/details/80982403
               2. h.264的poc计算 https://www.cnblogs.com/taigacon/p/3551001.html
               3. 视音频数据处理入门:h.264视频码流解析 https://blog.csdn.net/leixiaohua1020/article/details/50534369

代码中的注释, 有对sps,pps,slice的分析,未进行代码分析(有些可能不准确)。

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <arpa/inet.h>
  5 
  6 #define tab44 "    "
  7 #define printf_debug
  8 
  9 #define prtntf_str_len 10
 10 
 11 /************************************************************************************************************
 12 **                                        nalu header: 负责将vcl产生的比特字符串适配到各种各样的网络和多元环境中, 
 13                                                        覆盖了所有片级以上的语法级别(nalu的作用, 方便网络传输)
 14 **
 15 -------------------------------------------------------------------------------------------------------------
 16 **        字段名称               |    长度(bits)    |        有关描述
 17 -------------------------------------------------------------------------------------------------------------
 18 **        forbidden_bit          |    1             |        编码中默认值为0, 当网络识别此单元中存在比特错误时, 可将其设为1, 以便接收方丢掉该单元
 19 **        nal_reference_idc      |    2             |        0~3标识这个nalu的重要级别
 20 **        nal_unit_type          |    5             |          nalu的类型(类型1~12是h.264定义的, 类型24~31是用于h.264以外的, 
 21                                                              rtp负荷规范使用这其中的一些值来定义包聚合和分裂, 其他值为h.264保留)
 22 
 23 ** nal_unit_type:
 24     0                未使用
 25     1                未使用data partitioning, 非idr图像的slice
 26     2                使用data partitioning且为slice a
 27     3                使用data partitioning且为slice b
 28     4                使用data partitioning且为slice c
 29     5                idr图像的slice(立即刷新)
 30     6                补充增强信息(sei)
 31     7                序列参数集(sequence parameter set, sps)
 32     8                图像参数集(picture parameter set, pps)
 33     9                分界符
 34     10                序列结束
 35     11                码流结束
 36     12                填充
 37     13...23            保留
 38     24...31            未使用
 39     
 40 ** sps, pps. slice等信息就不解析了. 为了减少bits, 用了哥伦布编码(自己解析比较麻烦, 但是网上有很多).
 41 
 42 ** sps信息说明:
 43         1. 视频宽高, 帧率等信息;
 44         2. seq_parameter_set_id, 指明本序列参数集的id号, 这个id号将被picture参数集引用;
 45         3. pic_width_in_mbs_minus1, 加1指定以宏块(16*16)为单位的每个解码图像的宽度, 即width = (pic_width_in_mbs_minus1 + 1) * 16
 46         4. pic_height_in_map_units_minus1;
 47         5. pic_order_cnt_type, 视频的播放顺序序号叫做poc(picture order count), 取值0,1,2;
 48         6. time_scale, fixed_frame_rate_flag, 计算帧率(fps).
 49            视频帧率信息在sps的vui parameters syntax中, 需要根据time_scale, fixed_frame_rate_flag计算得到: fps = time_scale / num_units_in_tick.
 50            但是需要判断参数timing_info_present_flag是否存在, 若不存在表示fps在信息流中无法获取.
 51            同时还存在另外一种情况: fixed_frame_rate_flag为1时, 两个连续图像的hdr输出时间频率为单位, 获取的fps是实际的2倍.
 52 
 53 ** pps信息说明:    
 54         1. pic_parameter_set_id, 用以指定本参数集的序号, 该序号在各片的片头被引用;
 55         2. seq_parameter_set_id, 指明本图像参数集所引用的序列参数集的序号;
 56         3. 其他高深的暂时还不理解, 指明参考帧队列等.
 57         
 58 ** slice信息说明:
 59         1. slice_type, 片的类型;
 60         2. pic_parameter_set_id, 引用的图像索引;
 61         3. frame_num, 每个参考帧都有一个连续的frame_num作为它们的标识, 它指明了各图像的解码顺序. 非参考帧也有,但没有意义;
 62         4. least significant bits;
 63         5. 综合三种poc(pic_order_cnt_type), 类型2应该是最省bit的, 因为直接从frame_num获得, 但是序列方式限制最大;
 64            类型1, 只需要一定的bit量在sps标志出一些信息还在slice header中表示poc的变化, 但是比类型0要节省bit, 但是其序列并不是随意的, 要周期变化;
 65            对于类型0因为要对poc的lsb(pic_order_cnt_lsb, last bit)进行编码所以用到的bit最多, 优点是序列可以随意.
 66            ** 自我理解, 不一定准确(这边算显示顺序, 要根据sps中的pic_order_cnt_type, 为2, 意味着码流中没有b帧, frame_num即为显示顺序;
 67               为1, 依赖frame_num求解poc; 为0, 把poc的低位编进码流内, 但这只是低位, 而poc的高位picordercntmsb则要求解码器自行计数,
 68               计数方式依赖于前一编码帧(prevpicordercntmsb与prevpicordercntlsb.
 69               
 70            ** 一般的码流分析所见(未仔细证实): pic_order_cnt_type=2, 只有frame_num(无b帧);
 71               pic_order_cnt_type=1, 暂未分析到;
 72               pic_order_cnt_type=0, pic_order_cnt_lsb指示显示顺序, 一般为偶数增长(0, 2, 4, 6, 据说是什么场方式和帧方式, 场时其实是0 0 2 2 4 4).
 73               
 74            ** 编码与显示的原因: 视频编码顺序与视频的播放顺序, 并不完全相同, 视频编码时, 如果采用了b帧编码, 由于b帧很多时候都是双向预测得来的,
 75               这时会先编码b帧的后向预测图像(p帧), 然后再进行b帧编码, 因此会把视频原来的播放顺序打乱, 以新的编码顺序输出码流,
 76               而在解码断接收到码流后, 需要把顺序还原成原本的播放顺序, 以输出正确的视频. 在编解码中, 视频的播放顺序序号叫做poc(picture order count).
 77               
 78 ** 总结: 1. 码流中有很多sps(序列), 一个序列中有多个图像, 一个图像中有多个片, 一个片中有多个块;
 79          2. sps中有seq_parameter_set_id. pps中有pic_parameter_set_id, 并通过seq_parameter_set_id指明关联的序列.
 80             slice中有pic_parameter_set_id, 指明关联的图像;
 81          3. sps中可计算宽高以及帧率, pic_order_cnt_type(显示顺序的类型);
 82             slice header中可算出解码的顺序, 以及根据pic_order_cnt_type算出显示顺序.            
 83 ************************************************************************************************************/
 84 typedef enum e_h264_nalu_priority
 85 {
 86     nalu_priority_disposable = 0,
 87     nalu_priority_low         = 1,
 88     nalu_priority_high       = 2,
 89     nalu_priority_highest    = 3,
 90 } e_h264_nalu_priority;
 91 
 92 typedef enum e_h264_nalu_type
 93 {
 94     nalu_type_slice    = 1,
 95     nalu_type_dpa      = 2,
 96     nalu_type_dpb      = 3,
 97     nalu_type_dpc      = 4,
 98     nalu_type_idr      = 5,
 99     nalu_type_sei      = 6,
100     nalu_type_sps      = 7,
101     nalu_type_pps      = 8,
102     nalu_type_aud      = 9,
103     nalu_type_eoseq    = 10,
104     nalu_type_eostream = 11,
105     nalu_type_fill     = 12,
106 } e_h264_nalu_type;
107 
108 typedef struct t_h264_nalu_header
109 {
110     unsigned char forbidden_bit:1, nal_reference_idc:2, nal_unit_type:5;
111 } t_h264_nalu_header;
112 
113 typedef struct t_h264_nalu
114 {
115     int startcodelen;
116     
117     t_h264_nalu_header h264naluheader;
118     
119     unsigned int bodylen;
120     
121     unsigned char *bodydata;
122 } t_h264_nalu;
123 
124 /**********************************************************************************
125  1. h264的起始码: 0x000001(3 bytes)或0x00000001(4 bytes);
126  2. 文件流中用起始码来区分nalu.
127 ***********************************************************************************/
128 static int findstartcode3bytes(unsigned char *scdata)
129 {
130     int isfind = 0;
131 
132     if ((0==scdata[0]) && (0==scdata[1]) && (1==scdata[2]))
133     {
134         isfind = 1;
135     }
136     
137     return isfind;
138 }
139 
140 static int findstartcode4bytes(unsigned char *scdata)
141 {
142     int isfind = 0;
143 
144     if ((0==scdata[0]) && (0==scdata[1]) && (0==scdata[2]) && (1 == scdata[3]))
145     {
146         isfind = 1;
147     }
148     
149     return isfind;
150 }
151 
152 static int getnaludatalen(int startpos, int h264bitssize, unsigned char *h264bits)
153 {
154     int parsepos = 0;
155     
156     parsepos = startpos;
157     
158     while (parsepos < h264bitssize)
159     {
160         if (findstartcode3bytes(&h264bits[parsepos]))
161         {
162             return parsepos - startpos;
163         }
164         else if (findstartcode4bytes(&h264bits[parsepos]))
165         {
166             return parsepos - startpos;
167         }
168         else
169         {
170             parsepos++;
171         }
172     }
173     
174     return parsepos - startpos; // if file is end
175 }
176 
177 static void parsenaludata(const unsigned int nalulen, unsigned char* const nuludata)
178 {
179     static int nalunum = 0;
180     
181     unsigned char *data = null;
182     unsigned char prioritystr[prtntf_str_len+1] = {0};
183     unsigned char typestr[prtntf_str_len+1] = {0};
184     
185     t_h264_nalu_header h264naluheader = {0};
186     
187     data = nuludata;
188     
189     memset(&h264naluheader, 0x0, sizeof(t_h264_nalu_header));
190     
191     h264naluheader.nal_reference_idc = data[0]>>5 & 0x3;
192     h264naluheader.nal_unit_type = data[0] & 0x1f;
193     
194     nalunum++;
195     
196 #ifdef printf_debug
197     switch (h264naluheader.nal_reference_idc)
198     {
199         case nalu_priority_disposable:
200             sprintf(prioritystr, "dispos");
201             break;
202             
203         case nalu_priority_low:
204             sprintf(prioritystr, "low");
205             break;
206 
207         case nalu_priority_high:
208             sprintf(prioritystr, "high");
209             break;
210 
211         case nalu_priority_highest:
212             sprintf(prioritystr, "highest");
213             break;
214 
215         default:
216             break;
217     }
218     
219     switch (h264naluheader.nal_unit_type)
220     {
221         case nalu_type_slice:
222             sprintf(typestr,"slice");
223             break;
224             
225         case nalu_type_dpa:
226             sprintf(typestr,"dpa");
227             break;
228             
229         case nalu_type_dpb:
230             sprintf(typestr,"dpb");
231             break;
232             
233         case nalu_type_dpc:
234             sprintf(typestr,"dpc");
235             break;
236             
237         case nalu_type_idr:
238             sprintf(typestr,"idr");
239             break;
240             
241         case nalu_type_sei:
242             sprintf(typestr,"sei");
243             break;
244             
245         case nalu_type_sps:
246             sprintf(typestr,"sps");
247             break;
248             
249         case nalu_type_pps:
250             sprintf(typestr,"pps");
251             break;
252             
253         case nalu_type_aud:
254             sprintf(typestr,"aud");
255             break;
256             
257         case nalu_type_eoseq:
258             sprintf(typestr,"eoseq");
259             break;
260             
261         case nalu_type_eostream:
262             sprintf(typestr, "eostream");
263             break;
264             
265         case nalu_type_fill:
266             sprintf(typestr, "fill");
267             break;
268         
269         default:
270             break;
271     }
272     
273     printf("%5d| %7s| %6s| %8d|\n",nalunum,prioritystr,typestr,nalulen);
274 #endif
275     
276 }
277 
278 int main(int argc, char *argv[])
279 {
280     int filelen = 0;
281     int nalulen = 0;
282     int h264bitspos = 0;
283 
284     unsigned char *h264bits = null;
285     unsigned char *naludata = null;
286     
287     file *fp = null;
288     
289     if (2 != argc)
290     {
291         printf("usage: flvparse **.flv\n");
292 
293         return -1;
294     }
295 
296     fp = fopen(argv[1], "rb");
297     if (!fp)
298     {
299         printf("open file[%s] error!\n", argv[1]);
300 
301         return -1;
302     }
303     
304     fseek(fp, 0, seek_end);
305     
306     filelen = ftell(fp);
307     
308     fseek(fp, 0, seek_set);
309     
310     h264bits = (unsigned char*)malloc(filelen);
311     if (!h264bits)
312     {
313         printf("maybe file is too long, or memery is not enough!\n");
314         
315         fclose(fp);
316     
317         return -1;
318     }
319     
320     memset(h264bits, 0x0, filelen);
321     
322     if (fread(h264bits, 1, filelen, fp) < 0)
323     {
324         printf("read file data to h264bits error!\n");
325         
326         fclose(fp);
327         free(h264bits);
328         
329         h264bits = null;
330         
331         return -1;
332     }
333     
334     fclose(fp);
335     
336     printf("-----+-------- nalu table ------+\n");
337     printf(" num |    idc |  type |   len   |\n");
338     printf("-----+--------+-------+---------+\n");
339 
340     while (h264bitspos < (filelen-4))
341     {
342         if (findstartcode3bytes(&h264bits[h264bitspos]))
343         {
344             nalulen = getnaludatalen(h264bitspos+3, filelen, h264bits);
345 
346             naludata = (unsigned char*)malloc(nalulen);
347             if (naludata)
348             {
349                 memset(naludata, 0x0, nalulen);
350                 
351                 memcpy(naludata, h264bits+h264bitspos+3, nalulen);
352                 
353                 parsenaludata(nalulen, naludata);
354                 
355                 free(naludata);
356                 naludata = null;
357             }
358             
359             h264bitspos += (nalulen+3);
360         }
361         else if (findstartcode4bytes(&h264bits[h264bitspos]))
362         {
363             nalulen = getnaludatalen(h264bitspos+4, filelen, h264bits);
364 
365             naludata = (unsigned char*)malloc(nalulen);
366             if (naludata)
367             {
368                 memset(naludata, 0x0, nalulen);
369 
370                 memcpy(naludata, h264bits+h264bitspos+4, nalulen);
371 
372                 parsenaludata(nalulen, naludata);
373                 
374                 free(naludata);
375                 naludata = null;
376             }
377             
378             h264bitspos += (nalulen+4);
379         }
380         else
381         {
382             h264bitspos++;
383         }
384     }
385 
386     return 0;
387 }
view code

 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网