微软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是必须要有的),它们指示存储在文件中的数据流的格式和数据。
第一个强制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的另一个流数据子块可以可选地遵循流格式子块,该块中的数据用于配置解码数据所需的驱动程序。该块的格式也会根据流数据上使用的压缩类型而变化。
第二个必须的LIST块包含实际的AVI数据,它的标识符为movi,并且必须作为AVI块中的第二块出现。
movi块中的数据可以以LIST记录的形式分组(包含一个或多个子块的LIST块,每个子块具有标识符“rec”),只有从CD-ROM中读取的交错数据才能被存储为一系列LIST记录(数据交错时从CD-ROM读取效率更高).如果数据不是交错的,它将作为movi块本身内的单个数据块存储。
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块。
在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