AVI文件格式及其应用研究

时间:2022-05-07 05:01:47

AVI文件格式及其应用研究

摘 要:AVI文件是Windows操作系统下最常用的流媒体文件格式之一,了解AVI文件格式是正确使用AVI文件的基础,以一个具体的AVI文件的二进制码为例,详细解释和研究AVI文件的各个字段的具体含义,并指出AVI-1文件与AVI-2文件格式的区别,在此基础上,可以对AVI文件进行各种编程操作。给出使用VFW(Video For Windows)用Visual C++编程语言从AVI-2文件中获取视频帧,转换为BMP图像,加以显示并存储为位图文件的编程方法。

关键词:流媒体文件;VC;AVI;VFW

中图分类号:TP311.1 文献标识码:A 文章编号:1004-373X(2008)02-119-04

Study on AVI File Format and Its Application

XU Dianwu

(Mechano-Electronic College,China University of Petroleum,Beijing,102249,China)オ

Abstract:Under Windows environment,AVI is a popular stream media format.To work with AVI file,it is the basic to know about AVI file format.In this paper,the example ofAVI file binary code is presented in binary code,to illustrate every chunk in the file.The format of AVI files,and especially the difference between AVI-1 and AVI-2 format is discussed.Programming method for pickup video frames from an AVI file and save it as BMP files in Visual C++ is presented.

Keywords:stream medium file;VC;AVI;VFWオ

1 引 言

自微软公司推出AVI文件格式以来,AVI逐渐成为Windows 操作系统上最常用的流媒体文件格式之一[1]。AVI是视频数据和音频数据交叉编码(Audio Video Interleaved)的一种RIFF文件,其多用于音视频捕捉、编辑、播放等,其文件名后缀为.avi。应用程序的开发者在编写多媒体处理软件时,除要对AVI文件进行播放、编辑、制作外,也经常需要从一个AVI文件中提取一视频帧并存储为BMP图像文件,这些都需要对AVI文件格式有深入了解,本文在对AVI文件格式进行解析的基础上,给出了从AVI文件中获取位图的程序设计方法。这里介绍的AVI文件格式不包括OpenDML AVI M-JPEG文件格式分委员会制定的内容以及nAVI(一种改进的ASF格式)的内容。

2 RIFF文件格式简介

RIFF(Resource Interchange File Format)文件格式是一个信息交换框架[1],AVI文件格式是以RIFF为基础的,所以在很多文献中,AVI文件又被称为AVI RIFF文件[2]。

RIFF文件组成的基本单元叫作“块”(chunk),使用FOURCC码(Four Character Code,4字符码,Windows中的数据类型为FOURCC)来标识文件中的每个块。FOURCC是由4个ASCII码组成的一个DWORD型数据,高字节在后,如4字符abcd的FOURCC码是0X64636261。RIFF就是一个FOURCC,表示这是一个RIFF块。在一个RIFF文件中,除RIFF块和LIST块可以包含子块(Sub Chunk)外,其他任何块均不可以包含子块。

块的格式分为2种,可以包含子块的块(RIFF,LIST)的格式为:FOURCC、块长度(4字节,DWORD类型,长度不包括FOURCC和块长度占用的8个字节)、块数据类型、块数据。不可以包含子块的块的格式是:FOURCC、块长度、块数据,没有块数据类型部分。

3 AVI文件格式

AVI文件是一种最复杂的RIFF文件。现在常用的AVI文件有2种:AVI-1和AVI-2。在AVI-2文件中,通常包含2个流,一个视频流和一个音频流(被称为标准AVI格式),但只有一个视频流或音频流也是合法的。在AVI-1文件中只包含一个DV(digital Video)数据流(视频采集设备的输出数据,其中既有视频信息也有音频信息),在文件中以单个流的形式存在,其主要优点是占用较少的存储空间,对AVI-1的支持主要来自DirectShow中的DV Muxer和DV Splitter筛选器(Filter)[2]。虽然目前AVI-2仍然是最常使用的格式,但AVI-1由于其自身的优点和受到微软最新技术的支持,将来可能会比AVI-2更流行。虽然这2种AVI文件不兼容,但他们都以RIFF格式为基础,除了实际的流数据格式不同以外,差别主要存在于文件的strh块和strf块。

