AVI音视频封装格式学习(一)——微软RIFF文件格式摘要

微软RIFF文件格式摘要:RIFF File Format Summary

名称解析:

    RIFF    Resource Interchange File Format   资源交换文件格式
    TIFF    Tag Image File Format    

用途:

    RIFF是微软Windows系统本机的设备控制接口和通用文件格式。它主要用来存储多媒体应用中的音频,视频,图像信息。
 

说明:

    RIFF是一种复杂的格式,目的在于适用于多媒体应用的各种类型的数据。由于它非常的新且受供应商的控制,因此将来IRFF的规格可能会发生变化。

     供应商规格可用于IRFF格式。

----------------------------------------------------------------------------------------

    RIFF是微软为了windos GUI 而创建的一种多媒体文件格式。RIFF本身并没有定义任何存储数据的新方法,但是,RIFF定义了一个结构化的框架,它包含了现有的数据格式。由此可知,用户可以创建由两种或更多现有文件格式组成的新的复合格式。

内容:

    多媒体应用需要存储和管理各种数据,包括位图,音频数据,视频数据和外围设备控制信息。RIFF提供了一种很好的方式来存储所有这些不同类型的数据。RIFF文件中包含的数据类型可以通过文件的后缀名来确认,比如下面的文件后缀名:

(a)Audio/visual interleaved data (.AVI)

(b)Waveform data (.WAV)

(c)Bitmapped data (.RDI)

(d)MIDI information (.RMI)

(e)A bundle of other RIFF files (.BND)

    注意:AVI是现在IRFF规范中使用最全面的一种类型,WAV虽然也有使用,但是非常简单,WAV开发者通常使用旧的规范来构建它们。

    因为RIFF涉及到各种多媒体文件的名字。RIFF经常是被它所包含的媒体文件所描述(比如大家熟悉的AVI格式),而不是作为一种格式名字被大家熟悉。因此,对于刚开始接触RIFF文件的人特别弄容易混淆概念。比如包含音频和视频数据的文件我们一般称它为AVI文件,而不是IRFF文件。只有编程开发人员才会知道或是留意其实AVI文件和IRFF文件是同一种文件格式。

    另外一个问题就是,大家容易将RIFF和TIFF(Tag Image File Format)文件弄混淆.这两种格式都可以添加或删除文件的数据结构,但是它们内部的数据结构却相差非常大,与TIFF不同的是RIFF文件格式是基于Electronic Arts交换文件格式(IFF)的结构。虽然这两种格式都使用相同的数据存储概念,但它们在设计上却是不兼容的。

文件组织结构:

    RIFF是一种包含多个嵌套数据结构的二进制文件格式,RIFF文件中的每个数据结构都称为块(chunk).块在RIFF文件中没有固定位置,因此标准偏移值不能用于定位它们的字段,换句话说就是:块是由用户来定义,所以每个块没有统一的位置。块包含数据,如数据结构,数据流或称为子块的其他块。 每个RIFF块都具有以下基本结构:

typedef struct _Chunk
{
	DWORD ChunkId;              /* Chunk ID marker */
	DWORD ChunkSize;            /* Size of the chunk data in bytes */
	BYTE ChunkData[ChunkSize];  /* The chunk data */
} CHUNK;

ChunkId  包含四个ASCII字符,用于标识块包含的数据。例如,字符RIFF用于识别包含RIFF数据的块。如果ID小于四个字符,则使用空格(ASCII 32)在右侧填充。注意:RIFF是以小端模式写入的

ChunkSize  是存储在ChunkData字段中的数据的长度,不包括添加到数据中的任何填充数据(为了数据对齐会在结尾添加0,但是添加的0不算长度)。注意:结构体中ChunkId 四个字节和 ChunkSize四个字节的长度也不在ChunkSize的长度计算中。

ChunkData 中的数据是以4字节对齐的,如果实际数据是奇数,就在数据末尾添加0,ChunkSize的值是不包含末尾为了对齐添加的0的。

    子块(Subchunk)也具有与块(chunk)相同的结构,一个子块就是包含在另一个块中的任何块.只有RIFF块和列表块(LIST chunk)包含子块,其它的块都只包含数据。

    一个RIFF文件本身就是一个完整的RIFF块,文件中的所有其他块和子块都包含在该块中。如果你要读取RIFF文件,那么你应该忽略不能识别的或是不使用的块。如果你是在写一个RIFF文件,那么你应该将所有未知的或是不使用的块全部写入文件中,不要丢掉它们(一般根据不同需求写入)。

文件细节:

    用于存储音频和视频信息的RIFF文件称为AVI文件。RIFF AVI文件格式通常只包含一个AVI块;但是,其他类型的块也可能出现。解析一个AVI文件,你可以忽略不需要的块,同时要确保文件中有存储一个AVI块。

    虽然微软使用标准的表示法来描述RIFF文件内部数据结构,我们相信使用我们自己的类C语法来说明RIFF AVI文件中块和子块的位置是更清晰的。每个块的ChunkId已经在注释中列出:

