RIFF(Resource Interchange File Format)
RIFF——资源交换文件格式,由多个分离的数据块(chunk)组成。
FOURCC Identifier(four-character code identifier)
数据块中的数据类型由FOURCC标识符表示,一个FOURCC标识符是一个32位(四字节)的无符号整形,由四个表征RIFF文件中数据块类型的ASCII字符组合而成。例如在一个小端字节序的系统中,FOURCC标识符“abcd”表示成0x64636261。FOURCC标识符可以包含空格,所以“abc”是一个有效的FOURCC标识符。音频文件利用FOURCC来表征音频格式块、音频数据块或者其他块类型。例如:
RIFF、fmt、data这三个是非常常见的FOURCC标识符,对它们的描述可以见下面。
标准的RIFF块在其数据部分最开始的四字节包含一个“WAVE”或者“XWMA”的文件类型。fmt块包含了音频文件的格式头,这个块中的数据取自WAVEFORMATEX,WAVEFORMATEXTENSIBLE,ADPCMWAVEFORMAT三个结构体中的一种。data块包含了音频文件的音频数据。
RIFF文件
一个RIFF文件是一个包含零个或者多个其他块的RIFF块。
RIFF块由"RIFF",fileSize,fileType,data组成。“RIFF”是ASCII码表示的FOURCC标识符。fileSize是一个四字节的值,给出了文件中的数据长度,该长度包含了fileType和其后跟着的数据,但是不包括“RIFF”标识符这四个字节以及fileSize本身的长度。
其余块由chunkID,chunkSize,data组成。chunkID是一个FOURCC标识符。chunkSize是一个四字节的值,给出了其后跟着的数据的长度。数据长度总是向上对齐到最近的一个WORD边界上,chunkSize表示的是有效的数据长度,不包括对齐填充的数据和chunkID以及chunkSize本身的长度。比如data块、fmt块和fact块都是这种类型的块。
fmt块
fmt块的chunkID是FOURCC标识符“fmt ”,chunkSize就是后面数据的长度,这里的数据指的是WAVEFORMATEX,WAVEFORMATEXTENSIBLE,ADPCMWAVEFORMAT之类的结构体。
1.typedef struct tWAVEFORMATEX {
2. WORD wFormatTag; // 音频格式类型
3. WORD nChannels; // 通道数
4. DWORD nSamplesPerSec; // 采样率
5. DWORD nAvgBytesPerSec; // 每秒字节数,也即平均数据传输速率
6. WORD nBlockAlign; // 以字节为单位的快对齐大小
7. WORD wBitsPerSample; // 每个样本的字节数
8. WORD cbSize; // 表征附在WAVEFORMATEX结构体后面的额外数据的长度,以字节为单位,没有额外数据就是0,在PCM中,该字段甚至都可以没有,则chunkSize就是16
9.} WAVEFORMATEX, *PWAVEFORMATEX, *NPWAVEFORMATEX, *LPWAVEFORMATEX;
以上是结构体WAVEFORMATEX,共18字节。
对于wBitsPerSample,如果是PCM,则等于8或者16,如果wFormatTag = WAVE_FORMAT_IEEE_FLOAT,则等于32。对于G726(它编码后的比特率有16kbit/s、24kbit/s、32kbit/s、40kbit/s,分别对应数据压缩比为8:1、16:3、4:1和16:5,码字分别为2、3、4和5 bits),如果是16:5的压缩比,也即输出的单个样本占5bit,那么wBitsPerSample就等于5。所以我的理解是该字段表征的是编码后每个样本所占的比特数。
对于nBlockAlign,微软给的说明是,如果是PCM或者32位的FLOAT,则等于 (nChannels*wBitsPerSample)/8,表示的是单个音频样本的大小。对于其他的格式,需要根据制造商对于该格式的详细信息计算该值。处理音频数据时,需要以BlockAlign为单位进行处理,而不应该从该单位的中间某个地方处取数据进行处理。所以可以理解成数据处理的最小单元大小。以上面G726为例进行说明,每个采样点编码成5bit,那么40bit就是一个BlockAlign。
示例
上面是一张从网上截取的标准PCM的WAV格式文件,按照上面的介绍来对其进行分析。
文件由RIFF块、fmt块和data块组成。data块从序号12开始直到最后,块的chunkID是“data”,chunkSize是0xF83,不包含填充部分(填充了一个字节)和chunkID以及chunkSize本身的长度。前面的RIFF块由FOURCC标识符“RIFF”开始,紧接着是fileSize0xFA8(=4008=4016-8,除去了“RIFF”标识符这四个字节以及fileSize本身的长度),然后是WAVE文件类型和fmt块。fmt块中包含了音频文件的相关参数,比如压缩类型、采样率、通道数等等。然后就是后面的data块,包含了实际的音频文件数据。
这个示例中的fmt块没有额外数据,连字段cbSize也没有,所以fmt块的chunkSize是16,比上面介绍的WAVEFORMATEX结构体少了2个字节的cbSize。
微软的ADPCM就是一个利用额外信息的例子。在这种格式中,cbSize等于32,额外信息就是编码和解码过程中用到的系数对。
1.typedef struct adpcmwaveformat_tag {
2. WAVEFORMATEX wfx; // WAVEFORMATEX部分,下面是额外信息部分
3. WORD wSamplesPerBlock;
4. WORD wNumCoef;
5. ADPCMCOEFSET aCoef[];
6.} ADPCMWAVEFORMAT;
WAVEFORMATEX结构描述的音频格式是WAVEFORMATEXTENSIBLE结构的子集。例如,WAVEFORMATEX可以描述单声道或者双声道,8bit宽或者16bit宽的PCM音频,或者是32bit浮点型采样值。此外,它还可以描述常见的非PCM型音频,如AC-3、WMA Pro。WAVEFORMATEX可以准确地描述单声道或者双声道格式,这些格式每个样本的有效比特位数和样本容器的大小一致。比如8bit或者16bit的PCM,可以认为它们的样本容器尺寸就是8bit或者16bit。如果要描述多于两个声道的PCM格式,或者PCM格式中单个样本的有效比特位数比样本容器尺寸小(比如单个样本20bit存储在3个字节中),则需要WAVEFORMATEXTENSIBLE结构。WAVEFORMATEXTENSIBLE结构利用通道掩码对多个通道的配置进行了映射,也指出了单样本的有效比特位数和样本容器尺寸。
WAVEFORMATEX中的wFormatTag成员描述了格式类型,16bit宽度(wFormatTag中第一个字母w(word)应该就是表示16bit宽度的意思!?)的格式类型定义在头文件Mmreg.h中。如果要描述一个非PCM类型的格式,并且这种格式类型也没有定义在头文件Mmreg.h中,用WAVEFORMATEXTENSIBLE结构,里面的GUID来表示类型。必要时,硬件提供商可以独立地产生一个GUID来表示一个新的类型。这个新产生的GUID类型也不需要向微软注册。
WAVEFORMATEX和PCMWAVEFORMAT十分类似,区别就是后者没有cbSize。通常来说,对于PCM类型,cbSize应该被忽略,因此在处理PCM时,WAVEFORMATEX和PCMWAVEFORMAT被同等对待。在处理IEEE 32位float型时,cbSize也设成零。对于其他的类型,cbSize表示附在WAVEFORMATEX结构后面的额外信息的长度,以字节为单位。
微软文档中有这么一段文字“If wFormatTag = WAVE_FORMAT_EXTENSIBLE,set cbSize to sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) plus the size of any format-specific data that is appended to the WAVEFORMATEXTENSIBLE structure。”这个不是非常理解,先摘录在此。
下面的图描述了WAVEFORMAT、PCMWAVEFORMAT、WAVEFORMATEX和WAVEFORMATEXTENSIBLE之间的结构关系。
代码
1.typedef struct{
2. char riff[4];
3. unsigned int u32ChunkSize;
4. char rifftype[4];
5. char chunkID[4];
6. unsigned int u32ChunkDataSize;
7. unsigned short u16EncodeMode;
8. unsigned short u16NbChannels;
9. unsigned int u32SampleRate;
10. unsigned int u32AverageBytesPerSecond;
11. unsigned short u16BlockAlign;
12. unsigned short u16BitWidth;
13. char chunkIDEx1[4];
14. unsigned int u32ChunkExDataSize;
15.}WAVEFORMATEX_S;
这个WAVEFORMATEX_S是完全按照上面的图来构造的,实际上是有其他形式的,可以自行查阅相关资料。
下面的代码在addr地址起始处加上WAVEFORMATEX_S结构体(结构体后面添加裸音频流数据代码自行完成)。实际测试中,在该结构体后面加上海思3516D芯片编码g711u的裸码流后可以用ffplay播放。相反如果不加WAVEFORMATEX_S结构体,而是在ffplay命令行里面将g711u相关的音频参数输入进去,则不能播放。
1.int GetSampleRates(AUDIO_SAMPLE_E enSampleRate)
2.{
3. if (enSampleRate == SAMPLE_RATE_8000)
4. {
5. return 8000;
6. }
7. else if (enSampleRate == SAMPLE_RATE_11025)
8. {
9. return 11025;
10. }
11. else if (enSampleRate == SAMPLE_RATE_12000)
12. {
13. return 12000;
14. }
15. else if (enSampleRate == SAMPLE_RATE_22050)
16. {
17. return 22050;
18. }
19. else if (enSampleRate == SAMPLE_RATE_24000)
20. {
21. return 24000;
22. }
23. else if (enSampleRate == SAMPLE_RATE_48000)
24. {
25. return 48000;
26. }
27. else if (enSampleRate == SAMPLE_RATE_64000)
28. {
29. return 64000;
30. }
31. else if (enSampleRate == SAMPLE_RATE_96000)
32. {
33. return 96000;
34. }
35. else if (enSampleRate == SAMPLE_RATE_16000)
36. {
37. return 16000;
38. }
39. else if (enSampleRate == SAMPLE_RATE_32000)
40. {
41. return 32000;
42. }
43. else if (enSampleRate == SAMPLE_RATE_44100)
44. {
45. return 44100;
46. }
47. else
48. {
49. return -1;
50. }
51.
52.}
53.int GetAIOCHNums(SOUND_MODE_E enSoundMode){
54. if (enSoundMode == AUDIO_SOUND_SINGLE)
55. {
56. return 1;
57. }
58. else if (enSoundMode == AUDIO_SOUND_DOUBLE)
59. {
60. return 2;
61. }
62. else
63. {
64. return -1;
65. }
66.}
67.int GetBitsWidth(BIT_WIDTH_E enBitWidth){
68. if (enBitWidth == BIT_WIDTH_16)
69. {
70. return 16;
71. }
72. else if (enBitWidth == BIT_WIDTH_8)
73. {
74. return 8;
75. }
76. else if (enBitWidth == BIT_WIDTH_24)
77. {
78. return 24;
79. }
80. else
81. {
82. return -1;
83. }
84.}
85.int GetWaveEncodeMode(AUDIO_STREAM_MODE_E enMode){
86. if (enMode == STREAM_MODE_G711A)
87. {
88. return 6;
89. }
90. else if (enMode == STREAM_MODE_G711U)
91. {
92. return 7;
93. }
94. else if (enMode == STREAM_MODE_G726)
95. {
96. return 64;
97. }
98. else if (enMode == STREAM_MODE_PCM)
99. {
100. return 1;
101. }
102. else if (enMode == STREAM_MODE_ADPCMA)
103. {
104. return 2;
105. }
106. else
107. {
108. return -1;
109. }
110.}
111.
112.int add_waveformatex(unsigned char* addr, int s32DataLen, AUDIO_PARAM_S* pstAudioParams){
113. WAVEFORMATEX_S stWaveFormatExHeader;
114. int s32HeadLen = sizeof(WAVEFORMATEX_S);
115.
116. stWaveFormatExHeader.riff[0] = 'R';
117. stWaveFormatExHeader.riff[1] = 'I';
118. stWaveFormatExHeader.riff[2] = 'F';
119. stWaveFormatExHeader.riff[3] = 'F';
120. stWaveFormatExHeader.u32ChunkSize = (s32DataLen + s32HeadLen + 4) / 4 * 4 - 8; // 向上取整到4字节对齐
121. stWaveFormatExHeader.rifftype[0] = 'W';
122. stWaveFormatExHeader.rifftype[1] = 'A';
123. stWaveFormatExHeader.rifftype[2] = 'V';
124. stWaveFormatExHeader.rifftype[3] = 'E';
125. stWaveFormatExHeader.chunkID[0] = 'f';
126. stWaveFormatExHeader.chunkID[1] = 'm';
127. stWaveFormatExHeader.chunkID[2] = 't';
128. stWaveFormatExHeader.chunkID[3] = ' ';
129. stWaveFormatExHeader.u32ChunkDataSize = 16;
130. stWaveFormatExHeader.u16EncodeMode = GetWaveEncodeMode(pstAudioParams->enMode);
131. stWaveFormatExHeader.u16NbChannels = GetAIOCHNums(pstAudioParams->enSoundMode);
132. stWaveFormatExHeader.u32SampleRate = GetSampleRates(pstAudioParams->enReSampleRate);
133. stWaveFormatExHeader.u32AverageBytesPerSecond = stWaveFormatExHeader.u32SampleRate * GetBitsWidth(pstAudioParams->enBitWidth) / 8;
134. stWaveFormatExHeader.u16BlockAlign = GetBitsWidth(pstAudioParams->enBitWidth) / 8 * stWaveFormatExHeader.u16NbChannels;
135. stWaveFormatExHeader.u16BitWidth = GetBitsWidth(pstAudioParams->enBitWidth);
136. stWaveFormatExHeader.chunkIDEx1[0] = 'd';
137. stWaveFormatExHeader.chunkIDEx1[1] = 'a';
138. stWaveFormatExHeader.chunkIDEx1[2] = 't';
139. stWaveFormatExHeader.chunkIDEx1[3] = 'a';
140. stWaveFormatExHeader.u32ChunkExDataSize = s32DataLen;
141. memcpy(addr, &stWaveFormatExHeader, s32HeadLen);
142.
143. return 0;
144.}
但是,对于海思3516d编码出来的ADPCM类型的音频数据,采用下面的WAVEFORMATEX_ADPCM_S结构体,实测不能播放,不知为何!?现记录如下:
1.typedef struct{
2. char riff[4];
3. unsigned int u32ChunkSize;
4. char rifftype[4];
5. char chunkID[4];
6. unsigned int u32ChunkDataSize;
7. unsigned short u16EncodeMode;
8. unsigned short u16NbChannels;
9. unsigned int u32SampleRate;
10. unsigned int u32AverageBytesPerSecond; //编码后每秒的字节数
11. unsigned short u16BlockAlign;
12. unsigned short u16BitWidthPerEncSmp; //编码后每个样本点的比特数
13. unsigned short u16CbSize;
14. unsigned short u16SmpPerBlock;
15. unsigned short u16NumCoef;
16. signed short aCoeff[14];
17. char chunkIDEx1[4];
18. unsigned int u32ChunkExDataSize;
19.}WAVEFORMATEX_ADPCM_S;
1.int add_waveformatex(unsigned char* addr, int s32DataLen, AUDIO_PARAM_S* pstAudioParams){
2. WAVEFORMATEX_ADPCM_S stWaveFormatExHeader;
3. signed short aCoeff[14] = {256,0, 512,-256, 0,0, 192,64, 240,0, 460,-208, 392,-232};
4. int s32HeadLen = sizeof(WAVEFORMATEX_ADPCM_S);
5.
6. stWaveFormatExHeader.riff[0] = 'R';
7. stWaveFormatExHeader.riff[1] = 'I';
8. stWaveFormatExHeader.riff[2] = 'F';
9. stWaveFormatExHeader.riff[3] = 'F';
10. stWaveFormatExHeader.u32ChunkSize = s32DataLen + s32HeadLen - 8;
11. stWaveFormatExHeader.rifftype[0] = 'W';
12. stWaveFormatExHeader.rifftype[1] = 'A';
13. stWaveFormatExHeader.rifftype[2] = 'V';
14. stWaveFormatExHeader.rifftype[3] = 'E';
15. stWaveFormatExHeader.chunkID[0] = 'f';
16. stWaveFormatExHeader.chunkID[1] = 'm';
17. stWaveFormatExHeader.chunkID[2] = 't';
18. stWaveFormatExHeader.chunkID[3] = ' ';
19. stWaveFormatExHeader.u32ChunkDataSize = 18;
20. stWaveFormatExHeader.u16EncodeMode = GetWaveEncodeMode(pstAudioParams->enMode);
21. stWaveFormatExHeader.u16NbChannels = GetAIOCHNums(pstAudioParams->enSoundMode);
22. stWaveFormatExHeader.u32SampleRate = GetSampleRates(pstAudioParams->enReSampleRate);
23. stWaveFormatExHeader.u32AverageBytesPerSecond = stWaveFormatExHeader.u32SampleRate * GetAencOutBitsWidth(pstAudioParams->enMode) / 8;
24. stWaveFormatExHeader.u16BlockAlign = 244; // 这个值有待进一步确认
25. stWaveFormatExHeader.u16BitWidthPerEncSmp = GetAencOutBitsWidth(pstAudioParams->enMode);
26. stWaveFormatExHeader.u16CbSize = 32;
27. stWaveFormatExHeader.u16SmpPerBlock = 488;
28. stWaveFormatExHeader.u16NumCoef = 7; // 7对14个系数
29. memcpy(stWaveFormatExHeader.aCoeff, aCoeff, stWaveFormatExHeader.u16NumCoef * sizeof(signed short)*2);
30. stWaveFormatExHeader.chunkIDEx1[0] = 'd';
31. stWaveFormatExHeader.chunkIDEx1[1] = 'a';
32. stWaveFormatExHeader.chunkIDEx1[2] = 't';
33. stWaveFormatExHeader.chunkIDEx1[3] = 'a';
34. stWaveFormatExHeader.u32ChunkExDataSize = s32DataLen;
35. memcpy(addr, &stWaveFormatExHeader, s32HeadLen);
36.
37. return 0;
38.}