如何计算MP3的总时长问题

MP3是大家所熟悉的一种音乐播放格式,它其实是mpeg标准中的mpeg1的layer3编码,这个是和压缩技术相关的,对于mpeg的了解,大家可以去网上找一些格式解析的文档,在这里我们只要知道MP3这种格式是什么就够了,在以下的内容中,会有详细解释。

MP3中有一个关键词就是:帧,MP3是由若干个帧组成。

Mp3的文件结构

MP3文件大体分为三部分:TAG_V2(ID3V2),Frame, TAG_V1(ID3V1),其中ID3V1在整个MP3文件的末尾128个字节,包含了作者,作曲,专辑等信息,而ID3V2是在文件的开头部分,是对ID3V1的扩展包含MP3的一些信息如作者,专辑,发行日等等,它的大小不固定,可以从他的标签头记录的是个字节中得到标识和大小。

 

ID3V2 ID3V2到现在一共有4个版本,但流行的播放软件一般只支持第3版,既ID3v2.3。由于ID3V1记录在MP3文件的末尾,ID3V2就只好记录在MP3文件的首部了(如果有一天发布ID3V3,真不知道该记录在哪里)。也正是由于这个原因,对ID3V2的操作比ID3V1要慢。而且ID3V2结构比ID3V1的结构要复杂得多,但比前者全面且可以伸缩和扩展。 下面就介绍一下ID3V2.3。 每个ID3V2.3的标签都一个标签头和若干个标签帧或一个扩展标签头组成。关于曲目的信息如标题、作者等都存放在不同的标签帧中,扩展标签头和标签帧并不是必要的,但每个标签至少要有一个标签帧。标签头和标签帧一起顺序存放在MP3文件的首部。

一、标签头

char Header[3];    /*必须为"ID3"否则认为标签不存在*/

char Ver;    /*版本号ID3V2.3就记录3*/

char Revision;    /*副版本号此版本记录为0*/

char Flag;    /*存放标志的字节,这个版本只定义了三位,稍后详细解说*/

char Size[4];    /*标签大小,包括标签头的10个字节和所有的标签帧的大小*/(我实际测试不包括标签头的10个字节。是笔误吗?百度一搜都是的。竟然还有的注释:有的文档认为不包括。我去,还带这的?难道标准不一样?那么,标准又是什么?标准仅仅是"有的文档"?)

1、标志字节 标志字节一般为0,定义如下: abc00000 a -- 表示是否使用Unsynchronisation(这个单词不知道是什么意思,字典里也没有找到,一般不设置) b -- 表示是否有扩展头部,一般没有(至少Winamp没有记录),所以一般也不设置 c -- 表示是否为测试标签(99.99%的标签都不是测试用的啦,所以一般也不设置)

2、计算标签大小:

一共四个字节,但每个字节只用7位,最高位不使用恒为0。所以格式如下

0xxxxxxx 0xxxxxxx 0xxxxxxx 0xxxxxxx

计算大小时要将0去掉,得到一个28位的二进制数,就是标签大小(不懂为什么要这样做),计算公式如下:

int total_size  =  (Size[0]&0x7F)*0x200000

+(Size[1]&0x7F)*0x4000    //这个地方原来是0x400,可能是由于原作者笔误,然后百度一下到处都是0x400

+(Size[2]&0x7F)*0x80

+(Size[3]&0x7F)

按道理来说,跳过标签大小,就是第一帧的帧头位置,但是有时候却不是,所以我们仍然要搜索判断是否是帧头,下面我们来看mp3的帧结构。

这句是因为扩展标签帧吗?有时候不是,那是什么情况下不是?

二、标签帧

每个标签帧都有一个10个字节的帧头和至少一个字节的不固定长度的内容组成。它们也是顺序存放在文件中,和标签头和其他的标签帧也没有特殊的字符分隔。得到一个完整的帧的内容只有从帧头中的到内容大小后才能读出,读取时要注意大小,不要将其他帧的内容或帧头读入。帧头的定义如下:

char FrameID[4]; /*用四个字符标识一个帧,说明其内容,稍后有常用的标识对照表*/

char Size[4]; /*帧内容的大小,不包括帧头,不得小于1*/

 char Flags[2]; /*存放标志,只定义了6位,稍后详细解说*/

1.帧标识 用四个字符标识一个帧,说明一个帧的内容含义,常用的对照如下:

 TIT2=标题 表示内容为这首歌的标题,下同

TPE1=作者 TALB=专集

 TRCK=音轨 格式:N/M 其中N为专集中的第N首,M为专集中共M首,N和M为ASCII码表示的数字

