解析mp3文件,获得mp3中的专辑图片

这仍然也是操作的是字节,所以其他语言都是适用的。

mp3文件的首尾都存有文件的信息,存在尾部的称为ID3V1,在首部的称为 ID3V2。专辑图片是存放在部的ID3V2。所以这里解析ID3v2

主要的知识点,都是资料中的:

ID3V2 到现在一共有 4 个版本,但流行的播放软件一般只支持第 3 版,既 ID3v2.3。由于 ID3V1 记录
在 MP3 文件的末尾,ID3V2 就只好记录在 MP3 文件的首部了(如果有一天发布 ID3V3,真不知道该记录在哪
里)。也正是由于这个原因,对 ID3V2 的操作比 ID3V1 要慢。而且 ID3V2 结构比 ID3V1 的结构要复杂得多,
但比前者全面且可以伸缩和扩展。
下面就介绍一下 ID3V2.3。
每个 ID3V2.3 的标签都一个标签头和若干个标签帧或一个扩展标签头组成。关于曲目的信息如标题、作者
等都存放在不同的标签帧中,扩展标签头和标签帧并不是必要的,但每个标签至少要有一个标签帧。标签
头和标签帧一起顺序存放在 MP3 文件的首部。
1、标签头
在文件的首部顺序记录 10 个字节的 ID3V2.3 的头部。数据结构如下:
char Header[3];     /*必须为"ID3"否则认为标签不存在*/
char Ver;     /*版本号 ID3V2.3 就记录 3*/
char Revision;     /*副版本号此版本记录为 0*/
char Flag;     /*存放标志的字节,这个版本只定义了三位,稍后详细解说*/
char Size[4];     /*标签大小,包括标签头的 10 个字节和所有的标签帧的大小*/
1).标志字节
标志字节一般为 0,定义如下:
abc00000
a -- 表示是否使用 Unsynchronisation(这个单词不知道是什么意思,字典里也没有找到,一般不设置)
b -- 表示是否有扩展头部,一般没有(至少 Winamp 没有记录),所以一般也不设置
c -- 表示是否为测试标签(99.99%的标签都不是测试用的啦,所以一般也不设置)
2).标签大小
一共四个字节,但每个字节只用 7 位,最高位不使用恒为 0。所以格式如下
0xxxxxxx 0xxxxxxx 0xxxxxxx 0xxxxxxx
计算大小时要将 0 去掉,得到一个 28 位的二进制数,就是标签大小(不懂为什么要这样做),计算公式如
下:
int total_size;
total_size =    (Size[0]&0x7F)*0x200000
   +(Size[1]&0x7F)*0x400
   +(Size[2]&0x7F)*0x80
   +(Size[3]&0x7F)
2、标签帧
每个标签帧都有一个 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',并把这个字节计算在帧内容的
大小中。
    这个文件SoundPlayer是在播放音乐的时候得到专辑
