一、史话
如果算上模拟时代的话,多媒体传输并非多么新鲜的事情。早在上世纪三十年代,人们便可以在家观赏奥运赛事:来自柏林现场的活动画面连同声音通过电缆或者无线电波被传送到世界各地1), 虽然是黑白图像,但就质量来说并不见得就比YouTube上NBC的北京2008差。从某种意义上讲,数字技术的突飞猛进对多媒体通信的推动并非它能够在 多大程度上提高媒体内容的质量——这方面某些斯基们所起的所用可能会更大——而是它可以令媒体的传播更便捷、更便宜,于是,如果愿意的话,现今每一个人都 可以演一出好戏发到世界各个角落里去吸引眼球,就如上世纪三十年代那位元首所做的一样。
在模拟时代,最大的一个大难题是如何有效利用带宽。这里的带宽可以理解为传输媒介,对于以电磁信号为介质的传输方式,通常指的是一个频率范围。一般情况下 每一路电视节目要占据6-8MHz的无线频谱或者电缆频谱,而且即使是在空闲的时候,这些被占据的频谱也无法被释放,有些电视台只好传送几根呆滞的彩条 (三十年前出生的人几乎都有过观赏彩条的经历)。相对而言,引入交换机制的电话网看上去会好一点:电信局确然可以保证成千上万对用户同时进行通话,可是分 给每对用户的只有可怜兮兮的4KHz带宽,而且,在双方沉默无语的时候,这些带宽并不能够被挪作他用,结果,人们在讲电话的时候总是竭力地寻找话题填满所 有的通话时间,以免因产生的空闲而支付昂贵的电话费用。
上世纪四十年代末,香农提出了关于信息及其传送的关键理论,于是人们发现,对于给定的带宽,其理论上的传输能力要比当时通信系统实际所实现的大得多。然而,在模拟的范畴内,无论是从信源的角度还是从信道的角度,靠近香农极限都是一件相当困难的事情。
不妨以广播电视为例,回顾一下先前的人们为节省带宽而付出的努力:
残留边带调制:图像信号的下边带只保留了少部分带宽(N制中是1.25MHz)。
亮色混合:由于扫描产生的图像信号具有某种程度上的周期性,在频谱上则表现为以行扫描频率为间隔的众多脉冲,这使得亮度信号和色差信号在同一带宽范围内进行叠加成为可能,条件是它们在频域相互错开半个行扫描频率。换句话说,人们利用一种巧妙的方法在不增加带宽的前提下把黑白电视升级为彩色电视。
隔行扫描:老式的CRT显示器要求刷新频率大于50Hz,否则会让人感觉到闪烁,但人眼感知运动画面的最低要求仅为每秒钟二十余帧左右,为了在不增加冗余 的带宽(亦即不增加帧频)的前提下消除闪烁现象,人们引入了隔行扫描技术,即通过将一幅图像拆作相互交错的两场来实现二倍于帧频的刷新频率。(利用 frame buffer将同一副图像连续显示多次其实亦可解决这个问题,可惜在模拟电视时代该方法的实现具有一定的技术难度)。
计算机专业出身的同学可能对上述历史并不很熟悉,然而不可否认的是,尽管多媒体技术通常被认为是计算机学科的一个分支,其源起却在通信领域。在ITU的学僚们开始筹划将电信网络改造为能够传送“综合业务”的多媒体网络时,计算机还仅仅被认为是一种计算机器。
最先被数字化的是语音。早在上世纪六十年代,贝尔实验室的PCM技术便开始被应用于电话网。通过简单的采样和量化,这种技术将4KHz带宽的语音转换为 64Kbps的数字信息,使之能够在采用时分复用的电话干线中传输。有趣的是,当时,普通的电话双绞线反而无法传输这种数字语音——64Kbps的数据率 对于它们来说太大了。难道数字媒体要求更多的带宽?并非如此,从香农的理论出发很容易找出问题所在:其一,PCM技术所产生的信息速率大大超出了语音信息 本身的熵率——这是种很粗糙的编码技术;其二,33.6kbps的调制技术远远没有达到双绞线可以提供的传输极限。结论就是,数字化并非就是简单的采样和 量化,它是一个复杂的信息表示过程:既包括信源编码——在保证信息可恢复的前提下产生尽量少的数据速率;也包括信道编码——在给定带宽的前提下如何承载更 大的数据速率。对于多媒体信息来说,前者更是备受关注。譬如,同样是4KHz的语音,如果采用新的编码技术如G.723.1,产生的数据速率只有 5~6kbps,对于双绞线来说绰绰有余。
图1. NTSC频谱结构:
图2. 一个L1载波的频谱分配:
图3. 一个DS载波的时隙分配:
视频面临的挑战更大一些,按照4:4:4采样的N制彩色电视信号产生的数据率高达60.8Mbps,即使采用目前极先进的调制手段——如号称接近香农极限 的DVB-C2@1024QAM,也会将8MHz的带宽全部吃掉。因此,早期的数字视频传输研究都集中于内容简单的低分辨率图像(CIF及QCIF)。 ITU是最初的推动者,上世纪八十年代,该组织一直致力于一项彻底改造老式电话网的工程,企图实现包括用户接入在内的全数字化网络,以提供包括数字语音、 数字视频和数据的“综合业务”。不幸的是,由于新的数字调制方式的出现以及因特网的冲击,这种被称作ISDN的网络很快就过时了,但由其产生的视频压缩标 准H.261却开了数字视频传输的先河,成为后来一系列技术诸如H.263、H.264、RM以及MPEG系列的鼻祖。
开会和制定标准是ITU学僚们的特长,但要坐等他们引导世界进入多媒体时代,恐怕需要相当大的耐心。幸运的是,PC的面世使个人处理多媒体数据成为可能,哪怕是一台装有DOS的286,相信也会比一台ISDN电话机能给人带来更大的遐想空间。
1991年制定了第一个多媒体PC的标准:
16 MHz 386SX CPU
2 MB RAM
30 MB hard disk
256-color, 640×480 VGA video card
1x (single speed) CD-ROM drive using no more than 40% of CPU to read, with < 1 second seek time
Sound card outputting 22 kHz, 8-bit sound; and inputting 11 kHz, 8-bit sound
Windows 3.0 with Multimedia Extensions.
看上去它似乎还做不了什么,但它将来能。
世界总是如此,多少风光无限者其实早已薄暮西山,多少貌不惊人者却是在暗自酝酿着爆发的能量,又有多少明眼人可以看得出来?
二、挑战
通过对模拟音视频数据采样、量化得到的原始数字音视频的数据量庞大无比:
RGB图像分辨率 | 数据量 |
---|---|
QCIF(176×144) | 76,032 Byte |
CIF(352×288) | 304,128 Byte |
QVGA(320×240) | 230,400 Byte |
VGA(640×480) | 921,600 Byte |
SVGA(800×600) | 1,440,000 Byte |
SD-PAL(720×576) | 1,244,160 Byte |
SD_NTSC(720×480) | 1,036,800 Byte |
HD(1280×720) | 2,8764,800 Byte |
FHD(1920×1080) | 6,220,800 Byte |
而当前的数字存储媒介和传输信道所能承载的数据速率相当有限:
媒介/传输方式 | 容量/速率 |
---|---|
CD-ROM | 650M Byte |
DVD-ROM | 4.7G Byte |
Blueray | 25G/50G Byte |
Voice Modem | 33.4 kbps |
ISDN | 64 kbps |
T1 1.544 | Mbps |
GSM | 15 kbps |
UTMS | 2.8 Mbps |
为了弥补二者的差距,我们需要在竭力降低传输代价的前提下,提供给受众主观感受上尽可能良好的音视频信息。这里面对人类的听觉和视觉感受系统的研究是非常重要的,以视觉系统(HVS)为例:
- 人眼能够识别的分辨率是有限的,而且对水平和垂直方向较其他方向更加敏感,对灰度信息较颜色信息更加敏感,对静止画面较活动画面更加敏感;
- 人眼还会在主观上放大边缘区域的对比度;
- 更关注感兴趣区域等。
三、传输与存储
3.1 数字媒体流
二进制比特是数字媒体传输和存储的基本形式,每个比特非0即1,任何数字媒体信息必然由若干连续的0或1形成的比特序列来表示。之所以称之为流,是由于声音和视频等媒体信息总是在时间线上展开的,譬如DVD光盘上的一个电影片段、手机通话过程中的一段语音、或者电脑中的一个MP3文件。
3.1.1 多媒体原始流
原始流的概念来源于ISO的MPEG-2标准,通常指音频、视频或数据编码器输出的二进制比特流,也即可以直接作为解码器输入的比特流。原始流是数字媒体传输和存储系统中最基本、最底层的数据单元,而更高级的、面向应用的数据单元都是在原始流的基础上按照特定协议层层封装而来的。
3.1.1.1 音频原始流的结构
3.1.1.2 视频原始流的结构
目前,常见的视频编码基本基于混合编码框架(详见4.3.1),因此视频原始流的结构也大都类似。
视频等价于时间轴上的一个图像序列,根据混合编码方案,每一副图像都将被切割为大小相等的方块,除了某些特殊的应用场景之外,以此为基本编码单位的编码过程按照扫描顺序在图像中自左至右、自上而下进行。在H.265之前的视频标准中,这一基本编码单位的大小为16像素见方,称为宏块;H.265中这一大小得到修正,最大可达到64像素见方,以实现对超高分辨率的最佳支持。为了适应包传输网络,有些标准允许一副图像被划分成多个Slice,每个Slice由图像中若干连续的基本编码单位组成。通过引入Slice,视频数据包的大小得到了控制。此外,由于Slice之间一般不允许存在编解码的依赖关系,即使发生丢包也不会出现错误蔓延。
由于混合编码方案使用了基于时间轴的预测技术,在解码过程中,图像数据之间往往是存在依赖关系的,跟据依赖关系可以将图像分为三种类型:完全不依赖于其他图像的图像称为关键帧,完全不为其它图像依赖的称为可丢弃帧,其余图像属于普通的参考帧,除了可丢弃帧之外,关键帧或参考帧的缺失或错误都会导致依赖于该图像的其他图像发生错误,且这种错误会随着依赖关系蔓延,直至找到下一个关键帧结束当前的依赖关系,如下图所示。对于的多媒体应用来说,事先获取图像的类型信息是非常有意义的,某些特定的操作需要这些信息,如随机访问和丢帧——前者要求尽快找到一个关键帧,而后者可以在需要的时候提高处理速度,但需要识别一副图像是不是可丢弃。类型信息通常会包含在图像头中,有时也会作为元数据附加到原始流之外。
在原始流中,图像的顺序与显示顺序不一定相同,这是由于编码器在编码过程中可能会对图像顺序进行了重排,因此,解码器需要负责恢复图像的显示顺序。
3.1.2 13818-1传输流
13818-1主要用于解决数字广播系统中音频、视频及数据信息的复用问题,实际上就是给出一种同时传输音频、视频及数据的有效途径。如前所述,在模拟时代,复用的问题可以通过频分来解决,比如模拟电视通过将音频数据调制到视频信息带宽之外来实现图像和声音的的同时传输。针对数字化的信息,13818-1定义了一个基于数据包的时分复用系统。这是一个面向比特流的传输系统,定义了传输流和节目流两种不同形式的比特流:
- 传输流由固定长度的TS包组成,主要应用于数字广播;
- 节目流则以数据组为单位,应用于数字存储系统,数据组长度是可变的,通常也比TS包要大得多。
13818-1系统还必须保证在数据接收端重建音视频及数据信息的同步,这要求在传输的数据中插入足够多的时间信息。此外,多组使用不同时间基准的音视频信息也可以按照13818-1规定的方式同时传输,这使得同时传输多个多媒体业务节目成为可能,不过,只有传输流支持这种方式。
传输流的组成:
3.1.2.1 TS包
标准的TS包包含188个字节,除去4个字节的头信息,还能传输184个字节的数据。头信息重,13位的PID值是最重要的部分,那是一个类似子信道号的值,标识着TS包的数据“身份”。PID值为0x1FFF的TS包为空包,插入空包到传输流中只是为了调整复用结构。payload_unit_start_indicator是头信息中另一个比较重要的位段,对于以PES数据为载荷的TS包,该位设1表示这个TS包中传输的是PES包的起首部分;对于以PSI数据为载荷的TS包,该位设1表示这个TS包中传输的是PSI段的起首部分,而TS载荷的第一个字节是表示PSI数据在载荷中的位置的pointer字段。此外,transport_error_indicator字段表示包内的数据是否有比特错误;transport_scrambling_control表示包内的数据是否加密。
transport_packet(){
sync_byte:8
transport_error_indicator:1
payload_unit_start_indicator:1
transport_priority:1
PID:13
transport_scrambling_control:2
adaptation_field_control:2
continuity_counter:4
if(adaptation_field_control=='10' || adaptation_field_control=='11'){
adaptation_field()
}
if(adaptation_field_control=='01' || adaptation_field_control=='11') {
for (i=0;i<N;i++){
data_byte
}
}
}
除了实际的PES或PSI载荷之外,跟在TS包头后面的还可能是adaption域,这由TS头中的另一个2比特的字段adaptation_field_control标志决定。 adaption域包含的内容有:
- discontinuity_indicator用于指示系统时钟的不连续事件
- random_access_indicator用于指示随机访问点
- elementary_stream_priority_indicator用于指示ES数据优先度
- PCR字段包含系统时钟的采样值,以27MHz时钟周期为单位,在PID为特定值的TS包中传输
- OPCR字段包含原始系统时钟的采样值,在PID为特定值的TS包中传输。
- splice_countdown字段用于指示音视频载荷的衔接点
- private_data包含13818-1规定外的数据。
3.1.2.2 PES包
在13818系统中,通过为每一个TS包指定特定的PID可以实现多路数据的复用传输,PID不同的TS包可能承载着不同类型的数据,来自不同的编码器或数据发生单元,需要接收端送至不同的接收处理单元。
PES包是由TS包承载传输的标准载荷之一,其内容包括MPEG定义的音、视频信息(11172/13818/14496)、ECM/EMM信息、DSMCC信息以及各种自定义数据等。在传输之前,PES包必须被分割并分配到若干(至少一个)TS包的数据载荷部分。
一个PES包序列形成一路13818系统的数据流,其承载的数据被称作原始流数据,它们可能是某个视频编码器的输出、某个音频编码器的输出,某个数据发生器的输出或者某种控制信息,为了使接收端能够无缝地获取到这些数据并将其送至特定的处理单元如视频解码器、音频解码器、数据处理单元或控制单元,传输这一路PES数据的TS包必须使用相同的PID(即占用同一个子信道),并且保证在传输过程中不会发生乱序。
对于所有的PES包,首部有三个字段是必需的:
- packet_start_code_prefix(0x000001)
- stream_id:一个字节的流标志字段,指示PES的载荷类型)
- PES_packet_length: 两个字节的长度字段,这个数值可以设为零。
对于载荷类型除program_stream_map、padding_stream、private_stream_2、ECM、EMM、program_stream_directory、DSMCC_stream及ITU-T Rec. H.222.1 type E_stream外的PES包,13818-1还规定了更多的标准字段作为补充性的首部信息:
- PES_scrambling_control指示PES载荷是否被加扰;
- PES_priority指示PES载荷的优先级;
- copyright指示PES载荷是否存在版权保护;
- original_or_copy指示PES载荷属于原始信息还是拷贝信息;
- PTS和DTS是以90kHz时钟周期为单位的时间标签,分别表示显示时间和解码时间;
- ESCR字段包含ES系统时钟的采样值,以27MHz时钟的周期为单位
- ES_rate指示ES流速率。
3.1.2.3 辅助信息
承载辅助信息的数据在13818-1系统中被称作表(table),**每一种表由一个8比特的table id标识**。其中,预定义的表有三种:节目关联表(PAT)、条件接收表(CAT)和节目映射表(PMT),它们占用的table id分别为0、1和2,此外,0x03~0x3f范围内的table id为13818-1保留,0x40~0xFE范围内的table id可用作私有扩展(DVB、ATSC等)。
在13818-1系统中,这些表需要按照规定的时间间隔重复传输,以使得接收端在任意的时间点都可以尽快地拿到所需要的全部辅助信息。每一种表会占用一个PID,表中的数据以section的方式来组织,每个section的长度不超过4096字节,如果表的长度大于4096字节,则被分为多个section来传输。 section的组织方式如下:
方式一:
section() {
table_id 8
'1' 1
private_indicator 1
reserved 2
section_length 12
table_id_extension 16
reserved 2
version_number 5
current_next_indicator 1
section_number 8
last_section_number 8
for (i=0;i<section_length-9;i++) {
data_byte
}
CRC_32 32
}
方式二:
section() {
table_id 8
'0' 1
private_indicator 1
reserved 2
section_length 12
for (i=0;i<N;i++) {
data_byte
}
CRC_32 32
}
其中:
- table_id标识表的类型。
- private_indicator表示该表是否为私有信息。
- section_length为紧随该域的所有数据的长度(含4字节的CRC校验)。
- table_id_extension是对table id的一个16比特的扩充,对于不同的表有不同的含义。
- version_number是个5比特的版本号。在重复轮播的过程中,表的内容也会发生变化,每次发生变化之后,要求这个版本号加一。
- current_next_indicator是一个标志位,标志该表的内容即可生效还是在不久的将来生效,若该位为0,则表示这个表是使用与将来的,起到一个通知接收端表内容即将发生变化的作用。
- 如果一个表由多个section传送,则第一个section的序号为0,section_number表示当前section的序号,last_section_number表示最后一个section的序号,也就是传送这个表的section的总数减去1。
最后,每一个section的数据会被分配到对应某一个PID的TS包载荷中。
3.1.2.4 复用的实现和PSI表
13818-1系统通过将不同的数据分配到PID不同的TS包内传输实现复用,而且,通过节目专用信息(PSI)表,该系统还可以实现多路多媒体业务节目的同时传输。
13818-1内定义的节目专用信息表有四种,分别为
- 节目关联表(PAT)
- 节目映射表(PMT)
- 网络信息表(NIT)
- 条件访问表(CAT)
PAT给出了一个传输流中各路多媒体业务节目的信息,其table id为0,在pid为0的TS包中传输,使用如下的语法结构:
program_association_section() {
table_id 8
section_syntax_indicator 1
'0' 1
reserved 2
section_length 12
transport_stream_id 16
reserved 2
version_number 5
current_next_indicator 1
section_number 8
last_section_number 8
for (i=0; i<N;i++) {
program_number 16
reserved 3
if(program_number == '0') {
network_PID 13
}
else {
program_map_PID 13
}
}
CRC_32 32
}
除去标准的section字段之外,需要关注的是:
- transport_stream_id给出该PAT所属的传输流的id,以与网络中的其他传输流区分开来。
- program_number是本传输流内的某路节目的编号。
- program_map_PID是用于传输PMT的pid。
其中,N循环中包含了多组program_number和program_map_PID的结对,描述了用以传输各路节目的PMT section所使用的pid。(program_number为0除外,它对应的program_map_PID为传输NIT section所使用的pid)
PMT给出了某路多媒体业务节目的业务信息,即该多媒体节目中包含哪些媒体,分别为何种类型,用于传输各个媒体数据的pid为多少等,其table id为2,传输使用的pid在PAT中指定,section格式如下:
TS_program_map_section() {
table_id 8
section_syntax_indicator 1
'0' 1
reserved 2
section_length 12
program_number 16
reserved 2
version_number 5
current_next_indicator 1
section_number 8
last_section_number 8
reserved 3
PCR_PID 13
reserved 4
program_info_length 12
for (i=0; i<N; i++) {
descriptor()
}
for (i=0;i<N1;i++) {
stream_type 8
reserved 3
elementary_PID 13
reserved 4
ES_info_length 12
for (i=0; i<N2; i++) {
descriptor()
}
}
CRC_32 32
}
其中,program_number为该PMT所描述的节目的编号,和PAT中的program_number相同。N1循环中给出节目中各个媒体的信息:
stream_type为媒体类型,如0x01为11172-1视频格式、0x02为13818-2视频格式、0x03为11172-1音频格式、0x04为13818-2音频格式、0x0f为13818-7音频格式、0x10为14496-2视频格式、0x11为14496-3音频格式、0x1b为14496-10视频格式等。
elementary_PID为传输该媒体数据使用的pid值。
3.1.2.5 时钟信息
13818-1传输流中的每一路节目都有一个独立的27MHz的时钟,该时钟的采样值被封装到TS包的adaption域内定期传送给接收端(至少100毫秒一次),使接收端的时钟得以保持和发送端的同步,这些采样值被称为节目时钟参考(PCR),是一个42比特的值,表示0.037微妙的时钟嘀嗒,极限值大约是45个小时。对于特定的某一路节目,用以传输PCR的pid是固定的,需要在该节目的PMT中指明。当PCR发生突变的时候,发送端需要预先通知接收端,这同样要使用到adaption域,时钟突变发生之前,传输PCR的adaption域的discontinuity_indicator需要被置1。
音视频等媒体的时间标签封装在PES头中,有PTS和DTS两种,分别表示媒体的播放时间和解码时间,它们都是以27M的系统时钟为基准的,但时间的颗粒度更粗一些,为90KHz。PTS和DTS的位长都是33比特,表示11微妙的时钟嘀嗒,极限值大约是26个小时。
3.1.2.6 同步
13818-1系统是强同步的,这意味着,接收端不仅要保证特定节目内的每一种媒体(音频、视频及字幕等)按照给定的速率播放、媒体间的同步关系得以保持(唇音同步、字幕同步等),还要保证与发送端使用一致的时钟,也就是说所有接收端在播放同一个广播节目时的表现必须完全相同。
因此,接收端必须根据传输流中的PCR值内建一个本地时钟,而且,还必须依据收到的PCR值对这个本地时钟不断校正,以弥补编码和传输过程中引入的PCR抖动。而所有媒体的播放控制都依赖于这个本地时钟,目标就是令每个媒体包的播放时刻与它的时间戳完全一致。
3.2 容器
在数字媒体领域,容器专指数字媒体数据的封装格式。音频、视频及其他数据借助于某个特定的容器可以被组织、复用到同一个文件之中,从而满足某些特定的操作要求(主要是针对播放器),如播放、暂停、快进、后退、跳转等。 容器不涉及多媒体数据的具体编码方式。
3.2.1 AVI
AVI是微软使用的一种多媒体文件格式,由于迄今为止Windows一直是PC的主流操作系统,AVI也成为PC中最流行的多媒体文件格式。不过这种格式并非微软的原创,它最早是由电子艺界提出的,其特征是使用一种称为chunk的数据块来存储多媒体数据及其附加信息。每个chunk只有八字节的头,前四字节是四个ASCII码,作为该chunk的标识;后四字节为一个整数,表示紧跟在头信息后面的数据的长度,结构非常简单。此外,还有分别以”RIFF”和”LIST”为标识的两种复合chunk,它们的数据内容为多个chunk组成的序列,从而使数据的层次化得以实现。
3.2.1.1 结构
一个avi文件在结构上由一个RIFF复合chunk组成,因此,首四个字节 “52494646”是RIFF的ASCII码,接下来的四个字节表示该复合chunk的长度,亦即该avi文件的长度减8,至于数据部分,则首先是四个字节“41564920”,即ASCII的“AVI ”,表示这个复合chunk的具体名称,然后才是组成该avi的各个chunk。通常,一个avi文件包括一个名为“hdrl”的LIST,存放所有头相关信息;一个名为“movi”的LIST,存放所有媒体数据;然后是一个标识为“idx1”的chunk,存放相关索引信息。索引信息描述了各个数据块在 LIST中的位置,有助于提高SEEK操作的速度。
需要注意的是,chunk中的数据是要求双字节对齐的,如果某个chunk的长度是奇数,那么其后要填一个零。
AVI RIFF File Reference from the Microsoft site:
“The data is always padded to nearest WORD boundary. ckSize gives the size of the valid data in the chunk; it does not include the padding, the size of ckID, or the size of ckSize.”
图3. AVI文件的结构:
RIFF[AVI ]+60337434B
LIST[hdrl]+8830B
avih+56B
{FPS:1000000/41667, 0bps, 0 Byte Aligned, /HASINDEX/INTERLEAVED 2758 frames, Inital 0, 2 streams, Buffer:0 Bytes, 1280x720}
LIST[strl]+4244B
strh+56B
{[vids], [divx], Initial 0, 24/1, 0+2758, Buffer:368324 Bytes, quality:0x2710, Size of Sample:0, }
strf+40B
{1280x720, 24 Bits, DX50}
JUNK+4120B
LIST[strl]+4234B
strh+56B
{[auds], [], Initial 1, 24000/1, 0+2753491, Buffer:12000 Bytes, quality:0x0, Size of Sample:1, }
strf+30B
{id=0x55, 2 ch, Sample rate:44100, Bit rate:192000, 1 block align, 0 bits, }
JUNK+4120B
LIST[odml]+260B
dmlh+248B
LIST[INFO]+56B
ISFT+44B
{VirtualDubMod 1.5.10.1 (build 2439/release)}
JUNK+1318B
LIST[movi]+60239170B
01wb+12000B
00dc+70699B
01wb+1000B
00dc+465B
01wb+1000B
00dc+466B
01wb+1000B
......
idx1+88016B
{fourcc=01wb, flag=0x10, pos=4, len=12000}
{fourcc=00dc, flag=0x10, pos=12012, len=70699}
{fourcc=01wb, flag=0x10, pos=82720, len=1000}
{fourcc=00dc, flag=0x0, pos=83728, len=465}
{fourcc=01wb, flag=0x10, pos=84202, len=1000}
{fourcc=00dc, flag=0x0, pos=85210, len=466}
{fourcc=01wb, flag=0x10, pos=85684, len=1000}
......
以上结构可以通过一系列简单的C函数来理解,主要涉及到五个Microsoft数据结构:
typedef struct _avimainheader {
FOURCC fcc; // 'avih'
DWORD cb; // size of the header, initial 8 bytes excluded
DWORD dwMicroSecPerFrame; // frame period in microsecond
DWORD dwMaxBytesPerSec; // maximum bitrate
DWORD dwPaddingGranularity; // alignment for data, in bytes
DWORD dwFlags;
DWORD dwTotalFrames; // total number of frames of data in the file
DWORD dwInitialFrames; // initial frame for interleaved files. Noninterleaved files should specify zero
DWORD dwStreams; // the number of streams in the file
DWORD dwSuggestedBufferSize;// suggested buffer size for reading the file
DWORD dwWidth; // width of the AVI file in pixels
DWORD dwHeight; // height of the AVI file in pixels
DWORD dwReserved[4]; // reserved. Set this array to zero.
} AVIMAINHEADER;
typedef struct _avistreamheader {
FOURCC fcc; // 'strh'
DWORD cb; // size of the header, initial 8 bytes excluded
FOURCC fccType; // data type of the stream
FOURCC fccHandler; // data handler, preferred codec for audio and video
DWORD dwFlags;
WORD wPriority; // highest priority might be the default stream
WORD wLanguage; // Language tag
DWORD dwInitialFrames;
DWORD dwScale; // dividing dwRate by dwScale gives the number of samples per second
DWORD dwRate;
DWORD dwStart; // starting time for this stream
DWORD dwLength; // length of this stream
DWORD dwSuggestedBufferSize; // how large a buffer should be used to read this stream
DWORD dwQuality; // an indicator of the quality of the data
DWORD dwSampleSize; // the size of a single sample of data, 0 for video
struct {
short int left;
short int top;
short int right;
short int bottom;
} rcFrame; // destination rectangle for display
} AVISTREAMHEADER;
typedef struct {
WORD wFormatTag;
WORD nChannels;
DWORD nSamplesPerSec;
DWORD nAvgBytesPerSec;
WORD nBlockAlign;
WORD wBitsPerSample;
WORD cbSize;
} WAVEFORMATEX;
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
}BITMAPINFO, *PBITMAPINFO;
typedef struct tagBITMAPINFOHEADER {
DWORD biSize; // The number of bytes required by the structure
LONG biWidth; // The width of the bitmap, in pixels
LONG biHeight; // The height of the bitmap, in pixels
WORD biPlanes; // must be set to 1
WORD biBitCount; // The number of bits-per-pixel
DWORD biCompression; // The type of compression, codec indicator
DWORD biSizeImage; // The size of image buffer
LONG biXPelsPerMeter; // The horizontal resolution, in pixels-per-meter
LONG biYPelsPerMeter; // The virtical resolution, in pixels-per-meter
DWORD biClrUsed; // The number of color indexes in the color table that are actually used by the bitmap
DWORD biClrImportant; // The number of color indexes that are required for displaying the bitmap
}BITMAPINFOHEADER, *PBITMAPINFOHEADER;
//————
//TO BE ADD
//————
3.2.2 ISO标准及其衍生
3.2.2.1 QTFF
QTFF(Quick Time File Format)是苹果公司推出的多媒体文件格式,第一个版本于1991年随着Quick Time多媒体框架一起问世,通常以“MOV”为扩展名。
QTFF的基本组成单位是Atom,最基本的Atom是由长度、类型和数据三部分组成的,其中长度和类型分别为4字节长,长度部分能够支持扩展为8个字节。某些复杂一点的Atom规定在长度和类型之后增加1个字节的版本号和3个字节的标记位,以支持更多扩展。类型字段则往往是四个ASIC字符,这在多媒体文件格式中及其常见。一个Atom的数据允许包含子Atom,因此,在理论上Atom是可以自由嵌套的。
3.2.2.2 ISO媒体文件标准
2001年,ISO在QTFF的基础上制定了一个多媒体文件标准(ISO/IEC 14496-12),由于具备非常好的功能性和扩展性,该标准逐渐得到了业界的认可。在网站http://www.mp4ra.org/atoms.html列出了目前经过认证的ISO文件扩展,其中包含了3GPP、MP4以及QTFF格式。
ISO媒体文件将QTFF中的Atom重定义为盒子(Box),构造方式并无变化,而且基本的盒子的定义也与QTFF保持一致。虽然QTFF的出现先于ISO,但仍可以将QTFF看作是ISO标准的某种扩展。
图4. 一个典型的ISO格式文件
这里有一段C++代码,用于实现ISO媒体文件的解析。
盒子的定义和包含关系大致如下:
- ftyp:文件类型,主要包括当前格式的版本号,兼容性等信息。
- mdat:数据块,以Chunk为单位交错存放的媒体数据。
- moov:影片信息,含有子字段。
- mvhd:影片信息头,含有影片的时间粒度及此粒度下的影片时长等字段。
- trak:媒体轨道信息,含有子字段。
- tkhd:媒体轨道信息头,含有媒体轨道的标识号、以影片时间粒度度量的时长等信息。
- mdia:媒体信息,含有子字段。
- mdhd:媒体头,该媒体的时间粒度及此粒度下的时长。
- hdlr:媒体类型,含有一个FourCC标识的媒体类型,如vide、soun、subt等。
- minf:媒体详细信息,含有子字段。
- vmhd/smhd/……:视频、音频及其他媒体的基本描述
- dinf:数据信息。
- stbl:样本表,内含媒体信息的详细信息。
其中,moov→trak→mdia→minf中的stbl是一个比较重要的box,其中:
- stsd box内有解码器需要的媒体描述信息
- stss内有关键帧信息;
- stts、stco、stsc、stsz用于构建索引
- stts给出每个数据帧的时间信息
- stco给出每个数据Chunk在文件中的偏移
- stsc给出个各个数据Chunk中包含的数据帧
- stsz给出各个数据帧的长度
所有媒体数据则统一存放在mdat box中,没有同步字,没有分隔符,只能根据索引进行访问。mdat的位置比较灵活,可以位于moov之前,也可以位于moov之后,但必须和stbl中的信息保持一致。但是,如果mdat的位置在moov之前,通过流的方式播放文件会出现问题,因为没有办法在一开始就获得文件的媒体信息和索引。
Box的扩展通过uuid实现。用户可以使用类型为’uuid’的box,以16个特定的字节作为标识,定义自己的数据格式。
目前,各种类ISO 14496-12格式如MOV、F4V、3GP等在数码相机、互联网视频、移动视频等领域应用相当广泛,然而由于HTML5的问世,其主导地位受到基于MKV的WebM格式的威胁。
3.2.3 ASF
1995年起微软着手开发新的媒体格式ASF,这是一种适合网络传输的流媒体格式。相对于AVI而言,ASF引入了很多改进,包括:
- 使用128比特的GUID代替四个字节的fourCC;
- 使用64比特的长度域,支持超大文件;
- 定义了三个基本的顶层对象:Header,Data和Index,这些对象可以独立存储、传输;
- 数据打包允许分割(一个原始媒体数据分成多个包)和分组(多个原始媒体数据打入一个包);
- 数据包的首部包含有时间信息。
- 支持基于绝对时间、相对偏移的索引信息,支持多个媒体流的独立索引。
3.2.4 RealMedia
这是RealNetworks开发的一种多媒体文件格式,早期只支持固定比特率(CBR),扩展名为rm,属于最早的互联网流媒体格式之一,曾经红极一时。然而,虽然CBR支持有限带宽下的高效数据传输,却无法保证视频质量,这使得rm一度成为低质视频的代名词,有时某些画面几乎是惨不忍睹。后来,RM逐渐退出了互联网视频舞台,取而代之的是FLV和基于ISO格式的F4V,虽然后来RealNetworks在新的编码器中增加了变比特率(VBR)的支持(以rmvb为文件扩展名),仍旧无力回天,目前,Real格式仅在中国地区比较流行。
3.2.5 MKV
MKV是俄罗斯人于2002年发起的一个开放标准,2010年成为WebM格式的基础,借助HTML5的兴起,有望成为目前流行格式FLV/F4V的有力竞争对手。
3.2.5.1 文件结构
MKV文件的基本数据单元叫做Element,每个Element依然以ID/Size的形式开始,不同的是ID/Size采用可变长度的EBML编码。
和一般的多媒体容器类似,MKV定义:
- SEGMENTINFO来承载文件信息
- TRACKS/TRACKENTRY来承载媒体流的信息
- CUES来承载索引信息。
除此之外,MKV还定义了CHAPTERS来支持类似于DVD的章节功能,定义了ATTACHMENTS允许将文件作为附件。
MKV采用二级结构存储媒体数据,首先,MKV文件中包含有多个CLUSTER,而每个CLUSTER包含若干BLOCKGROUP,BLOCKGROUP内的BLOCK Element存储一个或多个媒体数据帧以及某些附加信息。承载不同媒体流的数据的BLOCKGROUP在CLUSTER中交错存放,CLUSTER首部会给出一个时间戳,作为其内部各BLOCKGROUP中的媒体数据的时间信息的基准。为了减少数据量,也可以不使用BLOCKGROUP而将BLOCK直接存放在CLUSTER中,这种情况下的BLOCK称为SIMPLEBLOCK。
3.2.5.2 索引和随机访问
在MKV文件中,每个索引项由一个CUEPOINT表示,其中包含一个CUETIME和多个CUETRACKPOSITIONS,CUETIME表示当前索引项对应的时间点,CUETRACKPOSITIONS则给出该时间点上某个媒体流对应的媒体数据的在文件中位置,每个CUETRACKPOSITIONS包含track号,目标CLUSTER相对于文件的偏移量,目标BLOCKGROUP在CLUSTER中的编号。通常,MKV只对关键帧作索引。
3.2.5.3 章节信息
3.2.5.4 打包和解包
通常,MKV文件的媒体数据经由CLUSTER和BLOCKGROUP二级封装,如下:
CLUSTER
Timecode(uint)
Postion(uint)
Prevsize(unit)
BLOCKGROUP
Reference(int)
Duration(int)
BLOCK
Tracknumber(vint)
Timecode(sint16)
Flags(int8)
Framedata
CLUSTER首部的Postion给出了该CLUSTER相对于SEGMENT数据起始的偏移量,Prevsize给出了上一个CLUSTER的字节数(含ID/Size部分),为文件损坏的情况下进行重同步提供了有效的线索。
BLOCKGROUP中的Reference以相对时间的形式给出了当前媒体帧对其它帧的依赖关系,Duration为媒体数据的持续时间(一般用于字幕)。
BLOCK中的Tracknumber标识媒体数据所属的媒体流;Timecode是媒体数据的时间标签,相对于CLUSTER中的Timecode。
Flags各位元定义如下:
Bit 0x80: keyframe:
No frame after this frame can reference any frame before
this frame and vice versa (in AVC-words: this frame is an
IDR frame). The frame itself doesn't reference any other
frames.
Bits 0x06: lace type
00 - no lacing
01 - Xiph lacing
11 - EBML lacing
10 - fixed-size lacing
Bit 0x08 : invisible: duration of this block is 0
Bit 0x01 : discardable: this frame can be discarded if the decoder
is slow
其中,Lace允许将多个媒体帧封装到一个BLOCK中。如果使用Lace,则紧跟BLOCK首部信息的是一个标识总帧数的字节,然后是一系列帧长度的信息,语法因Lace方法不同而不同,然后才是媒体帧数据。
MKV CLUSTER也有简化的语法,使用SIMPLEBLOCK代替BLOCKGROUP:
CLUSTER
Timecode(uint)
Postion(uint)
Prevsize(unit)
BLOCK
Tracknumber(vint)
Timecode(sint16)
Flags(int8)
Framedata
3.2.5.5 Divx HD
3.2.5.6 WebM
3.3 同步问题
媒体同步主要涵盖了三个方面的内容:
(1) 媒体自身的同步,即保持媒体样本在时间轴上的相对关系,以满足受众的感知要求,譬如需要按照正确的采样率播放一段声音,如果采样率不对,受众听到的声音会走样
(2) 媒体之间的同步,这是为了保证媒体之间的时间关系,譬如唇音同步:声音和口型要对得上,也即声音和图像的播放在时间轴上要保持一致。
(3) 发送端和解收端的同步,这是一种最强烈的同步,要求接收端各媒体在时间轴上的分布与发送端保持完全一致,譬如电视直播应用中,各个接收端播放某个媒体样本的时刻必须一致。除直播和实时通信以外的多媒体系统通常只需要满足前两种同步关系既可。
3.3.1 同步机制的实现
首先探讨如何实现媒体自身的同步要求,主要是声音和视频。由于两种媒体的播放设备存在着差异,其同步机制也不尽相同。
声音数据的同步控制和播放通常完全由硬件实现,音频芯片有内置的时钟,只要采样率设置正确,数字样本可以被准确地转换为模拟音频。为了保持一定的播放速度,音频芯片需要源源不断地读取数据到其内置缓冲中,如果不能及时拿到数据,会出现缓冲下溢,而如果缓冲满的情况下向音频芯片写入数据,则会出现缓冲上溢。所以,音频芯片驱动通常会提供回调机制,应用程序利用这种回调机制发送数据给硬件,这也是一种典型的“拉”的数据传输方式,可以有效避免上下溢的发生。视频则不然,视频设备只提供一个用于更新图像的帧缓冲,图像数据需要在一个外部定时器的控制下写入帧缓冲中,写入的时刻也需要由外部控制。
音频同步机制一(回调方式)
当音频播放设备的数据缓冲快要空的时候会激活某种回调机制,从而使上层注册的回调函数得以调用。回调函数负责从本地数据缓冲拷贝数据到音频设备缓冲,如果本地数据缓冲空则启动解码过程,以获取更多的数据。在这一过程中,音频设备控制着音频播放的同步,假如回调函数因某种原因没有及时拷贝数据,则音频设备发生缓冲下溢,体现为声音输出的停顿。音频同步机制二(轮询方式)
在这种方式下,音频数据的写入由独立的线程来实现,该线程定时检查本地数据缓冲和设备缓冲的水平,然后根据情况从解码队列中获取音频数据或将本地数据写入到设备缓冲。在这种方式下,如果不能及时获取解码数据,则会导致音频设备缓冲下溢,体现为声音输出的停顿。视频同步机制(轮询方式)
视频的同步方式略有不同,完全依赖于外部时钟。这种方式也需要启动一个定时器,定时检查解码队列,如果解码队列中有图像,则比较该图像的时间戳和当下的系统时钟,如果系统时钟已经到了播放的时间,则拷贝该图像到帧缓冲中。而如果系统时钟远远超过了图像的时间戳所指示的时间,说明发生了“图像迟到”,体现为屏幕上的画面停顿。
再看音视频间的同步。音视频间的同步机制建立在音频同步机制和视频同步机制的基础上,通常有以下三种策略:
基于音频时钟的策略
这种策略下,音频的同步方式可以采用以上的两种方式的任意一种,视频同步以音频播放的时间为准,即根据音频播放的时间确定当前的图像是否“迟到”。如果发生了“图像迟到”,首先要将该图像丢弃,同时启动跳帧策略,通知解码器以适当的频率在解码后直接丢弃图像,直至重新恢复同步。如果“图像迟到”是因某种突发情况产生的(解码使用的CPU被突然大量占用、媒体文件中暂时无法获得视频数据等),突发情况消失后调帧策略会加快解码输出速度,在一定的时间内重新恢复同步。如果音频发生缓冲下溢,音频时钟会变慢,由于音频时钟同时是参考时钟,会导致视频的播放受到影响,如果缓冲下溢持续的时间很长,视频播放会发生停顿,但下溢消失之后同步可以立即恢复。基于视频时钟的策略
这种策略下音频同步以视频播放的时间为准,若本地缓冲中音频数据的时间戳远大于视频时钟,则暂停向音频设备缓冲注入数据;若本地缓冲中音频数据的时间戳远小于视频时钟,则丢弃该数据,这两种情况均会导致音频设备缓冲下溢。第一种情况往往是由视频方面获取解码数据时间过长引起的,因为那会导致视频时钟变慢,第二种情况则是由于获取音频解码数据时间过长引起的,这种情况下大量的音频数据会被丢弃,需要较长的时间恢复同步。基于独立时钟的策略
这种策略采用一个独立的时钟作为标准时钟。对于视频,如果发生了“图像迟到”,丢弃该图像之后立即调慢系统时钟,虽然同时会导致声音的缓冲下溢,但引发“图像迟到”的因素消失之后同步迅速恢复。同样,对于音频,如果获取音频数据时间太长导致缓冲下溢发生,也可以立即调慢系统时钟,虽然同时会导致图像停顿,但恢复同步的时间大大缩小。
音频设备缓冲下溢现象和视频的“图像迟到”现象都属于媒体失步现象,一个好的多媒体系统首先要竭力避免这种情况发生,通常认为,可以觉察到的视频延迟范围为-125ms到45ms,而能忍受的范围为-185ms到90ms3)[3]。其次,系统要具备发生失步之后尽可能快地恢复同步的能力,为达到此目的,需要综合传输协议设计、媒体容器设计、缓冲控制及同步策略等多个方面来设计系统。
3.3.2 失步的预防
上一节给出的策略不是为了避免失步,而是为了在发生失步的情况下恢复尽快同步。避免失步必须从协议、容器和缓冲策略几个方面来考虑:
- 首先,在设计传输协议和媒体容器时,要尽可能保证音视频数据适当的交错,避免长时间无法获取某种媒体数据的情况发生。这一条对于实时媒体的传输尤其重要,因为缓冲策略会增加延时,无法应用于实时通信。
- 其次,要根据传输协议构造一定大小的传输缓冲,以防止网络抖动和音视频交错不好导致的媒体数据迟到;
- 此外最好构造适当长度的解码缓冲来保存解码后的音视频数据,以防止解码抖动导致的媒体数据迟到。
后续还有压缩编码部分,见:
数字媒体技术揭秘(续)——压缩编码