带你写一个Mp文件解析器-Mp3文件结构全解析(一)

简介: ID3V2一共有四个版本,ID3V2.1/2.2/2.3/2.4,目前流行的播放软件一般只支持第三版即ID3V2.3,由于ID3V1记录在文件的末尾处,ID3V2就只能记录在文件的首部了,也是因为这个原因,对ID3V2的操作比ID3V1要慢,而且ID3V2的结构比ID3V1的结构复杂的多,但是ID3V2可以记录更多的信息,长度可变

Mp3文件结构全解析


MP3 文件是由帧(frame)构成的,帧是MP3 文件最小的组成单位。MP3的全称应为MPEG1 Layer-3 音频 文件,MPEG(Moving Picture Experts Group) 在汉语中译为活动图像专家组,特指活动影音压缩标准,MPEG 音频文件是MPEG1 标准中的声音部分,也叫MPEG 音频层,它根据压缩质量和编码复杂程度划分为三层,即Layer-1、Layer2、Layer3, 且分别对应MP1、MP2、MP3 这三种声音文件,并根据不同的用途,使用不同层次的编码。MPEG 音频编码的层次越高,编码器越复杂,压缩率也越高,MP1 和MP2 的压缩率分别为4:1 和6:1-8:1,而MP3 的压缩率则高达10:1-12:1,也就是说,一分钟CD 音质的音乐,未经压缩需要10MB的存储空间,而经过MP3 压缩编码后只有1MB 左右。不过MP3 对音频信号采用的是有损压缩方式,为了降低声音失真度,MP3采取了“感官编码技术”,即编码时先对音频文件进行频谱分析,然后用过滤器滤掉噪音电平,接着通过量化的方式将剩下的每一位打散排列,最后形成具有较高压缩比的MP3 文件,并使压缩后的文件在回放时能够达到比较接近原音源的声音效果。


用到资源: 胡广生.mp3: pan.baidu.com/s/1OYOPg0Ph… 提取码: 6yqv


整体结构


MP3 文件大体分为四部分:TAG_V2(ID3V2),Frame, TAG_V1(ID3V1),其他说明信息


  • ID3V2 包含了作者,作曲,专辑等信息,长度不固定,扩展了ID3V1 的信息量。
  • Frame 一系列的帧,个数由文件大小和帧长决定,每个FRAME 的长度可能不固定,也可能固定,由位率bitrate 决定, 每个FRAME 又分为帧头和数据实体两部分,帧头记录了mp3 的位率,采样率,版本等信息,每个帧之间相互独立
  • ID3V1 包含了作者,作曲,专辑等信息,长度为128BYTE。
  • 其他说明信息(不重要)


ID3V2解析


ID3V2一共有四个版本,ID3V2.1/2.2/2.3/2.4,目前流行的播放软件一般只支持第三版即ID3V2.3,由于ID3V1记录在文件的末尾处,ID3V2就只能记录在文件的首部了,也是因为这个原因,对ID3V2的操作比ID3V1要慢,而且ID3V2的结构比ID3V1的结构复杂的多,但是ID3V2可以记录更多的信息,长度可变


ID3V2.3由一个标签头和若干个标签帧或者一个扩展标签头组成,至少要有一个标签帧,每一个标签帧记录一种信息,例如作曲、标题等


标签头


位于文件开始处,长度为10字节,结构如下:


  • char Header[3]; /*必须为“ID3”否则认为标签不存在*/
  • char Ver; /*版本号ID3V2.3 就记录3*/
  • char Revision; /*副版本号此版本记录为0*/
  • char Flag; /*标志字节,只使用高三位,其它位为0 */
  • char Size[4]; /*标签大小*/


注:标签大小,不能确定具体包括哪些内容,解析歌曲文件后,发现没有哪些字节之和会等于该值,详见下面的实例分析


标志字节一般为0,定义如下(abc000000B)


  • a:表示是否使用Unsynchronisation
  • b:表示是否有扩展头部,一般没有,所以一般也不设置
  • c:表示是否为测试标签,99.99%的标签都不是测试标签,不设置


标签大小共四个字节,每个字节只使用低7位,最高位不使用恒为0,计算时将最高位去掉,得到28bit的数据,计算公式如下: Size=(Size[0]&0x7F)*0x200000+(Size[1]&0x7F)*0x400+(Size[2]&0x7F)*0x80+(Size[3]&0x7F)


