【FFmpeg实战】MP4封装格式分析

原文地址:https://www.cnblogs.com/moonwalk/p/16244932.html

解析工具:

  • https://gpac.github.io/mp4box.js/test/filereader.html (mp4box)

1. 概述

mp4 容器格式相较于 flv、ts 容器格式来说,其定义较为复杂,本篇文章主要记录自己的学习理解。

2. mp4 结构概览

一个 mp4 文件可以看作有两个大类:

  • 记录媒体信息相关的部分
  • 媒体负载部分

典型结构如下:

  +-+-+-+-+-+-+-+-+-+-+-+-+-+
  |  ftyp  |  moov |  mdat  |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+

其中:

  • ftyp(file type box),文件头,记录一些兼容性信息
  • moov(movie box),记录媒体信息
  • mdat(media data),媒体负载

mp4box 图示如下:

img

编辑

添加图片注释,不超过 140 字(可选)

3. box 结构

mp4 封装格式采用称为 box 的结构来组织数据。 结构如下:

  +-+-+-+-+-+-+-+-+-+-+
  |  header  |  body  |
  +-+-+-+-+-+-+-+-+-+-+

其它所有 box 都在语法上继承自此基本 box 结构。

3.1 box header

box 分为普通 box 和 fullbox。 普通 box header 结构如下:

字段类型描述
size4 Bytes包含 box header 的整个 box 的大小
type4 Bytes4 个 ascii 值,如果是 “uuid”,则表示此 box 为用户自定义,可忽略
large size8 Bytessize=1 时才有的字段,用于扩展,例如 mdat box 会需要此字段

fullbox 在上面的基础上新增了 2 个字段:

字段类型描述
version1 Bytes版本号
flags3 Bytes标识

3.2 box body

一个 box 可能会包含其它多个 box,此种 box 称为 container box。 因此 box body 可能是一种具体 box 类型,也有可能是其它 box。

4. ftyp(File Type Box)

ftyp 一般出现在文件的开头,用来指示该 mp4 文件使用的标准规范:

字段类型描述
major_brand4 bytes主版本
minor_version4 bytes次版本
compatible_brands[]4 bytes指定兼容的版本,注意此字段是一个 list,即可以包含多个 4 bytes 版本号

一个示例如下:

img

编辑

添加图片注释,不超过 140 字(可选)

5. moov(Movie Box)

moov 是一个 container box,一个文件只有一个,其包含的所有 box 用于描述媒体信息。 moov 的位置可以紧随着 ftyp 出现,也可以出现在文件末尾. 由于是一个 container box,所以除了 box header,其 box body 就是其它的 box。 一个示例如下:

img

编辑

添加图片注释,不超过 140 字(可选)

这些 box 并列出现,其中:

  • mvhd(moov header),用于简单描述一些所有媒体共享的信息
  • trak,即 track,轨道,用于描述音频流或视频流信息,可以有多个轨道,如上出现了 2 次,分别表示一路音频和一路视频流
  • udta(userdata),用户自定义,可忽略

5.1 mvhd(Movie Header Box)

mvhd 作为媒体信息的 header 出现(注意此 header 不是 box header,而是 moov 媒体信息的 header),用于描述一些所有媒体共享的基本信息。 mvhd 语法继承自 fullbox,注意下述示例出现的 version 和 flags 字段属于 fullbox header。

字段类型描述
version8 bit取 0 或 1,一般取 0
flags24 bit
creation_time64/32 bit创建时间,当 version=0 时取 32bit
modification_time64/32 bit修改时间,当 version=0 时取 32bit
timescale32 bit时间基
duration64/32 bit文件时长,当 version=0 时取 32bit
rate32 bit播放速率,默认取 0x00010000,即 1.0
volume16 bit音量,默认取 0x0100,即 1.0
reserved16 bit0
reserved2 x 32 bit0
matrix9 x 32 bit可忽略
pre_defined6 x 32 bit0
next_track_ID32 bit下一个紧邻的 track box id

6. trak

trak box 是一个 container box,其子 box 包含了该 track 的媒体信息。 一个 mp4 文件可以包含多个 track,track之间是独立的,trak box 用于描述每一路媒体流。 一个示例如下:

img

编辑

添加图片注释,不超过 140 字(可选)

