前言
MP4封装格式比较常见,但是有些会有问题,下面解析一下具体信息
正文
工具
比较好的是mp4在线工具
MP4主要结构
MP4主要结构类似于树状图。每个节点开头都有8位,前四位记录大小,后四位标记节点类型。根节点一般包含四个字节点(ftyp,free,mdat,moov)。
- ftyp(File Type Box)一般在文件的开始位置,描述的文件的版本、兼容协议等。
- mdat 多媒体的存储位置
- free预留位置,没用
- moov 关键信息,记录多媒体轨道和索引信息
moov关键的是track。track节点中包含了tkhd,记录track信息。供播放器显示多媒体信息。media记录关键索引信息,解码器搜寻sample位置。
meida中的mdhd记录识别的总时长,以及时间粒度。供后边分析pts和cts等操作。minf节点中的stbl才是真的记录采样点的位置信息。以及计算方法。
这里有个比较重要的概念。chunk。他是一些编码后的数据,成为一簇。方便管理以及避免错误的干扰
下面解析一个750s的视频,有130个chunk。45008帧数据,
stsd
描述,不重要,
stts
描述数据帧的时间间隔。因为间隔大部分一样,所以是统计信息,
相同时间间隔个数 时间间隔(单位是moov-trak-media-mdhd-timescale的信息)
比如一个典型的数据是:
entry_count | 1 |
---|---|
samples | {“sample_count”:45008,“sample_delta”:1001} |
显示当前轨道,所有帧时长是一样的。都是16.683毫秒,帧率是59.94005994005994
而timescale是1/60000.所以总时长是 45008 * 1001 /timescale(60000) = 750.8834 秒。时长应该是 750秒。130个chunk。
stss
entry_count | 130 |
---|---|
sync_samples.0 | 1 |
sync_samples.1 | 207 |
sync_samples.2 | 397 |
sync_samples.3 | 713 |
sync_samples.4 | 939 |
sync_samples.5 | 1139 |
sync_samples.6 | 1437 |
用来记录关键帧。方便快速seek
ctts用来记录视频帧和显示时间的间隔。主要是解决视频向后参考关键帧。音频一般情况下只有一条,
entry_count | 2 |
---|---|
samples.0 | {“sample_count”:1,“composition_offset”:1001} |
samples.1 | {“sample_count”:32339,“composition_offset”:0} |
视频比较复杂:
如下
entry_count | 36456 |
---|---|
samples.0 | {“sample_count”:1,“composition_offset”:1001} |
samples.1 | {“sample_count”:1,“composition_offset”:3003} |
samples.2 | {“sample_count”:2,“composition_offset”:0} |
samples.3 | {“sample_count”:3,“composition_offset”:1001} |
samples.4 | {“sample_count”:1,“composition_offset”:2002} |
samples.5 | {“sample_count”:1,“composition_offset”:0} |
samples.6 | {“sample_count”:1,“composition_offset”:1001} |
samples.7 | {“sample_count”:1,“composition_offset”:2002} |
samples.8 | {“sample_count”:1,“composition_offset”:0} |
samples.9 | {“sample_count”:4,“composition_offset”:1001} |
samples.10 | {“sample_count”:1,“composition_offset”:2002} |
这个sample顺序cts是根据stts来计算的,根据之前分析,则sample则根据顺序排列
index cts dts
1 | 0 | 1001 |
---|---|---|
2 | 1001 | 4004 |
3 | 2002 | 2002 |
4 | 3003 | 3003 |
5 | 4004 | 5005 |
6 | 5005 | 6006 |
7 | 6006 | 7007 |
8 | 7007 | 9009 |
9 | 8008 | 8008 |
10 | 9009 | 10010 |
11 | 10010 | 12012 |
13 | 11011 | 11011 |
14 | 12012 | 13013 |
比较明显,第二帧数据,应该在第四个时刻显示,但是第三,和第四帧解码需要第二帧,所以先解码第二帧,根据pts修正显示时刻。
stsc
sample-chunk映射表。
entry_count | 61 |
---|---|
chunks.0 | {“first_chunk”:1,“samples_per_chunk”:206,“sample_description_index”:1} |
chunks.1 | {“first_chunk”:2,“samples_per_chunk”:190,“sample_description_index”:1} |
chunks.2 | {“first_chunk”:3,“samples_per_chunk”:316,“sample_description_index”:1} |
chunks.59 | {“first_chunk”:62,“samples_per_chunk”:420,“sample_description_index”:1} |
chunks.60 | {“first_chunk”:130,“samples_per_chunk”:458,“sample_description_index”:1} |
这是记录chunk 一共130个。每个chunk可能有不同的sample。比如第一个有206 62到 130 的chunk每个里面有458个。
stsz
每个sample的字节大小
sample_count | 45008 |
---|---|
sample_size.0 | 513 |
sample_size.1 | 270 |
sample_size.2 | 85 |
sample_size.3 | 89 |
sample_size.4 | 1258 |
sample_size.5 | 1581 |
stco
每个chunk在文件中的偏移
entry_count | 130 |
---|---|
chunk_offset.0 | 48 |
chunk_offset.1 | 1011504 |
chunk_offset.2 | 3302476 |
… | … |
chunk_offset.129 | 213665857 |
这些用来记录关键的chunk的位置,具体每个sample参考sample具体的位置
后记
MP4的解析基本完成。原理还是比较 简单,以后如果写完解码器,自己就手写一个解封装模块,实现必要的功能