[关闭]
@ltlovezh 2023-03-05T11:40:53.000000Z 字数 2439 阅读 1788

MP4

MP4 封装容器


一个普通Mp4的简化版Box结构如下所示:
Mp4-Box

每个Box的作用如下所示:
Box-Info

stbl

某视频Track的stbl如下所示:
video_stbl
H264是stsd -> avc1 ->avcc,H265是stsd -> hvc1(hev1) -> hvcc。

某音频Track的stbl如下所示:
audio_stbl

stsd

主要包含音频流或者视频流的元数据。
针对视频流,avc1和hvc1(hev1)包含了视频流宽高,avcc包含了AVCDecoderConfigurationRecord,hvcc包含了HEVCDecoderConfigurationRecord,主要存储视频VPS、SPS和PPS。

针对音频流,mp4a包含了采样率、声道数和采样位数,如下所示:
mp4a
esds box则包含了AudioSpecialConfig,如下所示:
mp4a

stts

sample数量和每个sample的duration,可间接计算出总Sample数量。

某视频Track的stts box如下所示:
video-stts
前450帧,每帧的duration是512,最后一帧的duration是1024(单位是mdhd box记录的timescale)。

可以计算出共451个视频Sample

某音频Track的stts box如下所示:
audio-stts
前651帧,每帧的duration是1024,最后一帧的duration是1036(单位是mdhd box记录的timescale)。

可以计算出共652个音频Sample

stsz

每个Sample的Size,可以得到总的Sample数量。

某视频Track的stsz box如下所示:
video-stsz

总共451个视频Sample,并且指出了每个Sample的Size。

某音频Track的stsz box如下所示:
audio-stsz

总共652个音频Sample,并且指出了每个Sample的Size。

stsc

chunk和sample数量的映射关系,每个Entry指定了从第几个trunk开始,每个trunk包含多少Sample。

可以计算出chunk数量,以及每个chunk包含多少Sample。

某视频Track的stsc box如下所示:
video-stsc
第1 ~ 26个trunk,每个trunk包含15个视频Sample。
第27 ~ 28个trunk,每个trunk包含30个视频Sample。
第29个trunk包含一个视频Sample。

可以计算出共29个trunk,451个视频Sample。

某音频Track的stsc box如下所示:
audio-stsc
第1 ~ 2个trunk,每个trunk包含43个音频Sample。
第3个trunk包含2个音频Sample。
依次类推......

可以计算出共29个trunk,652个音频Sample。

stco

每个chunk的地址偏移量。

某视频Track的stco box如下所示:
video-stco
共29个trunk,指出了每个trunk的地址偏移,结合stsc,可以计算出每个视频Sample的地址偏移。

某音频Track的stco box如下所示:
audio-stco
共29个trunk,指出了每个trunk的地址偏移,结合stsc,可以计算出每个音频Sample的地址偏移。

stss

所有关键帧的Sample序号,如果不存在stss,那么每一个sample都是关键帧。

某视频Track的stss box如下所示:
video-stss
第1、251、412帧是关键帧(从1开始计数)

音频Track没有stss box,因为每一个音频帧都是关键帧。

ctts

有B帧的情况下,每个Sample PTS和DTS之间的差值。

  1. Sample.pts = Sample.dts + Sample.sample_offset;

video-ctts
指出了每个Sample,PTS - DTS的差值,单位是mdhd box记录的timescale。每个音视频帧,它的PTS是大于等于DTS的,因为必须先解码,再渲染。

音频Track没有ctts box,因为dts和pts一致,不需要修正。

基于Box如何得到音视频Sample

  1. class Chunk{
  2. // 当前Chunk包含的Sample数量
  3. int sampleNum;
  4. // 当前Chunk的地址偏移量
  5. int offset;
  6. }
  7. class Sample{
  8. // Sample序号,从0开始计数
  9. int index;
  10. // Sample的地址偏移量
  11. int offset;
  12. uint64_t pts;
  13. uint64_t dts;
  14. // Sample大小(字节)
  15. int size;
  16. // 当前Sample所属Chunk的索引
  17. int chunkIndex;
  18. // 是否关键帧
  19. bool iSKeyFrame
  20. }

首选,根据stts和stsz,创建出所有Sample结构体,计算出每个Sample的index、pts、dts、size属性。
其次,根据stsc和stco,创建出所有Chunk,计算出每个Chunk的sampleNum和offset属性。
接着,根据Chunk列表和Sample列表,计算出每个Sample的chunkIndex和offset属性。
然后,根据stss,标识每个Sample是否是关键帧。
最后,根据ctts,修正视频Sample的PTS。

Mp4 Tricks

控制moov位置

默认情况下,FFmpeg Mp4 Muxer在写完mdat之后,在文件末尾写入moov。这导致播放器必须下载整个文件,才能开始播放,首帧时间比较长。可以通过-movflags + faststart选项,把moov移到ftyp后面,尽快获取到moovbox。

  1. ffmpeg -i input.wav -c:a libfdk_aac -movflags +faststart output.m4a

Dash + FMp4

一个包含两个Fragment的FMP4:
FMP4

FMP4的Fragment由一个Moof和Mdat构成,如下所示:
FMp4-Fragment

ftyp + moov + [moof + mdat] + [moof + mdat] + [moof + mdat] + [......]

MP4解析器

  1. mp4parser
  2. mp4box.js
  3. MP4Box
  4. bento4

参考文章

  1. MP4封装解析实战
  2. MP4 视频文件格式解析及其播放原理
  3. Fmp4之Mp4
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注