所有的分析都基于ID3官方网站www.id3.org.同时所有的代码都在我的github中,我将它们用闭包集成在了一起,可以直接使用,非常方便,地址:https://github.com/jslinuxboy/ID3MusicDealer

MP3ID3v2.3版本文件基本分为两部分,一个为标签,其它的为数据。

文件标签分为标签头标签帧

11.标签头

而标签头有十个字节,即在文件最开始的1010个字节,它的数据结构如下:

char Header[3];//这个字符串一定为"ID3"char version;//版本号,而针对在下要讲的版本,理应为3.即为ID3v2.3char revision;//副版本号,好像一直都是0,没看到过它有变过char flags;//一些特殊的消息标记,只会使用此字节的高3位,其它的五位并没有什么卵用char size[4];//代表整个标签帧的大小,但是不包括这开始的10个字节,所以这里得到的size需要加上10才代表整个标签帧的大小

这里加起来总共1010个字节

然而我想说的是,这些东西都是些大佬定的,一种文件格式从来不是用代码来定义的,id3.org官网定义非常简单,感觉也比较明了,如下:

ID3v2/file identifier   "ID3"
ID3v2 version           $03 00
ID3v2 flags             %abc00000
ID3v2 size              4 * %0xxxxxxx

再缩减一下

$49 44 33 yy yy xx zz zz zz zz

49 44 33就是ID3,然后yy yy,这是真正的版本,虽然上面大佬们将version分为了主的版本和副的版本,xx则是标记,然后最后四个zz则是大小了

ID3v2 flags中的%abc00000\%abc00000,其中高三位表示如下:

a - Unsynchronisation
表示是否同步(自己乱翻译的),这个搞不清是什么鬼,个人英语不是很行,大概是为了数据帧同步帧数据,校正数据用的
b - Extended header
表示是否有扩展头部,这个扩展头部是用来补充标签信息的,原文如下:
The extended header contains information that is not vital to the correct parsing of the tag information, hence the extended header is optional. c - Experimental indicator
表示是否为试验测试,这个东西是什么鬼也不知道,没见过MP3音乐文件这个位进行了设置

ID3v2 size中的4∗%0xxxxxxx4 * \%0xxxxxxx表示的是44个字节,后面的%0xxxxxxx\%0xxxxxxx就是一个字节88位了。

然后计算标签帧的大小,ID3规定这四个字节中每个字节的最高位恒为00不使用,格式如下:

0xxxxxxx 0xxxxxxx 0xxxxxxx 0xxxxxxx

JS代码如下:

var size = size4 & 0x7f | ((size3 & 0x7f) << 7) | ((size2 & 0x7f) << 14) | ((size1 & 0x7f) << 21);

注意:这里的帧大小,并不包含帧头的1010个字节,只表示帧内容的大小

这里再说一个特殊消息标记的Extended header处理,当Extended header这个标记位设置为11时,在这最开始的1010个字节后面会增加有Extended header的内容,此处进行修改,按照原文的意思,这部分的大小是算在最开始10个字节的size中的,原文如下:

The ID3v2 tag size is the size of the complete tag after unsychronisation, including padding, excluding the header but not excluding the extended header (total tag size - 10). Only 28 bits (representing up to 256MB) are used in the size description to avoid the introducuction of ‘false syncsignals’.

然后这个Extended header信息内容格式如下:

Extended header size   $xx xx xx xx
Extended Flags         $xx xx
Size of padding        $xx xx xx xx
...

Extended header size有四个字节,表示接下来的数据占用多少个字节。

Extended Flags 这两个字节不知道干什么

接下来就都是扩展头部的数据了(我猜Extended Flags这两个字节好像没有,扩展头部本来就没有多大用,一般直接就滤掉了)

这里是JS代码实现标签头的识别:

getByteAt(iOffset);//得到iOffset位置的一个字节数据isBitSetAt(iOffset, iBit);//判断iOffset位置的字节的iBit位是1还是0readSynchsafeInteger32At(data, iOffset);//这是处理标签头的sizegetLongAt(iOffset, bBigEndian);//得到iOffset位置的Long数据,bBigEndian表示是低端还是高端/****************/var offset = 0,major = data.getByteAt(offset + 3),revision = data.getByteAt(offset + 4),unsynch = data.isBitSetAt(offset + 5, 7),xheader = data.isBitSetAt(offset + 5, 6),xtest = data.isBitSetAt(offset + 5, 5),size = this.readSynchsafeInteger32At(data, offset + 6);offset += 10;
if (xheader) {var xheadersize = data.getLongAt(offset, true);offset += xheadersize + 4;
}var id3 = {"version": '2.' + major + '.' + revision,"major": major,"flags": {"unsynchronisation": unsynch,"extended_header": xheader,"experimental_indicator": xtest},"size": size
};

