音视频封装: FLV & MP4

文章详细介绍了音视频封装的概念,以FLV和MP4为例,阐述了它们的数据组织结构。FLV由Tag组成,包括视频、音频和元数据Tag,而MP4则由Box构成,如ftypbox、mdatbox和moovbox,其中moovbox包含metadata信息和track信息。文章还讨论了时间戳、采样率等关键信息,并解释了如何在MP4中定位和计算sample的位置及时间。
摘要由CSDN通过智能技术生成

音视频封装其实就是将一帧帧视频和音频数据按照对应封装的标准有组织地存放在一个文件里面,并且再存放一些额外的基础信息,比如说分辨率、采样率等信息。那到底怎么组织这些基础信息还有音视频数据呢?我们接下来先看看 FLV 是怎么做的。

FLV

FLV 是一种非常常见的音视频封装,尤其是在流媒体场景中经常用到。FLV 封装也是比较简单的封装格式,它是由一个个 Tag 组成的。Tag 又分为视频 Tag、音频 Tag 和 ScriptTag,分别用来存放视频数据、音频数据和 MetaData 数据。

下图就是 FLV 的总体结构图:
在这里插入图片描述
其总体格式图如下:
在这里插入图片描述
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 个字节,格式如上图。

其中最重要的是时间戳,因为播放的速度还有音视频同步都需要依赖这个时间戳的值。时间戳占用 3~4 字节,如果 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 个字节为数组元素的个数。后面即为各数组元素的封装,数组元素为元素名称和值组成的对。常见的数组元素如下表所示:

在这里插入图片描述
音频 Tag Data 的第一个字节表示音频的编码方式、采样率和位宽等信息,如下图所示。之后就是音频数据了。

在这里插入图片描述
视频 Tag 的第 1 个字节包含了这个 Tag 的视频帧类型和视频编码方式,格式如下图:

在这里插入图片描述
对于 H264 数据,紧接着会有 4 字节的 AVC Packet Type 格式,如下图所示:

在这里插入图片描述
其中最重要的就是 CTS。这个是什么意思呢?这是因为 H264 有 B 帧这种类型,涉及到显示时间戳 PTS 和解码时间戳 DTS。前面 Tag Header 里的时间戳就是 DTS,PTS 等于DTS + CTS,这个需要注意一下。接下来就是存放具体的视频数据.

如果 AVC 包类型是 0,则数据格式如下图所示:
在这里插入图片描述
如果 AVC 包类型为 1,则数据格式如下图所示:
在这里插入图片描述
这就是 FLV 封装。

MP4

了解了 FLV 封装之后,我们再来看一下 MP4 封装。MP4 封装相比 FLV 更常见,但是也更复杂一些。其实它们的基本的思想还是一样的,就是用一个规定的格式组织存放音视频数据和一些基础信息。跟 FLV 由一个个 Tag 组成有点类似,MP4 由一个个 box 组成,每一个 box 存放了不同的数据,而且 box 里面还可以嵌套着 box。

MP4 最外层的 box 主要有三个,分别是 File Type box(ftyp box)、Movie
box(moov box)和 Media Data box(mdat 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 的结构如下图所示:
在这里插入图片描述
根据 Box Header 中的 type 我们将 box 分为不同类型的 box,每一种不同的 box 对应的Box Data 都是不一样。Box Data 里面又可以嵌套 box。MP4 的总体 box 分布图如下图所示:

在这里插入图片描述
首先,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,码流格式使用的里面的 MP4 格式。这里的 NLAU 不再包含 SPS 和 PPS,这些数据已经放到 moov box 里面了,此处 NALU 类型是图像数据或者 SEI 数据。

另一个 box 就是最重要的 moov box,用来存放 Metadata 信息。这个 box 可以放在ftyp 的后面也可以放置在文件的最后面。moov box 里面会一层层嵌套很多层 box。总体嵌套逻辑就是 movie 里面是 track,track 里面是 sample,多个 sample 又组成了一个个chunk。

moov box 首先有一个 mvhd box(movie header box)主要存放文件的基本信息,比如说 MP4 文件的创建时间、时间单位、总时长等信息。

moov box 中的另外一个重要的 box 就是 trak box,这个 box 音频和视频各有一个。具体是音频 trak 还是视频 trak,会在 trak box 中的 mdia box 里面的 hdlr box 中表示出来。

trak box 内部有一个 tkhd box(track 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 box(sample table box),里面存放着可以计算得到每一个 chunk 的偏移地址、每一个 sample 在文件中的地址信息和大小、每一个 sample 的时间戳和每一个视频 IDR 帧的地址信息。下面我们来详细介绍一下这些 box。

其中,stts box 中放置的是每一个 sample 的时长,这个值是 DTS。
在这里插入图片描述
ctts box 放置着 CTS,也就是每一个 sample 的 PTS 和 DTS 的差值。
在这里插入图片描述
stss box 中放置的是哪些 sample 是关键帧。

在这里插入图片描述
stsc box 中放置的是 sample 到 chunk 的映射表,也就是哪些 sample 属于哪个chunk。

在这里插入图片描述
stco box 或 co64 box 中放置着每个 chunk 在文件中的偏移地址。

在这里插入图片描述
stsz box 中放置着每一个 sample 的大小。

在这里插入图片描述

实践

接下来我们结合一个工程问题来实践一下。我们如何计算每一个 sample 在文件中的具体位置,判断它是不是关键帧,并计算它的具体时间。
计算 sample 的具体位置需要使用 stco(或 co64)、stsc 和 stsz。我们首先通过 stsc 将每一个 sample 属于哪一个 chunk 计算出来。这样每一个 chunk 的第一个 sample 就知道是哪个了。然后我们通过 stco 和 co64 就可以知道对应序号的 chunk 的第一个 sample在文件中的地址了。我们再通过 stsz 查询每个 sample 的大小,从 chunk 的第一个sample 的地址将中间的 sample 的大小一个个地加上去就可以得到每一个 sample 的地址了。

在这里插入图片描述
而 sample 是不是关键帧,我们只需要通过 stss 对应每一个 sample 序号查询就可以得到。

在这里插入图片描述
计算 sample 的时间我们需要用到 stts 和 ctts。我们先通过 stts 得到每一个 sample 的时长,第 n 个 sample 的结束时间就是第 n-1 个 sample 的结束时间加上第 n 个 sample 的时长。但是需要注意一下,这个是 DTS,我们还需要通过 ctts box 得到每一个 sample 的PTS 和 DTS 的差值。最后每一个 sample 的 PTS 就是等于 DTS 加上 CTS。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Learning together

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值