【音视频开发】视频数据格式、Dash协议的了解
I帧和IDR帧
DR(Instantaneous Decoding Refresh)–即时解码刷新。
I帧与IDR帧的相同点在于:
- I和IDR帧都是使用帧内预测的
- 都是同一个东西而已,在编码和解码中为了方便,要首个I帧和其他I帧区别开,所以才把第一个首个I帧叫IDR,这样就方便控制编码和解码流程
I帧与IDR帧的不同点在于:
- IDR帧的作用是立刻刷新,使错误不致传播,从IDR帧开始,重新算一个新的序列开始编码。而I帧不具有随机访问的能力,这个功能是由IDR承担
- IDR会导致DPB(DecodedPictureBuffer 参考帧列表——这是关键所在)清空,而I不会
- IDR图像一定是I图像,但I图像不一定是IDR图像
- 一个序列中可以有很多的I图像,I图像之后的图像可以引用I图像之间的图像做运动参考;对于IDR帧来说,在IDR帧之后的所有帧都不能引用任何IDR帧之前的帧的内容,与此相反,对于普通的I-帧来说,位于其之后的B-和P-帧可以引用位于普通I-帧之前的I-帧
- 从随机存取的视频流中,播放器永远可以从一个IDR帧播放,因为在它之后没有任何帧引用之前的帧。但是,不能在一个没有IDR帧的视频中从任意点开始播放,因为后面的帧总是会引用前面的帧
MP4 格式
FMP4格式
在Fragmented MP4文件中都有三个非常关键的 boxes:moov
、moof
和mdat
。
- moov(movie metadata box)
和普通MP4文件的‘moov’一样,包含了file-level的metadata信息,用来描述file。 mdat
(media data box)
和普通MP4文件的mdat
一样,用于存放媒体数据,不同的是普通MP4文件只有一个mdat
box,而Fragmented MP4文件中,每个fragment都会有一个mdat
类型的box。moof
(movie fragment box)
该类型的box存放的是fragment-level的metadata信息,用于描述所在的fragment。该类型的box在普通的MP4文件中是不存在的,而在Fragmented MP4文件中,每个fragment都会有一个moof
类型的box。
一个moof
和一个mdat
组成Fragmented MP4文件的一个fragment,这个fragment包含一个video track或audio track,并且包含足够的metadata以保证这部分数据可以单独解码。Fragment的结构如下所示。
Dash描述文件
MPD
媒体文件的描述文件(manifest),作用类似HLS的m3u8文件。MPD文件以XML格式组织Representation
对应一个可选择的输出(alternative)。如,480p video,720p video, 44100采样 audio,22050采样audio,都使用Representation描述。Segment(分片)
每个Representation会划分为多个Segment。Segment分为4类,其中,最重要的是:Initialization Segment(每个Representation都包含1个Init Seg),Media Segment(每个Representation的媒体内容包含若干Media Seg)
格式之间的关系
Dash中的数据对应的就是 FMP4 的不同数据块,FMP4每个数据块都能够单独的进行解码
Dash FMP4 与 HLS TS
服务器的cache效率会降低
实际的streaming应用场景中,往往需要cdn的支持,经常会被client请求的媒体分片就会存在距离client最近的edge server上。对于fmp4 streaming的情况,因为需要的文件更少,cache命中率也就更高。
举个例子:可能某一个audio track会和其他各种video track组合,那么就可以将这个audio track放在edge server上,而不用每次都跟origin server去请求。
相对地,在streaming TS流时,因为每一个音视频组合的都需要以复用文件的形式存储,组合数又非常多,相当于分母大了,edge server就会有很大的几率没有缓存需要的组合而要去向orgin server请求。
媒体文件存储成本和媒资管理成本增加
假设server端将video track编码为三个质量级别V1, V2, V3,audio track也被编码为三个质量级别A1, A2, A3,那么如果利用fmp4格式的特性,我们只需要存储这6份媒体文件,player在播放时再自主组合不同质量级别的音视频track即可。
而对于TS,则不得不提前将3x3=9种不同的音视频复用情况对应的媒体文件都存储到server端,平白无故多出三份文件的存储成本。实际中,因为要考虑到大屏端、小屏端、移动端、桌面端、不同语言、不同字幕等各种情况,使用TS而造成的冗余成本将更加可观。同时,存储文件的增加也意味着媒资管理成本的增加。这也是包括Netflix在内的一些公司选择使用fmp4做streaming格式的原因。
manifest文件更加复杂
fmp4格式的特性可以确保每一个单独的媒体分片都是可解密可解码的(当然player需要先从moov box中读到它们的编解码等信息),这意味着server端甚至根本不需要真的存储一大堆分片,player可以直接利用byte range request技术从一个大文件中准确地读出一个分片对应的media data,这也使得对应manifest(mpd)文件可以更加简洁。针对不同语言的音频,都只需要存一个大文件就够了。
相对地,在streaming TS流时,不得不在manifest(m3u8)文件中把成百上千个ts分片文件全都老老实实地记录下来。
无缝码流切换
无缝码流切换实现的关键在于
当第一个码流播放结束时,也就是发生切换的时间,第二个码流一定要以关键帧开始播放。在streaming TS流时,因为不能保证每一个TS chunk一定以关键帧开始,做码流切换时就意味着要同时download两个码流的相应分片,同时解析两个码流,然后找到关键帧对应的位置,才能切换。同时下载、解析两个码流的媒体内容对网络带宽以及设备性能都形成了挑战。而且有意思的是,如果当前网络环境不佳,player想要切换到低码率码流,结果还要在本来就不好的网络环境下同时进行两个码流的下载,可谓是雪上加霜。
而在fmp4中,除了保证各个分片一定以IDR帧开始外,还能保证不同码流的分片之间在时间线上是对齐的。而且streaming fmp4流时因为不要求音视频复用存储,也就意味着视频和音频的同步点可以不一样,视频可以全都以GOP边界作为同步点,音频可以都以sync frame作为同步点,这都使得无缝码流切换更简单。
- 平滑切换就是一定要切换的时候是以关键帧开始
Dash的大致流程
C++ Dash 移植的方案
- ffmpeg dash实现(C语言),一般都会基于ffmpeg的底层数据结构来完成dash的实现
- libdash (C++ 语言)库的时间相对比较老