4.2. VBR的MP3的播放时间(duration)计算方法

而对于VBR,由于每一帧的比特率都是变化的,所以计算起来就相对要复杂一些,下面就来详细介绍。

想要计算VBR的MP3的播放时间,总的来说,有两种方法:

4.2.1. 平均比特率法

这个方法,就是和CBR同样的思路,对于VBR的MP3来说,假如也像CBR的MP3一样,也有个类似的每一帧都是固定的某个值的比特率,那么计算整个VBR的播放时间,也就可以用上面CBR一样的公式去计算了。

由此,就有了平均比特率的概念,即,将所有帧的比特率的值相加,得到一个总的比特率的值,然后除于总的帧数,就得到了一个平均比特率,这样,使得理论上,此VBR相当于一个比特率为该平均比特率的CBR了。

不过,可以看出,需要计算平均比特率之前,要先得到每一帧的比特率的值,以及总的帧的数目,然后才可以计算平均比特率的值。

而为了得到每一帧的比特率的值,就要将整个VBR MP3文件都遍历一遍,以此找到所有的帧,并解析每一个帧的帧头,得到比特率索引值,然后查表得到比特率的值。

如此做的话,效率显然很低。因为此处只是为了计算整个VBR MP3的播放时间,却要遍历整个文件,还要解析每一帧的帧头,显得很是得不偿失。

所以,就有了更好的,效率更高的,下面要介绍的另一种方法,来计算VBR MP3的播放时间。

另外,需要提醒的是,对于平均比特率来说,往往和第一帧的比特率相差很大。因为常见的MP3音乐的开头部分,即第一帧或者前几帧,多数是一些无声的数据,或者本身包含信息量很少,比特率很低的数据。因此,其意味着,如果解码器对于VBR文件,误解为CBR文件,按照CBR所有帧的比特率都相同的逻辑,去解析第一帧,得到一个比特率,然后用此比特率来计算整个文件的播放时间的话,那么往往计算出的播放时间和实际的相差很大。这也就是后面引用中一个帖子里面遇到的情况,即,Media Player Classic播放VBR的MP3时的时间问题。

4.2.2. 总帧数法

总帧数法,即利用总的帧的数目,来计算VBR的播放时间。

此方法的前提,是我们前面就强调过的:

  1. MP3,即MPEG-1,Layer III,不论是CBR,还是VBR,每一帧的采样个数都是固定的1152个。即每一帧,都是固定的1152个采样。

  2. CBR和VBR中的固定和可变,都是指的是比特率Bitrate,而不是采样率Sample Rate。对于同一MP3文件,不论CBR还是VBR,采样率都是固定的。

了解了这两个前提后,就可以看出,对于VBR来说,虽然每一帧的比特率不同,但是每一帧的时间都是固定的,因为

每一帧的时间

= 该帧的采样个数 * 该帧的采样率

= 1152 * 采样率

其中:

  1. 采样个数:

    MPEG-1,Layer III,即MP3,不论是CBR还是VBR,都是固定的1152

  2. 采样率:

    对于单个的VBR文件,都是统一的,固定的,常见的是44100Hz。采样率可以通过解析第一帧的帧头得出采样率索引,然后查表得到采样率。

既然知道了每一帧的时间都是固定的,那么很容易就想到,如果知道VBR MP3有一共多少帧,那么就可以用 总的帧数 × 每一帧的时间 = 总的时间长度了。

所以,剩下的事情,就是去得到VBR MP3的总的帧数。

最简单,但是效率很低的方法就是,像上面方法1一样,遍历整个VBR文件,找出一共有多少帧,对于第一帧,解析第一帧的帧头,得到采样率。

这样有了采样率和总的帧数,就可以用上面的解释的原理来计算了,对应公式就是:

公式 4.2. VBR MP3总的时长(VBR Duration)

VBR Duration

= Total_Frame_Number * Time_Per_Frame

= Total_Frame_Number * (Sample_Number * Time_Per_Sample)

= Total_Frame_Number * (Sample_Number * (1 / Frame_Sample_Rate))

VBR MP3总的时长

= 总的帧数 * 单个帧的时长

= 总的帧数* (帧的采样个数 * 每个帧的时长)

= 总的帧数* (帧的采样个数 * (1 / 帧的采样率))


其中:

  1. 总的帧数:

    VBR中的总的帧的数目。

  2. 帧的采样个数:

    对于MP3(MPEG1,Layer III)来说,是固定的1152个采样。

  3. 帧的采样率:

    通过解析第一帧,即可得知帧采样率索引,查表,即可得此采样率。

