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

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

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

2. 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. {
  3. //import省略
  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;
package service
{//import省略public class SoundPlayer{    private var channel:SoundChannel;//声音通道private var sound:Sound;//声音private var fileUrl:String;private var loader:Loader = new Loader  ;private var bitmap:Bitmap;
Actionscript3.0代码
  1. public function SoundPlayer(){}
  2. //播放音乐的方法
  3. public function play(url:String):void
  4. {
  5. this.fileUrl = url;
  6. sound = new Sound  ;
  7. sound.load(new URLRequest(url));
  8. channel = sound.play();
  9. sound.addEventListener(Event.COMPLETE,soundLoaderComplete);
  10. }
  11. //soundLoaderComplete 歌曲加载成功 得到专辑图片
  12. private function soundLoaderComplete(event:Event):void
  13. {
  14. (event.target as Sound).removeEventListener(Event.COMPLETE,soundLoaderComplete);
  15. var fileStream:FileStream = new FileStream  ;
  16. fileStream.open(new File(this.fileUrl),FileMode.READ);
  17. //设置文件读取的位置;
  18. var buffer:ByteArray = new ByteArray  ;
  19. fileStream.readBytes(buffer,0,10);
  20. var id3Header:ID3V2Header = new ID3V2Header(buffer);
  21. //去除标签头
  22. while (true)
  23. {   //读取帧头
  24. var frameHeaderBuffer:ByteArray = new ByteArray  ;
  25. fileStream.readBytes(frameHeaderBuffer,0,10);
  26. var id3FrameHeader:ID3V2Frameheader = new ID3V2Frameheader(frameHeaderBuffer);
  27. if (id3FrameHeader.getFrameSize() <= 0)
  28. {
  29. //没有专辑图片
  30. //删除原来的图片
  31. loader.unload();
  32. loader.load(new URLRequest("../images/app.png"));
  33. loader.contentLoaderInfo.addEventListener(Event.COMPLETE,imageLoaderComplete);
  34. break;
  35. }
  36. //读取帧体
  37. var frameBodyBuffer:ByteArray = new ByteArray  ;
  38. fileStream.readBytes(frameBodyBuffer,0,id3FrameHeader.getFrameSize());
  39. //符标识;
  40. var key:String = id3FrameHeader.getFrameId();
      public function SoundPlayer(){}//播放音乐的方法public function play(url:String):void{this.fileUrl = url;sound = new Sound  ;sound.load(new URLRequest(url));channel = sound.play();sound.addEventListener(Event.COMPLETE,soundLoaderComplete);}//soundLoaderComplete 歌曲加载成功 得到专辑图片private function soundLoaderComplete(event:Event):void{(event.target as Sound).removeEventListener(Event.COMPLETE,soundLoaderComplete);var fileStream:FileStream = new FileStream  ;fileStream.open(new File(this.fileUrl),FileMode.READ);//设置文件读取的位置;var buffer:ByteArray = new ByteArray  ;fileStream.readBytes(buffer,0,10);var id3Header:ID3V2Header = new ID3V2Header(buffer);//去除标签头while (true){    //读取帧头var frameHeaderBuffer:ByteArray = new ByteArray  ;fileStream.readBytes(frameHeaderBuffer,0,10);var id3FrameHeader:ID3V2Frameheader = new ID3V2Frameheader(frameHeaderBuffer);if (id3FrameHeader.getFrameSize() <= 0){//没有专辑图片//删除原来的图片loader.unload();loader.load(new URLRequest("../images/app.png"));loader.contentLoaderInfo.addEventListener(Event.COMPLETE,imageLoaderComplete);break;}//读取帧体var frameBodyBuffer:ByteArray = new ByteArray  ;fileStream.readBytes(frameBodyBuffer,0,id3FrameHeader.getFrameSize());//符标识;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. break;
  14. }
  15. }
  16. fileStream.close();
  17. }
  18. //专辑图片加载完成
  19. private function imageLoaderComplete(event:Event):void
  20. {
  21. loader.contentLoaderInfo.removeEventListener(Event.COMPLETE,imageLoaderComplete);
              //专辑图片的符标识是“APIC”if (key == "APIC"){//专辑图片var imageBytes:ByteArray = new ByteArray  ;//前14个字节无用   去除掉frameBodyBuffer.position = 14;frameBodyBuffer.readBytes(imageBytes,0,frameBodyBuffer.length - 14);//删除原来的图片;loader.unload();loader.loadBytes(imageBytes);loader.contentLoaderInfo.addEventListener(Event.COMPLETE,imageLoaderComplete);break;}}fileStream.close();}//专辑图片加载完成private function imageLoaderComplete(event:Event):void{loader.contentLoaderInfo.removeEventListener(Event.COMPLETE,imageLoaderComplete);                                                                                     
Actionscript3.0代码
  1. this.bitmap = loader.content as Bitmap;
  2. }
  3. }
         this.bitmap = loader.content as Bitmap;}}}
