参考链接:1. mp4文件格式解析 https://www.cnblogs.com/ranson7zop/p/7889272.html
2. MP4文件格式分析及分割实现(附源码) https://blog.csdn.net/u013898698/article/details/77152347

1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 #define PRINTF_DEBUG 6 7 #define BOX_TYPE_FTYPE "ftyp" 8 #define BOX_TYPE_MOOV "moov" 9 #define BOX_TYPE_MVHD "mvhd" 10 #define BOX_TYPE_TRAK "trak" 11 #define BOX_TYPE_TKHD "tkhd" 12 #define BOX_TYPE_EDTS "edts" 13 #define BOX_TYPE_MDIA "mdia" 14 #define BOX_TYPE_MDHD "mdhd" 15 #define BOX_TYPE_HDLR "hdlr" 16 #define BOX_TYPE_MINF "minf" 17 #define BOX_TYPE_VMHD "vmhd" 18 #define BOX_TYPE_DINF "dinf" 19 #define BOX_TYPE_DREF "dref" 20 #define BOX_TYPE_STBL "stbl" 21 #define BOX_TYPE_STSD "stsd" 22 #define BOX_TYPE_STTS "stts" 23 #define BOX_TYPE_STSS "stss" 24 #define BOX_TYPE_STSC "stsc" 25 #define BOX_TYPE_STSZ "stsz" 26 #define BOX_TYPE_STCO "stco" 27 #define BOX_TYPE_UDTA "udta" 28 29 #define MAX_BOX_SIZE_LEN 4 30 #define MAX_BOX_TYPE_LEN 4 31 #define MAX_HANDLER_TYPE_LEN 4 32 #define MAX_FTYP_BRABDS_LEN 4 33 #define MAX_FTYP_BRABDS_NUM 4 34 #define MAX_STTS_ENTRY_NUM 8 35 #define MAX_STSS_ENTRY_NUM 8 36 #define MAX_STSC_ENTRY_NUM 100 37 #define MAX_STSZ_ENTRY_NUM 100 /* now parse 100 frame */ 38 #define MAX_STCO_ENTRY_NUM 100 39 #define MAX_MVHD_RESERVED_LEN 10 40 #define MAX_PRE_DEFINE_LEN 24 41 #define MAX_MATRIX_LEN 36 42 #define MAX_HDLR_NAME_LEN 100 43 44 45 typedef struct t_box_header 46 { 47 int boxSize; 48 49 unsigned char boxType[MAX_BOX_TYPE_LEN+1]; 50 51 long largeBoxSize; /* if boxSize=1 use, if boxSize=0, end of file */ 52 } T_BOX_HEADER; 53 54 /******************************************************************************************** 55 ** File Type Box (ftyp): file type, 表明文件类型 56 ** 57 -------------------------------------------------------------------------------------------- 58 ** 字段名称 | 长度(bytes) | 有关描述 59 -------------------------------------------------------------------------------------------- 60 ** boxsize | 4 | box的长度 61 ** boxtype | 4 | box的类型 62 ** major_brand | 4 | 63 ** minor_version | 4 | 版本号 64 ** compatible_brands | 4 * N | 本文件遵从的多种协议(ismo, iso2, mp41) 65 ********************************************************************************************/ 66 typedef struct t_box4ftyp_brand 67 { 68 unsigned char brands[MAX_FTYP_BRABDS_LEN+1]; 69 } T_BOX4FTYP_BRAN; 70 71 typedef struct t_box4ftyp 72 { 73 unsigned char major_brand[MAX_FTYP_BRABDS_LEN+1]; 74 75 int minor_version; 76 77 T_BOX4FTYP_BRAN compatible_brands[MAX_FTYP_BRABDS_NUM]; 78 } T_BOX4FTYP; 79 80 /************************************************************************************************************ 81 ** mvhd: movie header, 文件的总体信息: 时长, 创建时间等 82 ** 83 -------------------------------------------------------------------------------------------- 84 ** 字段名称 | 长度(bytes) | 有关描述 85 -------------------------------------------------------------------------------------------- 86 ** boxsize | 4 | box的长度 87 ** boxtype | 4 | box的类型 88 ** version | 1 | box版本,0或1,一般为0(以下字节数均按version = 0) 89 ** flags | 3 | 90 ** creation time | 4 | 创建时间(相对于UTC时间1904 - 01 - 01零点的秒数) 91 ** modification time | 4 | 修改时间 92 ** time scale | 4 | 文件媒体在1秒时间内的刻度值,可以理解为1秒长度的时间单元数 93 ** duration | 4 | 该track的时间长度,用duration和time scale值可以计算track时长 94 ** rate | 4 | 推荐播放速率,高16位和低16位分别为小数点整数部分和小数部分,即[16.16] 格式.该值为1.0 (0x00010000) 95 ** volume | 2 | 与rate类似,[8.8] 格式,1.0(0x0100)表示最大音量 96 ** reserved | 10 | 保留位 97 ** matrix | 36 | 视频变换矩阵 98 ** pre-defined | 24 | 99 ** next track id | 4 | 下一个track使用的id号 100 ** 101 if (version==1) 102 { 103 unsigned int(64) creation_time; 104 unsigned int(64) modification_time; 105 unsigned int(32) timescale; 106 unsigned int(64) duration; 107 } 108 else 109 { 110 unsigned int(32) creation_time; 111 unsigned int(32) modification_time; 112 unsigned int(32) timescale; 113 unsigned int(32) duration; 114 } 115 ************************************************************************************************************/ 116 typedef struct t_box4mvhd 117 { 118 int creation_time; 119 int modification_time; 120 int timescale; 121 int duration; 122 float rate; 123 float volume; 124 int next_track_id; 125 } T_BOX4MVHD; 126 127 /************************************************************************************************************ 128 ** tkhd: track header, track的总体信息, 如时长, 宽高等 129 ** 130 ------------------------------------------------------------------------------------------------------------- 131 ** 字段名称 | 长度(bytes) | 有关描述 132 ------------------------------------------------------------------------------------------------------------- 133 ** boxsize | 4 | box的长度 134 ** boxtype | 4 | box的类型 135 ** version | 1 | box版本,0或1,一般为0。(以下字节数均按version = 0) 136 ** flags | 3 | 按位或操作结果值,预定义如下; 137 0x000001 track_enabled,否则该track不被播放; 138 0x000002 track_in_movie,表示该track在播放中被引用; 139 0x000004 track_in_preview,表示该track在预览时被引用。 140 一般该值为7,如果一个媒体所有track均未设置track_in_movie和track_in_preview,将被理解为所有track均设置了这两项; 141 对于hint track,该值为0; 142 ** creation_time | 4 | 创建时间(相对于UTC时间1904 - 01 - 01零点的秒数) 143 ** modification_time | 4 | 修改时间 144 ** track_id | 4 | id号 不能重复且不能为0 145 ** reserved | 4 | 保留位 146 ** duration | 4 | track的时间长度 147 ** reserved | 8 | 保留位 148 ** layer | 2 | 视频层,默认为0,值小的在上层 149 ** alternate_group | 2 | track分组信息,默认为0表示该track未与其他track有群组关系 150 ** volume | 2 | [8.8] 格式,如果为音频track,1.0(0x0100)表示最大音量;否则为0 151 ** reserved | 2 | 保留位 152 ** matrix | 36 | 视频变换矩阵 153 ** width | 4 | 宽 154 ** height | 4 | 高,均为[16.16] 格式值 与sample描述中的实际画面大小比值,用于播放时的展示宽高 155 if (version==1) 156 { 157 unsigned int(64) creation_time; 158 unsigned int(64) modification_time; 159 unsigned int(32) track_ID; 160 const unsigned int(32) reserved = 0; 161 unsigned int(64) duration; 162 } 163 else 164 { 165 unsigned int(32) creation_time; 166 unsigned int(32) modification_time; 167 unsigned int(32) track_ID; 168 const unsigned int(32) reserved = 0; 169 unsigned int(32) duration; 170 } 171 ************************************************************************************************************/ 172 typedef struct t_box4tkhd 173 { 174 int flags; 175 int creation_time; 176 int modification_time; 177 int track_id; 178 int duration; 179 int layer; 180 int alternate_group; 181 float volume; 182 float width; 183 float height; 184 } T_BOX4TKHD; 185 186 /************************************************************************************************************ 187 ** mdhd: 包含了了该track的总体信息, mdhd和tkhd 内容大致都是一样的. 188 ** 189 ------------------------------------------------------------------------------------------------------------- 190 ** 字段名称 | 长度(bytes) | 有关描述 191 ------------------------------------------------------------------------------------------------------------- 192 ** boxsize | 4 | box的长度 193 ** boxtype | 4 | box的类型 194 ** version | 1 | box版本0或1 一般为0 (以下字节数均按version=0) 195 ** flags | 3 | 196 ** creation_time | 4 | 创建时间(相对于UTC时间1904 - 01 - 01零点的秒数) 197 ** modification_time | 4 | 修改时间 198 ** time_scale | 4 | 199 ** duration | 4 | track的时间长度 200 ** language | 2 | 媒体语言码,最高位为0 后面15位为3个字符[见ISO 639-2/T标准中定义] 201 ** pre-defined | 2 | 保留位 202 203 ** tkhd通常是对指定的track设定相关属性和内容, 而mdhd是针对于独立的media来设置的, 一般情况下二者相同. 204 ************************************************************************************************************/ 205 typedef struct t_box4mdhd 206 { 207 int creation_time; 208 int modification_time; 209 int timescale; 210 int duration; 211 short language; 212 } T_BOX4MDHD; 213 214 /************************************************************************************************************ 215 ** hdlr: Handler Reference Box, 媒体的播放过程信息, 该box也可以被包含在meta box(meta)中 216 ** 217 ------------------------------------------------------------------------------------------------------------- 218 ** 字段名称 | 长度(bytes) | 有关描述 219 ------------------------------------------------------------------------------------------------------------- 220 ** boxsize | 4 | box的长度 221 ** boxtype | 4 | box的类型 222 ** version | 1 | box版本0或1 一般为0 (以下字节数均按version=0) 223 ** flags | 3 | 224 ** pre-defined | 4 | 225 ** handler type | 4 | 在media box中,该值为4个字符 226 "vide"— video track 227 "soun"— audio track 228 "hint"— hint track 229 ** reserved | 12 | 230 ** name | 不定 | track type name,以‘ ’结尾的字符串 231 ************************************************************************************************************/ 232 typedef struct t_box4hdlr 233 { 234 unsigned char handler_type[MAX_HANDLER_TYPE_LEN+1]; 235 unsigned char name[MAX_HDLR_NAME_LEN+1]; 236 } T_BOX4HDLR; 237 238 /************************************************************************************************************ 239 ** vmhd: Video Media Header Box 240 ** 241 ------------------------------------------------------------------------------------------------------------- 242 ** 字段名称 | 长度(bytes) | 有关描述 243 ------------------------------------------------------------------------------------------------------------- 244 ** boxsize | 4 | box的长度 245 ** boxtype | 4 | box的类型 246 ** version | 1 | box版本0或1 一般为0 (以下字节数均按version=0) 247 ** flags | 3 | 248 ** graphics_mode | 4 | 视频合成模式,为0时拷贝原始图像,否则与opcolor进行合成 249 ** opcolor | 2 ×3 | {red,green,blue} 250 251 "vide"—vmhd 视频 252 "soun"— smhd 音频 253 "hint"—hmhd 忽略 254 ************************************************************************************************************/ 255 typedef struct t_box4vmhd 256 { 257 int graphics_mode; 258 } T_BOX4VMHD; 259 260 /************************************************************************************************************ 261 ** dref: data reference box 262 ** 263 ------------------------------------------------------------------------------------------------------------- 264 ** 字段名称 | 长度(bytes) | 有关描述 265 ------------------------------------------------------------------------------------------------------------- 266 ** boxsize | 4 | box的长度 267 ** boxtype | 4 | box的类型 268 ** version | 1 | box版本0或1 一般为0 (以下字节数均按version=0) 269 ** flags | 3 | 270 ** entry count | 4 | "url"或"urn"表的元素个数 271 ** "url"或"urn"列表 | 不定 | 272 273 ** "dref"下会包含若干个"url"或"urn", 这些box组成一个表, 用来定位track数据. 简单的说, track可以被分成若干段, 274 每一段都可以根据"url"或"urn"指向的地址来获取数据, sample描述中会用这些片段的序号将这些片段组成一个完整的track. 275 一般情况下, 当数据被完全包含在文件中时, "url"或"urn"中的定位字符串是空的. 276 ************************************************************************************************************/ 277 typedef struct t_box4dref 278 { 279 int entry_count; 280 } T_BOX4DREF; 281 282 /************************************************************************************************************ 283 ** stsd: Sample Description Box 284 ** 285 ------------------------------------------------------------------------------------------------------------- 286 ** 字段名称 | 长度(bytes) | 有关描述 287 ------------------------------------------------------------------------------------------------------------- 288 ** boxsize | 4 | box的长度 289 ** boxtype | 4 | box的类型 290 ** version | 1 | box版本0或1 一般为0 (以下字节数均按version=0) 291 ** entry count | 4 | "url"或"urn"表的元素个数 292 293 ** box header和version字段后会有一个entry count字段, 根据entry的个数, 每个entry会有type信息, 如"vide", "sund"等, 294 根据type不同sample description会提供不同的信息, 例如对于video track, 会有"VisualSampleEntry"类型信息, 295 对于audio track会有"AudioSampleEntry"类型信息. 视频的编码类型, 宽高, 长度, 音频的声道, 采样等信息都会出现在这个box中 296 ************************************************************************************************************/ 297 typedef struct t_box4stsd 298 { 299 int entry_count; 300 301 //TODO 302 } T_BOX4STSD; 303 304 /************************************************************************************************************ 305 ** stts: Time To Sample Box 306 ** 307 ------------------------------------------------------------------------------------------------------------- 308 ** 字段名称 | 长度(bytes) | 有关描述 309 ------------------------------------------------------------------------------------------------------------- 310 ** boxsize | 4 | box的长度 311 ** boxtype | 4 | box的类型 312 ** version | 1 | box版本,0或1,一般为0(以下字节数均按version = 0) 313 ** flags | 3 | 314 ** entry count | 4 | sample_count和sample_delta的个数 315 ** sample_count | 4 | 316 ** sample_delta | 4 | 317 318 ** "stts”"存储了sample的duration, 描述了sample时序的映射方法, 我们通过它可以找到任何时间的sample. "stts"可以 319 包含一个压缩的表来映射时间和sample序号, 用其他的表来提供每个sample的长度和指针. 表中每个条目提供了在同一个 320 时间偏移量里面连续的sample序号, 以及samples的偏移量. 递增这些偏移量, 就可以建立一个完整的time to sample表. 321 322 例: 说明该视频包含87帧数据(sample_count), 每帧包含512个采样(sample_delta). 总共512*87=44544个采样, 323 和我们前面mdhd box的Duration完全一致。 324 Duration/TimeScale = 44544/12288 = 3.625s, 正是我们的视频播放长度. 325 12288/512 = 24 p/s (帧率) 326 ************************************************************************************************************/ 327 typedef struct t_box4stts_entry 328 { 329 int sample_count; 330 int sample_delta; 331 } T_BOX4STTS_ENTRY; 332 333 typedef struct t_box4stts 334 { 335 int entry_count; 336 337 T_BOX4STTS_ENTRY entrys[MAX_STTS_ENTRY_NUM]; 338 } T_BOX4STTS; 339 340 /************************************************************************************************************ 341 ** stss: Sync Sample Box 342 ** 343 ------------------------------------------------------------------------------------------------------------- 344 ** 字段名称 | 长度(bytes) | 有关描述 345 ------------------------------------------------------------------------------------------------------------- 346 ** boxsize | 4 | box的长度 347 ** boxtype | 4 | box的类型 348 ** version | 1 | box版本,0或1,一般为0(以下字节数均按version = 0) 349 ** flags | 3 | 350 ** entry count | 4 | sample_num的个数 351 ** sample_num | 4 | 352 353 ** "stss"确定media中的关键帧. 对于压缩媒体数据, 关键帧是一系列压缩序列的开始帧, 其解压缩时不依赖以前的帧, 354 而后续帧的解压缩将依赖于这个关键帧. "stss"可以非常紧凑的标记媒体内的随机存取点, 它包含一个sample序号表, 355 表内的每一项严格按照sample的序号排列, 说明了媒体中的哪一个sample是关键帧. 如果此表不存在, 说明每一个sample 356 都是一个关键帧, 是一个随机存取点. 357 ************************************************************************************************************/ 358 typedef struct t_box4stss_entry 359 { 360 int sample_num; 361 } T_BOX4STSS_ENTRY; 362 363 typedef struct t_box4stss 364 { 365 int entry_count; 366 367 T_BOX4STSS_ENTRY entrys[MAX_STSS_ENTRY_NUM]; 368 } T_BOX4STSS; 369 370 /************************************************************************************************************ 371 ** stsc: Sample To Chunk Box 372 ** 373 ------------------------------------------------------------------------------------------------------------- 374 ** 字段名称 | 长度(bytes) | 有关描述 375 ------------------------------------------------------------------------------------------------------------- 376 ** boxsize | 4 | box的长度 377 ** boxtype | 4 | box的类型 378 ** version | 1 | box版本,0或1,一般为0(以下字节数均按version = 0) 379 ** flags | 3 | 380 ** entry count | 4 | entry的个数 381 ** first_chunk | 4 | 382 ** samples_per_chunk | 4 | 383 ** sample_des_index | 4 | 384 385 ** 用chunk组织sample可以方便优化数据获取, 一个thunk包含一个或多个sample. "stsc"中用一个表描述了sample与chunk的映射关系, 386 查看这张表就可以找到包含指定sample的thunk, 从而找到这个sample. 387 ************************************************************************************************************/ 388 typedef struct t_box4stsc_entry 389 { 390 int first_chunk; 391 int samples_per_chunk; 392 int sample_description_index; 393 } T_BOX4STSC_ENTRY; 394 395 typedef struct t_box4stsc 396 { 397 int entry_count; 398 399 T_BOX4STSC_ENTRY entrys[MAX_STSC_ENTRY_NUM]; 400 } T_BOX4STSC; 401 402 /************************************************************************************************************ 403 ** stsz: Sample To Chunk Box 404 ** 405 ------------------------------------------------------------------------------------------------------------- 406 ** 字段名称 | 长度(bytes) | 有关描述 407 ------------------------------------------------------------------------------------------------------------- 408 ** boxsize | 4 | box的长度 409 ** boxtype | 4 | box的类型 410 ** version | 1 | box版本,0或1,一般为0(以下字节数均按version = 0) 411 ** flags | 3 | 412 ** sample_size | 4 | 413 ** sample_count | 4 | entry的个数 414 ** entry_size | 4 | 415 416 ** "stsz"定义了每个sample的大小, 包含了媒体中全部sample的数目和一张给出每个sample大小的表. 这个box相对来说体积是比较大的. 417 ************************************************************************************************************/ 418 typedef struct t_box4stsz_entry 419 { 420 int entry_size; 421 } T_BOX4STSZ_ENTRY; 422 423 typedef struct t_box4stsz 424 { 425 int sample_size; 426 int sample_count; 427 428 T_BOX4STSZ_ENTRY entrys[MAX_STSZ_ENTRY_NUM]; 429 } T_BOX4STSZ; 430 431 /************************************************************************************************************ 432 ** stco: Chunk Offset Box 433 ** 434 ------------------------------------------------------------------------------------------------------------- 435 ** 字段名称 | 长度(bytes) | 有关描述 436 ------------------------------------------------------------------------------------------------------------- 437 ** boxsize | 4 | box的长度 438 ** boxtype | 4 | box的类型 439 ** version | 1 | box版本,0或1,一般为0(以下字节数均按version = 0) 440 ** flags | 3 | 441 ** entry_count | 4 | 442 ** chunk_offset | 4 | 443 444 ** "stco"定义了每个thunk在媒体流中的位置, sample的偏移可以根据其他box推算出来. 位置有两种可能, 32位的和64位的, 445 后者对非常大的电影很有用. 在一个表中只会有一种可能, 这个位置是在整个文件中的, 而不是在任何box中的. 446 这样做就可以直接在文件中找到媒体数据, 而不用解释box. 需要注意的是一旦前面的box有了任何改变, 这张表都要重新建立, 因为位置信息已经改变了. 447 ************************************************************************************************************/ 448 typedef struct t_box4stco_entry 449 { 450 int chunk_offset; 451 } T_BOX4STCO_ENTRY; 452 453 typedef struct t_box4stco 454 { 455 int entry_count; 456 457 T_BOX4STCO_ENTRY entrys[MAX_STCO_ENTRY_NUM]; 458 } T_BOX4STCO; 459 460 typedef struct t_box 461 { 462 T_BOX_HEADER boxHeader; 463 464 unsigned char *boxData; 465 } T_BOX; 466 467 static void DealBox4ftyp(const T_BOX *box) 468 { 469 int i = 0; 470 int j = 0; 471 int brandsNum = 0; 472 473 T_BOX4FTYP box4ftyp = {0}; 474 475 memset(&box4ftyp, 0x0, sizeof(T_BOX4FTYP)); 476 477 memcpy(box4ftyp.major_brand, box->boxData, 4); 478 box4ftyp.major_brand[MAX_FTYP_BRABDS_LEN] = '