Asterisk播放mp4(5)——MP4文件解析

Asterisk 现有版本不支持播放视频文件(支持视频通话),无法满足发送视频通知、视频 IVR 等场景。本系列文章,通过学习音视频的相关知识和工具,尝试实现一个通过 Asterisk 播放 mp4 视频文件的应用。
  • Asterisk播放mp4(1)——音频和PCM编码
  • Asterisk播放mp4(2)——音频封装
  • Asterisk播放mp4(3)——搭建开发环境
  • Asterisk播放mp4(4)——H264&AAC
  • Asterisk播放mp4(5)——MP4文件解析
  • Asterisk播放mp4(6)——音视频同步
  • Asterisk播放mp4(7)——DTMF
本文关注在MP4文件中,h264aac的媒体数据是如何存放的,又如何进行访问?为后续解析文件后,通过RTP进行发送进行准备。
MP4文件格式 MP4文件封装格式,对应的标准为ISO/IEC 14496-12,即:信息技术视听对象编码的第12部分:ISO 基本媒体文件格式(Information technology Coding of audio-visual objects Part 12: ISO base media file format)。MP4文件可以嵌入任何形式的数据,不过通常存放的是AVC(H.264)编码的视频和AAC/MPEG-4(Part 2)编码的音频。
MP4文件由许多box组成,box可以嵌套。box分为headerdata两部分。header包括size(4字节),type(4字节)和largesize(8字节,可选)。其中,size指明了整个box的大小,包括header部分。如果box很大(例如存放具体视频数据的mdat box),超过了uint32的最大数值,size就被设置为1,并用接下来的8位uint64来存放大小。box中的字节序为网络字节序,也就是大端字节序(Big-Endian),就是一个32位的4字节整数存储方式为高位字节在内存的低端。
下面看看几个主要的box
box类型 层级 名称 说明
ftyp 1 File Type Box 有且只有1个,只能被包含在文件层,而不能被其他 box 包含。该 box 应该被放在文件的最开始,指示该 MP4 文件应用的相关信息。
moov 1 Movie Box 有且只有1个,只能被包含在文件层。moov是一个container box,包含若干子box,例如:mvhd,trak等,子box中存放媒体的元数据(metadata)信息。
mvhd 2 Movie Header Box 描述了媒体一些基本信息,例如:timescale,duration等。
trak 2 Track Box trak也是一个container box,其子box包含了该track的媒体数据引用和描述(hint track除外)。一个MP4文件中的媒体可以包含1个或多个track,它们之间彼此独立,有自己的时间和空间信息。trak至少包含tkhd和mdia这两个box,此外还有很多可选的box。其中tkhdtrack header boxmdiamedia box,该box是一个包含一些track媒体数据信息box的container box。
mdat 1 Media Data 实际媒体数据,最终解码播放的音视频数据都在这里面。对于h264aac编码的媒体来说,其视频mdat中内容是nal,对于音频来说,其内容为aac的一帧。mdat中的帧依次存放,每个帧的位置、时间、长度都由moov中的信息指定。
Asterisk播放mp4(5)——MP4文件解析
文章图片
mp4 box trak是mp4中最复杂的部分,包含了读取媒体数据所需的各种信息。要理解trak下的box首先要掌握几个基本概念,包括:track,sample,trunk。
  • track:表示sample的集合,对于媒体数据来说,track表示一个视频或音频序列。
  • sample:video sample即为一帧视频,或一组连续视频帧,audio sample即为一段连续的压缩音频。
  • chunk:一个track中的几个sample组成的单元。