以《胡广生.mp3》为例,使用UltraEdit工具打开如下,读者可自己对照上述结构,本章结束会给出详细的结构分析


image.png


标签帧


每个标签帧都有10个字节的帧头(和标签头不是一个东西,虽然他们刚好都是10字节,标签头只有一个,每个标签帧都有一个帧头)和至少一个字节的内容构成,标签帧与标签头/其他标签帧无特殊字节分割,只能通过帧头信息来确定帧内容的大小。


帧头长度10字节,定义如下:


  • char ID[4]; /*标识帧,说明其内容,例如作者/标题等*/
  • char Size[4]; /*帧内容的大小,不包括帧头,不得小于1*/
  • char Flags[2]; /*标志帧,只定义了6 位*/


标识帧,常见的内容如下:


  • TIT2=标题
  • TPE1=作者
  • TALB=专集
  • TRCK=音轨格式:N/M 其中N为专集中的第N首,M为专集中共M首,N和M    为ASCII 码表示的数字
  • TYER=年代是用ASCII 码表示的数字
  • TCON=类型直接用字符串表示
  • COMM=备注格式:"eng\0备注内容",其中eng 表示备注所使用的自然语言


帧内容大小,计算公式如下:


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


标志帧,使用每个字节的高三位,其他位均为0(abc00000B xyz00000B)


  • a -- 标签保护标志,设置时认为此帧作废
  • b -- 文件保护标志,设置时认为此帧作废
  • c -- 只读标志,设置时认为此帧不能修改
  • x -- 压缩标志,设置时一个字节存放两个BCD 码表示数字
  • y-- 加密标志
  • z-- 组标志,设置时说明此帧和其他的某帧是一组


image.png


实例分析


歌曲信息


<<胡广生.mp3>>


结构解析


标签头


image.png


标签头:10字节,00H-09H


1 2 3 4 5 6 7 8 9 10
49 44 33 04 00 00 00 03 10 27
I(73) D(68) 3(51) 4(V2.4) 0(revision) 0(flag) 0x00 0x03 0x10 0x27


(Size[0]&0x7F)*0x200000+(Size[1]&0x7F)*0x400+(Size[2]&0x7F)*0x80+(Size[3]&0x7F) = (0x00&0x7F)*0x200000 + (0x03&0x7f)*0x400 + (0x10&0x7f)*0x80 + (0x27&0x7f) = 0x00 + 0xc00 + 0x800 + 0x27 = 0x1427 = 5159个字节


TIT2标签帧


image.png


地址 字节 说明
AH-DH 4 54 49 54 32 TIT2标签帧说明
EH-11H 4 00 00 00 09 帧内容大小: 9个字节
12H-13H 2 00 00 标志,均为0
14H- 1CH 9 01 FF FE E1 80 7F 5E 1F 75 帧内容


TALB标签帧


image.png


地址 字节 说明
1dH-20H 4 54 41 4C 42 TALB标签帧说明
21H-24H 4 00 00 00 09 帧内容大小: 9个字节
25H-26H 2 00 00 标志,均为0
27H- 2FH 9 01 FF FE E1 80 7F 5E 1F 75 帧内容


TPE1标签帧


image.png


地址 字节 说明
30H-33H 4 54 50 45 31 TPE1标签帧说明
34H-37H 4 00 00 00 09 帧内容大小: 9个字节
38H-39H 2 00 00 标志,均为0
27H- 2FH 9 01 FF FE FB 4E 20 7D 50 6C 帧内容


APIC标签帧


This frame contains a picture directly related to the audio file. Image format is the MIME type and subtype for the image. In the event that the MIME media type name is omitted, "image/" will be implied. The "image/png" or "image/jpeg" picture format should be used when interoperability is wanted. Description is a short description of the picture, represented as a terminated textstring. The description has a maximum length of 64 characters, but may be empty. There may be several pictures attached to one file, each in their individual "APIC" frame, but only one with the same content descriptor. There may only be one picture with the picture type declared as picture type $01 and $02 respectively. There is the possibility to put only a link to the image file by using the 'MIME type' "-->" and having a complete URL instead of picture data. The use of linked files should however be used sparingly since there is the risk of separation of files.


<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>


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


image.png


省略


image.png