struct _RIFF   /* "RIFF" */
{
	struct _AVICHUNK   /* "AVI " */
	{
		struct _LISTHEADERCHUNK   /* "hdrl" */
		{
			AVIHEADER AviHeader;     /* "avih" */
			struct _LISTHEADERCHUNK  /* "strl" */
			{
				AVISTREAMHEADER	StreamHeader; /* "strh" */
				AVISTREAMFORMAT	StreamFormat; /* "strf" */
				AVISTREAMDATA	StreamData;   /* "strd" */
			}
		}
		struct _LISTMOVIECHUNK  /* "movi" */
		{
			struct _LISTRECORDCHUNK  /* "rec " */
			{
				/* Subchunk 1 */
				/* Subchunk 2 */
				/* Subchunk N */
			}
		}
		struct _AVIINDEXCHUNK  /* "idx1" */
		{
			/* Index data */
		}
	}
}

    以上结构表示仅包含一个AVI块的RIFF文件的内部数据布局。该块遵循先前描述的块数据结构的格式。AVI块由4个字符的块标识符“AVI”标识(注意最后的空白字符)。AVI块包含两个强制LIST子块(hdrl和movi两个LIST是必须要有的),它们指示存储在文件中的数据流的格式和数据。

AVI 头子块(AVI Header Subchunk)

    第一个强制LIST块包含主AVI头部子块并具有标识符hdrl,AVI Header 子块中的信息定义了整个AVI块的格式。hdrl块必须显示为AVI块中的第一个块,AVI Header子块的格式如下:

typedef struct _AVIHeader
{
	DWORD TimeBetweenFrames;     /* Time delay between frames */
	DWORD MaximumDataRate;       /* Data rate of AVI data */
	DWORD PaddingGranularity;    /* Size of single unit of padding */
	DWORD Flags;                 /* Data parameters */
	DWORD TotalNumberOfFrames;   /* Number of video frame stored */
	DWORD NumberOfInitialFrames; /* Number of preview frames */
	DWORD NumberOfStreams;       /* Number of data streams in chunk*/
	DWORD SuggestedBufferSize;   /* Minimum playback buffer size */
	DWORD Width;                 /* Width of video frame in pixels */
	DWORD Height;                /* Height of video frame in pixels*/
	DWORD TimeScale;             /* Unit used to measure time */
	DWORD DataRate;              /* Data rate of playback */
	DWORD StartTime;             /* Starting time of AVI data */
	DWORD DataLength;            /* Size of AVI data chunk */
} AVIHEADER;

结构体变量解析:

TimeBetweenFrames  包含一个值,指示帧之间的延迟量(以微秒为单位)。

MaximumDataRate  值表示每秒字节数的AVI数据的数据速率

PaddingGranularity 指定数据中使用的填充的多个字节大小,使用时,该字段的值通常为2048

Flags  包含特定于AVI文件及其数据的参数设置。这些参数对应于标志字段的位值,如下所示:
Bit 4
    AVI块包含索引子块(idx1)
Bit 5
    使用索引数据来确定如何读取AVI数据,而不是具有RIFF文件的块的物理顺序.
Bit 8
    AVI文件是交错存放的.
Bit 16
    AVI文件针对实况视频捕捉进行了优化.
Bit 17

    AVI文件包含受版权保护的数据.

TotalNumberOfFrames  指示存储在movi子块中的视频数据的总帧数

NumberOfInitialFrames  指定实际AVI数据之前文件中的帧数,对于非交错数据,该值为0

NumberOfStreams 块中包含数据流的个数。包含音频和视频流的文件在此字段中该值为2,而仅包含视频数据的AVI文件该值为1。在当前版本的RIFF格式中,允许一个音频和一个视频流,也可以是只有一个音频流或是只有一个视频流。

SuggestedBufferSize 要分配给AVI数据播放的最小缓冲区大小,对于非交错的AVI数据,此值至少是文件中最大块的大小。对于交错的AVI文件,这个值应该是整个AVI记录的大小。

Width Height 以像素为单位指示视频图像的大小

TimeScale 是用于测量此块中时间的单位。它与DataRate一起使用来指定流将使用的时间刻度。对于视频流,这个值应该是帧速率并且通常该值为30。对于音频流,这个值通常是音频采样率。

DataRate 除以TimeScale的值用来计算每秒采样数。

StartTime 是AVI数据的开始时间,通常为0。

DataLength 是由TimeScale值指定的单位中AVI块的大小。

    hdrl子块还包含一个或多个带标识符strl的LIST块。每个存储在AVI块中的数据流都会有一个LIST块。

    三个子块存储在strl LIST块中,第一个是Stream Header子块,它具有标识符strh。该头部包含特定于存储在strl LIST块中的数据流的信息。数据流的头是必需的,并具有以下格式

typedef struct _StreamHeader
{
	char  DataType[4];           /* Chunk identifier ("strl") */
	char  DataHandler[4];        /* Device handler identifier */
	DWORD Flags;                 /* Data parameters */
	DWORD Priority;              /* Set to 0 */
	DWORD InitialFrames;         /* Number of initial audio frames */
	DWORD TimeScale;             /* Unit used to measure time */
	DWORD DataRate;              /* Data rate of playback */
	DWORD StartTime;             /* Starting time of AVI data */
	DWORD DataLength;            /* Size of AVI data chunk */
	DWORD SuggestedBufferSize;   /* Minimum playback buffer size */
	DWORD Quality;               /* Sample quailty factor */
	DWORD SampleSize;            /* Size of the sample in bytes */
} STREAMHEADER;

