5.6 AVI文件
AVI(Audio Video Interleaved)是由微软公司在1992年11月推出的一种多媒体文件格式。它可将单个或多个视频流和音频流以交叠顺序封装在一个文件中,以达到音频与视频可同步播放的效果。AVI文件对视频文件采用了一种压缩比较高的有损压缩方式,虽然画面质量不是很高,但其应用范围仍非常广泛。AVI格式支持256色和RLE压缩,主要用来保存电视、电影等多媒体影像信息。对于机器视觉和图像处理分析应用来说,若对图像的质量要求不是很高,使用AVI文件可以将多帧图像保存在同一个文件中,方便处理。
AVI文件本质上是一种RIFF(Resource Interchange File Format)文件格式。RIFF格式文件使用诸如'RIFF'、'AVI'、'LIST'等四字符码(Four-Character Code, FOURCC)来组织数据。其中,四字符码允许含有空格,如'AVI'。在RIFF文件中,四字符码和数据的安排顺序如表5-13所示。此外还应注意,Windows操作系统使用从低到高的字节存放顺序(Little-Endian,Intel),因此诸如DWORD类型的数据0xA8B9C0D1在文件或内存中的存储顺序为D1 C0 B9 A8。
表5-13 RIFF文件结构
RIFF文件通常使用列表(List)和块(Chunk)来组织文件中的实际数据,且列表可以嵌套子列表和数据块。其中列表和数据块的结构如表5-14所示。
表5-14 列表(List)的结构
AVI文件类型用一个四字符码'AVI'来表示。整个AVI文件由RIFF文件头、两个媒体流列表(一个用于描述媒体流格式,另一个用于保存媒体流数据)和一个可选的索引块3部分构成。AVI文件的展开结构大致如图5-14所示,其中LIST(listType(listData))形式表示列表,ckID(ckData)形式表示数据块,[Optional Element]表示可选项。
图5-14 AVI文件的展开结构
文件头一开始用RIFF('AVI'…)表示AVI文件类型。紧跟其后的是AVI文件必需的第一个列表hdrl,它用于描述AVI文件中各个流的格式信息(AVI文件中的每一路媒体数据都称为一个流)。hdrl列表嵌套了一系列块和子列表,第一个嵌套的avih块用于记录AVI文件的全局信息,如流的数量、视频图像的宽和高等。它通常可以通过下面的AVIMAINHEADER数据结构来操作:
在avih块后嵌套了一个或多个strl子列表,这些strl子列表按顺序对媒体流进行说明。也就是说第一个strl子列表用来说明第一个流,第二个strl子列表用于说明第二个流,以此类推。文件中有多少个流,这里就对应有多少个strl子列表。每个strl子列表至少包含一个strh块和一个strf块,而保存编解码器配置信息的strd块和保存流名字的strn块为可选。strl子列表中的strh块包含了流的结构信息,它可以通过下面的AVISTREAMHEADER数据结构来操作:
strl子列表strf块用于说明流的具体格式。如果流数据为视频流,使用BITMAPINFO数据结构来描述;而如果是音频流,则使用一个WAVEFORMATEX数据结构来描述。当AVI文件中的所有流都使用一个strl子列表说明以后,hdrl列表的任务也就完成了。
紧随hdrl列表之后的就是AVI文件必需的另一列表movi,它用于保存真正的视频和音频媒体流数据。可以将数据块直接嵌在movi列表里面,也可以将几个数据块组合成一个rec列表后再编排进movi列表。读取AVI文件内容时,一般需要将一个rec列表中的所有数据块一次性读出。当AVI文件中包含多个流时,数据块使用四字符码对它们进行区别。该四字符码由2个字节的流编号和2个字节的类型码组成。类型码包括db(非压缩视频帧)、dc(压缩视频帧)、pc(改用新的调色板)和wb(音缩视频)4种。例如第一个流为音频,第二个流是压缩视频,则表示音频流数据块的四字符码为00wb,表示压缩视频数据块的四字符码为01dc。
对于视频数据来说,在AVI数据序列中间还可以定义一个新的调色板,每个改变的调色板数据块用编号和类型码pc来表示,新的调色板使用数据结构AVIPALCHANGE来定义。应注意的是,若一个流的调色板中途可能改变,则应在这个流格式的描述中,也就是AVISTREAMHEADER结构的dwFlags中包含AVISF_VIDEO_PALCHANGES标记。另外,文字流数据块可以使用随意的类型码表示。
最后,紧跟在hdrl列表和movi列表之后的是AVI文件可选的索引块。索引块对文件中每一个媒体数据块进行索引,并且记录它们相对于AVI文件开头或相对于movi列表的偏移。索引块中的信息赋予了AVI文件灵活随机存取内数据的能力。若AVI文件包含索引块,则应在AVIMAINHEADER结构的dwFlags中包含AVIF_HASINDEX标记。索引块使用四字符码idx1来表征,整个块可使用数据结构AVIOLDINDEX来定义:
此外,还有一种专门用于内部数据对齐的特殊的数据块,用一个四字符码'JUNK'来表征,应用程序应忽略这些数据块的实际意义。值得一提的是,很多AVI文件还支持由Matrox OpenDML集团于1996年2月开发的格式后缀,这些文件非正式地称为AVI 2.0,并得到微软公司的支持。上述AVI文件格式的介绍,并不包括OpenDML AVI文件格式扩展部分的内容。
AVI本身只是一个数据组织的框架,内部的图像数据和声音顺据格式可以任意形式编码。AVI格式可以跨多个平台使用,但它体积往往很大,而且压缩标准不统一,解码时需要对应版本的解码器。另外,由于索引块放在了文件尾部,所以在播放流媒体时往往需要较长时间的等待。
NI Vision同样提供了高度模块化的AVI文件操作VI,如图5-15所示。使用这些VI可以创建或打开AVI文件、获取AVI文件中图像的帧数及类型信息、读写AVI文件中的图像帧以及关闭AVI文件。由于AVI文件格式没有统一的压缩标准,不同的编码器的速度、生成的AVI文件大小以及文件中图像的质量各不相同。因此读写文件时通常还需要获知系统已安装的AVI编码解码器(CODEC)的名字,根据需求选择相应的CODEC, NI Vision也提供了针对此操作的VI。
图5-15 AVI文件的操作VI
有了NI Vision提供的AVI文件操作VI,读写AVI文件就变得极为简单。图5-16展示了将一个文件夹中的PNG文件写入AVI文件的例子。程序开始时先使用List Folder函数列出文件夹\pcb中的所有PNG文件,然后取出文件夹中的第一个PNG文件,通过IMAQ GetFileInfo获取它在X、Y方向上的分辨率,用于统一要写入AVI文件的图像大小参考。IMAQ Create为图像的读取在内存中分配缓冲区,此后IMAQ ReadFile会将第一幅图像读入内存。有了第一幅图像数据作为参考,IMAQ Codec就可以根据该图像的类型、尺寸、像素位深度等信息,枚举出系统中已安装的与图像兼容的AVI编码解码器名称。在此例子中,未对各种编码解码器的性能进行比较,而是直接使用枚举出的第一个AVI编码解码器进行AVI文件操作。
IMAQ AVI2 Create根据第一个编码解码器的名称,在\pcb目录中新创建了一个名为new.avi的文件。创建AVI文件时,可以指定帧率和文件的质量。帧率指播放AVI文件时每秒显示的帧数,通常与视频制式统一,如PAL为25帧/秒,NTSC为30帧/秒。此处为了便于观察效果,设置帧率为3。文件的质量指编码器对图像压缩时的相对质量,用0~1000范围内的数字表示,数字越大,文件中图像的质量越好。紧随其后,For循环内的IMAQ ReadFile可逐一将\pcb目录中的PNG文件取出,并由IMAQ Resample按照第一幅图像的大小对它们重新采样后写入AVI文件中。等全部图像都写入AVI文件后,程序将关闭打开的AVI文件,并释放分配的内存缓冲区。程序执行完成后,如果检查\pcb目录,就会发现其中多了一个包含全部文件的new.avi文件。
图5-16 将多幅图像写入AVI 文件
读取AVI文件中图像帧的程序更为简单,如图5-17所示。程序中IMAQ AVI2 Open首先打开用户通过对话框选择的AVI文件,IMAQ AVI2 Get Info立即从打开的文件中获得总帧数、帧率及图像类型信息。一旦IMAQ Create为图像帧的读取在内存中分配了缓冲,则For循环就会逐一读取AVI文件中的图像帧,如果图像的读取速度较每帧应播放的时间(帧率的倒数)快,则应相应等待以按照帧率进行播放。所有图像读取完成后,应关闭打开的AVI文件并释放缓冲区,才能退出程序。
图5-17 将多幅图像写入AVI 文件