地址 字节 说明
43H-46H 4 41 50 49 43 APIC标签帧说明
47H-4AH 4 00 01 43 65 帧内容大小: 83789个字节
38H-39H 2 00 00 标志,均为0
27H- 2FH 9 00 69 ... 帧内容


帧内容大小:


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


=0x010x10000+0x430x100+0x65


=0x10000+0x4300+0x65


=0x14365=82789


82789 / 16 = 5174 = 0x1436 82789 % 16 = 6


0x1436 + 0x2 = 0x1438 3 + 6 = 9


实现一个MP3解析器之标签解析### 标签解析


根据上面分析,先要读取标签头,先读取十个字节的标签头,定义一个结构体:


struct tagheader {
  char ID[3];          // The first 4 bytes should be ID3
  char version[2];     // $03 00
  char flags;          // $abc00000 : a:unsynchronisation if set; b:extended header exist if set; c:experimental indicator if set
  char size[4];        // (total tag size - 10) excluding the tagheader;
};


读取十个字节:


struct tagheader header;
  size_t sz;
  if (read(fd, &header, sizeof(header)) < 0) {
    perror("Read File: ");
    exit(1);
  }


计算标签大小:


sz = (header.size[0] & 0x7F) * 0x200000 + (header.size[1] & 0x7F) * 0x400 + (header.size[2] & 0x7F) * 0x80 + (header.size[3] & 0x7F);


接下来遍历读取每一个标签帧,每个标签帧又有十个字节的标签帧头,同样定义一个结构体:


struct frameheader {
  char frameid[4];    // TIT2 MCDI TRCK ...
  char size[4];
  char flags[2];      // %abc00000  %ijk00000 | a 0:frame should be preserved 1:frame should be discard
};


接下来循环读取标签帧头:


struct frameheader header;
  int framesz = 0;
  int start = 0;
  while(start <= tagsize){
    if (read(fd, &header, sizeof(header)) < 0) {
      perror("Read File: ");
      return -1;
    }
    start += sizeof(header);
    if(start > tagsize){
      printf("has parsed all\n");
      return 1;
    }
    framesz = header.size[0]*0x100000000 + header.size[1]*0x10000 + header.size[2]*0x100 + header.size[3];
    start+=framesz;
    ...


计算到标签body大小后,读取标签大小内容,并且转换成可读内容:


...
    char* input = malloc(framesz);
    if(read(fd, input, framesz)< 0){
      printf("error");
      return -1;
    }
    if(strncmp(header.frameid, "APIC", 4) == 0){//attach picture是图片内容,不可以转为文字
      printf("The %s is:\t\t Attach Picture, size = %d\n", header.frameid, framesz);
      continue;
    }
    framesz -= 1;   // framesz include the encode of the ID, so minus the encode byte; depend on the type of the tagid.
    if (framesz <= 0) continue;
    char* encode = (*input == 1)? "UTF-16" : "GB18030";  // The biggest problem is here, hard to know encode,so just guess
    size_t outsize = framesz * 2;
    char* result = malloc(outsize);
    bzero(result, outsize);
    char *temp = input+1;
    if (strncmp(header.frameid, "PRIV", 4) == 0) {   // PRIV's handle is a bit of special, still don't understand 
      encode = "ISO-8859-1";
      temp = input;
      framesz +=1;
    }
    if ((doconv(temp, framesz, encode, result, outsize)) == -1) {
      perror("doconv: ");
      continue;
    }
    printf("The %s is:\t\t%s, size = %d\n", header.frameid, result, framesz);
    free(result);
  }


到此为止,已经可以把所有的标签内容读取成功并打印了.详细代码参考:github.com/qingkouwei/…

Mp3格式定义参考官网:id3.org/id3v2.4.0-s…


附录