trak下有很多box,最重要也是最复杂的是trak/mdia/minf/stbl,它下面又包含了多个boxsample table指明sampe时序和存储地址的表。利用这个表,可以解析sample的时序、类型、大小以及在文件中的位置。下面对这些box做个简单的说明:
box类型 名称 说明
stsd Sample Description Box 包含h264编码的spspps。可以有一个到多个sample description
stsc Sample To Chunk Box 指定了chunksample的对应关系。从first_chunk这个chunk序号开始,每个chunk都有samples_per_chunksample,每个sample都可以通过sample_description_index这个索引,在stsd中找到描述信息。
stsz Sample Size Box 指定了每个sample的大小。
stco Sample Size Box 指定了每个chunk的在整个文件中的起始地址。
stss Sync Sample Box 关键帧。
stts DecodingTime to Sample 用于计算sampledts,其中sample_counts定义连续多少个sample的dts具有相同的差值,sample_deltadts的差值。
ctts Composition Time to Sample 每个sample的构成时间(Composition Time)和解码时间(DT)之间的差值。如果不存在ctts,则代表该流不存在B帧,那么CT就直接等于DT。
Asterisk播放mp4(5)——MP4文件解析
文章图片
帧顺序及时间 上图是ISO/IEC 14496-12规范中给出的示例。第2行代表了视频帧的存储序列,帧后面的编号代表了显示顺序。视频流编码时,如果支持B帧,P帧会先于B帧编码,因此帧编码顺序(存储顺序)和帧播放顺序不一致。通过这个图我们就更容理解上面两个和时间相关的box的含义。stts定义的是表格中的第3行,DT,用于计算出每个sampledtsctts对应的是表格中的第6行,Composition offset,用于计算出每个samplepts(Compostion Time)
DTS(Decode Time Stamp):标识读入内存中的视频帧什么时候开始送入解码器中进行解码。PTS(Presentation Time Stamp):用于度量解码后的视频帧什么时候被显示出来 。这两个值对应的并不是秒,毫秒这些时间单位,而是时间刻度,它们的值除以mdhd中的timescale(每秒钟有多少个时间刻度)转换为时间。
制作样本 为了控制篇幅我们忽略音频流,只分析视频流。
ffmpeg -t 10 -lavfi sine -t 10 -lavfi color=red sine-red-10s.mp4
Asterisk播放mp4(5)——MP4文件解析
文章图片
FFmpeg命令行输出 sine-red-10s.mp4 Asterisk播放mp4(5)——MP4文件解析
文章图片
vi %!xxd sine-red-10s.mp4 mdat中存放的就是媒体数据,视频就是h264NALU,我们可以将mp4中的数据和h264裸流中的数据进行对比。
ffmpeg -t 10 -lavfi color=red red-default.h264
我们直接生成h264的裸流,其中I/P/B帧的数据和生成mp4文件中的h264流的数量是一致的。
Asterisk播放mp4(5)——MP4文件解析
文章图片
FFmpeg命令行输出 red.h264 通过ffmpeg生成mp4文件时有很多参数可以指定,faststart是比较常用的一个,其作用是将moov挪到mdat前面,这样就可以实现边下载边播放。
ffmpeg -t 10 -lavfi sine -t 10 -lavfi color=red -movflags faststart sine-red-10s-faststart.mp4
Asterisk播放mp4(5)——MP4文件解析
文章图片
FFmpeg输出 sine-red-10s-faststart.mp4 Asterisk播放mp4(5)——MP4文件解析
文章图片
sine-red-10s-faststart.mp4 mp4文件的结构太复杂了,直接看二进制数据很费劲,下面我们通过一个在线工具解析mp4数据。
Asterisk播放mp4(5)——MP4文件解析
文章图片
ftyp Asterisk播放mp4(5)——MP4文件解析
文章图片
video - mdhd 后面计算时序时要用到timescaleduration这两个参数,duration / timescale = 10秒。
Asterisk播放mp4(5)——MP4文件解析
文章图片
video - stsd 从stsd/avc1/avcC中可以获得h264spspps。另外,需要特别注意lengthSizeMinusOne这个参数,其含义是用几个字节表示nalu的长度,实际的长度是值加1,这里就是3+1=4h264裸流中,分割nalu通常采用的是Annex B这种用起始码的方式(用3字节或4字节的起始码),但是在MP4中使用的是AVCC方式(填加字节指定nalu的大小)。
Asterisk播放mp4(5)——MP4文件解析
文章图片
video - stsz stsc中指明track共有250个采样,以及每个采样的大小。这里需要注意两点:1、采样的大小是NALU的实际大小加4,因为前面有4字节记录用来记录尺寸;2、采样不一定是一个NALU,实际上第一个采样(759)就包括了SEIIDR两个NALU
Asterisk播放mp4(5)——MP4文件解析
文章图片
video - stsc stsc中定义了chunksample的对应关系,以及sample的描述信息(stsd)。这里第一个chunk包含了两个sample,后面每个chunk包含一个sample
Asterisk播放mp4(5)——MP4文件解析
文章图片
video -stco stco中定义了chunk在文件中的地址。通过stszstscstco就可以在文件中获得任意一个sample9792是第一个chunk的偏移量,第一个chunk包含2个采样,前2个采样的大小分别为75917,可以计算出下一个chunk的偏移量是10568,它不等于下一个视频chunk的偏移量10991,通过查看音频的stco,可知音频流的第一个chunk的偏移量是10568,这说明视频和音频的chunk是交错存储的。
Asterisk播放mp4(5)——MP4文件解析
文章图片
video - stts stts中记录了每个sample间相差的时间刻度512。前面的timescale的值为12800,512/12800=0.04秒,说明每帧之间的解码时间间隔为0.04秒。
Asterisk播放mp4(5)——MP4文件解析
文章图片
video - ctts ctts提供的是composition timedecoding time的差值,它们的和就是composition time
通过解析red.h264文件的slice可以知道每帧的大小和类型,这样方便我们理解decode time
Asterisk播放mp4(5)——MP4文件解析
文章图片
解析red.h264文件的nalu
1 视频帧 I P B B B P B B B
2 DT(stts) 0 512 1024 1536 2048 2560 3072 3584 4096
3 Composition offset(ctts) 1024 2560 1024 0 512 2560 1024 0 512
4 CT 1024 3072 2048 1536 2560 5120 4096 3584 4608
5 seq 1 5 3 2 4 9 7 6 8
6 size 65 13 10 10 10 19 12 10 10
上表中的第1行(DT)代表的是文件中视频帧的存储顺序,第4行(CT)代表的是视频帧的播放时间(单位是时间刻度),第5行(seq)代表的是视频帧的播放顺序。可以看到存储顺序和播放顺序是不一致的,这是因为视频帧中包含B帧,它是双向依赖帧,解析是可能要依赖P(或B帧),所以虽然B帧的播放顺序靠前,但是解码的时候必须要先解码P帧,才能解码B帧。另外,在avcc中一个sample的前4个字节代表这个包的大小,因此stsz中记录的采样大小比NALU的大小多4。
通过对比h264裸流和mp4中的视频数据,我们可以得到一致的结果,就是说mp4中的sttsctts记录h264的解析结果,方便了数据的直接访问。
参考 QuickTime container 有box类型的定义
MP4文件格式解析
在线MP4解析工具
ISO/IEC 14496-12:2015 下载地址
【Asterisk播放mp4(5)——MP4文件解析】FFmpeg formats - mov, mp4, ismv

    推荐阅读