一、引言
通过FFmpeg命令:
cpp
./ffmpeg -i XXX.wav
可以判断出某个文件是否为WAV格式的音频文件:
所以FFmpeg是怎样判断出某个文件是否为WAV格式的音频文件呢?它内部其实是通过wav_probe函数来判断的。从文章《FFmpeg源码:av_probe_input_format3函数分析》中我们可以知道:
FFmpeg中实现容器格式检测的函数是av_probe_input_format3函数,其内部通过循环while ((fmt1 = av_demuxer_iterate(&i))) 拿到所有容器格式对应的AVInputFormat结构,然后通过score = fmt1->read_probe(&lpd)语句执行不同容器格式对应的解析函数,根据是否能被解析,以及匹配程度,来判断出这是哪种容器格式。而WAV格式的音频文件对应的解析函数就是wav_probe函数。
二、wav_probe函数的定义
wav_probe函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为5.0.3)的源文件libavformat/wavdec.c中:
cpp
static int wav_probe(const AVProbeData *p)
{
/* check file header */
if (p->buf_size <= 32)
return 0;
if (!memcmp(p->buf + 8, "WAVE", 4)) {
if (!memcmp(p->buf, "RIFF", 4) || !memcmp(p->buf, "RIFX", 4))
/* Since the ACT demuxer has a standard WAV header at the top of
* its own, the returned score is decreased to avoid a probe
* conflict between ACT and WAV. */
return AVPROBE_SCORE_MAX - 1;
else if ((!memcmp(p->buf, "RF64", 4) ||
!memcmp(p->buf, "BW64", 4)) &&
!memcmp(p->buf + 12, "ds64", 4))
return AVPROBE_SCORE_MAX;
}
return 0;
}
其作用就是检测某个文件是否为WAV格式的音频文件。
形参pd:输入型参数,为AVProbeData类型的指针。
AVProbeData结构体声明在libavformat/avformat.h中:
cpp
/**
* This structure contains the data a format has to probe a file.
*/
typedef struct AVProbeData {
const char *filename;
unsigned char *buf; /**< Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero. */
int buf_size; /**< Size of buf except extra allocated bytes */
const char *mime_type; /**< mime_type, when known. */
} AVProbeData;
p->filename为:需要被推测格式的文件的路径。
p->buf:指向"存放从路径为p->filename的文件中读取出来的二进制数据"的缓冲区。
p->buf_size:缓冲区p->buf的大小,单位为字节。注:FFmpeg判断某个文件的格式时不会读取完整个文件,只会读取它前面的一部分,比如最开始的2048个字节。只要根据前面的这些字节就足够判断出它的格式了,所以p->buf_size的值一般就是2048。
p->mime_type:一般为NULL,可忽略。
返回值:返回一个类型为整形的分值。返回0表示该文件完全不符合WAV格式。返回一个大于0的值表示该文件比较符合WAV格式,但还需要在av_probe_input_format3函数中执行其它容器格式对应的解析函数来进行对比,最终通过最高分来确定到底是哪种容器格式。
三、wav_probe函数的内部实现原理
wav_probe函数中,首先有如下判断逻辑:如果能读取到的文件中的二进制数据量小于32个字节,远远小于WAV Header中必须包含的数据量,wav_probe函数返回0,表示该文件完全不符合WAV格式:
cpp
/* check file header */
if (p->buf_size <= 32)
return 0;
由《音视频入门基础:WAV专题(2)------WAV格式简介》可以知道,WAV文件的第0到3字节为"区块编号",内容必须为"RIFF"或"RIFX";第8到11字节为"档案格式",内容必须固定为"WAVE"。所以通过下面代码来判断该文件是否符合上述条件,如果符合返回AVPROBE_SCORE_MAX - 1(也就是返回99分),意味着该文件比较符合WAV格式。 从文章《FFmpeg源码:av_probe_input_format3函数分析》中我们可以知道容器格式探测函数的分值最高为100分,100分才是完全符合这种格式。所以为什么下面代码中不是返回100分只是返回99分呢?因为ACT音频格式可能会包含一个标准的WAV文件头,所以FFmpeg将分值减小来避免探测ACT和WAV格式时产生的冲突:
cpp
f (!memcmp(p->buf + 8, "WAVE", 4)) {
if (!memcmp(p->buf, "RIFF", 4) || !memcmp(p->buf, "RIFX", 4))
/* Since the ACT demuxer has a standard WAV header at the top of
* its own, the returned score is decreased to avoid a probe
* conflict between ACT and WAV. */
return AVPROBE_SCORE_MAX - 1;
如果不符合上述的判断,通过下面代码判断该文件是否为WAVE 64位扩展格式Wave64,如果符合,返回AVPROBE_SCORE_MAX(也就是返回100分),意味着该文件完全符合WAV格式。FFmpeg内部把WAV和Wave64当成一种文件格式来处理:
cpp
else if ((!memcmp(p->buf, "RF64", 4) ||
!memcmp(p->buf, "BW64", 4)) &&
!memcmp(p->buf + 12, "ds64", 4))
return AVPROBE_SCORE_MAX;