但是,可以看到,虽然此遍历整个文件以得到总的帧数的方法,但是还是显得效率不高。此处我们毕竟只是需要知道总的帧数而已,却还是要遍历文件。

对此问题,想象一下,要是有人在VBR的文件头部,单独提供了这个总的帧数,那么不就可以省去了我们再去遍历整个文件了吗?

而实际情况是,你所想到的事情,别人已经帮你实现了。^_^。

现实中,VBR文件中,就是已经有了对应的头Header,用于存放VBR相关的信息。

这样的头信息,也就是下面将要介绍的XING和VBRI。

4.2.3. VBR的两种Header:XING和VBRI

VBR的帧头,记录了和VBR相关的一些信息,至少包含了我们前面介绍的,用于方便我们计算VBR的播放时长的总的帧数。

VBR MP3的帧头,主要有两种类型,XING和VBRI。

此外,VBR的头中,往往还包含了一个用于定位的TOC(table of content)目录表。即用于在快进或快退的时候,通过表中的信息,可以方便地定位到对应的位置。如果没有此TOC表,需要单独去计算出对应的位置,比较麻烦。

关于它们的具体格式和含义,下面就对其进行详细解释。

4.2.3.1. Xing TAG / Xing头(header)

此tag由XING公司推出的算法/规范,所以叫做XING。

对于大多数的VBR文件都加了此头,但并不全是。此头位于MPEG音频头后面的某个特定位置(多数是0x24)。包含了此XING头的第一个帧,其后的数据是空的,所以即使解码器没有考虑到此头,也可以正常处理此帧。对于Layer III的文件来说,比如常见的MP3,此VBR放在边信息(side information)之后。

下表是XING头的具体格式及含义:

表 4.1. XING 头的格式及含义

位置(字节) 长度(字节) 含义 示例
0 4 4个ASCII字符的VBR头 ID,要么是Xing,要么是Info,无NULL结尾(普通字符串都以NULL,即\0结尾) 'Xing'
4 4

存放一个标志,用于表示接下来存在哪些域/字段,各字段逻辑或的结果:

0x0001 存在总帧数(Frames)字段
0x0002 存在文件大小(Bytes)字段
0x0004 存在TOC字段
0x0008 存在音频质量指示字段

0x0007 就表示下面存在:

总帧数
文件大小总字节数
TOC表
8 4 总帧数(Frames),大端[可选] 7344
8或12 4 文件总大小,单位字节,大端[可选] 45000
8,12,16 100 TOC表,大端[可选]
8或12, 16, 108, 112 ,116 4 音频质量指示,最差0,最好100,大端[可选] 0

虽然知道了XING头的具体含义,可以去根据具体的值,解析出对应的含义了,但是,由于其是放在side information之后的,所以,要先定位,找到XING头,关于其位置,用如下公式计算:

公式 4.3. XING头位置

XING头位置

= MPEG头位置 + MPEG帧头大小 + 边信息大小

= MPEG头位置 + 4字节 + 边信息大小


其中:

  1. MPEG头位置:

    即通过程序去找到连续的11个bit都是1的位置,即可同步MPEG的帧,找到对应的MPEG头的开始处。

  2. 边信息大小:

    详细信息,后面用到此公式时会具体解释。

根据头的格式,Xing头里面必须包含ID和flag这两个段。其他字段都是可选的,是否包含,要看flag的值。有时候这个Xing头,CBR里面也有,此时,前面的ID的值就是Info,而不是Xing了。

4.2.3.2. VBRI头(header)

据了解,目前此头信息,只有用Fraunhofer的编码器生成的MPEG音频文件,才会用到此头。

其和Xing头不一样,其放在第一个MPEG头的后面,大小正好是32字节。其位置,长度和示例,都是以字节为单位。

下表是VBRi头的具体格式及含义,单位为字节:

表 4.2. VBRI头的格式及含义

位置(字节) 长度(字节) 含义 示例
0 4 4个ASCII字符的VBR头ID:“VBRI”无NULL结尾 “VBRI”
4 2 版本ID,大端,类型:DWORD 1
6 2 延迟,类型:float 7344
8 2 音频质量指示 75
10 4 文件总大小,大端,类型:DWORD 45000
14 4 总的帧数,大端,类型:DWORD 7344
18 2 TOC表的表项数目,大端,类型:WORD 100
20 2 TOC表项的缩放因子,大端,类型:DWORD 1
22 2 单个TOC表项的大小,单位字节,最大为4,大端,类型:DWORD 2
24 2 帧数/表项,大端,类型:WORD 845
26 用于检索的TOC表,整型值,可以通过每个表项大小乘于表项个数得到此TOC表的总大小,大端