Actionscript3.0代码 复制代码 收藏代码
  1. package service 
  2.     //import省略 
  3.  
  4.     public class SoundPlayer 
  5.     {   private var channel:SoundChannel;//声音通道 
  6.         private var sound:Sound;//声音 
  7.         private var fileUrl:String; 
  8.         private var loader:Loader = new Loader  ; 
  9.         private var bitmap:Bitmap; 
Actionscript3.0代码 复制代码 收藏代码
  1. public function SoundPlayer(){} 
  2.  
  3. //播放音乐的方法 
  4. public function play(url:String):void 
  5.     this.fileUrl = url; 
  6.     sound = new Sound  ; 
  7.     sound.load(new URLRequest(url)); 
  8.     channel = sound.play(); 
  9.  
  10.     sound.addEventListener(Event.COMPLETE,soundLoaderComplete); 
  11. //soundLoaderComplete 歌曲加载成功 得到专辑图片 
  12. private function soundLoaderComplete(event:Event):void 
  13.     (event.target as Sound).removeEventListener(Event.COMPLETE,soundLoaderComplete); 
  14.     var fileStream:FileStream = new FileStream  ; 
  15.     fileStream.open(new File(this.fileUrl),FileMode.READ); 
  16.     //设置文件读取的位置; 
  17.     var buffer:ByteArray = new ByteArray  ; 
  18.     fileStream.readBytes(buffer,0,10); 
  19.     var id3Header:ID3V2Header = new ID3V2Header(buffer); 
  20.     //去除标签头 
  21.     while (true) 
  22.     {   //读取帧头 
  23.         var frameHeaderBuffer:ByteArray = new ByteArray  ; 
  24.         fileStream.readBytes(frameHeaderBuffer,0,10); 
  25.         var id3FrameHeader:ID3V2Frameheader = new ID3V2Frameheader(frameHeaderBuffer); 
  26.         if (id3FrameHeader.getFrameSize() <= 0
  27.         { 
  28.             //没有专辑图片 
  29.             //删除原来的图片 
  30.             loader.unload(); 
  31.             loader.load(new URLRequest("../images/app.png")); 
  32.             loader.contentLoaderInfo.addEventListener(Event.COMPLETE,imageLoaderComplete); 
  33.             break; 
  34.         } 
  35.  
  36.         //读取帧体 
  37.         var frameBodyBuffer:ByteArray = new ByteArray  ; 
  38.         fileStream.readBytes(frameBodyBuffer,0,id3FrameHeader.getFrameSize()); 
  39.  
  40.         //符标识; 
  41.         var key:String = id3FrameHeader.getFrameId(); 
Actionscript3.0代码 复制代码 收藏代码
  1.         <span style="color: rgb(255, 0, 0);">//专辑图片的符标识是“APIC”</span> 
  2.         if (key == "APIC"
  3.         { 
  4.             //专辑图片 
  5.             var imageBytes:ByteArray = new ByteArray  ; 
  6.             //前14个字节无用   去除掉 
  7.             frameBodyBuffer.position = 14
  8.             frameBodyBuffer.readBytes(imageBytes,0,frameBodyBuffer.length - 14); 
  9.             //删除原来的图片; 
  10.             loader.unload(); 
  11.             loader.loadBytes(imageBytes); 
  12.             loader.contentLoaderInfo.addEventListener(Event.COMPLETE,imageLoaderComplete); 
  13.  
  14.             break; 
  15.         } 
  16.     } 
  17.     fileStream.close(); 
  18.  
  19. //专辑图片加载完成 
  20. private function imageLoaderComplete(event:Event):void 
  21.     loader.contentLoaderInfo.removeEventListener(Event.COMPLETE,imageLoaderComplete);                                                                                      
Actionscript3.0代码 复制代码 收藏代码
  1.         this.bitmap = loader.content as Bitmap; 
  2.     } 
  3.  
  4.  
标签头
ID3V2Header.as
As3.0代码 复制代码 收藏代码
  1. package service 
  2.     import flash.utils.ByteArray; 
  3.  
  4.     public class ID3V2Header 
  5.     { 
  6.  
  7.         private var identi:ByteArray = new ByteArray;// ID3标示 3 
  8.         private var major:ByteArray = new ByteArray;// 版本号 3 1 
  9.         private var revsion:ByteArray = new ByteArray;// 0 1 
  10.         private var flags:ByteArray = new ByteArray;// 1 
  11.         private var total_size:int;// 标签体大小 4 
  12.  
  13.  
  14.         public function ID3V2Header(data:ByteArray) 
  15.         { 
  16.             // constructor code 
  17.             data.readBytes(identi,0,3); 
  18.             data.readBytes(major,0,1); 
  19.             data.readBytes(revsion,0,1); 
  20.             data.readBytes(flags,0,1); 
  21.  
  22.             total_size = (data.readByte() & 0x7F) * 0x200000 + (data.readByte() & 0x7F) * 0x400 
  23.                         + (data.readByte() & 0x7F) * 0x80 + (data.readByte() & 0x7F); 
  24.              
  25.         } 
  26.          
  27.          
  28.         //返回整个标签体的大小 
  29.         public function getTotal_size():int{ 
  30.             return total_size; 
  31.         } 
  32.  
  33.     } 
  34.  
帧头   ID3V2Frameheader.as
As3.0代码 复制代码 收藏代码
  1. package service 
  2.     import flash.utils.ByteArray; 
  3.  
  4.     public class ID3V2Frameheader 
  5.     { 
  6.  
  7.         private var frameId:ByteArray = new ByteArray;//数据帧标示       4 
  8.         private var frameSize:int;//数据帧体大小   4 
  9.         private var flags:ByteArray =  new ByteArray;//0        2 
  10.          
  11.         public function ID3V2Frameheader(data:ByteArray) 
  12.         { 
  13.             // constructor code 
  14.             data.readBytes(frameId,0,4); 
  15.             frameSize = data.readByte()*0x1000000 
  16.              +data.readByte()*0x10000 
  17.              +data.readByte()*0x100 
  18.              +data.readByte(); 
  19.             data.readBytes(flags,0,2); 
  20.              
  21.         } 
  22.          
  23.         //返回数据帧表示 
  24.         public function getFrameId():String{ 
  25.             return this.frameId.toString(); 
  26.         } 
  27.          
  28.         //返回这一帧的总共数据大小 
  29.         public function getFrameSize():int{ 
  30.             return this.frameSize; 
  31.         } 
  32.          
  33.  
  34.     } 
  35.  

在这里简单说下mp3文件的首部,

1.首先是一个标签头(10个字节), 里面有一个重要的信息是可以通过计算得出标签体的大小

2.然后就是标签体,其中是出若干的帧组成,帧又是由帧头(10个字节),帧体组成(大小不定)

3.帧头中主要有帧标识(APIC,TIT2等等),帧体的大小(可计算大小)

 

本文转至: http://erziwuyulunbi.iteye.com/blog/1627993

展开阅读全文

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