wav文件分为三步分:
1:头,占固定的12个字节;
2:中间是部分是一个fmt的chunk,记录了channel,mask,samplerate等信息;
3:最后是一个data的chunk,包含编码后的数据。
如下图所示:
下面具体分析每一块的内容
一:header包含的12字节的含义
组织结构图如下:
前四个字节后后四个字节是固定的字段,用来作为WAV文件的标识符,在用循环调用sniff解析文件类型的时候,读取的就是这前后各四个字节,代码如下:
bool SniffWAV(
const
sp
&source, String8 *mimeType, float
*confidence,
char header[12];
if (source->readAt(0, header,
sizeof(header)) < (ssize_t)sizeof(header))
{
return false;
}
if (memcmp(header, "RIFF", 4) ||
memcmp(&header[8], "WAVE", 4)) {
return false;
}
*mimeType =
MEDIA_MIMETYPE_CONTAINER_WAV;
*confidence =
0.3f;
return true;
}
中间的四个字节表示整个wav文件的长度,即totalSize
二:fmt chunk的组织结构
这里只分析一般的wav的fmt结构,至于扩展的类似分析,一般的wav文件的fmt
chunk占16个字节,主要用来保存一下内容:
mWaveFormat = U16_LE_AT(formatSpec);
enum
{
WAVE_FORMAT_PCM
= 0x0001,
WAVE_FORMAT_ALAW
=
0x0006,
WAVE_FORMAT_MULAW
=
0x0007,
WAVE_FORMAT_EXTENSIBLE =
0xFFFE
};
mNumChannels = U16_LE_AT(&formatSpec[2]);
声音通道数
mSampleRate =
U32_LE_AT(&formatSpec[4]);码率
mSampleRate =
U32_LE_AT(&formatSpec[4]);每个sample中包含的bit数目
上面类似于U32_LE_AT这种函数都是大小端转换的函数,简单说就是调换字节顺序,前面的blog中有分析。
组织结构如下:
前面的变量代表保存的信息,后面的数字代表占用几个字节
三: data chunk 保存多媒体数据的部分,解码播放读取源数据就是从这里开始从前往后读取
前四个字节表明chunk的类型,紧跟着字节表明这个chunk的大小,后面剩下的字节为多媒体数据
其中获取到多媒体数据的长度后,加上fmt里面获取的信息,就可以计算这个音频文件的时长,计算公式如下:
四:补充1
解析完成之后,会将游标mOffset设置到Audio
data的位置,这样下次读取数据的时候就可以跳过一些头字节,读取音频数据了;
五:补充2
这里计算文件总长度和计算data类型的chunk的时候是根据具体的chunk的header中的后四个字节读出来的,一般情况下不会有问题,如果遇到
正常编码后长度为5M的文件,被某个软件截取了前面的1M,但是并没有去改那几个字节的数据,这样会导致通过头信息计算出来的文件的长度和播放时长会和实
际情况严重不符,遇到这种情况,就需要采用其他方式来计算文件的长度了,如:
int fileLength = lseek(fd, 0, SEEK_END);
多媒体数据部分的长度为
mDataSize = fileLength - 12 - 8
- 16 - 8;
12 表示文件头,8为fmt的头,16为fmt的chunkSize,8为data的头
综合考虑两种情况的代码如下:
mDataOffset = offset;
mDataSize = chunkSize;
int
fileLength = lseek(fd, 0, SEEK_END);
if(fileLength - 12 - 8 - 16 -
8 < mDataSize)
mDataSize
= fileLength - 12 - 8 - 16 - 8;
这样计算出来的音频文件的播放时长就不会有问题了。