You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

161 lines
12 KiB
Markdown

2 years ago
# 14MP4 & FLV不要再说AVI了
你好,我是李江。
前面我们花了很长的时间学习了视频编码和视频传输弱网对抗的知识点。从今天开始我们来学习几个视频封装和播放的知识点。我们先来学习一下什么是音视频封装。之后我们再学习如何做音视频同步。
其实相比视频编码和传输音视频封装应该是非常简单的知识点了。而且我们前面还学习过RTP打包RTP打包音视频数据其实一定程度上也可以算是一种封装。我们今天再介绍两种常用的封装一种是FLV一种是MP4相信你对这两种文件一点儿也不陌生。
音视频封装其实就是将一帧帧视频和音频数据按照对应封装的标准有组织地存放在一个文件里面并且再存放一些额外的基础信息比如说分辨率、采样率等信息。那到底怎么组织这些基础信息还有音视频数据呢我们接下来先看看FLV是怎么做的。
## FLV
FLV是一种非常常见的音视频封装尤其是在流媒体场景中经常用到。FLV封装也是比较简单的封装格式它是由一个个Tag组成的。Tag又分为视频Tag、音频Tag和Script Tag分别用来存放视频数据、音频数据和MetaData数据。
下图就是FLV的总体结构图
![图片](https://static001.geekbang.org/resource/image/38/c0/38cc18a2a824e2001ae4d38818e691c0.jpeg?wh=1920x1080)
其总体格式图如下:
![图片](https://static001.geekbang.org/resource/image/9b/f0/9b575096e64d54e20a9398828d73f3f0.jpeg?wh=1806x1406)
### FLV Header
其中FLV Header占用9个字节。前3个字节是文件的标识固定是FLV。之后的1个字节表示版本。在之后的1个字节中的第6位表示是否存在音频数据第8位表示是否存在视频数据其他位都为0。最后的4个字节表示从文件开头到FLV Body开始的长度一般就是等于9。
### FLV Body
在FLV Header之后就是FLV Body了这就是存放主要数据的地方放置着一个个Tag。在每一个Tag前面都有一个4字节的Previous Tag Size表示前一个Tag的大小方便往回倒。再之后就是具体的Tag了。Tag又是由Tag Header和Tag Data组成其中Tag Header占用11个字节格式如上图。
**其中最重要的是时间戳,因为播放的速度还有音视频同步都需要依赖这个时间戳的值。**时间戳占用34字节如果3字节不够的话则需要使用1字节的扩展时间戳作为时间戳的高8位。还需要注意的一个点就是时间戳的单位是ms。RTP的时间戳单位是1/90000秒MP4的时间戳是可以自定义的。这个时间戳的单位也是至关重要的不要弄错了。
接下来就是Tag Data数据了。Tag Data有Script、音频和视频。首先来看一下Script Tag的Data。这个Tag存放的是MetaData数据主要包括宽、高、时长、采样率等基础信息。
Script Data使用2个AMF包来存放信息。第一个AMF包是onMetaData包。第1个字节表示的是AMF包的类型一般是字符串类型值是0x02之后是2字节的长度一般长度总是10值是0x000A。之后就是10字节长度字符串了值是onMetaData。
第二个AMF包的第一个字节是数组类型值是0x08紧接着4个字节为数组元素的个数。后面即为各数组元素的封装数组元素为元素名称和值组成的对。常见的数组元素如下表所示
![图片](https://static001.geekbang.org/resource/image/da/30/da922577ddc53bcac52b1bcccd433130.jpeg?wh=1340x1172)
音频Tag Data的第一个字节表示音频的编码方式、采样率和位宽等信息如下图所示。之后就是音频数据了。
![图片](https://static001.geekbang.org/resource/image/4a/5a/4a226cc369df7fd8ee7f469694e47f5a.jpeg?wh=1328x688)
视频Tag的第1个字节包含了这个Tag的视频帧类型和视频编码方式格式如下图
![图片](https://static001.geekbang.org/resource/image/1f/51/1fd4c7db11320f1f163b969f2d411451.jpeg?wh=1342x350)
对于H264数据紧接着会有4字节的AVC Packet Type格式如下图所示
![图片](https://static001.geekbang.org/resource/image/a8/a1/a86a39594d3295f431763959d53487a1.jpeg?wh=1336x452)
**其中最重要的就是CTS。**这个是什么意思呢这是因为H264有B帧这种类型涉及到显示时间戳PTS和解码时间戳DTS。前面Tag Header里的时间戳就是DTSPTS等于DTS + CTS这个需要注意一下。接下来就是存放具体的视频数据。
如果AVC包类型是0则数据格式如下图所示
![图片](https://static001.geekbang.org/resource/image/12/bb/12d630497d4f0e540a764e62dd7f34bb.jpeg?wh=1336x1246)
如果AVC包类型为1则数据格式如下图所示
![图片](https://static001.geekbang.org/resource/image/be/81/be79ca64a849568c1e19ce3399750e81.png?wh=1206x324)
这就是FLV封装。
## MP4
了解了FLV封装之后我们再来看一下MP4封装。MP4封装相比FLV更常见但是也更复杂一些。其实它们的基本的思想还是一样的就是用一个规定的格式组织存放音视频数据和一些基础信息。跟FLV由一个个Tag组成有点类似MP4由一个个box组成每一个box存放了不同的数据而且box里面还可以嵌套着box。
MP4最外层的box主要有三个分别是File Type boxftyp box、Movie boxmoov box和Media Data boxmdat box。其中最重要、最复杂的就是moov box了它里面存放了音视频的基本信息和每一个音视频数据的具体位置。
还有一点需要说明的就是在MP4文件中视频的一帧和音频的一段编码数据称为一个sample。连续的几个sample称之为chunk而视频的所有sample称为一个视频track同样音频的所有sample也称为一个音频track。
因此一般MP4文件是由音频track和视频track组成而track由sample组成其中若干个sample组成一个chunk。
**好了下面我们就来看看比较重要的box吧。**因为MP4的box特别多我们不会一个个都介绍我们只介绍一下比较重要的box。
每一个box都是由Box Header和Box Data组成。Box Header的结构如下图所示
![图片](https://static001.geekbang.org/resource/image/e8/90/e888133ea73c584a711098869f6f6790.jpeg?wh=1198x572)
根据Box Header中的type我们将box分为不同类型的box每一种不同的box对应的Box Data都是不一样。Box Data里面又可以嵌套box。MP4的总体box分布图如下图所示
![图片](https://static001.geekbang.org/resource/image/7f/d1/7f22c603689c17b42098b32eyyf034d1.jpeg?wh=1920x1333)
首先ftyp box放在MP4文件的开始用来表示文件类型该box的Box Data包含了4字节的主版本major brand、4字节的版本号minor version和若干个4字节数组组成的兼容版本compatible\_brands
**mdat box是MP4的音视频数据存放的地方**。mdat box 基本由头部和数据两部分组成box type是 “mdat” 的ASCII码值。对于H264来说是一个个NALU码流格式使用的是[第05讲](https://time.geekbang.org/column/article/461658)里面的MP4格式。这里的NLAU不再包含SPS和PPS这些数据已经放到moov box里面了此处NALU类型是图像数据或者SEI数据。
**另一个box就是最重要的moov box用来存放Metadata信息。**这个box可以放在ftyp的后面也可以放置在文件的最后面。moov box里面会一层层嵌套很多层box。总体嵌套逻辑就是movie里面是tracktrack里面是sample多个sample又组成了一个个chunk。
moov box首先有一个mvhd boxmovie header box主要存放文件的基本信息比如说MP4文件的创建时间、时间单位、总时长等信息。
moov box中的另外一个重要的box就是trak box这个box音频和视频各有一个。具体是音频trak还是视频trak会在trak box中的mdia box里面的hdlr box中表示出来。
trak box内部有一个tkhd boxtrack header box主要是表示track的一些基本信息比如说视频的宽高信息和音频的音量信息等。
trak box还有一个mdia box它是媒体信息box。它包含了3个子box一个是mdhd box一个是刚才提到的hdlr box一个是最重要的minf box这个box里面包含了sample的很多信息这些信息是找到并正确使用音频和视频数据的关键。
mdhd box里面最重要的一个值就是时间单位time scale这个时间单位是sample的时间戳的单位控制播放速度和音视频同步都需要使用到这个值。
hdlr box主要包含了track的类型信息表明是音频还是视频track。
minf box里面包含了一个stbl boxsample table box里面存放着可以计算得到每一个chunk的偏移地址、每一个sample在文件中的地址信息和大小、每一个sample的时间戳和每一个视频IDR帧的地址信息。下面我们来详细介绍一下这些box。
其中stts box中放置的是每一个sample的时长这个值是DTS。
![图片](https://static001.geekbang.org/resource/image/6a/75/6a94cf7fcee63b81bd1375f42a206275.jpeg?wh=1342x1250)
ctts box放置着CTS也就是每一个sample的PTS和DTS的差值。
![图片](https://static001.geekbang.org/resource/image/58/8d/58b59fc37129abcb6e7398f98cb6ea8d.jpeg?wh=1348x1254)
stss box中放置的是哪些sample是关键帧。
![图片](https://static001.geekbang.org/resource/image/94/8a/94347b1d78b04a9995yyea1110yy338a.jpeg?wh=1332x1068)
stsc box中放置的是sample到chunk的映射表也就是哪些sample属于哪个chunk。
![图片](https://static001.geekbang.org/resource/image/03/a5/034ec8ab5228e2c65240c7fdb2f190a5.jpeg?wh=1338x1446)
stco box或co64 box中放置着每个chunk在文件中的偏移地址。
![图片](https://static001.geekbang.org/resource/image/5c/ca/5ca7437ed49766cfc827fa75891680ca.jpeg?wh=1332x1104)
stsz box中放置着每一个sample的大小。
![图片](https://static001.geekbang.org/resource/image/aa/99/aa0b8bef4d0c23da67c1468e92682599.jpeg?wh=1336x1214)
好了跟sample相关的box就是这些。
### 工程实践
接下来我们结合一个工程问题来实践一下。我们如何计算每一个sample在文件中的具体位置判断它是不是关键帧并计算它的具体时间。
计算sample的具体位置需要使用stco或co64、stsc和stsz。我们首先通过stsc将每一个sample属于哪一个chunk计算出来。这样每一个chunk的第一个sample就知道是哪个了。然后我们通过stco和co64就可以知道对应序号的chunk的第一个sample在文件中的地址了。我们再通过stsz查询每个sample的大小从chunk的第一个sample的地址将中间的sample的大小一个个地加上去就可以得到每一个sample的地址了。
![图片](https://static001.geekbang.org/resource/image/aa/7c/aaac0910d536a50191854cae468b307c.jpeg?wh=1904x852)
而sample是不是关键帧我们只需要通过stss对应每一个sample序号查询就可以得到。
![图片](https://static001.geekbang.org/resource/image/54/c5/54fa75cc33d0690c8c206f6bac6b82c5.jpeg?wh=1904x841)
计算sample的时间我们需要用到stts和ctts。我们先通过stts得到每一个sample的时长第n个sample的结束时间就是第n-1个sample的结束时间加上第n个sample的时长。但是需要注意一下这个是DTS我们还需要通过ctts box得到每一个sample的PTS和DTS的差值。最后每一个sample的PTS就是等于DTS加上CTS。
![图片](https://static001.geekbang.org/resource/image/02/7b/02d684c8ea5e8405fb8f2ce341b90d7b.jpeg?wh=1913x896)
好了以上就是MP4封装的主要内容。
## 总结
今天我们主要介绍了一下两种音视频封装格式分别是FLV和MP4。这两种封装格式是我们工作和生活中经常需要用到的。
* FLV在流媒体场景经常会用到其实直播RTMP协议和HTTP-FLV协议里面也是用的FLV封装所以还是很重要的。
* MP4封装就是平时视频文件最常用的封装了它主要由一个个box组成其中最重要的就是跟sample有关的box你需要重点掌握。当然你也不需要背下来了解主要思想即可等真正用到的时候查询一下就可以了。
## 思考题
好了今天的课程到这里就结束了。在课程的最后我给你留一道思考题。请你想一想为什么FLV相比MP4更适合流媒体
你可以在留言区留下你的答案,或者你有任何问题都可以在留言区和我交流,我们下节课再见。