TYER=年代 是用ASCII码表示的数字

TCON=类型 直接用字符串表示

COMM=备注 格式:"eng\0备注内容",其中eng表示备注所使用的自然语言

2.大小 这个可没有标签头的算法那么麻烦,每个字节的8位全用,格式如下

xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx

 算法如下:

 int FSize; FSize = Size[0]*0x100000000 +Size[1]*0x10000 +Size[2]*0x100 +Size[3];

3.标志 只定义了6位,另外的10位为0,但大部分的情况下16位都为0就可以了。格式如下:

 abc00000 ijk00000

a -- 标签保护标志,设置时认为此帧作废

 b -- 文件保护标志,设置时认为此帧作废

 c -- 只读标志,设置时认为此帧不能修改(但我没有找到一个软件理会这个标志)

 i -- 压缩标志,设置时一个字节存放两个BCD码表示数字

 j -- 加密标志(没有见过哪个MP3文件的标签用了加密)

k -- 组标志,设置时说明此帧和其他的某帧是一组

值得一提的是winamp在保存和读取帧内容的时候会在内容前面加个'\0',并把这个字节计算在帧内容的大小中。

 

Mp3的帧详解

每一帧其实包括 帧头,附加信息,主数据,其实我们只要找到帧头,帧头中所包含的数据就能让我们掌控这一帧的信息,帧头固定4个字节(32bit),格式如下

AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM

下面是就是每个位置代表的含义:

标识 长度 含义 示例
A 11 用于同步帧,找到此帧头(所有位均置 1) 11111111111
B 2 音频版本 ID
00 - 版本是 MPEG 2.5 (MPED-2 的非官方扩展版本)
01 – 保留
10 – 版本是 MPEG 2 (ISO/IEC 13818-3)
11 – 版本是 MPEG 1 (ISO/IEC 11172-3) 通过ID查表得其他信息
11
C 2 Layer 的索引
00 – 保留
01 - Layer III
10 - Layer II
11 - Layer I
01
D 1 保护位
1 – 无 CRC 0 – 用 16位的 CRC保护下面的帧头
1
E 4 比特率索引(查表) 1001
F 2 采样率索引 (查表) 10
G 1 填充位,如果为1,计算帧长时,要多加1 1
H 1 私有位 (仅用于标示性的) 1
I 2 声道的模式
00 – 立体声
01 – 混合立体声
10 – 双声道 (两个单声道)
11 – 一个声道 (单声道)
01
J 2 联合立体声(joint stereo) 采用联合立体声编码方式的两个声道具有关联性。例如MS_stereo将两个声道相加、相差后处理,相减后去掉了左右声道相同的成份,后续的压缩可得到更高的压缩率。 10
K 1 版权保护,0=no 1=yes 1
L 1 原始版本,0=no 1=yes 0
M 1 预加重 00 - none
01 - 50/15 ms
10 - reserved
11 - CCIT J.17
01

1)比特率

其中E和F位置的值,是通过mpeg ID和layer索引查标准的值,我写成了一个数组,直接查表得到比特率

int  bitrate[5][15] = {
/* MPEG-1 */
{ 0,  32000,  64000,  96000, 128000, 160000, 192000, 224000,  /* Layer I   */
256000, 288000, 320000, 352000, 384000, 416000, 448000 },
{ 0,  32000,  48000,  56000,  64000,  80000,  96000, 112000,  /* Layer II  */
128000, 160000, 192000, 224000, 256000, 320000, 384000 },
{ 0,  32000,  40000,  48000,  56000,  64000,  80000,  96000,  /* Layer III */
112000, 128000, 160000, 192000, 224000, 256000, 320000 },
/* MPEG-2 ,MPEG-2.5 */
{ 0,  32000,  48000,  56000,  64000,  80000,  96000, 112000,  /* Layer I   */
128000, 144000, 160000, 176000, 192000, 224000, 256000 },
{ 0,   8000,  16000,  24000,  32000,  40000,  48000,  56000,  /* Layer    */
64000,  80000,  96000, 112000, 128000, 144000, 160000 } /* II & III  */
};

2)采样频率:

int samplingrate[4][3] = {  //value s are in Hz
{11025 , 12000 , 8000},      //MPEG Version 2.5
{0,0,0},                                       //reserved
{22050, 24000, 16000},   //MPEG Version 2 (ISO/IEC 13818-3)
{44100, 48000, 32000}     //MPEG Version 1 (ISO/IEC 11172-3)
};

3)每帧持续时间

在这里在介绍一个比较重要的概念那就是,每帧持续时间,MP3有一个特殊性,那就是每帧持续时间是固定的:

每帧持续时间(毫秒) = 每帧采样数 /  采样频率 * 1000

我们从第一帧的帧头可以得音频版本 ID和layer版本,从而可以确定每帧采样个数

mpeg 1   mpeg 2   mpeg 2.5

layer I    384         384        384

layer II   1152      1152      1152

layer III  1152      576        576

现在我们应该很清楚的知道,为什么我们只需要知道MP3是mpeg1的layerIII层的原因了;(但是按照我现在的理解只要是layer III的就是MP3,呵呵,可以讨论)

(写这个有点慢,因为不会用,不知道咋添加表格,不习惯)

4)计算总时长

下面我们来看计算时间的公式:

播放时间 = 总帧bytes ÷ 比特率 × 8000

比特率:查表可得到;

总帧bytes:简单的看就是,总文件的大小-ID3信息,总文件大小就是读取整个文件的大小,ID3就是之前说过的ID3V2和ID3V1,ID3V1在末尾处128个字节,ID3V2在文件开始为止,大小可以从标签头得到,之前讲过,这样我们就能准确的得到总帧bytes,我们就能计算出来mp3的总时长了.

但是这只是对固定比特率(CBR)的MP3总时长的计算方式,对于变比特率(VBR)的MP3,由于每帧的比特率可能不同,用以上的公式就无法准确算出来mp3总时长,有些文档说可以计算平均比特率,但是这个估算值偏差有时候很大,下一次我来详细讲如果求变化比特率Mp3的总时长,牢牢记住上面的表格,一切的信息都能从那里来。


VBR格式的mp3计算时间

之前我们所讨论的都是对于一个固定比特率(CBR)的MP3进行求总时长,由于固定比特率的特殊性,很容易第一帧的帧头就能知道整个MP3的信息,但是对于变比特率(VBR)的MP3,比特率和帧长都不是固定的,所以我们就不能用之前的公式来计算总时长。

其实在第一帧的帧头后面还存着VBR的一些信息,里面存着文件长度,总帧数等信息,从而使我们很快算出变比特率的总时长,方法如下:

1)XING头标志

由于VBR是XING公司推出的算法,所以在MP3的第一帧里会有"XING"或者"Info"的关键字,具体格式

             位置       长度(字节)                                       含义
0 4 包含ASCII的字符XING或者Info,没有NULL结尾
4 4 标识位0x0001 - 存在总帧数(Frames)字段
0x0002 - 存在文件大小(Bytes)字段
0x0004 –存在 TOC字段
0x0008 – 存在音频质量指示字段
8 4 若标识位存在总帧数,那么这四个字节就是总帧数
12 4 若标识位存在文件大小,那么这四个字节就是文件大小

还有别的信息,这里不需要,我就不在这里说了。

2) VBRI头标志

还有一种头也同样标识和XING类似,只是有"VBRI"的关键字,具体格式

         位置     长度(字节)           含义
0 4 包含ASCII的字符VBRI,没有NULL结尾
10 4 文件大小
14 4 总帧数

我只写了对总时长有需要的位置,其他的暂时没用到,就不提了,不过可以讨论。

3)确定标志头位置

知道的有这样的标志头可以帮助我们很快找到需要的信息,那么如何定位呢,公式如下:

标志头位置 = MPEG 头位置 +  MPEG帧头大小(4字节)+ 边信息大小
对于边信息,我们可以根据头中所给J位置上2个字节得到

mode = (header>>6)&0x03

其中:'00'  Stereo; '01'  Joint Stereo (Stereo);   '10'  Dual channel (Two mono channels);

'11'  Single channel (Mono).

MPEG 1                                   MPEG2/2.5

Stereo ,Joint Stereo  Dual channel          32                                                    17

Mono                                                                    17                                                      9

现在已经很清楚了,我们就能定位出来头标志,然后读出我们需要的总帧数,之前一届我已经介绍了每帧持续时间,因此

VBR的MP3总时长(毫秒) =  总帧数*每帧持续时间

 

到此不知道我讲的清楚不清楚,这也是我通过自己做,和看别人的文档总结的一些,希望能帮助到需要的人,有什么问题可以和我讨论。

这个准确度还是比较高的,前提是从帧头中读到的信息是正确有效的,呵呵,我测得大部分MP3还是比较正确的,如果真的要准确,那就预读一遍MP3文件,把每一帧的找到,累加得到帧数,我做MP3帧索引表的时候就这么搜的,其实对于小文件还是比较快的。


发布了25 篇原创文章 · 获赞 4 · 访问量 7万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览