其中:

  • tkhd(track header box),用于简单描述该路媒体流的信息
  • edts(),
  • mdia(media box),用于详细描述该路媒体流的信息

6.1 tkhd(track header box)

tkhd 作为媒体信息的 header 出现(注意此 header 不是 box header,而是 track 媒体信息的 header),用于描述一些该 track 的基本信息。 tkhd 语法继承自 fullbox,注意下述示例出现的 version 和 flags 字段属于 fullbox header。

字段类型描述
version8 bit取 0 或 1,一般取 0
flags24 bit
creation_time64/32 bit创建时间,当 version=0 时取 32bit
modification_time64/32 bit修改时间,当 version=0 时取 32bit
track_ID32 bit本 track id
reserved32 bit0
duration64/32 bit本 track 时长,当 version=0 时取 32bit
reserved2 x 32 bit0
layer16 bit
alternate_group16 bit
volume16 bit音量,如果是 audio track,则为 0x0100,即 1.0,否则取 0
reserved16 bit0
matrix9 x 32 bit可忽略
width32 bit宽,[16.16] 格式,不必与sample的像素尺寸一致,用于播放时的展示宽高
height32 bit高,[16.16] 格式 ,不必与sample的像素尺寸一致,用于播放时的展示宽高

6.2 edts

6.3 mdia(media box)

mdia box 是一个 container box,其子 box 描述 track 更详细的媒体信息。 一个示例如下:

img

编辑

添加图片注释,不超过 140 字(可选)

其中:

  • mdhd(Media Header Box),用于简单描述该路媒体流的信息
  • hdlr(Handler Reference Box),主要定义了 track 类型
  • stbl(Media Information Box),用于描述该路媒体流的解码相关信息和音视频位置等信息

6.3.1 mdhd(Media Header Box)

mdhd 作为媒体信息的 header 出现(注意此 header 不是 box header,而是 media 媒体信息的 header),用于描述一些该 track 的基本信息。 mdhd 语法继承自 fullbox,注意下述示例出现的 version 和 flags 字段属于 fullbox header。

字段类型描述
version8 bit取 0 或 1,一般取 0
flags24 bit
creation_time64/32 bit创建时间,当 version=0 时取 32bit
modification_time64/32 bit修改时间,当 version=0 时取 32bit
timescale32 bit时间基,同 mvhd 中 timescale
duration64/32 bit本 track 时长,当 version=0 时取 32bit
pad1 bit0
language3 x 5 bit最高位为0,后面15位为3个字符(见ISO 639-2/T标准中定义)
pre_defined16 bit0

6.3.2 hdlr(Handler Reference Box)

hdlr 主要定义了 track 类型。 hdlr 语法继承自 fullbox,注意下述示例出现的 version 和 flags 字段属于 fullbox header。

字段类型描述
version8 bit取 0 或 1,一般取 0
flags24 bit
pre_defined32 bit0
handler_type32 bit取值为 vide:Video track,soun:Audio track,hint:Hint track,meta:Timed Metadata track,auxv:Auxiliary Video track
reserved3 x 32 bit0
name不定长度自定义名称,以 \0 结尾

7. 音视频 frame 与 sample、chunk 的关系

在继续介绍 stbl box 之前,需要先介绍一下 mp4 中定义的 sample 与 chunk:

  • sample,ISO/IEC 14496-12 中定义 samples 之间不能共享同一个时间戳,因此,在音视频 track 中,一个 sample 代表一个视频或音频帧
  • chunk,多个 sample 的集合,实际上音视频 track 中,chunk 与 sample 一一对应

8. stbl(Media Information Box)

stbl box 是一个 container box,是整个 track 中最重要的一个 box,其子 box 描述了该路媒体流的解码相关信息、音视频位置信息、时间戳信息等。 一个示例如下:

img

编辑

添加图片注释,不超过 140 字(可选)

其中:

  • stsd(sample description box),存储了编码类型和初始化解码器需要的信息,并与具体编解码器类型有关
  • stts(time to sample box),存储了该 track 每个 sample 到 dts 的时间映射关系
  • stss(sync sample box),针对视频 track,关键帧所属sample 的序号
  • ctts(composition time to sample box),存储了该 track 中,每个 sample 的 cts 与 dts 的时间差
  • stsc/stz2(sample to chunk box),存储了该 track 中每个 sample 与 chunk 的映射关系
  • stsz(sample size box),存储了该 track 中每个 sample 的字节大小
  • stco/co64(chunk offset box),存储了该 track 中每个 chunk 在文件中的偏移

