基本介绍
mp4文件格式又被称为 MPEG-4 Part 14,出自 MPEG-4标准第14部分。他是一种多媒体格式工具,广泛用于包装视频和音频数据流、海报、字幕和元数据等。(顺便⼀提,⽬前流⾏的视频编码格式AVC/H264定义在MPEG-4 Part 10)
mp4文件格式基于 Apple 公司的 QuickTime 格式,因此,QuickTime File Format Specification 也可以作为我们研究 mp4的重要参考。
预览
mp4文件由 box 组成,每个 box 分为 Header 和 Data。其中 Header 部分包含了 box 的类型和大小,Data 包含了子 box 或者数据,box 可以嵌套 box。
下图是一个典型 mp4文件的基本结构:
mp4文件的基本组成单元是 box,也就是说 mp4文件是由各种 box 组成,有 parent box,还有 childrenbox。因此,这些 boxes 之间存在一定的层次关系,总结如下表所示,标中标记出了各个 box 必选或可选特征。
ftyp | ✅ | file type and compatibility 文件类型和兼容性 |
|||||
---|---|---|---|---|---|---|---|
pdin | progressive download information | ||||||
moov | ✅ | container for all the metadata 所有元数据的容器 |
|||||
mvhd | ✅ | movie header, overall declarations 电影头,整体声明 |
|||||
trak | ✅ | container for an individual track or stream 单个音轨流的容器 |
|||||
tkhd | ✅ | track header, overall infomation about the tracker 轨的头部,关于该轨的概括信息,比如视频宽高 |
|||||
tref | track reference container | ||||||
edts | edit list container | ||||||
elst | an edit list | ||||||
mdia | ✅ | container for the media information in a track 轨媒体信息的容器 |
|||||
mdhd | ✅ | media header, overall infomation about the media 媒体头,关于媒体的总体信息 |
|||||
hdlr | ✅ | handler, declares the media 媒体的播放过程信息 |
|||||
minf | ✅ | media information container 媒体信息容器 |
|||||
vmhd | video media header, overall information (video track only) |
||||||
smhd | sound media header, overall information (sound track only) |
||||||
hmhd | hint media header, overall infomation (hint track only) |
||||||
nmhd | Null media header, overall information (some tracks only) |
||||||
dinf | ✅ | data information box, container 数据信息 box, 容器 |
|||||
dref | ✅ | data reference box, declares source (s) of media data in track 如何定位媒体信息 |
|||||
stbl | ✅ | sample table box, container for the time/space map 包含了 track 中的 sample 的所有时间和位置信息,一级 sample 的编解码等信息。利用这个表可以解析 sample 的时序、类型、大小及在各自存储容器中的位置。 |
|||||
stsd | ✅ | sample descriptions (codec types, initialization etc) 如果是视频,包含:编码类型、宽高、长度等信息; 如果是音频,包含:声道、采样率等信息; |
|||||
stts | ✅ | (decoding)time-to-sample 描述了 sample 时序的映射方法,我们可以通过它找到任何时间的 sample |
|||||
ctts | (composition)time to sample | ||||||
stsc | ✅ | sample-to-chunk, partial data-offset information 用 chunk 组织 sample 可以方便优化数据获取,一个 chunk 包含一个或多个 sample。 |
|||||
stsz | sample sizes (framing) 每个 sample 的大小。 虽然这里没有打勾,但是对于mp4很重要 |
||||||
stz2 | compact sample sizes (framing) | ||||||
stco | ✅ | chunk offset, partial data-offset information 定义每个 chunk 在媒体流中的偏移位置 |
|||||
co64 | 64bit chunk offset | ||||||
stss | shadow sync sample table | ||||||
stsh | sample padding bits | ||||||
padb | sample degtadation priority | ||||||
stdp | sample degradation priority | ||||||
sdtp | independent and disposable samples | ||||||
sbgp | sample-to-group | ||||||
sgpd | sample group description | ||||||
subs | sub-sample information | ||||||
mvex | movie extends box | ||||||
mehd | movie extends header box | ||||||
trex | ✅ | track extends defaults | |||||
ipmc | IPMP Control Box | ||||||
moof | movie fragment | ||||||
mfhd | ✅ | movie fragment header | |||||
traf | track fragment | ||||||
tfhd | ✅ | track fragment header | |||||
trun | track fragment run | ||||||
sdtp | independent and disposable samples | ||||||
sbgp | sample-to-group | ||||||
subs | sub-sample information | ||||||
mfra | movie fragment random access | ||||||
tfra | track fragment random access | ||||||
mfro | ✅ | movie fragment random access offset | |||||
mdat | media data container | ||||||
free | free space | ||||||
skip | free space | ||||||
udta | user-data | ||||||
cprt | copyright etc | ||||||
meta | metadata | ||||||
hdlr | ✅ | handler, declares the metadata (handler) type | |||||
dinf | data information box, container | ||||||
dref | data reference box, declares source (s) of metadata items | ||||||
ipmc | IPMP Control Box | ||||||
iloc | item location | ||||||
ipro | item protection | ||||||
sinf | protection scheme information box | ||||||
frma | original format box | ||||||
imif | IPMP Information box | ||||||
schm | scheme type box | ||||||
schi | scheme information box | ||||||
iinf | item information | ||||||
xml | XML container | ||||||
bxml | binary XML container | ||||||
pitm | primary item reference | ||||||
fiin | file delivery item information | ||||||
paen | partition entry | ||||||
fpar | file partition | ||||||
fecr | FEC reservoir | ||||||
segr | file delivery session group | ||||||
gitn | group id to name | ||||||
tsel | track selection | ||||||
meco | additional metadata container | ||||||
mere | metabox relation |
具体分析
fype
File Type Box 一般在文件的开始位置,描述的文件的版本,兼容协议等。
moov
Movie Box 包含了本为难中所有媒体数据的宏观描述信息以及每路媒体轨道的具体信息。一般位于放在文件末尾,但如果为了支持 http 边下载边播放则需要将 moov 提前。注意:当改变 moov 位置时,内部一些值需要重新计算。
moov 里面的 box 才是需要主要分析的 box
mdat
Media Data Box 存放具体的媒体数据。
Moov Insider
mp4的媒体数据信息主要存在在 Moov Box 中,是我们需要分析的重点。moov 主要组成部分如下:
mvhd
Movie Header Box 记录整个媒体文件的描述信息,如创建时间,修改时间,时间度量标尺、可播放时长等。
在下面这个例子中,可以获取文件信息如时长 Duration:5016ms。
udta
User Data Box,自定义数据。
track
Track Box 记录媒流信息,文件中可以存在一个或多个 track,他们之间时互相独立的。
tkhd
Track Header Box 包含关于媒体流的头信息。
在下面的示例中,可以看到流信息如视频流宽度720,长度1280.
音频的 tkhd 比如 duration、volume 等。
mdia
Media Box 时包含 track 媒体数据信息的 container box。子 box 包括:
- mdhd:Media Header Box 存放视频流创建时间,长度等信息;
- hdlr:Handler Reference Box 媒体的播放过程信息;
- minf:Media Information Box 解释 track 媒体数据的 handler-specific 信息。minf 同样是个 container box,其内部需要关注的内容是 stbl,这也是 moov 中最复杂的部分。stbl 包含了媒体流每一个 sample 在文件中的 offset,pts,duration 等信息。想要播放一个 mp4文件,必须根据 stbl 正确找到每个 sample 并送给解码器。
mdhd
Media Header Box 存放视频流创建时间,长度等信息。
视频的 mdhd,Time scale,Duration 等信息。
音频的 mdhd 和视频类似,但是要注意 Time scale,我们在计算时间戳的时候都要使用该 Time scale,对应我们流里面的 AVStream->time_base。
hdlr
Handler Reference Box 媒体的播放过程信息。
视频的 hdlr,重点 Component subtype:
音频的 hdlr,Component subtype:soun,如果我们多个音轨的时候,Component name:粤语。
我们分析文件另一路音轨。
minf
Media Information Box,解释 track 媒体数据的 handler- specific 信息。minf 同样是一个 container box,其内部需要关注的内容是 stbl,这也是 moov 中最复杂的部分。stbl 包含了媒体流每一个 sample 在文件中的 offset,pts,duration 等信息。想要播放一个 mp4文件,必须根据 stbl 正确找到每个 sample 并送给解码器。
而且需要注意的是,minf 里面的子容器,音频和视频轨是有区别的,比如视频轨:vmhd,音频轨 smhd。
vmhd:
smhd:
Stbl Insider
Sample Table Box,上文提到 media 中最主要的部分是存放文件中每个 sample 信息的 stbl。在解析 stbl 钱,我们需要区分 chunk 和 sample 这两个概念。
在 mp4文件中,sample 时一个媒体流的基本单元,例如视频流的一个 sample 代表实际的 nal 数据。chunk 时数据存储的基本单位,他是一系列 sample 数据的集合,一个 chunk 中可以包含一个或多个 sample。
一个 chunk 包含一个或多个 sample
stbl 用来描述每个 sample 的信息,包含一下几个主要的子 box:
stsd
Sample Description Box,存放解码必须的描述信息。
下面的示例中,对于 h264的视频流,其具体类型为 avcl,extensions 中其中存放有 sps,pps 等解码必要的信息。
视频的 stsd
里面包含了 avc1,avc1里包含了 avcC 和 pasp。
- avc1:包含了视频 Width、Height;
- avcC:包含了视频编码器相关的信息,包括 sps、pps 等信息。
音频的 stsd
包含了音频相关信息,比如采样率,通道数量等。
stts
Time-to-Sample Box,定义每个 sample 时长。Time-To-Sample 的 table entry 布局如下:
stts table entry 布局
- sample count:sample 个数
- sample duration:sample 持续时间
持续时间相同的连续 sample 可以放到一个 entry 里达到节省空间的目的。
这里先给出来的是视频的 stts,Number of entries,这个参数需要注意的并不是 sample 的个数,sample 的实际数量需要将每个 entry 的 sample count 进行累加才是真正的 sample 个数。
下面的示例中,第一个 sample 时间为3720,单位用 mdhd 的 time scale 进行换算,比如视频的是90000,此时换算成秒为3720➗90000=0x0133333333秒。
再给出个音频 stts,只是 mdhd 的 time scale 的差别,之前我们看到音频为44100,则计算第一个 sample 的时间1024/44100=0.0232199546485261秒。
stss
Sync Sample Box 同步 sample 表,存放关键帧列表,关键帧是为了支持随机访问。stss 的 table entry 布局如下:
下图示例中,该视频 track 有3个关键帧:
stsc
Sample-To-Chunk Box,sample-chunk 映射表。上文提到 mp4通常吧 sample 封装到 chunk 中,一个 chunk 可能会包含一个或者几个 sample。Sample-To-Chunk Atom 的 table entry 布局如下所示:
- First chunk 使用该表项的第一个 chunk 序号
- Samples per chunk 使用该表项的 chunk 中包含有几个 sample
- Sample description ID 使用该表项的 chunk 参考的 stsd 表项序号
下图示例中,可以看到该视频 track 一共有1个 stsc 表项,chunk 序列1-x,每个 chunk 包含一个 sample。
这里则说明每个chunk里面只有一个sample(一个chunk时可以有多个sample)
stsz
Sample Size Box,指定了每个 sample 的 size。Sample Size Atom 包含两个 sample 总数和一张包含了每个 sample size 表。
sample size 表的 entry 布局如下图:
下面的示例中,该视频一共有110个 sample,第一个 sample 大小为42072字节,第二个 sample 大小为7354个字节。
stco
Chunk Offset Box 制定了每个 chunk 在文件夹中的位置,这个表时确定每个 sample 在文件夹中位置的关键。该表包含了 chunk 个数和一个包含了每个 chunk 在文件中偏移位置表。每个表项的内存布局如下:
需要注意,这里 stco 只是制定的每个 chunk 在文件中的便宜位置,并没有给出每个 sample 在文件中的偏移。想要获得每个 sample 的偏移位置,需要结合 Sample Size Box(stsz) 和 Sample-To-Chunk(stsc)计算后取得。
下面的示例中,该视频流第一个 chunk 在文件中的偏移为4750,而这里是每个 chunk 只有一个 sample,此时第一个 sample 的起始位置就是4750,数据大小则参照 stsz->0x1D78,第一个 sample size 为172818。
如何计算 sample 偏移位置
上文提到通过 stco 并不能直接获取某个 sample 的偏移位置,下面举例说明如何获取某个 pts 对应的 sample 在文件中的位置。
大体需要以下步骤:
- 将 pts 转换到媒体对应的时间坐标系
- 根据 stts(decoding time-to-sample) 计算某个 pts 对应的 sample 序号
- 根据 stsc(sample-to-chunk)计算 sample 序号存在哪个 chunk 中
- 根据 stco(chunk offset)获取对应 chunk 在文件中的偏移位置
- 根据 stsz 获取 sample 在 chunk 内的偏移位置并加上第四步获取的偏移,计算出 sample 在文件中的偏移
评论