文件分析图片:


22.标签帧内容

帧头的定义

char ID[4];//用四个字符标识一个帧,表明这个帧的内容是什么char size[4];//帧内容的大小,不包括帧头char flags[2];//特殊的消息标记

官方定义

Frame ID       $xx xx xx xx (four characters)
Size           $xx xx xx xx
Flags          $xx xx
  • 帧标识:The frame ID made out of the characters capital A-Z and
    0-9.FrameID会是一串由A-Z和0-9的字符串组成,占用44个字节

    • 帧大小:The frame ID is followed by a size descriptor, making a total header size of ten bytes in every frame. The size is calculated as frame size excluding frame header

    • 帧标记:最后这个flags跟前面说的都一样为特殊标记

    • 常见有用的帧标识

      1. TIT2:歌曲标题名字
      2. TPE1:作者名字
      3. TALB:作品专辑
      4. TYER:作品产生年代
      5. COMM:备注信息
      6. APIC:专辑图片

      帧标记说明

      只定义了66位,另外的1010位为00,一般这些标记也不用,通常为00,格式如下:

      flags %abc00000 ijk00000 a -- 标签保护标志,设置时认为此帧作废b -- 文件保护标志,设置时认为此帧作废c -- 只读标志,设置时认为此帧不能修改(但我没有找到一个软件理会这个标志) i -- 压缩标志,设置时一个字节存放两个BCD 码表示数字 j -- 加密标志(没有见过哪个MP3 文件的标签用了加密) k -- 组标志,设置时说明此帧和其他的某帧是一组

      帧标识这一块有太多,各种各样的,到官方网站去看,这里会主要区分三种主要的标识信息(其他的都拜拜吧,通过看官方网站的信息你就知道为什么拜拜了)。

      11.T*,即以TT开头的帧标识,为文本标识,分为三部分

      第一部分为11个字节,这个字节一定是[0x00,0x01,0x02,0x03][0x00,0x01,0x02,0x03]中的一种,0x000x00代表这个标签帧后续的数据为iso-8859-1编码,0x010x01则是utf-16编码,0x020x02则是utf-16be编码,0x030x03则是utf-8编码

      第二部分根据编码确定是否存在
      如果为0x000x00编码的话就不会存在,字节就是直接读取

      如果为0x010x01和0x020x02那么这里会占用22个字节,会出现两种可能的数据,一种为FF FE表示小端,即数据存储是高数据在高位,一种为FE FF表示大端与小端相反

      如果为0x030x03编码则是会占用三个字节 EF BB BF

      第三部分就是数据

      这下面的官方的描述:

      <Header for 'User defined text information frame', ID: "TXXX">
      Text encoding    $xx
      Description    <text string according to encoding> $00 (00)
      Value    <text string according to encoding>

      虽然Text encoding就是一个字节,但是Description的字节数就是不确定的,最后就是数据内容(个人感觉这里好像少了些东西,感觉Description不是第二部分,也就是说虽然官方上有这个属性描述,但是实际上音乐文件中根本没有这玩意,有的只是确定编码格式的数据,也就是第二部分)

      JS代码讲解如下:

      ID3v2.readFrameData['T*'] = function(offset, length, data) {var charset = ID3v2.getTextEncoding(data.getByteAt(offset));return data.getStringWidthCharsetAt(offset + 1, length - 1, charset).toString();
      };

      其中getTextEncodinggetStringWidthCharsetAt是个人进行集成了的函数,会对上述的数据进行相应的处理(github中有源代码)

      22.APIC,专辑图片,好像整个MP3的数据就只有这个标识有图片

      这里直接以官方说明来讲解比较好:

      <Header for 'Attached picture', ID: "APIC">
      Text encoding   $xx
      MIME type       <text string> $00
      Picture type    $xx
      Description     <text string according to encoding> $00 (00)
      Picture data    <binary data>

      第一个为数据编码,和以T开头的一样,分为四种0x00,0x01,0x02,0x030x00,0x01,0x02,0x03

      第二个为MIME type数据了,表示的是什么类型图片,有image/jpeg,image/png…等,
      这里的字节数不确定,是用0x000x00作为字符串的结束标志,来停止读取的,也就是说MIME type数据需要一直读取,知道读取到了0x000x00也就是我们常见的字符串结束标志\0.

      第三个为Picture type,表示的是图片代表什么,是作者还是一些什么内容。

      第四个为Description,就是简单的图片描述了,这里和MIME type数据一样,是以\0为结束的,这里多说一句的是,这个属性好像也不经常用,它的值经常为”“

      第五部分就是图片数据了,记住这不是base64编码的数据。

      Picture type的值扩充说明(就是这些值表示这张图片的大概内容)

      $00     Other
      $01     32x32 pixels 'file icon' (PNG only)
      $02     Other file icon
      $03     Cover (front)
      $04     Cover (back)
      $05     Leaflet page
      $06     Media (e.g. lable side of CD)
      $07     Lead artist/lead performer/soloist
      $08     Artist/performer
      $09     Conductor
      $0A     Band/Orchestra
      $0B     Composer
      $0C     Lyricist/text writer
      $0D     Recording Location
      $0E     During recording
      $0F     During performance
      $10     Movie/video screen capture
      $11     A bright coloured fish
      $12     Illustration
      $13     Band/artist logotype
      $14     Publisher/Studio logotype

      JS代码:

      ID3v2.readFrameData['APIC'] = function(offset, length, data, v) {v = v || '3';//这里是为了以后扩展用的var start = offset,charset = ID3v2.getTextEncoding(data.getByteAt(offset));switch (v) {case '2':break;case '3':var format = data.getStringWidthCharsetAt(offset + 1, length, '');offset += 1 + format.bytesReadCount;break;}var bite = data.getByteAt(offset),type = ID3v2.pictureType[bite],desc = data.getStringWidthCharsetAt(offset + 1, length - (offset - start), charset);offset += 1 + desc.bytesReadCount;return {"format": format.toString(),"type": type,"description": desc.toString(),"data": data.getBytesAt(offset, (start + length) - offset)};
      };

      这些代码分别对应该类型数据处理

      33.COMM,备注消息,这个玩意一直在飞,全程都是懵逼的,这个属性感觉并没有什么卵用

      这里还是官方说明来讲解比较好

      <Header for 'Comment', ID: "COMM">
      Text encoding           $xx
      Language                $xx xx xx
      Short content descrip.  <text string according to encoding> $00 (00)
      The actual text         <full text string according to encoding>

      第一个不想说了,跟前面的一模一样;

      第二个表示接下来是什么语言,就是说是中文还是英文还是其它语言,一般是英文就是eng

      然后就是短描叙了,这里的字节数也是不确定的,也就是说这里是以\0为结尾的数据,需要不断读取直到\0结束

      接下来就是最终的数据了

      JS代码:

      ID3v2.readFrameData['COMM'] = function(offset, length, data) {var start = offset,charset = ID3v2.getTextEncoding(data.getByteAt(offset)),language = data.getStringAt(offset + 1, 3),shortdesc = data.getStringWidthCharsetAt(offset + 4, length - 4, charset);offset += 4 + shortdesc.bytesReadCount;var text = data.getStringWidthCharsetAt(offset, (start + length) - offset, charset);return {language: language,short_description: shortdesc.toString(),text: text.toString()};
      };

      自己对着看吧

      到这里三种类型就分析完了

      到此基本就可以了,标签分析完成,个人不进行歌曲的数据帧分析。


      这里进行编码数据扩充


      11.utf-16,即为UCS-2,这种编码会出现两种形式,一个为22字节也就是一个字,一个为四字节也就是两个字,
      当第一个字节小于0xD80xD8或者大于0xDF0xDF,则是第一种情况,否则就是第二种,其中0xDB−0xDF0xDB-0xDF为代理区(http://blog.csdn.net/wm_1991/article/details/52230716这个地址可以了解更多)
      当然在这个音乐文件中有小端和大端区分,所以我们经常会看到如下编码选项 UCS-2 Big Endian(大端),UCS-2 Little Endian(小端)

      22.utf-8,这个编码可以说是最操蛋的,网上的解释也参差不齐,俺也懒得去看官网了,这里讲解的只是最常用的,大众的。
      这个编码分为三种,一个为11字节(这里很明显是用一个字节来表示英文字母),然后就是22字节,接着就是33字节
      区分:

      • 第一字节小于0x800x80则为11个字节

      • 第一字节大于等于0xC20xC2小于0xE00xE0则是22字节

      • 第一字节大于等于0xE00xE0小于0xF00xF0则是33字节

      33.其他的编码就一股脑的读取一个字节就可以了

      JS代码如下:

      var stringUtils = {readUTF16String: function(bytes, bBigEndian) {//utf-16var ix = 0,offset1 = 1,offset2 = 0,bytesLength = bytes.length,bigEndian = bBigEndian;if (bytes[0] == 0xFE && bytes[1] == 0xFF) {bigEndian = true;ix = 2;} else if (bytes[0] == 0xFF && bytes[1] == 0xFE) {bigEndian = false;ix = 2;}if (bigEndian) {offset1 = 0;offset2 = 1;}var arr = [];while (ix < bytesLength) {var byte1 = bytes[ix + offset1],byte2 = bytes[ix + offset2],word1 = (byte1 << 8) + byte2;ix += 2;if (word1 == 0x0000) {break;} else if (byte1 < 0xD8 || byte1 > 0xDF) {arr.push(String.fromCharCode(word1));} else {var byte3 = bytes[ix + offset1],byte4 = bytes[ix + offset2],word2 = (byte3 << 8) + byte4;ix += 2;arr.push(String.fromCharCode(word1, word2));}}var string = new String(arr.join(""));string.bytesReadCount = ix;return string;},readUTF8String: function(bytes) {//utf-8var ix = 0,bytesLength = bytes.length;if (bytes[0] == 0xEF && bytes[1] == 0XBB && bytes[2] == 0xBF) {ix = 3;}var arr = [];while (ix < bytesLength) {var byte1 = bytes[ix++];if (byte1 == 0x00) {break;} else if (byte1 < 0x80) {arr.push(String.fromCharCode(byte1));} else if (byte1 >= 0xC2 && byte1 < 0xE0) {var byte2 = bytes[ix++];arr.push(String.fromCharCode(((byte1 & 0x1F) << 6) + (byte2 & 0x3F)));} else if (byte1 >= 0xE0 && byte1 < 0xF0) {var byte2 = bytes[ix++],byte3 = bytes[ix++];arr.push(String.fromCharCode(((byte1 & 0xFF) << 12) + ((byte2 & 0x3F) << 6) + (byte3 & 0x3F)));}}var string = new String(arr.join(""));string.bytesReadCount = ix;return string;},readNullTerminatedString: function(bytes) {var arr = [],bytesLength = bytes.length,i;for (i = 0; i < bytesLength;) {var byte1 = bytes[i++];if (byte1 == 0x00) break;arr.push(String.fromCharCode(byte1));}var string = new String(arr.join(""));string.bytesReadCount = i;return string;}
      };

      数据分析整体流程图

      这里多说一下个人写的代码中的对象ID3module

      StringUtils专门处理字符串的对象,比如通过UTF-16编码获取binaryFile专门对数据进行处理的对象,比如获得多少个字节b64Utils专门对数据进行base64编码解码的对象fileAPIReader专门生成文件二进制流的对象framesTable专门存储标签帧标识的对象_shortcuts用户可能需要的数据_defaultShortcuts默认对象可能需要的数据getFrameData将数据转换为数组的函数

MP3文件分析之ID3v2.3版本相关推荐

  1. 读取MP3文件内的ID3V2专辑图片并显示

    这里说的ID3V2一般指的2.3版本,MP3文件后128字节是ID3V1信息,ID3V1大小固定所以信息含量较少,由于项目需要,要读取专辑图片并显示,我找到ID3V2的格式说明,也不知准备不准确,但我 ...

  2. C语言读取mp3文件的信息

    MP3文件的ID3V1信息与ID3V2信息结构的分析 --吴俊涛2005/05/05 E-mail:bo_tao@126.com QQ:29248671 主 页:http://wjt276.home4 ...

  3. kjb文件 解析_批量修改MP3文件信息

    前两天本人在整理自己的歌单时(题外话,本人是一个热衷音乐的程序员噢,哈哈),发现之前下载的很多MP3文件里的歌手,标题等信息对不上,很多都是错的.对于患有"强迫症"的本人来说,当然 ...

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

    这仍然也是操作的是字节,所以其他语言都是适用的. mp3文件的首尾都存有文件的信息,存在尾部的称为ID3V1,在首部的称为 ID3V2.专辑图片是存放在部的ID3V2.所以这里解析ID3v2 主要的知 ...

  5. c++读取MP3文件专辑图片、歌手名等ID3v2信息

    之前用javascript做了一个本地音乐播放器(javascript不依赖库开发windows应用程序_sdxjwkq01的博客-CSDN博客_js开发windows应用程序),最近想加个显示MP3 ...

  6. 【Android 逆向】Android 系统文件分析 ( /proc/ 目录文件分析 | 记录系统和进程信息 | version 内核版本信息文件 )

    文章目录 一.proc 目录简介 二.version 内核版本信息文件 一.proc 目录简介 进入 /proc/ 目录 , 查看该目录下的文件 , 该目录中的文件的作用 , 主要是记录系统和进程的信 ...

  7. Android直播开发之旅(4):MP3编码格式分析与lame库编译封装

    转载请声明出处:http://blog.csdn.net/andrexpert/article/77683776 一.Mp3编码格式分析 MP3,全称MPEG Audio Layer3,是一种高效的计 ...

  8. ffmpeg开发之旅(4):MP3编码格式分析与lame库编译封装

    ffmpeg开发之旅(4):MP3编码格式分析与lame库编译封装 原文链接:http://blog.csdn.net/andrexpert/article/77683776 一.Mp3编码格式分析 ...

  9. java获取MP3文件的id3信息(包括封面图,歌词,艺术家,标题等)

    MP3结构简单介绍 有时我们会发现MP3文件在电脑上可以显示出封面图,艺术家,标题,唱片集等等信息,这是因为MP3中有ID3标签信息. MP3文件大体上分为三个部分:ID3V2+音频数据+ID3V1 ...

最新文章

  1. 微信遇到特殊服务器,解决微信网页授权,出现errcode:40163,errmsg:codebeenused,看似微信访问了2次这个回调接口的问题...
  2. docker学习之-什么是docker
  3. Docker : Error response from daemon: Get https://docker.elastic.co/v2/: net/http: TLS handshake time
  4. 多线程日记(17.5.3)
  5. 怎么把图片上的字去掉_视频片头怎么减掉,电脑如何剪切掉视频的开头「视频批量剪辑」...
  6. 【面向对象】第四单元总结——UML
  7. 信号与系统sa函数求积分_胡昉祖《导数的概念》和《定积分》
  8. 算法学习之路|到底买不买
  9. 网页设计之 HTML代码的理解.
  10. 窝在二线城市很难受,要杀回一线城市重造吗?
  11. 【第三方支付通道】第三方支付接口如何对接?
  12. Android shape动态修改颜色
  13. u盘恢复软件?windows小工具不要错过!
  14. Shiro记住我无效,被拦截;
  15. iOS Programming - Disallow Alphabetic Characters
  16. [原]海纳百川 有容乃大:SparkR与Docker的机器学习实战
  17. Vuex仿饿了么购物车功能
  18. WPF实现炫酷Loading控件
  19. 自定义/修改微信二维码样式
  20. 欺骗的艺术——第二部分(1)

热门文章

  1. keychain介绍
  2. 天龙八部TLBB搭建(三)CentOS7x系列服务器手工环境架设配置
  3. 尼康d850相机参数测试软件,尼康(Nikon)D850 单机数码相机ISO感光度评测-ZOL中关村在线...
  4. 火焰特性识别的Matlab实现方法
  5. if...then...else逻辑
  6. Mac系统显示已连接(可以打开qq)但是无法打开浏览器解决方案
  7. [每周一读]——不让时间偷走你的成功
  8. 凉宫春日的忧郁第六章
  9. 北京大学计算机科学实验班,北京大学信息科学技术学院2018年拟接收推荐-北京大学研究生院.pdf...
  10. 上海计算机考试分值,上海高考科目及分值