8.1 stsd(sample description box)

stsd 是个 container box,其存储了编码类型和初始化解码器需要的信息,并与具体编解码器类型有关:

字段类型描述
version8 bit取 0 或 1,一般取 0
flags24 bit
entry_count32 bitentry 个数
开始循环
AudioSampleEntry()不定大小子 box,当 handler_type=‘soun’ 时才有
VisualSampleEntry()不定大小子 box,当 handler_type=‘vide’ 时才有
HintSampleEntry()不定大小子 box,当 handler_type=‘hint’ 时才有
MetadataSampleEntry()不定大小子 box,当 handler_type=‘meta’ 时才有
结束循环

8.1.1 h264 stsd

对于 h264 视频,典型结构如下:

img

编辑

添加图片注释,不超过 140 字(可选)

其上(只列出 avc1 与 avcC,其余 box 可忽略):

  • avc1,是 avc/h264/mpeg-4 part 10 视频编解码格式的代称,是一个 container box,但是 box body 也携带自身的信息
字段类型描述
reserved6 x 8 bit0
data_reference_index16 bit
pre_defined16 bit0
reserved16 bit0
pre_defined3 x 32 bit0
width16 bit像素宽度
height16 bit像素高度
horizresolution32 bit每英寸的像素值(dpi),[16.16]格式的数据,固定为 0x00480000,72 dpi
vertresolution32 bit每英寸的像素值(dpi),[16.16]格式的数据,固定为 0x00480000,72 dpi
reserved32 bit0
frame_count16 bit每个 sample 中的视频帧数,固定为 1
compressorname32 bit0
depth16 bit0x0018,rgb24 位深
pre_defined16 bit-1
  • avcC(AVC Video Stream Definition Box),存储 sps && pps,即在 ISO/IEC 14496-15 中定义的 AVCDecoderConfigurationRecord 结构
字段类型描述
configurationVersion8 bit固定 1
AVCProfileIndication8 bit编码时设置的 profile,例如 base、main、high 等,具体参见 ISO_IEC_14496-10-AVC-2003.pdf, page 45
profile_compatibility8 bit
AVCLevelIndication8 bit编码时设置的 level
reserved6 bit'111111’b
lengthSizeMinusOne2 bit
reserved3 bit'111’b
numOfSequenceParameterSets8 bitsps 个数,一般为 1
sps 循环开始
sequenceParameterSetLength16 bit
sps8 * sequenceParameterSetLengthsps
sps 循环结束
numOfPictureParameterSets8 bitpps 个数,一般为 1
pps 循环开始
pictureParameterSetLength16 bit
pps8 * pictureParameterSetLengthpps
pps 循环结束

在 srs 中,解析 avcc/AVCDecoderConfigurationRecord 结构解析参见 srs/trunk/src/kernel/srs_kerner_codec.cpp::SrsFormat::avc_demux_sps_pps() 函数。

8.1.2 aac stsd

对于 aac 音频,典型结构如下:

img

编辑

添加图片注释,不超过 140 字(可选)

可以看到,aac stsd 结构比较复杂,box 众多。实际上,在 ISO/IEC 14496-3 中,定义了 AudioSpecificConfig 类型,aac stsd 结构主要信息就来自 AudioSpecificConfig。 具体不做分析,可以参看 srs 中:

  • 解析 AudioSpecificConfig 结构的 srs/trunk/src/kernel/srs_kerner_codec.cpp::SrsFormat::audio_aac_sequence_header_demux() 函数
  • 封装 aac stsd 结构的 srs/trunk/src/kernel/srs_kernel_mp4.cpp::SrsMp4Encoder::flush() 函数

8.2 stts(time to sample box)

存储了该 track 每个 sample 到 dts 的时间映射关系:

字段类型描述
version8 bit取 0 或 1,一般取 0
flags24 bit
entry_count32 bit条目个数
开始循环
sample_count32 bit播放时长为 sample_delta 的连续 sample 个数
sample_delta32 bit单个 sample 的播放时长,单位为 timescale
结束循环

