声明:MP4 的封装格式是比较复杂的,本文不会把 MP4 的各个字段,各种场景用法都罗列出来,那样会形成一个手册。
文本主要讲解 MP4 这种封装格式在音视频开发中比较常用的字段,还有一些关键的知识点。让初学者掌握 MP4 的一些基本概念,然后学会看MP4标准文档深入了解。
资源下载:
MP4 格式 是一个 box 的格式,box 容器 套 box 子容器,box 子容器 再套 box 子容器。看起来跟 json 很像。
下图 是 a.mp4 的 box 结构。使用的软件是 Mp4Explorer。
![](./wp-content/uploads/2022/01/mp4-1-1.png)
解析MP4 格式是这样的,先读4个字节。前4个字节是 size,代表这个box 有多大。再读 4 字节,后4字节 是 type,表明这个 box 是个什么box
请看 a.mp4 文件 的 16进制的表示,推荐使用 notepad ++ 32 位 hexeditor 插件查看,只有32位的才有这个插件。
![](./wp-content/uploads/2022/01/mp4-2.png)
如上图所示,用不同颜色画出了 3个 box。
- 黑色圈出来的 box 是 ftyp,全称 File Type Box。ftyp 前面的 4个字节是 20(16进制)。也就说,这个 ftyp box 是 32 字节的长度,32字节是包含 size 的4个字节的。ftyp 是一级 box,里面没有子box了。
- 蓝圈圈出来的 box 是 free,只有 8个字节大小,size 占 4个字节,type 占4个字节。 free 这个box我也不知道有什么用,不用特别关注。
- 红色圈出来的 box 是 mdata,全称 Media Data,这个box 是最重要的, ffmpeg 封装的 AVPacket 数据就存放在这个box 里面。mdata 前面 4个字节是大小,00 0b 5c 63 , 也就是 这个 mdata 数据有 0b5c63个字节。
由第一张图可以知道, mdata 这个box 之后就是 moov box,如何找到 moov box 的位置呢?
如上可知,mdata box 的起始位置是 00000028 ,mdata 这个box 有 0b5c63 这么大,所以 moov box 的位置等于 0b5c63 + 28 = 000b5c8b。
我们用 notepad ++ 跳转到 000b5c8b 看看能不能找到 moov 这个字符。
![](./wp-content/uploads/2022/01/mp4-3.png)
000b5c8b 的位置附近果然能看到 moov 这个字符串,moov 的ASII码是 6d 6f 6f 76。前面的4个字节数据 00 00 26 39 是 moov box的大小。
分析到这里, mp4 的box结构就比较清晰了,很简单,前面4个字节是size,后4字节是 type,后面的数据就是这个type类型的box具体的数据。
不过 size 有两个特殊值 0 和 1
- 当size等于0时,代表这个Box是文件的最后一个Box。
- 当size等于1时,说明Box长度需要更多的位来描述,在后面会定义一个64位的 largesize 用来描述Box的长度。
如图:
![](/wp-content/uploads/2022/02/mp4-4.png)
下面继续讲解 a.mp4 里面 的 moov box。
- mvhd ,全称 Movie Header Box。
- trak,全称 Track Box,通常一个音频流 或者视频流 对应一个 trak box。
- stbl,全称 Sample Table Box ,
MP4 最重要的就是 stbl 里面的子 box,众所周知,MP4 比 FLV 的优势就是可以快速的seek,这是因为MP4 里面有一个数据索引表,还有关键帧表。
这些表都在 stbl box 里面
1,stts,全称 Decoding Time to Sample Box 。
2,ctts,全称 Composition Time to Sample Box。 时间补偿,用来计算出 pts,因为mp4 是按 解码顺序存储,packet 按 dts 递增,所以需要 ctts 表计算出packet 的 pts。只有视频有,音频没有。因为音频 pts dts 是一样的。
3,stss,全称 Sync Sample Box,同步 采样表,表明第几帧是关键帧,关键帧可以用来同步,所以叫 Sync 没毛病。视频流才有这个 box,音频流没有。
下面是 stss 这个box 的截图,可以看到 第 1 帧,第 35 帧,第 152 帧 是关键帧。
![](./wp-content/uploads/2022/01/mp4-4-1.png)
4,stsz,全称 Sample Size Boxes,表明视频帧或者音频帧大小,FFmpeg 里面的AVPacket 的size 数据大小,就是从这个box中来的。
可以看到,音视频格式多种多种,flv 肯定没有stsz这个表,但是 FFmpeg 无论是解析flv,还是解析mp4,出来的 AVpacket 的size还是可以用的。
所以FFmpeg 实际上就是对纷繁复杂的各种格式进行了封装,让编程更通用一点。
下面是 stsz 这个box 的截图,可以看到 第一帧是比较大的,第一帧是 31945 个字节,因为他是关键帧。
![](./wp-content/uploads/2022/01/mp4-4-2.png)
5,stsc,全称 Sample To Chunk Box。mp4 会把一个或多个 sample 放到一个chunk 里面。就是一个 chunk可能会有多个视频帧,也可能只有一个,如图:
![](./wp-content/uploads/2022/01/mp4-4-3.png)
上图 是视频流的 stsc box 。里面有两行数据,第一个chunk里面有2个sample,第二个chunk里面有 1个sample。这个并不是说,这个视频流只有3个sample,也就是只有3帧,不可能的,而是第三,第四行省略了,也就是说,第三跟第四,等等,后面的chunk 里面都只有1个sample,跟第二个chunk一样。本视频流有239个chunk。因为本视频流一共240帧,第一个chunk里面有2帧,后面的都是1帧,所以计算出来只有239个chunk。
所以他这个 First chunk 在列表是代表 start 开始的chunk,如果有下一行,下一行的值就是end 结束。。
chunk 跟 sample 这个概念比较重要,MP4 索引定位找到某一帧,就是先找到chunk,再找到sample。
6,stco,全称 Chunk Offset Box,chunk的位置表。
![](./wp-content/uploads/2022/01/mp4-4-4.png)
从上图可以看出,第一个chunk 在a.mp4文件的 48 的位置,第一个chunk里面有两帧数据。我们用 notepad ++ 跳到 48 的地方,看看具体内容。
48 转成 16进制是 30。如下图:
![](./wp-content/uploads/2022/01/mp4-4-5.png)
从上图可以看到,00000030 的地方,前面紧紧挨着 mdat,mdat 字符后面就是具体的编码后的数据。
通过上面这些索引数据,我们找到了第一帧视频数据的位置,我们现在来验证一下对不对,打开 之前搭建好的 Qt creator ffplay的调试环境,断点查看 第一个 packet的数据。
![](./wp-content/uploads/2022/01/mp4-4-6.png)
我在 av_read_frame() 的位置打了个断点,可以看到读出来的第一个 AVPacket 的 pos 值的确是 48,size 等于 31945 也能跟 stsz box表对应上。
再来查看一下第一个 AVPacket 的data 里面的数据,如下图。
![](./wp-content/uploads/2022/01/mp4-4-7.png)
可以看到 FFplay 里面读出来的 AVPakcet 的data指针的数据跟我们在notepad ++ 48位置看到的数据是完全一样的。
第一帧视频的数据位置找到了,那第二帧视频数据怎么找呢?stsc 表里面说明,第一个chunk有两针数据,所以第二个视频帧是紧挨着第一帧的,所以第一帧的位置 48 加上第一帧大小 31945 等于 31993,换算成 16 进制就是 7cf9,所以第二帧数据 在文件的 00007cf9 的位置。如图:
![](./wp-content/uploads/2022/01/mp4-4-8.png)
大家可以自行用qt 打印第二个视频帧的AVPacket的data数据验证位置对不对。
stco (chunk索引表),stsc(sample chunk表),stsz (sample 大小表)。简单的通过这3个表找到对应帧的位置已经讲解完了。下面来讲解 按时间 seek,mp4可以怎么实现,例如 要拖动视频到 第2秒的时候播放,因为 a.mp4 是24帧率的视频,所以第二秒就是 1* 24 = 24,也就是从24帧开始播放,那24帧的数据在哪里呢?因为 stsc 表里面第一个chunk占了 2个帧,后面的chunk都只占一个帧,所以第24帧在第23个chunk里面,所以往 stco表找,找到 第23个chunk的偏移位置,也就是 64626 。如图:
![](./wp-content/uploads/2022/01/mp4-4-9.png)
本文主要只讲了 stbl,mdat 这两个box,因为MP4 最大的区别就是他有很多索引数据,例如关键帧索引,普通帧的位置索引等等。
其他的 trak ,tkhd,mdhd,等等,可以自行查看 MP4 的标准进行了解。
音视频格式纷繁复杂,下面介绍一种看 标准文档的方法,例如 mp4 的标准 ISO- 14496Part12 。
例如 之前介绍的 free box 不知道是做什么?我们可以打开 标准文件 w15177_14496-12_5th.-restyle-R5.doc ,然后搜索 free 这个关键词,就能看到以下说明。
![](./wp-content/uploads/2022/01/mp4-5.png)
没错,free box 的全称是 Free Space Box。想知道某些 box 的定义,直接在标准文件里面搜全称 等关键词即可。
看标准文件的目录,也能直接看出各种 box 的定义跟作用。
我个人建议可以抽时间把 257 页的mp4 标准耐心读一遍,这样就会对 mp4 格式非常熟悉了。257 页其实不多。
网上 mp4 的资料比较零碎,包括本文也是只讲了一小部分的 MP4 内容。完整的内容其实就在标准文件里面,所以学会看标准文件是非常重要的。
![](./wp-content/uploads/2022/01/mp4-6.png)
扩展知识:
正常情况下 ffmpeg 生成 moov 是在 mdat 写完成之后再写入,可以通过参数 faststart 将 moov 容器移动至 mdat 的前面,将 moov 提前 可以 ⽀持http边下载边播放,下面参考一个例子:
./ffmpeg -i input.flv -c copy -f mp4 -movflags faststart output.mp4
相关阅读:
- MP4 标准 ISO- 14496Part12 ,
- 5分钟入门 MP4 格式
- 《FFmpeg 从入门到精通》3.1章节 "音视频文件转MP4格式"
©版权所属:知识星球:弦外之音,QQ:2338195090。
由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以加我微信 Loken1。