DataType 包含一个4字符的标识符,指示流头指向的数据类型。当前版本的RIFF格式支持的标识符是:vids 用于视频数据,auds 用于音频数据

DataHandler 可能包含一个4字符的标识符,指定处理数据流的设备的首选类型。     

Flags 包含一组位标志用于指示与数据相关的参数设置。               

Priority 一般设置为0     

InitialFrames  以秒为单位指示音频在交错数据中位于视频之前的距离     

TimeScale DataRate StartTime DataLength SuggestedBufferSize 全部具有与hdr1块中相同名称的字段相同的功能

Quality   是一个范围在0到10,000之间的整数,表示用于编码样本的质量因子          

SampleSize 是单个数据样本的大小,如果此值为0,则样本大小不同,并且每个样本都存储在单独的子块中,如果此值不为零,则所有样本的大小相同,并存储在单个子块中。

    紧跟在流头结构之后的是具有标识符strf的流格式子块,这个头文件描述了流数据的格式.其格式因存储的数据类型(音频或视频)而异。这个子块也是必需的。

    具有标识符strd的另一个流数据子块可以可选地遵循流格式子块,该块中的数据用于配置解码数据所需的驱动程序。该块的格式也会根据流数据上使用的压缩类型而变化。

数据子块(AVI Data Subchunk)

第二个必须的LIST块包含实际的AVI数据,它的标识符为movi,并且必须作为AVI块中的第二块出现。

    movi块中的数据可以以LIST记录的形式分组(包含一个或多个子块的LIST块,每个子块具有标识符“rec”),只有从CD-ROM中读取的交错数据才能被存储为一系列LIST记录(数据交错时从CD-ROM读取效率更高).如果数据不是交错的,它将作为movi块本身内的单个数据块存储。

索引块(Index Chunk)

    AVI块也可能包含第三个块,称为索引块.索引块具有标识符idx1,并且必须出现在hdrl和movi块之后。该块包含AVI块内所有块的列表及其位置,并用于音频和视频数据的随机访问(比如任意时间点开始播放)。

索引块具有以下格式:

typedef struct _AviIndex
{
	DWORD Identifier;    /* Chunk identifier reference */
	DWORD Flags;         /* Type of chunk referenced */
	DWORD Offset;        /* Position of chunk in file */
	DWORD Length;        /* Length of chunk in bytes */
} AVIINDEX;

Identifier 包含它引用的块的4字节标识符(strh,strf,strd等)

Flags 变量中的位用于指示块包含的帧的类型,或者将索引结构标识为指向LIST块

Offset 以字节为单位指示该块与movi块开始位置的偏移量  

Length 以字节为单位的该块的大小

    idx1块中索引项包括AVI中所有的块和子块,在实际应用中,有些人只给movi中的子块添加了索引。这些结构不需要按照它们在AVI块内出现的顺序对每个块进行索引。idx1中的索引结构的顺序也可以用于控制存储在AVI块中的数据的显示顺序。如果索引包含在AVI块中,则必须在AVI头块的标志字段中设置适当的指示位。如果读取RIFF文件的应用程序决定使用索引块中的信息,则它必须先查找hdrl块并通过检查AVI头中的标志字段值来确定索引块是否存在。如果确实存在,读者将跳过AVI块中的所有块,直到遇到idx1块。

JUNK块

在AVI块中经常遇到的另一种类型的块是填充块或JUNK块(如此命名,因为它的块标识符是JUNK).该块用于将数据填充到特定边界,块的大小是它包含的填充字节数.如果您正在读取AVI数据,请勿使用JUNK块中的数据。在读数据时忽略它,在写数据时保留它。JUNK块使用标准块结构:

typedef struct _JunkChunk
{
	DWORD ChunkId;             /* Chunk ID marker (JUNK)*/
	DWORD PaggingSize;         /* Size of the padding in bytes */
	BYTE Padding[ChunkSize];   /* Padding */
} JUNKCHUNK;

计划该系列文章包括下面几篇:

           AVI音视频封装格式学习(一)——微软RIFF文件格式摘要

           AVI音视频封装格式学习(二)——AVI RIFF文件参考

           AVI音视频封装格式学习(三)——AVI 数据结构解析

           AVI音视频封装格式学习(四)——linux系统C语言AVI格式音视频封装应用

                                                                                                           2018.3.31 Wen Lee

  

---------------------------------------2022.08.20:21:17更新----------------------------------------

由于各种原因,后续文章内容将更新到公众号,本平台将不再做更新。

CSDN上相关文章的测试工程代码,也统一放到了公众号上,可以免费免积分下载

可以通过主页上的二维码,也可以通过搜索微信公众号 liwen01 进入公众号

liwen01   2022.08.20

---------------------------------------2022.08.20:21:17更新----------------------------------------
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值