经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C 语言 » 查看文章
h264文件分析(纯c解析代码)
来源:cnblogs  作者:leaffei  时间:2019/3/11 9:05:49  对本文有异议

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

 

原文链接:http://www.cnblogs.com/leaffei/p/10507295.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号