帧标识


  • AENC:Audioencryption
  • APIC:Attached picture
  • COMM:Comments
  • COMR:Commercial
  • ENCR:Encryptionmethod registration
  • EQUA:Equalization
  • ETCO:Event timingcodes
  • GEOB:Generalencapsulated object
  • GRID:Groupidentification registration
  • IPLS:Involvedpeople list
  • LINK:Linkedinformation
  • MCDI:Music CDidentifier
  • MLLT:MPEGlocationlookup table
  • OWNE:Ownership
  • PRIV:Private
  • PCNT:Playcounter
  • POPM:Popularimeter
  • POSS:Positionsynchronisation
  • RBUF:Recommendedbuffer size
  • RVAD:Relativevolume adjustment
  • RVRB:Reverb
  • SYLT:Synchronizedlyric/text
  • SYTC:Synchronizedtempo codes
  • TALB:Album/Movie/Showtitle
  • TBPM:BPM(beats perminute)
  • TCOM:Composer
  • TCON:Content type
  • TCOP:Copyrightmessage
  • TDAT:Date
  • TDLY:Playlistdelay
  • TENC:Encoded by
  • TEXT:Lyricist/Textwriter
  • TFLT:Filetype
  • TIME:Time
  • TIT1:Content groupdeion
  • TIT2:Title/songname/contentdeion
  • TIT3:Subtitle/Deionrefinement
  • TKEY:Initial key
  • TLAN:Language(s)
  • TLEN:Length
  • TMED:Media type
  • TOAL:Originalalbum/movie/show title
  • TOFN:Originalfilename
  • TOLY:Originallyricist(s)/text writer(s)
  • TOPE:Originalartist(s)/performer(s)
  • TORY:Originalrelease year
  • TOWN:Fileowner/licensee
  • TPE1:Leadperformer(s)/Soloist(s)
  • TPE2:Band/orchestra/accompaniment
  • TPE3:Conductor/performerrefinement
  • TPE4:Interpreted,remixed, or otherwise modified by
  • TPOS:Partof a set
  • TPUB:Publisher
  • TRCK:Tracknumber/Position in set
  • TRDA:Recordingdates
  • TRSN:Internetradio station name
  • TRSO:Internetradio station owner
  • TSIZ:Size
  • TSRC:ISRC(internationalstandard recording code)
  • TSSE:Software/Hardwareand settings used for encoding
  • TYER:Year
  • TXXX:Userdefinedtext information
  • UFID:Unique fileidentifier
  • USER:Terms of use
  • USLT:Unsychronizedlyric/text tranion
  • WCOM:Commercialinformation
  • WCOP:Copyright/Legalinformation
  • WOAF:Officialaudio file webpage
  • WOAR:Officialartist/performer webpage
  • WOAS:Officialaudio source webpage
  • WORS:Officialinternet radio station homepage
  • WPAY:Payment
  • WPUB:Publishersofficial webpage
  • WXXX:UserdefinedURL link