标签头
ID3V2Header.as
As3.0代码
  1. package service
  2. {
  3. import flash.utils.ByteArray;
  4. public class ID3V2Header
  5. {
  6. private var identi:ByteArray = new ByteArray;// ID3标示 3
  7. private var major:ByteArray = new ByteArray;// 版本号 3 1
  8. private var revsion:ByteArray = new ByteArray;// 0 1
  9. private var flags:ByteArray = new ByteArray;// 1
  10. private var total_size:int;// 标签体大小 4
  11. public function ID3V2Header(data:ByteArray)
  12. {
  13. // constructor code
  14. data.readBytes(identi,0,3);
  15. data.readBytes(major,0,1);
  16. data.readBytes(revsion,0,1);
  17. data.readBytes(flags,0,1);
  18. total_size = (data.readByte() & 0x7F) * 0x200000 + (data.readByte() & 0x7F) * 0x400
  19. + (data.readByte() & 0x7F) * 0x80 + (data.readByte() & 0x7F);
  20. }
  21. //返回整个标签体的大小
  22. public function getTotal_size():int{
  23. return total_size;
  24. }
  25. }
  26. }
package service
{import flash.utils.ByteArray;public class ID3V2Header{private var identi:ByteArray = new ByteArray;// ID3标示 3private var major:ByteArray = new ByteArray;// 版本号 3 1private var revsion:ByteArray = new ByteArray;// 0 1private var flags:ByteArray = new ByteArray;// 1private var total_size:int;// 标签体大小 4public function ID3V2Header(data:ByteArray){// constructor codedata.readBytes(identi,0,3);data.readBytes(major,0,1);data.readBytes(revsion,0,1);data.readBytes(flags,0,1);total_size = (data.readByte() & 0x7F) * 0x200000 + (data.readByte() & 0x7F) * 0x400+ (data.readByte() & 0x7F) * 0x80 + (data.readByte() & 0x7F);}//返回整个标签体的大小public function getTotal_size():int{return total_size;}}}
帧头   ID3V2Frameheader.as
As3.0代码
  1. package service
  2. {
  3. import flash.utils.ByteArray;
  4. public class ID3V2Frameheader
  5. {
  6. private var frameId:ByteArray = new ByteArray;//数据帧标示       4
  7. private var frameSize:int;//数据帧体大小   4
  8. private var flags:ByteArray =  new ByteArray;//0        2
  9. public function ID3V2Frameheader(data:ByteArray)
  10. {
  11. // constructor code
  12. data.readBytes(frameId,0,4);
  13. frameSize = data.readByte()*0x1000000
  14. +data.readByte()*0x10000
  15. +data.readByte()*0x100
  16. +data.readByte();
  17. data.readBytes(flags,0,2);
  18. }
  19. //返回数据帧表示
  20. public function getFrameId():String{
  21. return this.frameId.toString();
  22. }
  23. //返回这一帧的总共数据大小
  24. public function getFrameSize():int{
  25. return this.frameSize;
  26. }
  27. }
  28. }
package service
{import flash.utils.ByteArray;public class ID3V2Frameheader{private var frameId:ByteArray = new ByteArray;//数据帧标示       4private var frameSize:int;//数据帧体大小   4private var flags:ByteArray =  new ByteArray;//0        2public function ID3V2Frameheader(data:ByteArray){// constructor codedata.readBytes(frameId,0,4);frameSize = data.readByte()*0x1000000+data.readByte()*0x10000+data.readByte()*0x100+data.readByte();data.readBytes(flags,0,2);}//返回数据帧表示public function getFrameId():String{return this.frameId.toString();}//返回这一帧的总共数据大小public function getFrameSize():int{return this.frameSize;}}}

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

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

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

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

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

解析mp3文件,获得mp3中的专辑图片相关推荐

  1. 从MP3中读取专辑图片

    好吧,最近帮朋友写毕设时碰到这个的问题的,在网上也是多番查询,都不尽人意,于是自己参考多番之后,写了一个 其实吧,这个读取专辑图片也不是很难得,首先判断MP3文件中是否含有ID3V2的标签,关于ID3 ...

  2. java mp3 信息_android,java获取MP3文件信息(作者,专辑等)

    接着上一篇文章, 上一篇把ndk开发遍历手机文件分享了一下.遍历出来的MP3文件,我需要展示在android的listview里面,想展示的好看一点,结果发现又是一个坑. 根据mp3文件,获取作者啊专 ...

  3. 医用设备心跳心率检测mp3文件输出(mp3文件处理)(二)

    本系统是通过一套国外先进的医用设备将用户心跳心率存储到 mp3文件中, 该软件系统由于日本以非常高的价格出售, 公司决定由我破译硬件数据,开发一套替换日本软件产品.  该项目共花费2个月时间完成.   ...

  4. Java解析PDF文件(PDFBOX、itext解析PDF)导出PDF中的子图片,去除PDF中的水印

    前段时间,为了解析PDF,花了不少时间去学习PDFbox和itext,这两个都是处理PDF的开源库,有java和C#的.作为一个刚开始学习这两个开源库的,感觉百度上的资源还是太少了.我做的是一个关于P ...

  5. android 完美获取音乐文件中的专辑图片并显示

    其实实现音乐中的专辑图像提取是相当的容易的,网上大多的资料都是比较扯淡的,其实就几行代码而已. 看下面代码之前,我们所要做的工作是通过mediaplayer已经获取到了音乐文件中的诸多信息: Medi ...

  6. 获取mp3文件的封面,所属专辑以及以及其他基础信息

    使用的工具是ID3 Reader,有兴趣的朋友可以访问以下网址: 官方demo:http://web.ist.utl.pt/antonio.afonso/www.aadsm.net/libraries ...

  7. java获取专辑封面_从MP3中读取专辑封面图片

    首先判断MP3文件中是否含有ID3V2的标签,关于ID3V2的格式有一堆的说法 我嘛,不怎么关心,因此只攻专辑图片,也就是判断是否包含APIC这个标识 找到这个标识其实也就是和解析普通文件一样,每个像 ...

  8. 关于MP3文件中找不到TAG标签的问题

    今天做项目需要上传mp3文件,在postman中测试的时候发现找不到这个mp3文件中的TAG标签 心想着不应该啊,我这是正规音频啊,如果是图片把后缀改成mp3没有TAG是肯定的,但是我这个音频为什么也 ...

  9. 四、Mp3文件类型及其判断

    根据前两篇文章的分析,帧分为标签帧和数据帧,MP3文件类型是根据数据帧的类型来分的,文件类型如下表: 位率相等(Constant BitRate) CBR  Mp3文件 位率不等(Variable B ...

最新文章

  1. 如何使dropship第三方销售是基于发货数量,而不是基于LIV发票校验的数量
  2. 罗辑思维在全链路压测方面的实践和工作笔记
  3. 在vi里面实现字符串的批量替换
  4. swift operation
  5. OOAD实践之路——真实案例解析OO理论与实践(二、第一项任务:特性列表)
  6. 工程师如何解决穿衣搭配烦恼?——滴搭平台与算法 1
  7. .Net水晶报表的使用总结
  8. Python将浏览器cookies共享给requests库
  9. linux定时器的实现方法
  10. JS变量特点及分号用法
  11. 数据库中delete和drop的区别
  12. Spark on Hive Hive on Spark傻傻分不清?
  13. linux中如何压缩目录文件,如何在Linux中压缩和解压缩目录及其文件
  14. OMV搭建系列教程[6] – 安装AriaNG Web前端
  15. 我的Serverless实战—基于Serverless搭建一个简单的WordPress个人博客图文详解-JJZ
  16. win10设置默认中英文符号
  17. linux的s权限和t权限
  18. H5C3进阶——播放器
  19. 360n4s高通版卡刷包
  20. DNS知识点及服务搭建案例和解析状态异常

热门文章

  1. I2C、Arduino、ADXL345、
  2. tableFooterView中的按钮点击没反应
  3. cdd 变分 图像修复 matlab,图像修复中的TV模型
  4. 企业公众号怎么做内容?这四个阶段要做好
  5. 求解斐波那契数列(Fibonacci Numbers)算法居然有9种,你知道哪几种吗?
  6. realsense系列(三):播放深度图和RGB图
  7. Spring 中的Advice类型介绍
  8. OpenStack-Ironic裸金属简介
  9. 福昕阅读器文档无法高亮的问题
  10. DVB-S2 DVB-S2X DVB-DSNG 发射机 接收机FPGA IP