[MP3学习]MP3标签信息之IDv2.3

来源:互联网 发布:原宿风衣服淘宝 编辑:程序博客网 时间:2024/05/10 08:21

接上一篇文章:[MP3学习]MP3标签信息之ID3v1,IDv2

上一篇中完成了ID3v1的读取和修改工作,但是由于现在大部分MP3都支持ID3v2甚至更高版本,许多有ID3v1的标志但是却没有内容,所以,我们需要能够读取Id3v2版本的标签。参考http://en.wikipedia.org/wiki/ID3http://baike.baidu.com/view/66078.html ,图示如下:


对比上面的结果,我们发现:

/* * 事实上,改写完毕我们发现windows自己识别出来的信息并不是我们修改后的信息,也就是说我们的修改没有起作用,事实上到底是什么样的呢? * 通过UltraEdit读取,我们查看最后128个字节(如图)发现我们修改的信息确实已经写入,并且某些文件也可以读取到我们修改的信息,用播放器 * 播放时也可以正常播放,所以结论只有一个:我们的写法没错!那错在哪里呢?经Baidu,我们会发现,MP3的标签信息还有一种存储方式,即ID3v2. * 那么什么是ID3v2呢? * 由于ID3v1信息存储在了文件的最后128个字节里,那么ID3v2就不得不放弃选择存储在文件的末尾了,于是它被存储在了文件的起始位置。ID3v2信息 * 的存储和读取远远要比ID3v1信息复杂的多。这是因为ID3v2信息不再固定,而且由于这段信息存储在了文件的首端,所以重新写入的时候也远比ID3v1 * 麻烦的多。ID3v2到现在一共有4个版本,不过比较流行的MP3播放软件一般只支持第3版,即ID3v2.3。ID3v2信息包括两个部分,一个部分是标头信息, * 另一个部分是标体信息。 * 详细信息可以到http://en.wikipedia.org/wiki/ID3,http://baike.baidu.com/view/66078.html去了解一下。 *  * 1、标签头(固定10个字节),ID3v2Header * 2、标签帧(非固定,需计算),每个标签帧都有一个10个字节的帧头和至少一个字节的不固定长度的帧体组成,它们也是顺序存放在文件中, * 和标签头和其他的标签帧也没有特殊的字符分隔,得到一个完整的帧的内容只有从帧头中的到内容大小后才能读出,读取时要注意大小, * 不要将其它的帧的内容或帧头读入。 */struct ID3v2Header //标签头,10个字节,通过解析这段信息我们可以知道一个MP3文件是不是有ID3v2信息,如果有我们就知道了ID3v2的数据体的总长度。{char Identify[3];// ID3v2固定标志:ID3char Ver;// 主版本号,ID3v2就是3char Rever;// 副版本号,一般都为0char Flag;// 标志位,一般为0,字义为abc00000//a-表示是否使用Unsynchronisation//b-表示是否有扩展头部,一般没有,所以一般不设置//c-表示是否为测试标签(99.9%的标签都不是测试用的,所以一般不设置)char Size[4];// 标签大小,一共四个字节,但每个字节只使用7位,最高位不使用恒为0,所以格式如下:// 0xxxxxxx 0xxxxxxx 0xxxxxxx 0xxxxxxx,计算大小时要将0去掉,得到一个28位的二进制数,就是标签的大小.// ID3size =(Size[0]&0x7F)*0x200000 +(Size[1]&0x7F)*0x400 +(Size[2]&0x7F)*0x80 +(Size[3]&0x7F);};struct ID3v2Frame //标签帧,10个字节{char FrameID[4];// 标志对照符,如TEXT,TOPE,TDAT....char Size[4];// 帧体的大小,按照正常的8位存储的,FSize = Size[0]*0x100000000 + Size[1]*0x10000 + Size[2]*0x100 + Size[3];char Flag[2];// 存放标志,另外10位为0 但大部分的情况下16位都为0就可以了,格式如下://a-标签保护标志,设置时认为此帧作废//b-文件保护标志,设置时认为此帧作废//c-只读标志,设置时认为此帧不能修改(目前好像没有看到过)//i-压缩标志,设置时一个字节存放两个BCD码表示数字//j-加密标志(好像不太实用)//k-组标志,设置时说明此帧和其它的某帧是一组。};/* * 标签体(数据体),大小不固定 *  * 标头后面就是数据体了,例如,我们提取数据体的前十个字节,我们知道了这个数据结构存储的FrameID是TIT2,查上面的表, * 说明这个数据结构存储的是歌曲名信息。大小是00 00 00 17,换成十进制就是23。也就是歌曲名是这个子标头后的23个字节的信息。 * 也就是:“Take Me To Your Heart ”。接下来的一个数据结构的FrameID是TPE1,说明是歌手名,而大小是00 00 00 17,说明这个数据体有23个字节, * 也就是:“Michael Learns to Rock”。依次类推。这里需要大家知道的是一个汉字占用两个字节。在写入时,要注意计算字节数。 *//* FrameID标志内容 *  * TEXT: 歌词作者TENC: 编码WXXX: URL链接(URL)TCOP: 版权(Copyright)TOPE: 原艺术家  * TCOM: 作曲家TDAT: 日期TPE3: 指挥者TPE2: 乐队TPE1: 艺术家相当于ID3v1的Artist  * TPE4: 翻译(记录员、修改员)TYER: 即ID3v1的YearUSLT: 歌词TSIZ: 大小 * TALB: 专辑相当于ID3v1的AlbumTIT1: 内容组描述TIT2: 标题相当于ID3v1的Title  * TIT3: 副标题TCON: 流派(风格)相当于ID3v1的GenreAENC: 音频加密技术TSSE: 编码使用的软件(硬件设置) * TBPM: 每分钟节拍数COMM: 注释相当于ID3v1的CommentTDLY: 播放列表返录TRCK: 音轨(曲号)相当于ID3v1的Track  * TFLT: 文件类型TIME: 时间 TKEY: 最初关键字TLAN: 语言TLEN: 长度 * TMED: 媒体类型TOAL: 原唱片集TOFN: 原文件名TOLY: 原歌词作者TORY: 最初发行年份  * TOWM: 文件所有者(许可证者)TPOS: 作品集部分TPUB: 发行人TRDA: 录制日期  * TRSN: Intenet电台名称TRSO: Intenet电台所有者UFID: 唯一的文件标识符  TSRC: ISRC(国际的标准记录代码)      */

