封装格式分析-MP4 - 弦外之音

/ 0评 / 1

声明:MP4 的封装格式是比较复杂的,本文不会把 MP4 的各个字段,各种场景用法都罗列出来,那样会形成一个手册。

文本主要讲解 MP4 这种封装格式在音视频开发中比较常用的字段,还有一些关键的知识点。让初学者掌握 MP4 的一些基本概念,然后学会看MP4标准文档深入了解。


资源下载:

  1. a.mp4下载链接:百度网盘,提取码:nl0s 。
  2. Mp4Explorer.exe 下载链接:百度网盘,提取码:039r 。

MP4 格式 是一个 box 的格式,box 容器 套 box 子容器,box 子容器 再套 box 子容器。看起来跟 json 很像。

下图 是 a.mp4 的 box 结构。使用的软件是 Mp4Explorer。

解析MP4 格式是这样的,先读4个字节。前4个字节是 size,代表这个box 有多大。再读 4 字节,后4字节 是 type,表明这个 box 是个什么box

请看 a.mp4 文件 的 16进制的表示,推荐使用 notepad ++ 32 位 hexeditor 插件查看,只有32位的才有这个插件。

如上图所示,用不同颜色画出了 3个 box。

  1. 黑色圈出来的 box 是 ftyp,全称 File Type Box。ftyp 前面的 4个字节是 20(16进制)。也就说,这个 ftyp box 是 32 字节的长度,32字节是包含 size 的4个字节的。ftyp 是一级 box,里面没有子box了。
  2. 蓝圈圈出来的 box 是 free,只有 8个字节大小,size 占 4个字节,type 占4个字节。 free 这个box我也不知道有什么用,不用特别关注。
  3. 红色圈出来的 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 这个字符。

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

如图:



下面继续讲解 a.mp4 里面 的 moov box。

  1. mvhd ,全称 Movie Header Box。
  2. trak,全称 Track Box,通常一个音频流 或者视频流 对应一个 trak box。
  3. 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 帧 是关键帧。

4,stsz,全称 Sample Size Boxes,表明视频帧或者音频帧大小,FFmpeg 里面的AVPacket 的size 数据大小,就是从这个box中来的。

可以看到,音视频格式多种多种,flv 肯定没有stsz这个表,但是 FFmpeg 无论是解析flv,还是解析mp4,出来的 AVpacket 的size还是可以用的。

所以FFmpeg 实际上就是对纷繁复杂的各种格式进行了封装,让编程更通用一点。

下面是 stsz 这个box 的截图,可以看到 第一帧是比较大的,第一帧是 31945 个字节,因为他是关键帧。

5,stsc,全称 Sample To Chunk Box。mp4 会把一个或多个 sample 放到一个chunk 里面。就是一个 chunk可能会有多个视频帧,也可能只有一个,如图:

上图 是视频流的 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的位置表。

从上图可以看出,第一个chunk 在a.mp4文件的 48 的位置,第一个chunk里面有两帧数据。我们用 notepad ++ 跳到 48 的地方,看看具体内容。

48 转成 16进制是 30。如下图:

从上图可以看到,00000030 的地方,前面紧紧挨着 mdat,mdat 字符后面就是具体的编码后的数据。

通过上面这些索引数据,我们找到了第一帧视频数据的位置,我们现在来验证一下对不对,打开 之前搭建好的 Qt creator ffplay的调试环境,断点查看 第一个 packet的数据。

我在 av_read_frame() 的位置打了个断点,可以看到读出来的第一个 AVPacket 的 pos 值的确是 48,size 等于 31945 也能跟 stsz box表对应上。

再来查看一下第一个 AVPacket 的data 里面的数据,如下图。

可以看到 FFplay 里面读出来的 AVPakcet 的data指针的数据跟我们在notepad ++ 48位置看到的数据是完全一样的。

第一帧视频的数据位置找到了,那第二帧视频数据怎么找呢?stsc 表里面说明,第一个chunk有两针数据,所以第二个视频帧是紧挨着第一帧的,所以第一帧的位置 48 加上第一帧大小 31945 等于 31993,换算成 16 进制就是 7cf9,所以第二帧数据 在文件的 00007cf9 的位置。如图:

大家可以自行用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 。如图:


本文主要只讲了 stbl,mdat 这两个box,因为MP4 最大的区别就是他有很多索引数据,例如关键帧索引,普通帧的位置索引等等。

其他的 trak ,tkhd,mdhd,等等,可以自行查看 MP4 的标准进行了解。

音视频格式纷繁复杂,下面介绍一种看 标准文档的方法,例如 mp4 的标准 ISO- 14496Part12

例如 之前介绍的 free box 不知道是做什么?我们可以打开 标准文件 w15177_14496-12_5th.-restyle-R5.doc ,然后搜索 free 这个关键词,就能看到以下说明。

没错,free box 的全称是 Free Space Box。想知道某些 box 的定义,直接在标准文件里面搜全称 等关键词即可。

看标准文件的目录,也能直接看出各种 box 的定义跟作用。

我个人建议可以抽时间把 257 页的mp4 标准耐心读一遍,这样就会对 mp4 格式非常熟悉了。257 页其实不多。

网上 mp4 的资料比较零碎,包括本文也是只讲了一小部分的 MP4 内容。完整的内容其实就在标准文件里面,所以学会看标准文件是非常重要的。


扩展知识:

正常情况下 ffmpeg 生成 moov 是在 mdat 写完成之后再写入,可以通过参数 faststart 将 moov 容器移动至 mdat 的前面,将 moov 提前 可以 ⽀持http边下载边播放,下面参考一个例子:

./ffmpeg -i input.flv -c copy -f mp4 -movflags faststart output.mp4

相关阅读:

  1. MP4 标准 ISO- 14496Part12
  2. 5分钟入门 MP4 格式
  3. 《FFmpeg 从入门到精通》3.1章节 "音视频文件转MP4格式"

©版权所属:知识星球:弦外之音,QQ:2338195090。

由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以加我微信 Loken1。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注