因为接下来要分析的漏洞CVE-2010-2553,涉及到AVI文件格式。
所以,有必要详细了解一下AVI文件格式,这样,才能知道怎样构造出能达到漏洞利用的样本。
——当你的才华还配不上你的野心时,请静下来好好努力!
1、AVI简介
1.1、AVI基本概念
AVI是音频视频交错(Audio Video Interleaved)的缩写,它是Microsoft于1992年11月开发的一种符合RIFF文件规范的多媒体容器格式,作为其Windows视频软件的一部分。AVI文件可以在文件容器中同时包含音频和视频数据,该文件容器允许同步播放音频和视频。
许多AVI文件使用1996年2月由Matrox OpenDML组开发的文件格式扩展名。这些文件受Microsoft支持,并且被非正式地称为“AVI 2.0”。2010年,美国政府的国家档案和记录管理局将AVI定义为官方文件格式,用于保存数字视频。
AVI文件格式是Resource Interchange File Format(RIFF:资源交换文件格式)的子格式。RIFF是一种通用文件容器格式,用于将数据存储在带标签的块中。它主要用于存储多媒体,例如声音和视频,尽管它也可以用于存储任意数据。RIFF文件所包含的数据类型由该文件的扩展名来标识,能以RIFF文件存储的数据包括:音频视频交错格式数据(.AVI)、波形格式数据(.WAV)、位图格式数据(.RDI)、MIDI格式数据(.RMI)、调色板格式数据(.PAL)、多媒体电影格式数据(.RMN)、动画光标格式数据(.ANI)、其它RIFF文件(.BND)。
可以查看AVI文件结构的软件:
1、RIFF File Viewer - RIFFPad v0.73 by Menasoft
2、AtomicBrowser Version 2.0, Written by David Mojdehi
1.2、AVI文件类型
基本上,有3种类型的AVI文件:
AVI1.0: 原始的,旧的AVI文件。Open-DML(AVI2.0): AVI文件格式的扩展,版本1.02已在1996年2月28日推出。最重要的改进是:
- 对
文件大小几乎没有限制(例如,比NTFS所允许的要多得多)- 减少了33%的
开销Hybride-Files(混合文件): 出于兼容性原因,Open-DML文件包含附加的旧版索引。这不是那些文件的“官方”词汇,但它很好地描述了那种文件类型。仅包含一个RIFF列表的Hybride文件可以被视为任一文件类型。
AVI 1.0是最基本的格式,由于索引地址与大小用4字节表示,所以最大支持4G容量,而且与文件系统类型有关,如下:1
2
3
4
5
6-- Video for Windows (AVI 1.0)
- FAT (FAT16): 4GB (2GB practical, safe)
- FAT32: 4GB (2GB practical, safe)
- NTFS: 4GB (2GB practical, safe)
为了安全一般限制为2G。
1.3、基本数据结构
1.3.1、RIFF文件的基本数据结构
Chunk是组成RIFF文件的基本单元,其基本结构如下:
1 | struct chunk{ |
id: 此块的标识符,由4个ASCII字符组成的FourCC数据格式标识符,用以识别块中所包含的数据。如:“RIFF”、“LIST”、“AVI ”、“WAVE”等等。size: 存储在data域中的数据的长度,id和size域的大小不包括在该值内。data: 块内容,具有前一个字段中给定大小的数据。其中的数据是以字(Word)为单位排列的,如果该数据长度为奇数,则在最后添加一个空字节(Null)。
两个块标识符“RIFF”和“LIST”引入了可以包含子块的块。而其它块仅能含有数据。“RIFF”和“LIST”类型的Chunk结构如下:
1 | struct chunk{ |
可以看出“RIFF”和“LIST”也是Chunk,只是它的data由type和restdata两部分组成。
type: 由4个ASCII字符组成的FourCC数据格式标识符,代表“RIFF”文件的类型,如:“WAVE”、“AVI ”;或者是“LIST”块的类型,如AVI文件中的列表“hdrl”、“movi”。restdata:data中除type所占的4个字节后剩余的数据,包括块内容,包含若干“Chunk”和“LIST”。
FourCC:
FourCC(“four-character code”)是四个字节(通常为ASCII)的序列,用于唯一标识数据格式。在RIFF文件格式中,FourCC非常普遍,struct chunk中的id成员,“RIFF”和“LIST”的type成员,起始标识等信息都是用FourCC表示的。FourCC一般是四个字符,如'abcd'这样的形式,也可以三个字符包含一个空格,如'abc '这样的形式。AVI文件格式使用FourCC编码标识流类型,数据块,索引条目和其他信息。
1.3.2、AVI文件的基本数据结构
在AVI文件中,有两种基本单元“Chunks”和“Lists”,与RIFF文件中的基本单元差别不大,就是字段名称稍有不同,其结构如下:
Chunks:1
2
3
4
5typedef struct {
DWORD dwFourCC;
DWORD dwSize; //data
BYTE data[dwSize]; //contains headers or video/audio data
}CHUNK;
dwFourCC: 标识本“CHUNK”所包含的数据类型,如:“avih”(AVI Header)、“strh”(Stream Header)、“strf”(Stream Format)、“00dc”(编号为00的视频数据块)。dwSize: 本CHUNK所包含数据的大小,不包括dwFourCC和dwSize所占的8个字节。data: 本CHUNK所包含的数据,大小由上一个字段dwSize指定。可以为Header、视频/音频数据。
Lists:1
2
3
4
5
6typedef struct {
DWORD dwList;
DWORD dwSize; //dwFourCC+data
DWORD dwFourCC;
BYTE data[dwSize-4]; //contains Lists and Chunks
}LIST;
dwList: LIST的类型,其值可以为“RIFF”(“RIFF-List”)或“LIST”(“List”)。dwSize: 本LIST所包含数据的大小,嵌套List和Chunk的数据大小之和。包含dwFourCC和data。dwFourCC: 由4个ASCII字符组成的FourCC数据格式标识符,代表“RIFF”文件的类型,如:“WAVE”、“AVI ”;或者是“LIST”块的类型,如AVI文件中的列表“hdrl”、“movi”。data: 本LIST所包含的数据,其大小为dwSize-4。数据内容可以是若干个List和Chunk。
2、AVI文件结构
2.1、AVI文件整体布局
我们用如下的方式来表示一个LIST块(dwSize省略):
1 | LIST (dwFourCC (data)) |
可选块我们用“[]”括起来:
1 | ['idx1' (<AVI Index>) ] |
AVI文件格式将文件的数据分为一个一个“Chunk”,每个“Chunk”都由FourCC标签标识。AVI文件格式采用RIFF格式的文件中单个“Chunk”的形式,然后细分为两个必须的“List Chunk”(“hdrl”和“movi”)和一个可选的“Index Chunk”(“idx1”)。下面是一个简化的AVI文件布局:
1 | RIFF ('AVI ' |
“hdrl”列表定义了本AVI文件所保存的数据的格式,是第一个必须的LIST块。“movi”列表保存的是AVI的音频/视频序列数据,是第二个必须的LIST块。“idx1”为“movi”列表中包含的音频/视频序列数据块在文件中位置的列表。
AVI文件的实际数据中,使用了列表(List)和块(Chunk)的形式来组织。列表(List)可以嵌套列表(List)和块(Chunk)。整个AVI文件可以看成一个List数据块,其dwList为“RIFF”,称为“RIFF-List”块,其dwFourCC为“AVI”。一个AVI文件中只允许存在一个RIFF块。RIFF块中包含一系列的子块,其中有一种子块的dwList为“LIST”,称为LIST块,LIST块中可以再包含一系列的子块,但除了LIST块外的其他所有的子块都不能再包含子块。
“hdrl”和“movi”LIST块使用子块作为它们的数据,我们将其展开,以下示例显示了一个较为完整的AVI文件的布局:
1 | RIFF ('AVI ' |
每个AVI文件都具有以下布局:
1 | RIFF AVI //强制 |
将其中dwFourCC ='AVI'的RIFF-List称为'RIFF-AVI-List',将其中dwFourCC ='AVIX'的RIFF-List称为'RIFF-AVIX-List'。
与uint32(4字节)的表示范围不同,这些列表的大小限制不是4GB,而是
- 对于
AVI 1.0:大小(RIFF-AVI) <2GB- 对于
Open-DML:
- size(
RIFF-AVI) <1GB(假设某些混合应用程序(例如VirtualDub!)为2GB)- size(
RIFF-AVIX) <2GB
由于Windows XP会在未找到旧索引的情况下坚持读取整个第一个RIFF AVI列表,并且由于旧索引会导致开销,因此建议创建尽可能小的RIFF-AVI-List。
2.2、hdrl List(Headerlist)
“hdrl List”是AVI文件的文件头,其中包含的数据是有关视频的MetaData(元数据),例如其宽度、高度和帧频。“hdrl List”块包含两种子块,一种是dwFourCC为“avih”的Chunk,另一种是dwFourCC为“strl”的List。
2.2.1、avih Chunk(AVI Header)
“avih”块用于存储AVI文件的全局信息,如:流的数量、视频的宽度和高度等。此结构定义如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20typedef struct {
DWORD dwFourCC; // 必须为'avih'
DWORD dwSize; // 本数据结构的大小,不包括最初的8个字节(dwFourCC和dwSize两个字段)
BYTE data[dwSize]; // AVIMainHeader struct
}CHUNK;
typedef struct {
DWORD dwMicroSecPerFrame; // 每帧的持续时间(以毫秒ms为单位),定义avi的显示速率
DWORD dwMaxBytesPerSec; // 最大的数据传输率
DWORD dwPaddingGranularity; // 数据填充的粒度
DWORD dwFlages; // AVI文件的特殊属性,如是否包含索引块,音视频数据是否交叉存储
DWORD dwTotalFrame; // 文件中的总帧数
DWORD dwInitialFrames; // 说明在开始播放前需要多少桢
DWORD dwStreams; // 文件中包含的数据流个数
DWORD dwSuggestedBufferSize; // 建议使用的缓冲区的大小,
// 通常为存储一桢图像以及同步声音所需要的数据之和
DWORD dwWidth; // 图像宽
DWORD dwHeight; // 图像高
DWORD dwReserved[4]; // 保留值
} AVIMainHeader;
dwFourCC: 标识此块为“avih”,用于存储AVIMainHeader结构体数据。dwSize:AVIMainHeader结构体的字节数。dwMicroSecPerFrame: 一个视频帧的持续时间(以微秒为单位)。可以忽略此值(请参阅Stream Header),但任何AVI编写器均应正确写入该值。
重要说明:某些AVI修改软件,例如AVIFrate,会将帧速率值写入Stream Header,而不是dwMicroSecPerFrame。 因此,dwMicroSecPerFrame不应该被认为是可靠的!dwMaxBytesPerSec: 指定文件的大致最大数据速率。这个值表示系统每秒必须处理的字节数,以显示AVI序列,该序列由Main Header和Stream Header块中包含的其他参数指定。该值也不重要。其可靠性不应被高估。dwPaddingGranularity: 指定数据对齐方式(以字节为单位)。将数据填充为该值的倍数。dwFlages: 见下文。dwTotalFrame: 指定文件中数据帧的总数。包含在RIFF-AVI列表中的视频帧数(如果存在RIFF-AVIX-List,则不应包含整个文件中的帧总数。某些声称处理AVI文件的工具甚至假定了这一点,但它显然违反了Open-DML文件格式规范。此类应用程序已损坏。)。由于某些AVI文件muxer在此处写入了错误的值,因此不应认为此值是可靠的。dwInitialFrames: 为交错格式指定初始帧数(非交错格式应该指定为0)。如果要创建交错文件,请在该成员中的AVI序列的初始帧之前指定文件中的帧数。要为音频驱动程序提供足够的音频以供使用,必须将交错文件中的音频数据相对于视频数据倾斜。通常,音频数据应向前移动足够的帧,以允许大约0.75秒的音频数据被预加载。dwInitialRecords成员应设置为音频向前的帧数。还要在音频流头中为AVIStreamHeader结构的dwInitialFrames成员设置相同的值。dwStreams: 文件中包含的数据流个数。例如,一个带有音频和视频的文件有两个流。dwSuggestedBufferSize: 指定用于读取文件的建议缓冲区大小。通常,此大小应足够大以包含文件中最大的块。如果设置为零,或者设置得太小,则播放软件将不得不在播放过程中重新分配内存,这会降低性能。对于交错文件,缓冲区大小应足够大以读取整个记录,而不仅仅是块。dwWidth: 视频流宽度。以像素为单位。dwHeight: 视频流高度。以像素为单位。dwReserved:保留。将这个数组设置为0。
dwFlages中有效的Flags:
AVIF_HASINDEX: 该文件有索引。AVIF_MUSTUSEINDEX: 指示应用程序应该使用索引,而不是文件中块的物理顺序,来确定数据表示的顺序。例如,这个标志可以用来创建一个帧列表来编辑。AVIF_ISINTERLEAVED: Stream适当的相互交错。AVIF_WASCAPTUREFILE: 表示AVI文件是专门分配的用于实时视频采集的文件。应用程序在对设置了该标志的文件进行写入之前应该警告用户,因为用户可能会对该文件进行整理。AVIF_COPYRIGHTED: 表示AVI文件包含有版权的数据和软件。当使用这个标志时,软件不应该允许复制数据。AVIF_TRUSTCKTYPE(Open-DML only!): 该标志表示索引中的关键帧标志(AVIIF_KEYFRAME)是可靠的。如果未在Open-DML文件中设置此标志,则关键帧标志可能存在缺陷,而不会从技术上使文件无效。
2.2.2、strl List(Stream Header List)
“strl List”用于存储AVI文件中的数据流(音频流、视频流、字幕流)的相关信息。在“hdrl List”中的“avih Chunk”之后,有一个或多个“strl List”。
- 1、AVI文件中有多少个
数据流(视频流、音频流、字幕流),这里就对应有多少个“strl List”。每个“strl List”都包含AVI文件中一个数据流的相关信息。- 2、每个
“strl List”中至少包含一个“strh Chunk”和一个“strf Chunk”。- 3、
“strd Chunk”和“strn Chunk”是可选的。- 4、根据
“strl List”在“hdrl List”中的顺序,“hdrl List”中的Stream Header与“movi List”中的Stream Data是一一对应的。第一个“strl List”应用于Stream 0,第二个“strl List”应用于Stream 1,以此类推。
2.2.2.1、strh Chunk(Stream Header)
“strh Chunk”用于描述“strl List”对应的数据流的相关信息。此Chunk中的data中存储的是AVIStreamHeader结构体数据,其结构如下:
1 | typedef struct { |
dwFourCC: 标识此块为“strh”,用于存储AVIStreamHeader结构体数据。dwSize:AVIStreamHeader结构体的字节数。fccType: 表示数据流的种类,“vids”(视频流)、“auds”(音频流)、“mids”(MIDI流)、“txts”(字幕流)。fccHandler: 要使用的编解码器的FourCC。dwFlags: 数据流属性,定义了以下标志:
AVISF_DISABLED: 默认情况下不应激活此数据流。AVISF_VIDEO_PALCHANGES: Stream是使用调色板的视频流,其中在播放过程中调色板会发生变化。wPriority: 此数据流的播放优先级,当有多个相同类型的流时优先级最高的为默认流。wLanguage: 音频的语言代号。dwInitalFrames: 指定交错文件中音频数据在视频帧之前的偏移量。通常,这是大约0.75秒。如果要创建交错文件,请在该成员的AVI序列的初始帧之前指定文件中的帧数。有关更多信息,请参见AVIMainHeader结构的dwInitialFrames成员的备注。dwScale:数据量,视频的每桢的大小或者音频的采样大小。与dwRate一起使用以指定此流将使用的时间尺度。将dwRate除以dwScale可得出每秒的采样数。对于视频流,这是帧速率。对于音频流,此速率对应于播放音频的nBlockAlign字节所需的时间,而对于PCM音频,这只是采样速率。dwScale和dwRate应该互质。测试表明,例如10000000/400000代替25/1会导致文件在某些硬件MPEG4播放器上不起作用。dwRate: 参考dwScale。dwStart: 指定此流的开始时间。单位由AVIStreamHeader中的dwRate和dwScale成员定义。通常,它是0,但是它可以为不与文件同时启动的流指定延迟时间。对于VBR音频,此值指示在流开始之前要播放的无声帧数。dwLength: 指定流的长度,以dwRate和dwScale定义的单位为单位。dwSuggestedBufferSize: 指定应使用多大的缓冲区来读取此流。通常,它包含一个与流中存在的最大块相对应的值。使用正确的缓冲区大小可使播放效率更高。如果您不知道正确的缓冲区大小,请使用零。可以为0(在这种情况下,应用程序必须猜测),但不应该为0,因为Microsoft的AVI解析器在某些情况下无法正确处理此情况(例如,Open-DML文件中的MP3-CBR)。dwQuality: 指定流中数据质量的指示符。质量表示为0到10000之间的数字。对于压缩数据,这通常表示传递给压缩软件的质量参数的值。如果设置为–1,驱动程序将使用默认质量值。dwSampleSize: 指定单个数据样本的大小。如果样本大小不同,则将其设置为0。如果该数字不为0,则可以将多个数据样本分组到文件内的单个块中。如果为0,则每个数据样本(例如视频帧)必须位于单独的块中。对于视频流,此数字通常为0,但如果所有视频帧的大小相同,则可以为非0。对于音频流,此数字应与描述音频的WAVEFORMATEX结构的nBlockAlign成员相同。rcFrame: 指定由AVIMainHeader结构的dwWidth和dwHeight成员指定的影片矩形内的字幕或视频流的目标矩形。rcFrame成员通常用于支持多个视频流。将此矩形设置为与影片矩形对应的坐标,以更新整个影片矩形。该成员的单位是像素。目标矩形的左上角相对于影片矩形的左上角。
该结构的某些成员也存在于AVIMainHeader结构中。AVIMainHeader结构中的数据应用于整个文件,而AVIStreamHeader结构中的数据应用于一个流。
2.2.2.2、strf Chunk(Stream Format)
“strf Chunk”紧跟在“strh Chunk”之后,其描述了数据流中数据的格式。此Chunk中包含的数据取决于数据流的类型。对于视频流,该信息是BITMAPINFO结构,包括适当的调色板信息。对于音频流,该信息是WAVEFORMATEX结构。
视频流(BITMAPINFO)
BITMAPINFO结构定义了一个DIB(Device-Independent Bitmap:设备无关位图)的尺寸和颜色信息。
1 | typedef struct { |
dwFourCC: 标识此块为“strf”,用于存储BITMAPINFO结构体数据。dwSize:BITMAPINFO结构体的字节数。bmiHeader: 一个BITMAPINFOHEADER结构,包含关于DIB的尺寸和颜色格式的信息。bmiColors: bmiColors成员包含以下之一:
RGBQUAD的数组。数组元素组成了颜色表。- 一个16位
无符号整数数组,用于指定当前实现的逻辑调色板中的索引。对于使用DIB的函数,可以使用bmiColors。数组中的条目数取决于BITMAPINFOHEADER结构的biBitCount和biClrUsed成员的值。bmiColors表中的颜色按重要性顺序显示。
DIB(Device-Independent Bitmap:设备无关位图)由两个不同的部分组成:描述位图的尺寸和颜色的BITMAPINFO结构体,以及定义位图像素的字节数组。数组中的位打包在一起,但是每条扫描线必须用0填充,以在LONG数据类型边界上结束。如果位图的高度为正,则位图为自下而上的DIB,其原点为左下角。如果高度为负,则位图为自上而下的DIB,其原点为左上角。
当位图数组紧跟在BITMAPINFO头之后时,位图被压缩。压缩的位图由单个指针引用。对于压缩位图,在使用DIB_PAL_COLORS模式时,必须将biClrUsed成员设置为偶数,以使DIB位图数组从DWORD边界开始(对齐)。
BITMAPINFOHEADER结构体包含了设备无关位图(DIB)的尺寸和颜色格式的信息。
biSize: 指定此结构所需的字节数。如果它们被附加到此结构的末尾,则此值不包括颜色表的大小或颜色掩码的大小。biWidth: 指定位图的宽度,以像素为单位。biHeight: 指定位图的高度,以像素为单位。
- 对于
未压缩的RGB位图,如果biHeight为正,则该位图是自下而上的DIB,其原点位于左下角。如果biHeight为负,则位图是自上而下的DIB,其原点位于左上角。- 对于
YUV位图,无论biHeight的符号如何,位图始终是自顶向下的。解码器应提供具有正biHeight的YUV格式,但为了向后兼容,它们应接受具有正或负biHeight的YUV格式。- 对于
压缩格式,biHeight必须为正,无论图像方向如何。biPlanes: 指定目标设备的平面数。此值必须设置为1。biBitCount: 指定每个像素的位数(bits per pixel:bpp)。对于未压缩格式,此值为每个像素的平均位数。对于压缩格式,此值是在解码图像后未压缩图像的隐含位深度。biCompression:
- 对于
压缩视频和YUV格式,此成员是FourCC代码,指定为以小端序排列的DWORD。例如,YUYV视频的FourCC为“VYUY”或0x56595559。- 对于
未压缩的RGB格式,可以使用以下值:BI_RGB(未压缩的RGB)、BI_BITFIELDS(带颜色掩码的未压缩RGB。适用于16位和32位的位图。)- 对于
16-bpp位图,如果biCompression等于BI_RGB,则格式始终为RGB555。如果biCompression等于BI_BITFIELDS,则格式为RGB555或RGB565。使用AM_MEDIA_TYPE结构中的子类型GUID确定特定的RGB类型。biSizeImage: 指定图像的大小(以字节为单位)。对于未压缩的RGB位图,可以将其设置为0。biXPelsPerMeter: 指定位图目标设备的水平分辨率(以像素每米为单位)。biYPelsPerMeter: 指定位图目标设备的垂直分辨率(以像素每米为单位)。biClrUsed: 指定颜色表中位图实际使用的颜色索引数。biClrImportant: 指定被认为对显示位图很重要的颜色索引数。如果该值为零,则所有颜色都很重要。
RGBQUAD结构体成员含义:
rgbBlue: 颜色中蓝色的强烈程度。rgbGreen: 颜色中绿色的强烈程度。rgbRed: 颜色中红色的强烈程度。rgbReserved: 该成员是保留的,并且必须为零。
音频流(WAVEFORMATEX)
WAVEFORMATEX结构定义了波形音频数据的格式。 该结构仅包括所有波形音频数据格式共有的格式信息。 对于需要附加信息的格式,此结构作为附加信息包括在另一个结构中作为第一个成员。
支持两个以上通道或超过16位的样本大小(更高的采样分辨率)的格式可以用WAVEFORMATEXTENSIBLE结构来描述,该结构包括WAVEFORMATEX结构。
1 | typedef struct { |
WAVEFORMATEX结构体成员含义:
wFormatTag:波形音频格式类型。格式标签已向Microsoft Corporation注册用于许多压缩算法。可以在Mmreg.h头文件中找到格式标签的完整列表。 对于一个或两个通道的PCM数据,此值应为WAVE_FORMAT_PCM。当此结构包含在WAVEFORMATEXTENSIBLE结构中时,此值必须为WAVE_FORMAT_EXTENSIBLE。nChannels:波形音频数据中的通道数。单声道数据使用一个通道,立体声数据使用两个通道。nSamplesPerSec:采样率,以每秒采样数(赫兹:hz)为单位。如果wFormatTag为WAVE_FORMAT_PCM,则nSamplesPerSec的常用值为8.0kHz,11.025kHz,22.05kHz和44.1kHz。对于非PCM格式,必须根据制造商的格式标签规范来计算此成员。nAvgBytesPerSec: 格式标签所需的平均数据传输速率,以字节每秒为单位。如果wFormatTag是WAVE_FORMAT_PCM,则nAvgBytesPerSec应该等于nSamplesPerSec和nBlockAlign的乘积。对于非PCM格式,必须根据制造商的格式标签规范来计算此成员。nBlockAlign:块对齐,以字节为单位。块对齐是wFormatTag格式类型的最小数据原子单位。如果wFormatTag是WAVE_FORMAT_PCM或WAVE_FORMAT_EXTENSIBLE,则nBlockAlign必须等于nChannels和wBitsPerSample的乘积除以8(位/字节)。对于非PCM格式,必须根据制造商的格式标签规范来计算此成员。软件必须一次处理多个nBlockAlign字节数据。对设备的读写数据必须始终从块的开头开始。例如,在样本中间(即在非块对齐边界上)开始播放PCM数据是非法的。wBitsPerSample:wFormatTag格式类型的每个样本位数。如果wFormatTag是WAVE_FORMAT_PCM,则wBitsPerSample应该等于8或16。对于非PCM格式,必须根据制造商的格式标签规范设置此成员。如果wFormatTag是WAVE_FORMAT_EXTENSIBLE,则此值可以是8的任何整数倍,并表示容器的大小,不一定是样本的大小;例如,一个20位样本在24位容器中。某些压缩方案无法为wBitsPerSample定义值,因此该成员可以为0。cbSize: 附加在WAVEFORMATEX结构末尾的额外格式信息的大小(以字节为单位)。非PCM格式可以使用此信息来存储wFormatTag的其他属性。如果wFormatTag不需要其他信息,则必须将此成员设置为0。对于WAVE_FORMAT_PCM格式(仅WAVE_FORMAT_PCM格式),将忽略此成员。当此结构包含在WAVEFORMATEXTENSIBLE结构中时,该值必须至少为22。
使用额外信息的格式的一个示例是Microsoft自适应增量脉冲编码调制(MS-ADPCM)格式。 MS-ADPCM的wFormatTag是WAVE_FORMAT_ADPCM。cbSize成员通常将设置为32。为WAVE_FORMAT_ADPCM存储的额外信息是编码和解码波形音频数据所需的系数对。
WAVEFORMATEXTENSIBLE结构体成员含义:
Format: 指定基本格式的WAVEFORMATEX结构。wFormatTag成员必须为WAVE_FORMAT_EXTENSIBLE。cbSize成员必须至少为22。Samples: 描述样本格式的联合体(union)。
Samples.wValidBitsPerSample: 信号中的精度位数。通常等于WAVEFORMATEX.wBitsPerSample。但是,wBitsPerSample是容器大小,并且必须是8的倍数,而wValidBitsPerSample可以是不超过容器大小的任何值。例如,如果格式使用20位样本,则wBitsPerSample必须至少为24,而wValidBitsPerSample为20。Samples.wSamplesPerBlock: 一个音频数据压缩块中包含的样本数。此值用于缓冲区估计。该值与压缩格式一起使用,该压缩格式在每个块中具有固定数量的样本。如果每个压缩音频数据块中包含可变数量的样本,则可以将该值设置为0。在这种情况下,需要以其他方式获得缓冲区估计和位置信息。Samples.wReserved:保留给操作系统内部使用。设置为0。dwChannelMask:通道掩码,位掩码,指定将流中的通道分配给扬声器的位置。SubFormat: 数据的子格式,例如KSDATAFORMAT_SUBTYPE_PCM。子格式信息类似于WAVEFORMATEX结构的wFormatTag成员中的标签所提供的信息。
WAVEFORMATEXTENSIBLE可以描述WAVEFORMATEX可以描述的任何格式,但可以为两个以上的通道提供额外的支持,为了使每个样本的位数更精确,并支持新的压缩方案。
WAVEFORMATEXTENSIBLE可以安全地强制转换为WAVEFORMATEX,因为它仅配置WAVEFORMATEX.cbSize指定的额外字节。
dwChannelMask成员指定多通道流中存在哪些通道。最低有效位对应于左前扬声器,下一个最低有效位对应于右前扬声器,依此类推。这些位按意义顺序定义如下。
| Speaker Position | Flag Bit | Description |
|---|---|---|
| SPEAKER_FRONT_LEFT | 0x1 | 左前扬声器 |
| SPEAKER_FRONT_RIGHT | 0x2 | 右前扬声器 |
| SPEAKER_FRONT_CENTER | 0x4 | 前中扬声器 |
| SPEAKER_LOW_FREQUENCY | 0x8 | 低频扬声器 |
| SPEAKER_BACK_LEFT | 0x10 | 左后扬声器 |
| SPEAKER_BACK_RIGHT | 0x20 | 右后扬声器 |
| SPEAKER_FRONT_LEFT_OF_CENTER | 0x40 | 左前中置扬声器 |
| SPEAKER_FRONT_RIGHT_OF_CENTER | 0x80 | 右前中置扬声器 |
| SPEAKER_BACK_CENTER | 0x100 | 后中扬声器 |
| SPEAKER_SIDE_LEFT | 0x200 | 左侧扬声器 |
| SPEAKER_SIDE_RIGHT | 0x400 | 右侧扬声器 |
| SPEAKER_TOP_CENTER | 0x800 | 上层中置扬声器 |
| SPEAKER_TOP_FRONT_LEFT | 0x1000 | 上层左前扬声器 |
| SPEAKER_TOP_FRONT_CENTER | 0x2000 | 上层前中扬声器 |
| SPEAKER_TOP_FRONT_RIGHT | 0x4000 | 上层右前扬声器 |
| SPEAKER_TOP_BACK_LEFT | 0x8000 | 上层左后扬声器 |
| SPEAKER_TOP_BACK_CENTER | 0x10000 | 上层后中扬声器 |
| SPEAKER_TOP_BACK_RIGHT | 0x20000 | 上层右后扬声器 |
dwChannelMask中指定的通道必须以规定的顺序显示(从最低有效位开始)。例如,如果仅指定SPEAKER_FRONT_LEFT和SPEAKER_FRONT_RIGHT,则左前扬声器的采样必须首先出现在交错流中。dwChannelMask中设置的位数应与WAVEFORMATEX.nChannels中指定的通道数相同。
为了向后兼容,任何可以由独立的WAVEFORMATEX结构指定的波形格式也可以由WAVEFORMATEXTENSIBLE结构定义。因此,mmreg.h中的每个波形格式标签都具有一个对应的SubFormat GUID。下表显示了一些典型的波形格式标签及其相应的SubFormat GUID。这些GUID在Ksmedia.h中定义。
| Wave-Format Tag | SubFormat GUID |
|---|---|
| WAVE_FORMAT_PCM | KSDATAFORMAT_SUBTYPE_PCM |
| WAVE_FORMAT_IEEE_FLOAT | KSDATAFORMAT_SUBTYPE_IEEE_FLOAT |
| WAVE_FORMAT_DRM | KSDATAFORMAT_SUBTYPE_DRM |
| WAVE_FORMAT_ALAW | KSDATAFORMAT_SUBTYPE_ALAW |
| WAVE_FORMAT_MULAW | KSDATAFORMAT_SUBTYPE_MULAW |
| WAVE_FORMAT_ADPCM | KSDATAFORMAT_SUBTYPE_ADPCM |
由于WAVEFORMATEXTENSIBLE是WAVEFORMATEX的扩展版本,因此它可以描述其他WAVEFORMATEX无法单独描述的格式。供应商可以自由定义自己的SubFormat GUID,以标识没有波形格式标签的专有格式。
对于特定的扩展格式,以下结构定义为WAVEFORMATEXTENSIBLE。
| Definition | Value of SubFormat |
|---|---|
| WAVEFORMATIEEEFLOATEX | KSDATAFORMAT_SUBTYPE_IEEE_FLOAT |
| WAVEFORMATPCMEX | KSDATAFORMAT_SUBTYPE_PCM |
2.2.2.3、strd Chunk(Stream Header Data / Additional Header Data)
“strd Chunk”紧跟在“strf Chunk”之后,保存的是可选的额外的流的头信息数据。如果存在Stream Header Data(“strd”)块,则其遵循Stream Format(“strf”)块。该块的格式和内容由编解码器驱动程序定义。通常,驱动程序使用此信息进行配置。读取和写入AVI文件的应用程序不需要解释此信息。他们简单地将其作为存储块在驱动程序之间来回传输。
2.2.2.4、strn Chunk(Stream Name)
可选的“strn”块包含描述该流的以null结尾的文本字符串。该块包含流的名称。该流名称仅应使用纯ASCII,尤其不能使用UTF-8。
2.2.2.5、indx Chunk(Super Index Chunk)
只有符合Open-DML(AVI2.0)规范的AVI文件才可能存在此块。每个流在其Stream Header List(“strl”)中都包含一个“indx”块。该块是超级索引块(Upper Level Index Chunk或Super Index Chunk),索引的索引。
Upper Level Index(“Super Index”)指向其他索引块,并具有以下结构:
1 | typedef struct _avisuperindex { |
fcc:FourCC代码。与Chunk结构体中的dwFourCC相同。该值必须为'indx'。cb: 此结构体的大小,与Chunk结构体中的dwSize相同。不包括初始的8个字节(fcc和cb)。wLongsPerEntry: 每个索引项的大小,以4字节为单位。此值必须为4。每个aIndex[i]的大小为4*wLongsPerEntry个字节。(每个aIndex[i]的结构取决于特定类型的索引)bIndexSubType:索引子类型。必须为0或AVI_INDEX_SUB_2FIELD。bIndexType:索引类型。必须为AVI_INDEX_OF_INDEXES。nEntriesInUse: aIndex数组中有效的条目数。aIndex[0]..aIndex[nEntriesInUse-1]有效。dwChunkId: 标识被索引的对象的FourCC。索引指向的流的ID,例如“00dc”。因此,一个这样的索引块只能指向同一个流的数据。dwReserved:保留。将数组元素设置为0。aIndex: 包含下列成员的结构体数组。数组中的元素数量是根据cb的值计算的。
qwOffset: 从文件开头到该条目所指向的子索引块的偏移量,以字节为单位。dwSize:子索引块的大小(该条目指向的标准或域索引块的大小),以字节为单位。dwDuration:子索引所覆盖的文件的持续时间,以流节拍(stream ticks)为测量单位,如在AVI Stream Header中指出的(dwScale/dwRate)。对于视频或VBR音频,通常指帧数。
“indx Chunk”(Super Index Chunk)的示意图如下:

2.3、INFO List
“INFO”列表是已注册的全局表单类型,可以存储有助于识别块内容的信息。此信息很有用,但不会影响程序解释文件的方式;例如版权信息和注释。“INFO”列表是列表类型为“INFO”的“LIST”块。以下示例显示了示例“INFO”列表块:
1 | LIST('INFO' INAM("Two Trees"Z) |
“INFO”列表应仅包含以下块。可以定义新的块,但是应用程序应忽略它不理解的任何块。下面列出的块可能仅出现在“INFO”列表中。每个块都包含一个ZSTR或以null结尾的文本字符串(所有文本字符串必须对齐)。
| Chunk ID | Description |
|---|---|
| IARL | 存档位置(Archival Location)。指示文件主题的存档位置。 |
| IART | 艺术家(Artist)。列出文件原始主题的艺术家;例如,“Michaelangelo.”。 |
| ICMS | 受委托的(Commissioned)。列出受委托的文件主题的人员或组织的名称;例如“Pope Julian II.”。 |
| ICMT | 注释(Comments)。提供有关文件或文件主题的一般注释。如果注释长几个句子,请以句号结尾每个句子。不要包含换行符。 |
| ICOP | 版权(Copyright)。记录文件的版权信息;例如,“Copyright Encyclopedia International 1991.”。如果有多个版权,请用分号和空格隔开。 |
| ICRD | 创建日期(Creation date)。指定创建文件主题的日期。以年-月-日格式列出日期,左边用0填充一位数的月和日;例如1553年5月3日的“1553-05-03”。 |
| ICRP | 裁剪(Cropped)。描述图像是否已裁剪,如果已裁剪,则如何裁剪;例如“lower-right corner.”。 |
| IDIM | 大小(Dimensions)。指定文件原始主题的大小;例如,“8.5 in h,11 in w” |
| IDPI | 每英寸点数(Dots Per Inch)。存储用于生成文件的数字化转换器的每英寸点数设置,例如“300”。 |
| IENG | 工程师(Engineer)。存储处理文件的工程师的姓名。如果有多个工程师,请用分号和空格分隔名称;例如,“Smith, John; Adams, Joe.” |
| IGNR | 类型(Genre)。描述原始作品,例如“landscape,”、“portrait,”、“still life,”等。 |
| IKEY | 关键字(Keywords)。提供引用文件或文件主题的关键字列表。用分号和空格分隔多个关键字;例如,“Seattle; aerial view; scenery.” |
| ILGT | 亮度(Lightness)。描述生成文件所需的数字化转换器的亮度设置的更改。请注意,此信息的格式取决于所使用的硬件。 |
| IMED | 媒介(Medium)。描述文件的原始主题,例如“computer image,”(计算机图片)、“drawing,”(绘画)、“lithograph,”(石板画)等。 |
| INAM | 名称(Name)。存储文件主题的标题,例如“Seattle From Above.” |
| IPLT | 调色板设置(Palette Setting)。指定数字化图像时要求的颜色数量,例如“256”。 |
| IPRD | 产物(Product)。指定文件最初打算使用的标题的名称,例如“Encyclopedia of Pacific Northwest Geography.”。 |
| ISBJ | 主题(Subject)。描述文件的内容,例如“Aerial view of Seattle.”。 |
| ISFT | 软件(Software)。标识用于创建文件的软件包的名称,例如“Microsoft WaveEdit.”。 |
| ISHP | 清晰度(Sharpness)。标识生成文件所需的数字化转换器的清晰度变化(格式取决于所使用的硬件)。 |
| ISRC | 源(Source)。标识提供文件原始主题的人员或组织的名称;例如“Trey Research.”。 |
| ISRF | 原始形式(Source Form)。标识被数字化的材料的原始形式,例如“slide,”、“paper,”、“map,”等。这不一定与IMED相同。 |
| ITCH | 技术员(Technician)。标识将主题文件数字化的技术人员;例如“Smith, John.”。 |
2.4、movi List
头信息(hdrl List)之后是一个“movi”列表,其中包含流中的实际数据,即视频帧和音频样本。数据块可以直接驻留在“movi”列表中,或者它们可能会被归类到“rec”列表中。“rec”分组意味着应该一次性从磁盘读取分组的块,并且该块旨在用于从CD-ROM交错播放的文件。
1 | LIST movi |
将块分组为rec-Lists可防止在使用Microsoft AVI Splitter进行重放时过多的搜索,但不允许在某些独立的重放设备上进行播放。
流的最大块大小应小于相应的dwSuggestedBufferSize值。否则,某些播放器,尤其是Microsoft AVI Splitter,可能会发生故障。
标识每个数据块的FourCC包含一个两位数字的流编号,后面跟着的是一个用于定义块中信息的类型的两个字符的编码。
| Two-Character Code | Description |
|---|---|
| ..db | 未压缩视频帧数据块 |
| ..dc | 压缩视频帧数据块 |
| ..wb | 音频数据块 |
| ..tx | 字幕数据块 |
| ix.. | 标准索引块 |
| ..pc | 调色板更换数据块 |
例如,如果Stream0包含音频,则该流的数据块将具有“00wb”形式的FourCC。如果Stream1包含视频,则该流的数据块将具有“01db”或“01dc”形式的FourCC。视频数据块还可以定义新的调色板条目,以便在AVI序列期间更新调色板。每个调色板更换数据块(“..pc”)都包含一个AVIPALCHANGE结构。如果流包含调色板更换数据块(“..pc”),请在该流的AVISTREAMHEADER结构的dwFlags成员中设置AVISF_VIDEO_PALCHANGES标志。文本流可以使用任意Two-Character Code。
AVIPALCHANGE结构体定义了AVI文件中的调色板更换的相关信息。
1 | typedef struct { |
bFirstEntry: 指定要更改的第一个调色板条目的索引。bNumEntries: 指定要更改的调色板条目数,或者指定零以更改所有256个调色板条目。wFlags: 保留。peNew: 指定大小为bNumEntries的PALETTEENTRY结构的数组。
PALETTEENTRY结构指定逻辑调色板中条目的颜色和用法。逻辑调色板由LOGPALETTE结构定义。
peRed: 调色板条目的红色强度值。peGreen: 调色板条目的绿色强度值。peBlue: 调色板条目的蓝色强度值。peFlags: 指示如何使用调色板条目。
peFlags可以设置为0或以下值之一。
| Value | Meaning |
|---|---|
| PC_EXPLICIT | 说明逻辑调色板条目的低位字指定的是硬件调色板索引。该标志允许应用程序显示显示设备面板的内容。 |
| PC_NOCOLLAPSE | 说明将颜色放置在系统调色板中的未使用条目中,而不是与系统调色板中的现有颜色匹配的条目中。如果系统调色板中没有未使用的条目,则颜色将正常匹配。一旦此颜色出现在系统调色板中,其他逻辑调色板中的颜色就可以与此颜色匹配。 |
| PC_RESERVED | 说明将逻辑调色板条目用于调色板动画。该标志可防止其他窗口将颜色与调色板条目匹配,因为颜色经常变化。如果有未使用的系统调色板条目可用,则将颜色放置在该条目中。否则,该颜色不可用于动画。 |
LOGPALETTE结构定义了一个逻辑调色板。
palVersion: 系统的版本号。palNumEntries: 逻辑面板中条目的数量。palPalEntry: 指定一组PALETTEENTRY结构,这些结构定义逻辑调色板中每个条目的颜色和用法。
调色板条目表中的颜色应按重要性顺序显示,因为逻辑调色板中较前的条目最有可能放置在系统调色板中。
2.5、idx1 Chunk
2.5.1、AVI 1.0 index
可选索引块(“idx1”)可以位于“movi”列表之后。索引包含数据块及其在文件中的位置的列表。它由一个AVIOLDINDEX结构组成,该结构具有每个数据块的条目(包括“rec”块)。如果文件包含索引,请在AVIMAINHEADER结构的dwFlags成员中设置AVIF_HASINDEX标志。
AVIOLDINDEX结构由初始RIFF块(Chunk结构体:fcc和cb成员)和“movi”列表中每个数据块的一个索引条目组成。AVIOLDINDEX结构体的定义如下:
1 | typedef struct _avioldindex { |
fcc: 指定FourCC代码。必须为“idx1”。cb: 指定本结构体的大小,与Chunk结构体中的dwSize相同。不包括初始的8个字节(fcc和cb)。aIndex: 包含下列成员的结构体的数组。
dwChunkId: 指定用于标识AVI文件中流的FourCC。FourCC的格式必须为“xxyy”,其中xx是流编号,而yy是两个字符的代码,用于标识流的内容。请看前面介绍“movi List”时的内容。dwFlags: 请看下文。dwOffset: 指定数据块在文件中的位置。该值应指定为距“movi”列表开头的偏移量(以字节为单位);但是,在某些AVI文件中,它是距文件开头的偏移量。AVI文件解析器必须能够处理两个版本。dwSize: 指定数据块的大小,以字节为单位。
dwFlags指定以下标志的0个或多个的按位组合:
| Value | Meaning |
|---|---|
| AVIIF_KEYFRAME | 此条目所指的数据块是关键帧。 |
| AVIIF_LIST | 此条目所指的数据块是一个“rec”列表。不是一个Chunk。 |
| AVIIF_FIRSTPART | 此条目所指的数据块需要使用其后的帧;它不能单独存在。 |
| AVIIF_LASTPART | 此条目所指的数据块需要使用其之前的帧;它不能单独存在。 |
| AVIIF_NO_TIME | 此条目所指的数据块不影响流的计时。例如,应该为调色板更换数据块(“..pc”)设置这个标志。 |
如果既未设置AVIIF_FIRSTPART也未设置AVIIF_LASTPART,则该块可以单独使用,换句话说,其相应的流至少有一个数据包。这对于将VBR音频流存储在AVI文件中非常重要。
“idx1 Chunk”的标准形式示意图:

2.5.2、AVI 2.0 index(Open-DML)
AVI2.0索引可以显示为单个块(“idx1”)。或者,可以在“movi”块中插入索引段(“ix..”)。如果将索引段(“ix..”)放置在“movi”块中,则超级索引(“Super Index Chunk”或“indx Chunk”)包含索引段(“ix..”)的索引。AVIMETAINDEX结构是索引段(“ix..”)和超级索引(“indx”)的基础结构。
AVIMETAINDEX结构体是AVI2.0索引的基本结构(“indx”格式)。AVIMETAINDEX结构体的定义如下(可参考“indx Chunk”节):
1 | typedef struct _avimetaindex { |
fcc:FourCC代码。取值为“indx”或“ix..”,其中“..”是流编号。cb: 指示此结构体的大小,不包括初始的8个字节(fcc和cb)。wLongsPerEntry: 每个索引项的大小,以4字节为单位。bIndexSubType:索引子类型。含义取决于bIndexType的值。bIndexType: 请看下文。nEntriesInUse: adwIndex数组中有效的条目数。dwChunkId: 标识被索引的对象的FourCC。如果索引的对象是一个流,则该成员与AVIOLDINDEX结构的dwChunkId成员具有相同的含义。dwReserved: 该成员的含义取决于索引类型。adwIndex:索引项的数组。该数据的格式取决于索引类型。
bIndexType可以具有以下值:
| Value | Meaning |
|---|---|
| AVI_INDEX_OF_INDEXES 0x00 |
每个索引条目都指向另一个索引。将AVIMETAINDEX结构视为AVISUPERINDEX结构。bIndexSubType的值必须为0。 |
| AVI_INDEX_OF_CHUNKS 0x01 |
每个索引条目指向文件中的一个数据块。 1、如果bIndexSubType为0,将AVIMETAINDEX结构视为AVISTDINDEX结构。每个索引条目都是一个AVISTDINDEX_ENTRY结构。 2、如果bIndexSubType为AVI_INDEX_SUB_2FIELD,则该索引是一个域索引块(Field Index)。 DirectShow不支持域索引(Field Index)。 |
| AVI_INDEX_IS_DATA 0x80 |
adwIndex数组包含一个数据表,而不是一个索引项列表。 |
2.6、JUNK Chunk
根据需要插入“JUNK”块,可以在AVI文件中对齐数据。应用程序应忽略“JUNK”块的内容。