仿照第一篇文章,读取ID3v2版本标签,代码如下:

void ReadID3v2(char* pfile){FILE *pf = fopen(pfile,"r");ID3v2Header mp3header;fseek(pf,0,SEEK_SET);fread(&mp3header,sizeof(mp3header),1,pf);if (mp3header.Identify[0]!='I' || mp3header.Identify[1]!='D' || mp3header.Identify[2]!='3' ){printf("此歌曲不支持ID3v2标准!\n");fclose(pf);return;}printf("ID3v2标志:%.3s\n",mp3header.Identify);printf("ID3v2版本:%d\n",mp3header.Ver);int ID3size =(mp3header.Size[0]&0x7F)*0x200000 +(mp3header.Size[1]&0x7F)*0x400 +(mp3header.Size[2]&0x7F)*0x80 +(mp3header.Size[3]&0x7F);printf("标签大小:%d\n***********\n",ID3size);ID3v2Frame mp3Frame;int FSize=0;char str[256]={0};char str2[5]={0};for (int i=0;i<ID3size;i=i+11+FSize){memset(&mp3Frame,0,sizeof(mp3Frame));memset(&str,0,sizeof(str));fseek(pf,10+i,SEEK_SET);//移动到标签帧头fread(&mp3Frame,sizeof(mp3Frame),1,pf);FSize =(int)(mp3Frame.Size[0]*0x100000000 + mp3Frame.Size[1]*0x10000 + mp3Frame.Size[2]*0x100 + mp3Frame.Size[3]-1);//原则上是不用-1的,但是实际发现,总有一个字节的差距,为了计算方便-1,所以出现-1时标明此区块无内容if (FSize>0){fseek(pf,10+11+i,SEEK_SET);//移动到内容区fread(str,FSize,1,pf);GetStr(mp3Frame.FrameID,str2);printf("%s-%s:\t%s\n",str2,mp3Frame.FrameID,str);}else{return;}//getchar();}fclose(pf);}


主函数:
int main(int argc, char* argv[]){/*  MP3的ID3v标签有好几种格式,一般用的最多的是v1和v2.3,所以一般只需要处理这两种,但是有的还会存储图片和歌词,这些地方也可以得到歌曲信息,  所以,如有需要再读取其他地方的歌曲信息。 */ ReadID3v2("H:/mp3/2.mp3");//ReadID3v1("H:/mp3/ok.mp3");return 0;}

其中,FrameI文本转换函数如下:(用Map方法可能会好点)

void GetStr(char* oldstr,char* str)//通过FrameID获取对应的中文名{if (0==memcmp((LPCTSTR)"TIT2",oldstr,4)){memcpy(str,"标题",4);} else if(0==memcmp((LPCTSTR)"TPE1",oldstr,4)){memcpy(str,"作者",4);}else if(0==memcmp((LPCTSTR)"TALB",oldstr,4)){memcpy(str,"专辑",4);}else if(0==memcmp((LPCTSTR)"TRCK",oldstr,4)){memcpy(str,"音轨",4);}else if(0==memcmp((LPCTSTR)"TYER",oldstr,4)){memcpy(str,"年代",4);}else if(0==memcmp((LPCTSTR)"COMM",oldstr,4)){memcpy(str,"备注",4);}else if(0==memcmp((LPCTSTR)"TCON",oldstr,4)){memcpy(str,"类型",4);}else{memcpy(str,"未知",4);//其他的不是很重要,所以省略了}

如此,整个读取工作就完成了。例子结果如下:


修改方法请仿照第一个。

但是,在查看资料时,我们还发现,ID3v2标签还支持歌词和图片,但是经搜索,相关的资料不是很全,短时间内无法弄明白,所以,这个方面的东西,先暂告一段落,学习后再继续。