下面为AVI文件的格式,左边小括号处省略了4字节的文件大小或列表大小或块大小,凡FOURCC都用4个字母表示,除RIFF和LIST外,所有的4字符码都用单引号括起来,方括号表示可选:

RIFF (′AVI′[JY]//RIFF文件头,块的数据类型是AVI,

[JY]表示空格。

LIST (′hdrl′[JY]//列表,列表中的数据类型是hdrl,

[JY]下面紧跟avih子块。

′avih′(AVI文件的全局信息,本行构成一个AVIMAINHEADER结构,长度为64个字节)。

LIST (′strl′[JY]//strl列表,表示本列表是一个流的列表,

[JY]其后紧跟strh子块。

′strh′([JY]//流的头信息,本行构成一个AVISTREAMHEADER

[JY]结构,长度为64字节)

′strf′( [JY]//流的格式信息子块,描述流中数据的格式。)

[ ′strd′([JY]//附加的流头数据,一般为

[JY]编解码器驱动程序所定义) ]

[ ′strn′(以0结束的字符串,流的名字) ]

...

)

...

)[JY]//hdrl列表块到此结束

LIST (′movi′[JY]//movi列表块,包含了流的实际数据,

[JY]数据可以是子块,也可以将子块组织成rec 列表,

[JY]一个rec列表中的数据应该一次性地从磁盘读出。

{SubChunk | LIST (′rec ′

SubChunk1

SubChunk2

...

)

...

}

...

)//movi列表块到此结束

[′idx1′ (索引块) ]

)//RIFF块到此结束,这就是一个完整的AVI RIFF文件

从上面的格式可以看出,一个AVI RIFF文件由3大部分组成:RIFF文件头;hdrl列表;movi列表。其中hdrl列表包含avih子块和strl子列表,文件中有多少个流,hdrl列表中就有多少个strl子列表,strl子列表在hdrl中的次序就是流的序号(隐含)。

strl子列表由strh子块、strf子块、strd子块(可选)、strn子块(可选)构成。movi列表中存储的是流的实际数据,movi列表中数据子块的种类有:##db,##dc,##pc,##wb,其中##表示数据所属的流的序号,如00db,01dc。db表示未压缩的视频帧;dc表示压缩的视频帧;wb表示音频数据;pc表示调色板变化,其后的数据是一个AVIPALCHANGE结构,此时strh中的dwFlages字段必须置为AVISF_VIDEO_PALCHANGES。

movi列表最后可以有一个可选的索引块(idxl),他指出流的数据在文件中的位置,以便于对数据进行随机访问。一个AVI RIFF文件如果有索引块,则将AVIMAINHEADER结构中的dwFlags字段置为AVIF_HASINDEX。AVI文件中的JUNK块为数据对齐而存在,应用程序应该忽略JUNK块的内容。

AVI-1中只有一个流(DV data stream),其AVISTREAMHEADER(strh块)中的fccType字段是4字符码′iavs′,fccHandler字段是′dvsd′,′dvhd′,′dvsl′三者之一(分别对应3种DV格式),strl子列表中的strf块是DVINFO结构(32字节)。AVI-2中一般有2个流,视频流的AVISTREAMHEADER(strh块)中的fccType字段是4字符码′vids′(也可以是′dvsd′,′dvhd′,′dvsl′三者之一)[2], 音频流的4字符码是′auds′,其视频流的strl子列表中的strf块是BITMAPINFOHEADER结构(40字节),其后可以跟随DVINFO结构。AVI-2中音频流的strf块是WAVEFORMATEX结构。

AVI RIFF文件格式只规定了文件的组成方式,即各种数据如何在文件中排列等,对于文件中的数据并没有做出编码格式的约束,如MPEG-1、DivX等都可以作为文件中数据的编码格式。数据也可以未经压缩。AVI文件可以看成是一个数据容器,AVI格式只规定了将数据装入这个容器的方法,但对数据本身的编码格式并不涉及。要操作一个AVI文件(播放、抓图、编辑),必须在系统中安装与该AVI文件中的编码器对应的解码器,才能进行正确的工作。

4 AVI-2文件格式的例

下面以AVI文件bfh.avi(风景介绍片)为例,将该AVI文件打开,逐字节解释其中的数据(见图1):

52494646是RIFF的ASCII码;7E443400是文件长度,高位字节在后,(0034447E)16,(3425406)10;41564920是AVI的ASCII码,表示空格;表示RIFF块(文件)的数据类型是AVI。4C495354是LIST的ASCII码,表示这是一个列表。10h开始的4字节表示本列表长度,(132)16,(306)10。接着6864726C是hdrl的ASCII码,表明这是hdrl列表,61766968是avih的ASCII码,表明这是hdrl列表中的avih块,第二行最后4字节是16进制的38,十进制的56;表示avih块的长度是56个字节(加上8正好是AVIMAINHEADER结构的长度),接下来的56个字节就是avih的内容,描述AVI文件的整体信息,AVIMAINHEADER结构中各个字段的内容不再分析,avih块到057h结束。

从0058h开始的4C495354是LIST的ASCII码,其后4字节(74)16,即(116)10是本列表的大小。60h开始的7374726C是strl的ASCII码,表示这是strl(流)列表,从64h到a3h的64个字节是一个AVISTREAMHEADER结构;64h开始的73747268是strh的ASCII码,表示这是strl列表中strh块(流的头信息);下面的4字节是strh块的大小(38)16,十进制的56,76696473是vids的ASCII码,是流的类型,表示这是一个视频流,从上面对AVI的2种格式的分析可知,这是一个AVI-2文件,如果是AVI-1文件,此处应该是iavs的ASCII码。70h开始的4字节是′MSVC′(已经注册的解码器的4字符码 )的ASCII码4D535643,AVISTREAMHEADER结构其他字段不再罗列,90h这一行最后4个字节,表示显示窗口左上角x,y,其值为0,0。a0h开始的2字节是窗口显示的右下角x坐标,值为十进制的128,以象素为单位,再2字节是右下角y坐标,十进制的112,至此,AVISTREAMHEADER结束。

a4h开始的4字节是strf的ASCII码73747266,后跟的4字节(28)16,(40)10是strf块的长度,前面说过,在AVI-2中,如果是视频流,strf就是一个BITMAPINFOHEADER结构,而该结构的长度是40字节,再后的4字节(28)16,就是BITMAPINFOHEADER的第一个字段,指明该结构的长度,所以有2个(28)16相连。从boh开始,就是BITMAPINFOHEADER其余的内容,(80)16是图像的宽度,120象素。(70)16是图像的高度,112象素,这些都与前面的信息相符合。0001两字节是位平面数,(0018)16是每象素比特数(24位)。接下来4字节44495633是′DIV3′(压缩编码格式的四字符码)的ASCII码,是BITMAPINFOHEADER的biCompression域的值,指压缩方式,这是和普通的BMP文件不同的。后面的字段不再分析。值得指出的是,这里可以后跟一个DVINFO结构(可选),如果是AVI-1文件,这里用DVINFO结构取代BITMAPINFOHEADER结构。接下来的4C495344是LIST的ASCII码,表明下面又是一个列表,列表大小是6Ah,doh行最后4字节是strl的ASCII码,表明这是一个“流”列表。后面的数据不再分析。

5 从AVI文件中提取位图

Windows提供的VFW和DirectShow[3]都可以处理和操作AVI文件,考虑到VFW为大多数程序员所熟悉以及AVI-2仍然是最常使用的AVI文件格式,下面给出使用VFW从AVI-2文件中提取视频帧加以显示并存储为BMP位图文件的程序。

VFW 提供的AVIFile动态链接库使用OLE技术实现,在AVIFile中的API函数将AVI文件中的数据当作“流”来处理,而不必逐一地寻找其中的块(chunk)。使用该库要首先调用函数AVIFileInit来初始化,使用完毕调用函数AVIFileExit后释放。首先使用MFC AppWizard创建一个名为avi的单文档项目,然后重载CDocument类的虚函数OnOpenDocument,在此函数中打开AVI文件,读出其中的视频帧,存储为BMP文件并在屏幕上加以显示(在VC++6.0中调试通过):

BOOL CAviDoc::OnOpenDocument(LPCTSTR lpszPathName)

{ if (!CDocument::OnOpenDocument(lpszPathName)) return FALSE;

AVIFileInit(); [JY]//初始化

AVIFileOpen(&pAviFile,lpszPathName,OF_SHARE_DENY_WRITE,0l);[JY]//打开AVI文件

AVIFileGetStream(pAviFile,&pAviStream,streamtypeVIDEO,0L );[JY]//打开流

AVIStreamInfo(pAviStream,&aviStrInfo,sizeof(AVISTREAMINFO));[JY]//获取流的信息

long lStreamSize;[JY]//流格式长度

pBmpInfoH = new BITMAPINFOHEADER;[JY]//pBmpInfoH是文档类的成员变量

AVIStreamFormatSize(pAviStream,0,&lStreamSize);[JY]//获取流的格式信息的长度

AVIStreamReadFormat(pAviStream,0L,pBmpInfoH,&lStreamSize);

PGETFRAME pgf;

pgf = AVIStreamGetFrameOpen(pAviStream,NULL);[JY]//为解压帧做准备

[JY]//解压读出第0帧,紧缩DIB的数据以返回值pData为起始地址

pData = (LPBYTE)AVIStreamGetFrame(pgf,0L);[JY]//文档类的成员变量,类型为LPBYTE

pBmpInfoH->biCompression = 0;

BITMAPFILEHEADER bmpFH;[JY]//位图文件头

bmpFH.bfType = 0x4d42;

bmpFH.bfSize = bmpInfoH.biWidth*bmpInfoH.biHeight*3 + 54;

bmpFH.bfReserved1 = 0;bmpFH.bfReserved2 = 0;

bmpFH.bfOffBits = 54;

CFileDialog dlg(FALSE);[JY]//获取用户要存储成的位图文件名和路径

if(IDOK==dlg.DoModal ())str.Format ("%s",dlg.GetPathName() );

CFile bmp(str,CFile::modeWrite|CFile::modeCreate);

bmp.Write(&bmpFH,sizeof(BITMAPFILEHEADER));[JY]//写入位图文件头

bmp.WriteHuge(pData,bmpFH.bfSize-14);[JY]//写入紧缩DIB

bmp.Close(); AVIFileExit();

UpdateAllViews(NULL);[JY]//更新视图,在视图类的OnDraw函数中显示位图

return TRUE;

}//程序中省略所有安全性检查语句。位图的显示在视类的OnDraw函数(略)中。

图2是程序执行的结果:从bfh.avi中提取的第0帧转换成的位图的显示。

上面程序中pAviFile,pAviStream,aviStrInfo都是文档类的成员变量,类型分别为PAVIFILE,PAVISTREAM,AVISTREAMINFO。在文档类的构造函数中将所有指针初始化为NULL。还可以循环调用函数AVIStreamFindNextKey寻找所有的关键帧加以显示并存储为BMP文件。需要注意,函数AVIStreamGetFrame返回的是一个“紧缩DIB”的地址,即以一个位图的BITMAPINFOHEADER开始的地址,且所需的缓冲区由该函数自己开辟,不需应用程序动态申请。程序要加入#include,且在Project->Settings->Link中键入vfw32.lib。

6 结 语

由于Windows是很流行的操作系统,AVI是Microsoft公司大力支持的格式,因此在编写基于Windows操作系统的流媒体图像处理程序方面,AVI占有不可替代的位置。准确理解AVI文件格式以及针对AVI文件的编程方法在工程实践中具有重要的意义。AVI-1文件格式由于占用存储空间小,受到DirectShow的支持,其将来会成为AVI文件中最流行的格式。

参 考 文 献

[1]姜楠,王健.常用多媒体文件格式压缩标准解析[M].北京:电子工业出版社,2005.

[2]DirectX Documentation for C++[EB/OL].Microsoft Corporation,2005.

[3]陆其明.DirectShow 开发指南[M].北京:清华大学出版社,2003.

注:本文中所涉及到的图表、注解、公式等内容请以PDF格式阅读原文。

上一篇:基于优先级的包插入队列调度算法仿真研究 下一篇:幅相误差对数字波束形成系统的影响