这里为了节约条目的个数,采用了压缩存储的方式,即 sample_count 个连续的 sample 如果 sample_delta 时长一样,那么用一个条目就能表示了。 一个音频 track 的示例如下:

img

编辑

添加图片注释,不超过 140 字(可选)

一个视频 track 的示例如下:

img

编辑

添加图片注释,不超过 140 字(可选)

8.3 ctts(composition time to sample box)

存储了该 track 中,每个 sample 的 pts 与 dts 时间差(cts = pts - dts):

字段类型描述
version8 bit取 0 或 1,一般取 0
flags24 bit
entry_count32 bit条目个数
开始循环
sample_count32 bitcts 为 sample_offset 的连续 sample 个数
sample_offsetsigned/unsigned 32 bit单个 sample 的 cts,单位为 timescale。支持负数,当 version=1 时为 signed
结束循环

注意:

  • 此 box 在 dts 和 pts 不一样的情况下必须存在,如果一样,不用包含此 box
  • 如果 box 的 version=0,意味着所有 sample 都满足 pts >= dts,因而差值用一个无符号的数字表示。只要存在一个 pts < dts,那么必须使用 version=1、有符号差值来表示
  • 关于 ctts 的生成,可以参看 srs/trunk/src/kernel/srs_kernel_mp4.cpp::SrsMp4SampleManager::write_track() 函数 pts、dts、cts 满足公式:pts - dts = cts

8.4 stss(sync sample box)

针对视频 track,关键帧所属sample 的序号。

字段类型描述
version8 bit取 0 或 1,一般取 0
flags24 bit
entry_count32 bit条目个数
开始循环
sample_number32 bitsample 计数,从 1 开始
结束循环

一个视频示例如下:

img

编辑切换为居中

添加图片注释,不超过 140 字(可选)

8.5 stsc/stz2(sample to chunk box)

存储了该 track 中每个 sample 与 chunk 的映射关系。

字段类型描述
version8 bit取 0 或 1,一般取 0
flags24 bit
entry_count32 bit条目个数
开始循环
first_chunk32 bit一组 chunk 的第一个 chunk 的序号,chunk 的编号从 1 开始
samples_per_chunk32 bit每个 chunk 有多少个 sample
sample_description_index32 bitstsd box 中 sample desc 信息的索引
结束循环

一个音频示例如下:

img

编辑切换为居中

添加图片注释,不超过 140 字(可选)

  • 第一组 chunk 的 first_chunk 序号为 1,每个 chunk 的 sample 个数为 1,因为第二组 chunk 的 first_chunk 序号为 2,可知第一组 chunk 中只有一个 chunk
  • 第二组 chunk 的 first_chunk 序号为 2,每个 chunk 的 sample 个数为 2,因为第三组 chunk 的 first_chunk 序号为 24,可知第二组 chunk 中有 22 个 chunk,有 44 个 sample

8.6 stsz(sample size box)

存储了该 track 中每个 sample 的字节大小。

字段类型描述
version8 bit取 0 或 1,一般取 0
flags24 bit
sample_size32 bit指定默认的 sample 字节大小,如果所有 sample 的大小不一样,这个字段为 0
sample_count32 bittrack 中 sample 的数量
开始循环
entry_size32 bit每个 sample 的字节大小
结束循环

8.7 stco/co64(chunk offset box)

存储了该 track 中每个 chunk 在文件中的偏移。

字段类型描述
version8 bit取 0 或 1,一般取 0
flags24 bit
entry_count32 bit
开始循环
chunk_offset32 bitchunk 在文件中的位置
结束循环

9. mdat

mdat 也是一个 box,拥有 box header 和 box body。 mdat 可以引用外部的数据,参见 moov --> udta --> meta,这里不讨论,只讨论数据存储在本文件中的形式。 对于 box body 部分,采用一个一个 samples 的形式进行存储,即一个一个音频帧或视频帧的形式进行存储。 码流组织方式采用 avcc 格式,即 AUD + slice size + slice 的形式。

9.1 通过时间进行定位寻址/随机访问

参考 https://www.jianshu.com/p/529c3729f357 6.2 节

  >>> 音视频开发 视频教程: https://ke.qq.com/course/3202131?flowToken=1031864 
  >>> 音视频开发学习资料、教学视频,免费分享有需要的可以自行添加学习交流群: 739729163  领取

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值