音乐类型


  • 0="Blues";
  • 1="ClassicRock";
  • 2="Country";
  • 3="Dance";
  • 4="Disco";
  • 5="Funk";
  • 6="Grunge";
  • 7="Hip-Hop";
  • 8="Jazz";
  • 9="Metal";
  • 10="NewAge";
  • 11="Oldies";
  • 12="Other";
  • 13="Pop";
  • 14="R&B";
  • 15="Rap";
  • 16="Reggae";
  • 17="Rock";
  • 18="Techno";
  • 19="Industrial";
  • 20="Alternative";
  • 21="Ska";
  • 22="Deathl";
  • 23="Pranks";
  • 24="Soundtrack";
  • 25="Euro-Techno";
  • 26="Ambient";
  • 27="Trip-Hop";
  • 28="Vocal";
  • 29="Jazz+Funk";
  • 30="Fusion";
  • 31="Trance";
  • 32="Classical";
  • 33="Instrumental";
  • 34="Acid";
  • 35="House";
  • 36="Game";
  • 37="SoundClip";
  • 38="Gospel";
  • 39="Noise";
  • 40="AlternRock";
  • 41="Bass";
  • 42="Soul";
  • 43="Punk";
  • 44="Space";
  • 45="Meditative";
  • 46="InstrumentalPop";
  • 47="InstrumentalRock";
  • 48="Ethnic";
  • 49="Gothic";
  • 50="Darkwave";
  • 51="Techno-Industrial";
  • 52="Electronic";
  • 53="Pop-Folk";
  • 54="Eurodance";
  • 55="Dream";
  • 56="SouthernRock";
  • 57="Comedy";
  • 58="Cult";
  • 59="Gangsta";
  • 60="Top40";
  • 61="ChristianRap";
  • 62="Pop/Funk";
  • 63="Jungle";
  • 64="NativeAmerican";
  • 65="Cabaret";
  • 66="NewWave";
  • 67="Psychadelic";
  • 68="Rave";
  • 69="Showtunes";
  • 70="Trailer";
  • 71="Lo-Fi";
  • 72="Tribal";
  • 73="AcidPunk";
  • 74="AcidJazz";
  • 75="Polka";
  • 76="Retro";
  • 77="Musical";
  • 78="Rock&Roll";
  • 79="HardRock";
  • 80="Folk";
  • 81="Folk-Rock";
  • 82="NationalFolk";
  • 83="Swing";
  • 84="FastFusion";
  • 85="Bebob";
  • 86="Latin";
  • 87="Revival";
  • 88="Celtic";
  • 89="Bluegrass";
  • 90="Avantgarde";
  • 91="GothicRock";
  • 92="ProgessiveRock";
  • 93="PsychedelicRock";
  • 94="SymphonicRock";
  • 95="SlowRock";
  • 96="BigBand";
  • 97="Chorus";
  • 98="EasyListening";
  • 99="Acoustic";
  • 100="Humour";
  • 101="Speech";
  • 102="Chanson";
  • 103="Opera";
  • 104="ChamberMusic";
  • 105="Sonata";
  • 106="Symphony";
  • 107="BootyBass";
  • 108="Primus";
  • 109="PornGroove";
  • 110="Satire";
  • 111="SlowJam";
  • 112="Club";
  • 113="Tango";
  • 114="Samba";
  • 115="Folklore";
  • 116="Ballad";
  • 117="PowerBallad";
  • 118="RhythmicSoul";
  • 119="Freestyle";
  • 120="Duet";
  • 121="PunkRock";
  • 122="DrumSolo";
  • 123="Acapella";
  • 124="Euro-House";
  • 125="DanceHall";
  • 126="Goa";
  • 127="Drum&Bass";
  • 128="Club-House";
  • 129="Hardcore";
  • 130="Terror";
  • 131="Indie";
  • 132="BritPop";
  • 133="Negerpunk";
  • 134="PolskPunk";
  • 135="Beat";
  • 136="ChristianGangstaRap";
  • 137="Heavyl";
  • 138="Blackl";
  • 139="Crossover";
  • 140="ContemporaryChristian";
  • 141="ChristianRock";
  • 142="Merengue";
  • 143="Salsa";
  • 144="Trashl";
  • 145="Anime";
  • 146="JPop";
  • 147="Synthpop";


参考


下篇带你写一个Mp文件解析器-Mp3文件结构全解析(二)接着分析MP3文件的音频内容解析 示例代码地址:git@github.com:qingkouwei/mp3parser.git如果觉得对你有帮助点个赞吧!!!

目录
相关文章
|
15天前
|
XML JavaScript 前端开发
xml文件使用及解析
xml文件使用及解析
|
28天前
|
算法 Linux C++
【Linux系统编程】解析获取和设置文件信息与权限的Linux系统调用
【Linux系统编程】解析获取和设置文件信息与权限的Linux系统调用
29 0
|
1月前
|
安全 Java 数据库连接
jdbc解析excel文件,批量插入数据至库中
jdbc解析excel文件,批量插入数据至库中
21 0
|
17天前
|
存储 编译器 Linux
【C语言】自定义类型:结构体深入解析(二)结构体内存对齐&&宏offsetof计算偏移量&&结构体传参
【C语言】自定义类型:结构体深入解析(二)结构体内存对齐&&宏offsetof计算偏移量&&结构体传参
|
5天前
|
XML C# 数据格式
C# 解析XML文件
C# 解析XML文件
13 1
|
17天前
|
存储 算法
【算法与数据结构】深入解析二叉树(二)之堆结构实现
【算法与数据结构】深入解析二叉树(二)之堆结构实现
|
1月前
|
SQL Java 数据库连接
springboot解析txt文件顺便加到数据库中(nohup文件)
springboot解析txt文件顺便加到数据库中(nohup文件)
112 1
|
1月前
|
存储 编解码 索引
了解FFmpeg音频通道布局结构:AVChannelLayout结构体解析
了解FFmpeg音频通道布局结构:AVChannelLayout结构体解析
17 1
|
1月前
|
XML Java 数据格式
使用java解析XML文件的步骤
使用java解析XML文件的步骤
10 0
|
1月前
|
存储 安全 Linux
C++文件格式深度解析:从底层结构到关键特性
C++文件格式深度解析:从底层结构到关键特性
250 3
C++文件格式深度解析:从底层结构到关键特性

推荐镜像

更多