AVI格式解析

AVI格式解析  

http://handsomeboy226.blog.163.com/blog/static/9665175200891373140372/

2008-10-13 19:31:40|  分类: 视频文件格式|字号 订阅

AVI是音频视频交错(Audio Video Interleaved)的英文缩写,它是Microsoft公司开发的一种符合RIFF文件规范的数字音频与视频文件格式,原先用于Microsoft Video for Windows (简称VFW)环境,现在已被Windows 95/98OS/2等多数操作系统直接支持。AVI格式允许视频和音频交错在一起同步播放,支持256色和RLE压缩,但AVI文件并未限定压缩标准,因此,AVI文件格式只是作为控制界面上的标准,不具有兼容性,用不同压缩算法生成的AVI文件,必须使用相应的解压缩算法才能播放出来。常用的AVI播放驱动程序,主要是Microsoft Video for WindowsWindows 95/98中的Video 1,以及Intel公司的Indeo Video

  在介绍AVI文件前,我们要先来看看RIFF文件结构。AVI文件采用的是RIFF文件结构方式,RIFFResource Interchange File Format,资源互换文件格式)是微软公司定义的一种用于管理windows环境中多媒体数据的文件格式,波形音频waveMIDI和数字视频AVI 都采用这种格式存储。构造RIFF文件的基本单元叫做数据块(Chunk),每个数据块包含3个部分,

  14字节的数据块标记(或者叫做数据块的ID

  2、数据块的大小

  3、数据

  整个RIFF文件可以看成一个数据块,其数据块IDRIFF,称为RIFF块。一个RIFF文件中只允许存在一个RIFF块。RIFF块中包含一系列的子块,其中有一种字块的ID"LIST",称为LISTLIST块中可以再包含一系列的子块,但除了LIST块外的其他所有的子块都不能再包含子块。

  RIFFLIST块分别比普通的数据块多一个被称为形式类型(Form Type)和列表类型(List Type)的数据域,其组成如下: 

  14字节的数据块标记(Chunk ID

  2、数据块的大小

  34字节的形式类型或者列表类型

  4、数据

  下面我们看看AVI文件的结构。AVI文件是目前使用的最复杂的RIFF文件,它能同时存储同步表现的音频视频数据。AVIRIFF块的形式类型是AVI,它包含3个子块,如下所述:

  1、信息块,一个ID"hdrl"LIST块,定义AVI文件的数据格式。

  2、数据块,一个ID "movi"LIST块,包含AVI的音视频序列数据。

  3、索引块,ID "idxl"的子块,定义 "movi"LIST块的索引数据,是可选块。

  AVI文件的结构如下图所示,下面将具体介绍AVI文件的各子块构造。

  1、信息块,信息块包含两个子块,即一个ID avih 的子块和一个ID strl LIST块。


  "avih"子块的内容可由如下的结构定义:

typedef struct 
{
 DWORD dwMicroSecPerFrame ; //显示每桢所需的时间ns,定义avi的显示速率
 DWORD dwMaxBytesPerSec; // 最大的数据传输率
 DWORD dwPaddingGranularity; //记录块的长度需为此值的倍数,通常是2048
 DWORD dwFlages; //AVI文件的特殊属性,如是否包含索引块,音视频数据是否交叉存储
 DWORD dwTotalFrame; //文件中的总桢数
 DWORD dwInitialFrames; //说明在开始播放前需要多少桢
 DWORD dwStreams; //文件中包含的数据流种类
 DWORD dwSuggestedBufferSize; //建议使用的缓冲区的大小,
 //通常为存储一桢图像以及同步声音所需要的数据之和
 DWORD dwWidth; //图像宽
 DWORD dwHeight; //图像高
 DWORD dwReserved[4]; //保留值
}MainAVIHeader;


  "strl" LIST块用于记录AVI数据流,每一种数据流都在该LIST块中占有3个子块,他们的ID分别是"strh","strf", "strd"
"strh"
子块由如下结构定义。

typedef struct 
{
 FOURCC fccType; //4字节,表示数据流的种类 vids 表示视频数据流
 //auds 音频数据流
 FOURCC fccHandler;//4字节 ,表示数据流解压缩的驱动程序代号
 DWORD dwFlags; //数据流属性
 WORD wPriority; //此数据流的播放优先级
 WORD wLanguage; //音频的语言代号
 DWORD dwInitalFrames;//说明在开始播放前需要多少桢
 DWORD dwScale; //数据量,视频每桢的大小或者音频的采样大小
 DWORD dwRate; //dwScale /dwRate = 每秒的采样数
 DWORD dwStart; //数据流开始播放的位置,以dwScale为单位
 DWORD dwLength; //数据流的数据量,以dwScale为单位
 DWORD dwSuggestedBufferSize; //建议缓冲区的大小
 DWORD dwQuality; //解压缩质量参数,值越大,质量越好
 DWORD dwSampleSize; //音频的采样大小
 RECT rcFrame; //视频图像所占的矩形
}AVIStreamHeader;


  "strf"子块紧跟在"strh"子块之后,其结构视"strh"子块的类型而定,如下所述;如果 strh子块是视频数据流,则 strf子块的内容是一个与windows设备无关位图的BIMAPINFO结构,如下:

typedef struct tagBITMAPINFO
{
 BITMAPINFOHEADER bmiHeader;
 RGBQUAD bmiColors[1]; //颜色表
}BITMAPINFO;

typedef struct tagBITMAPINFOHEADER
{
 DWORD biSize;
 LONG biWidth;
 LONG biHeight;
 WORD biPlanes;
 WORD biBitCount;
 DWORD biCompression;
 DWORD biSizeImage;
 LONG biXPelsPerMeter;
 LONG biYPelsPerMeter;
 DWORD biClrUsed;
 DWORD biClrImportant;
}BITMAPINFOHEADER;


  如果 strh子块是音频数据流,则strf子块的内容是一个WAVEFORMAT结构,如下:

typedef struct 
{
 WORD wFormatTag; 
 WORD nChannels; //声道数
 DWORD nSamplesPerSec; //采样率
 DWORD nAvgBytesPerSec; //WAVE声音中每秒的数据量
 WORD nBlockAlign; //数据块的对齐标志
 WORD biSize; //此结构的大小
}WAVEFORMAT


  "strd"子块紧跟在strf子块后,存储供压缩驱动程序使用的参数,不一定存在,也没有固定的结构。

  "strl" LIST块定义的AVI数据流依次将 "hdrl " LIST 块中的数据流头结构与"movi" LIST块中的数据联系在一起,第一个数据流头结构用于数据流0,第二个用于数据流1,依次类推。

  数据块中存储视频和音频数据流,数据可直接存于 "movi" LIST块中。数据块中音视频数据按不同的字块存放,其结构如下所述,

  音频字块
    "##wb"
    Wave 数据流
  视频子块中存储DIB数据,又分为压缩或者未压缩DIB
    "##db"
    RGB数据流
    "##dc"
  压缩的图像数据流

  看到了吧,avi文件的图像数据可以是压缩的,和非压缩格式的。对于压缩格式来说,也可采用不同的编码,也许你曾经遇到有些avi没法识别,就是因为编码方式不一样,如果没有相应的解码,你就没法识别视频数据。AVI的编码方式有很多种,比较常见的有 mpeg2mpeg4divx等。

索引块,索引快包含数据块在文件中的位置索引,能提高avi文件的读写速度,其中存放着一组AVIINDEXENTRY结构数据。如下,这个块并不是必需的,也许不存在。

typedef struct 
{
 DWORD ckid; //记录数据块中子块的标记
 DWORD dwFlags; //表示chid所指子块的属性
 DWORD dwChunkOffset; //子块的相对位置
 DWORD dwChunkLength; //子块长度
};

*********************************************************************************************************************************************

首先声明:本文的内容都是我从开发过程中总结出来的,以我的理解在尽可能短的篇幅里对 DV AVI 文件的分析作介绍。真要作开发还需要参考原始的文档。

AVI 文件总是以 12 个字节开始的,就是 'RIFF' + size + 'AVI '。这里 size 是一个 4 字节的整数,声明其后的字节数(包括'AVI '4个字节数)

现在问题就出来了,这样的格式就是限定了 size 的最大取值只能是 4G,后来人们就扩展了 AVI 的结构——当分析到声明的字节数后,如果后面是扩展格式,那么就继续分析。

扩展部分类似 AVI 的格式,只不过从 'AVI ' 变成了 'AVIX',而且可能有多个扩展部分。因此这一部分的分析代码就是:

head = struct.unpack('<4sI4s', avifile.read(12)) if head[0] != 'RIFF' or head[2] != 'AVI ': return None while True: xread = readChunk(avifile, head[1]-4, 0) # 分析剩余的数据 s = avifile.read(12) if 0 == len(s): # 如果没有什么可读的了,自然是分析完了 break head = struct.unpack('<4sI4s', s) if head[0] != 'RIFF' or head[2] != 'AVIX': break

由于 AVI 内部嵌套的数据块的格式也类似 4bytes info + size + data 这样的结构,因此 readChunk 被设计成一个递归函数,返回值为 0 -1,中途解析失败就返回 -1,根据此返回值退出嵌套调用。(回过头来看这样一段程序,递归调用分析的可读性很糟糕,主要是因为开始编程的时候对 Python 没有太多的认识所致)

可能是为了便于编程,各个数据块被设计成 4 字节对齐的,但 data 的大小未必是 4 的整数倍,从文件中读出来的 size 只是表示 data 的长度,有时候必须计算对齐。下面两行语句就是作这个的:

page = (head[1] - 1)/4 chunksize = (page + 1) * 4

为了便于播放器去 seek 一个特定的位置,比如从文件的第 12 32 秒开始播放,需要一个索引方案可以快速定位到相应的数据。这就是 'idx1' chunk 里面定义的东东。但传统的定义里面偏移量最大只能为 4G,因此扩展格式里面增加了 super index,或者说 index index,里面可以放 longlong 64 位整数来避免这种寻址困境,估计在我有生之年都不会有这么大个的数据文件问世。

readChunk 函数的主要功能就是生成一个 index 列表,然后从这个列表的最前面和最后面分别 seek 到相应的数据存储区域,找出时间码。如果发现 AVI 里面有 super index,就在 readChunk 返回后,再根据 super index 生成 index 列表。程序里面这个列表变量名为 offset

分析 DV 格式获取时间的函数是 readtimeDV 可能是每次记录 12000 字节数据(类似磁盘扇区的概念??),因此在每 12000 字节数据里面都会存储一个时间码。我的当时参考的代码里面在每个 index 指向的数据块里循环了 15 次还是 10 次,但我发现我这里只能循环 12 次就碰到了数据的尽头,后来估计是 PAL/NTSC 的差异,也就没有继续追究下去。

转载于:https://www.cnblogs.com/zhgyee/archive/2011/09/29/2195959.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值