=================================================================
音视频入门基础:WAV专题系列文章:
音视频入门基础:WAV专题(1)——使用FFmpeg命令生成WAV音频文件
音视频入门基础:WAV专题(3)——FFmpeg源码中,判断某文件是否为WAV音频文件的实现
音视频入门基础:WAV专题(4)——FFmpeg源码中获取WAV文件音频压缩编码格式、采样频率、声道数量、采样位数、码率的实现
音视频入门基础:WAV专题(5)——FFmpeg源码中解码WAV Header的实现
音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息
音视频入门基础:WAV专题(7)——FFmpeg源码中计算WAV音频文件每个packet的size值的实现
音视频入门基础:WAV专题(8)——FFmpeg源码中计算WAV音频文件AVStream的time_base的实现
音视频入门基础:WAV专题(9)——FFmpeg源码中计算WAV音频文件每个packet的duration和duration_time的实现
音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现
音视频入门基础:WAV专题(11)——FFmpeg源码中计算WAV音频文件每个packet的pts_time、dts_time的实现
=================================================================
注:本文有部分内容引用了维基百科:https://zh.wikipedia.org/wiki/WAV
一、引言
Waveform Audio File Format(缩写WAVE或WAV)是微软与IBM公司所开发在个人电脑存储音频流的编码格式,在Windows平台的应用软件受到广泛的支持。此格式属于资源交换文件格式(RIFF)的应用之一(关于RIFF格式可以上微软的官网了解:https://learn.microsoft.com/zh-cn/windows/win32/xaudio2/resource-interchange-file-format--riff-)。WAV音频文件通常会将采用脉冲编码调制(音频压缩编码格式为PCM)的音频存储在区块中。由于此音频格式未经过压缩,所以在音质方面不会出现失真的情况,但文件的体积较大。
WAV格式的官方文档可以从https://www.mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html 下载:
二、WAV文件的Header
WAV文件遵守RIFF格式的规则,文件最前面的数据为文件头(header),使音频播放器能够简单掌握文件的基本信息。注意:文件头不一定为固定的44字节,由是否存在扩展块和是否存在可选区块决定。文件头中的内容以区块(chunk)为最小单位(注意:是子区块包含区块),每一区块长度为4字节,而区块之上则由子区块包裹,每一子区块长度不受限制,但须在前头先声明标签及长度(单位为字节)。文件头的前3个区块记录文件格式及长度;接着第一个子区块(标签为"fmt",被称为“Format chunk”)包含多个区块,记录声道数量、采样率等信息;另外一个子区块(标签为"data",被称为“Data chunk”)才包含真正的音频资料(或者说音频数据),长度则视音频长度而定。内容如下表所示。须注意的是,每个区块的字节序不尽相同(也就是说有些区块按照大端字节序存贮,有些按照小端字节序存贮),而音频数据本身则是采用小端字节序:
注:
1.上图描述有误,“位元(组)率”实际应为“=声道数 * 采样频率 * 量化位数 / 8”(图片中漏乘了声道数量),它代表每秒钟数据的字节数。
2.上图仅仅列出了WAV文件Header中必须包含的区块。Format chunk和Data chunk是必须的。但实际Header中还可能存在可选区块,Format chunk中也可能包含扩展块。
3.上图中“总区块大小”的描述有误,其区块内容不一定为“N+36”。
4.上图中的WAV Header是以其遵守了RIFF格式的规则为例,实际WAV文件还可能遵守RIFX格式的规则。
三、WAV文件实例分析
用notepad++打开《音视频入门基础:WAV专题(1)——使用FFmpeg命令生成WAV音频文件》中生成的WAV文件,下图红框中的数据为文件头(header):
(一)WAV Header中的前3个区块
第0到3字节为WAV Header中的第一个区块:“区块编号”,内容为“RIFF”或“RIFX”。“RIFF”指定该文件中真正的音频数据按照小端字节序存贮,“RIFX”指定该文件中真正的音频数据按照大端字节序存贮。下图中的值为“RIFF”,表示该文件遵守RIFF格式的规则,音频数据按照小端字节序存贮:
第4到7字节为WAV Header中的第二个区块:“总区块大小”,值等于:该WAV文件所有区块减去“”区块编号和“总区块大小”所占的字节数,即“该WAV文件所有区块所占空间 - 8”,单位为字节。由于该区块是小端字节序,所以下图中的总区块大小为0x03184846,也就是十进制的51923014,表示总区块大小为51923014字节:
第8到11字节为WAV Header中的第三个区块:“档案格式”,内容固定为“WAVE”,表示该文件为WAVE / WAV格式的文件:
(二)WAV Header中的子区块:Format chunk
第12到15字节为“子区块1标签”,内容固定为“fmt ”(最后一个字符是空格),表示这是第一个子区块的开头,该子区块被称为Format chunk,记录声道数量、采样率等信息:
第16到19字节为“子区块1大小”,单位为字节。如果子区块1中无扩展块,值为16,子区块1中有扩展块,值为“16 + 2字节扩展块长度 + 扩展块内容”。当WAV文件中的音频数据使用的不是PCM压缩编码格式时,Format chunk中就会有扩展块。下图中的值为0x10(小端字节序),也就是十进制的16(字节),所以该wav文件的Format chunk中无扩展块:
“子区块1大小”就是下图红框中的区块加起来的总大小。也就是子区块1中,去掉“子区块1标签”和“子区块1大小”这两个区块后的总大小:
第20到21字节为“音讯格式”,也就是所谓的音频压缩编码格式,值一般为0x01(小端字节序),表示音频压缩编码格式为PCM:
第22到23字节为声道数量。值为1是单声道,为2则是双声道(立体声)。下图中的值为0x02(小端字节序),表示音频为双声道:
第24到27字节为“取样频率”,也就是音频的采样频率,单位为Hz。下图中的值为0xAC44(小端字节序),换算成10进制就是44100,表示音频的采样频率为44100Hz:
第28到31字节为“位元(组)率”,即音频的码率(单位为byte per second,音频每秒播放的字节数)。其值为:声道数 * 采样频率 * 采样位数 / 8。下图中的值为0x02B110(小端字节序),也就是10进制的176400,表示音频的码率为176400Byte/s。可以计算出来:176400 = 2 * 44100 * 16 / 8,符合上述计算公式:
第32到33字节为“区块对齐”,即每个采样点所需的字节数,其值为:声道数 * 采样位数 / 8。下图中的值为0x04(小端字节序),换算成10进制就是4。 4 = 2 * 16 / 8,符合上述计算公式:
第34到35字节为“位元深度”,即采样位数。下图中的值为0x10(小端字节序),换算成10进制就是16,表示音频的采样位数是16位:
由于我们上述读取到的“子区块1大小”为16字节。所以至此“子区块1”读取结束。后面的内容为另外一个子区块的。
(三)WAV Header中的可选区块
WAV文件的Header中还可能包含一些可选的区块,如:Fact chunk、Cue points chunk、Playlist chunk、Associated data list chunk等。这些区块在WAV文件中不是必须的,WAV文件的Header中可能存在、也可能不存在这些区块。list chunk是其中一种可选区块,包含关于版权、作者、文件工程师和其他类似文本的信息。由于我们演示的这个WAV文件是通过FFmpeg生成的,FFmpeg在它的Header中加入了list chunk。
从该WAV文件的第36到39字节中读取到了“子区块1”后面区块的标签:“LIST”,表示这是list chunk的开头:
WAV文件遵守RIFF格式的规则,根据微软官方对RIFF格式的描述:https://learn.microsoft.com/zh-cn/windows/win32/xaudio2/resource-interchange-file-format--riff-
我们可以知道chunkSize(区块大小)是一个4字节值。所以下图红框中,也就是该WAV文件的第40到43字节为list chunk这个子区块的大小,下图中的值为0x1A(小端字节序),换算成10进制就是26,表示子区块list chunk中,去掉“list chunk标签”和“list chunk大小”这两个区块后的总大小是26字节:
所以据此,我们可以推断出list chunk中去掉“list chunk标签”和“list chunk大小”后的有效数据的范围是从该WAV文件的第44字节到第69字节:
至此该子区块(list chunk)读取结束。后面的内容为另外一个子区块的。
(四)WAV Header中的子区块:Data chunk
从该WAV文件的第70到73字节中读取到了“子区块list chunk”后面区块的标签:“data”,表示这是Data chunk的开头,该子区块存放真正的音频数据:
第74到77字节为“子区块Data chunk的大小”,单位为字节。下图中的值为0x03184800(小端字节序),换算成十进制是51922944,表示该WAV文件中存放的真正的音频数据的大小为51922944字节。由上述第20到21字节中的“音讯格式”,我们可以知道该WAV文件中音频的压缩编码格式为PCM。所以该WAV文件中PCM音频数据的大小为51922944字节。该值 = (采样频率*采样位数*声道)*时间 / 8(单位:字节数)。我们把实际数据带入该公式:51922944 ≈ (44100 * 16 * 2) * 294 / 8,可以看到是符合该公式的。“约等于”是因为该WAV文件的总时长不是正好的294秒:
“子区块Data chunk的大小”占用的空间为4字节,所以WAV存放真正的音频数据的极限是2 ^ 32 = 4294967296字节,也就是说它最多只能容纳4GB的音频流信息,这是Wave作为32bit时代产物的局限性。要想存贮超过4G的音频数据,可以使用WAVE 64位扩展格式Wave64。
从第78字节开始存放的就是真正的音频数据了。从上面我们可以知道该WAV文件的Header为从第0到第77字节,也就是说它的Header占78字节。我们在Windows系统中通过“属性”查看该WAV文件的大小,可以看到是51923022 字节。51923022 = 51922944(PCM音频数据的大小) + 78(Header的大小)。可以看出来,数值是完全符合的:
四、删除WAV文件头测试
由上面我们可以知道该WAV文件的Header为从第0到第77字节,所以删掉该文件头以后WAV文件能不能播放呢?我们做一个测试:
备份WAV文件。在notepad++中删掉该WAV文件的Header:
然后发现用vlc打开没反应。也就是说无法打开了:
从上面我们可以知道该WAV文件的第20到21字节值为0x01,表示音频压缩编码格式为PCM。所以去掉WAV Header后,剩下的数据就是PCM音频数据。我们可以按照《音视频入门基础:PCM专题(3)——使用Audacity工具分析PCM音频文件》,通过导入PCM音频文件的方法,来打开去掉Header后的WAV文件:
果然可以成功打开播放:
五、Wave64格式
想存贮超过4G的音频数据,可以使用WAVE 64位扩展格式Wave64,具体可以参考:《什么是Wave64和RF64?》、《F64 | MBWF | WAV 64-bit | Learn, Get Free Samples Now》
六、总结
1.WAV的文件头不一定为固定的44字节,由是否存在扩展块和是否存在可选区块决定。所以如果不通过第三方库,自己写解析WAV文件的程序时要注意这一点。
2.WAV文件头包含了音频采样频率、采样位数、声道数信息。而WAV文件遵守RIFF格式的规则,RIFF规定了符号和字节序。所以播放器可以根据这些信息播放WAV文件。同样的,即使去掉了WAV Header,只要我们播放时指定了这五个参数(这五个参数可以参考:《音视频入门基础:PCM专题(1)——使用FFmpeg命令生成PCM音频文件并播放》),而我们又同时知道了它的音频压缩编码方式,我们就能播放该音频文件。
3.WAV是文件格式。PCM是音频压缩编码格式。WAV文件中的音频压缩编码格式一般是PCM,但也可以是其它。
4.WAV文件只能容纳4GB的音频流信息,要想存贮超过4G的音频数据,可以使用WAVE 64位扩展格式Wave64。
七、参考
维基百科:http://https://zh.wikipedia.org/wiki/WAV