音频WMA格式中ID3、专辑图的解析,Java实现
需要WMA格式的TAG信息,整理了一下,在这里与大家分享一下。参考转自:http://blog.csdn.net/werocpp/article/details/5594067。
首先介绍下WMA文件头的结构,如下图
/*************************************************************************
// 分为文件头和各个帧数据(文件头前16个字节WMA格式是固定的,8个字节的大小是高位存在后面,以后遇到大小都是高位存在后面)
+--------------------------------------------------------------+
| Header (30 bytes) HeadFlag:16; HeadSize:8; Unknow:6 |
+--------------------------------------------------------------+
| Frames (1....n) |
+--------------------------------------------------------------+
// 所有的TAG信息存放在标准帧和扩展帧中,其他帧可以不予考虑,标准帧以及扩展帧的16个字节标识头都是固定的
// 所有的信息都是UNICODE编码
// 标准帧结构
+--------------------------------------------------------------+
| Header (24 bytes) HeadFlag:16; HeadSize:8; |
+--------------------------------------------------------------+
| 标题信息大小(2 bytes) |
+--------------------------------------------------------------+
| 艺术家信息大小(2 bytes) |
+--------------------------------------------------------------+
| 版权信息大小(2 bytes) |
+--------------------------------------------------------------+
| 备注信息大小(2 bytes) |
+--------------------------------------------------------------+
| 未知信息大小(2 bytes) |
+--------------------------------------------------------------+
| 标题信息内容(0x00 0x00结束) |
+--------------------------------------------------------------+
| 艺术家信息内容(0x00 0x00结束) |
+--------------------------------------------------------------+
| 版权信息内容(0x00 0x00结束) |
+--------------------------------------------------------------+
| 备注信息内容(0x00 0x00结束) |
+--------------------------------------------------------------+
| 未知信息内容(0x00 0x00结束) |
+--------------------------------------------------------------+
// 扩展帧结构
+--------------------------------------------------------------+
| Header (24 bytes) HeadFlag:16; HeadSize:8; |
+--------------------------------------------------------------+
| 扩展信息个数EXNO(2 bytes) |
+--------------------------------------------------------------+
| EXINFO (1....EXNO) |
+--------------------------------------------------------------+
// 每个扩展信息EXINFO结构
+--------------------------------------------------------------+
| EXINFO NAME Size (2 bytes) 扩展信息名字大小 |
+--------------------------------------------------------------+
| 扩展信息名称 |
+--------------------------------------------------------------+
| 标志FLAG (2 bytes) |
+--------------------------------------------------------------+
| 值的大小 (2 bytes) |
+--------------------------------------------------------------+
| 实际的值 (若是图片格式参考ID3V2.3) |
+--------------------------------------------------------------+
当扩展信息名字为WMFSDKVersion时,这个值表示的是这个WMA文件的版本;
当扩展信息名字为WM/AlbumTitle时,这个值代表的就是专辑名;
当扩展信息名字为WM/Genre时,这个值代表的就是流派;
下面再来看看那个标志Flag,这个基本上是为没什么用的(通常值为0),
对WM/TrackNumber和WM/Track这两个扩展信息名字有用,
当Flag为3的时候后面的值(也就是曲目信息)是以4个字节的整数的形式表示,
当Flag为0的时候,曲目信息是以普通的字符串形式表示的。
// 查看http://msdn.microsoft.com/en-us/library/ms867702.aspx
Java代码:
public classWmaReadId3 {
private static finalStringTAG="id3";
private int[]header= {0x30,0x26,0xB2,0x75,0x8e,0x66,0xCF,0x11,0xA6,0xD9,0x00,0xAA,0x00,0x62,0xCE,
0x6C};
private int[]WMA_STANDTAG= {0x33,0x26,0xB2,0x75,0x8E,0x66,0xCF,0x11,0xA6,0xD9,0x00,0xAA,0x00,0x62,
0xCE,0x6C};// 标准帧
private int[]WMA_EXTAG= {0x40,0xA4,0xD0,0xD2,0x07,0xE3,0xD2,0x11,0x97,0xF0,0x00,0xA0,0xC9,0x5E,
0xA8,0x50};// 扩展帧
private int[]ALBUM= {0x57,0x00,0x4d,0x00,0x41,0x00,0x6c,0x00,0x62,0x00,0x75,0x00,0x6d,0x00,0x54,
0x00,0x69,0x00,0x74,0x00,0x6c,0x00,0x65,0x00};
private int[]YEAR= {0x57,0x00,0x4d,0x00,0x59,0x00,0x65,0x00,0x61,0x00,0x72,0x00};
private int[]APIC_TAG= {0x69,0x00,0x6D,0x00,0x61,0x00,0x67,0x00,0x65,0x00,0x2F,0x00,0x6A,0x00,0x70,
0x00,0x65,0x00,0x67,0x00,0x00,0x00,0x00,0x00};
privateInputStreammp3ips;
publicStringcharset="UTF-8";// 预设编码为GBK
privateId3v2Infoinfo;
publicWmaReadId3(InputStream inputStream) {
this.mp3ips= inputStream;
info=newId3v2Info();
}
publicId3v2InforeadId3() {
if(mp3ips!=null) {
try{
intreslen =mp3ips.available();// 获取字节总长
mp3ips.skip(0);
byte[] wmadata =new byte[16];
mp3ips.read(wmadata);
booleanisWmaFile = compareArrays(wmadata,header);
if(!isWmaFile) {
returninfo;
}
byte[] hDataSize =new byte[8];
mp3ips.read(hDataSize);
intheadSize =0;
for(inti =0;i < hDataSize.length;i++) {
headSize |= (((int) hDataSize[i] &0xff) << (8* i));
}
mp3ips.skip(6);
intoffset =30;// 偏移量
while(offset < headSize) {
byte[] tagHead =new byte[16];// 帧头
mp3ips.read(tagHead);
offset += tagHead.length;
byte[] tagSize =new byte[8];// 帧长度
mp3ips.read(tagSize);
offset += tagSize.length;
intframeSize =0;
for(inti =0;i < tagSize.length;i++) {
frameSize |= (((int) tagSize[i] &0xff) << (8* i));
}
if(compareArrays(tagHead,WMA_STANDTAG)) {// 是标准帧头
byte[] tmpbuf =new byte[10];
mp3ips.read(tmpbuf);
int[] Tagsize =new int[5];
for(inti =0;i <5;i++) {
Tagsize[i] = (((int) tmpbuf[i *2] &0xff) | ((int) tmpbuf[i *2+1] &0xff) <<8);
}
// read 5 tag.
for(inti =0;i <5;i++) {
if(Tagsize[i] >0) {
byte[] tempData =new byte[Tagsize[i]];
mp3ips.read(tempData);
offset += tempData.length;
switch(i) {
case0:// Title
String title = Byte2Unicode(tempData);
info.setTit2(title);
break;
case1:// Artist
String artist = Byte2Unicode(tempData);
info.setTpe1(artist);
break;
}
}
}
}else if(compareArrays(tagHead,WMA_EXTAG)) {// 是扩展帧头
byte[] extSize =new byte[2];
mp3ips.read(extSize);
offset += extSize.length;
intexTagNum =0;
for(inti =0;i < extSize.length;i++) {
exTagNum |= (((int) extSize[i] &0xff) << (8* i));
}
for(inti =0;i < exTagNum;i++) {
byte[] size1Date =new byte[2];
mp3ips.read(size1Date);
offset += size1Date.length;
intsize1 =0;
for(intj =0;j < size1Date.length;j++) {
size1 |= (((int) size1Date[j] &0xff) << (8* j));
}
byte[] extTagName =new byte[size1];// 扩展信息名
mp3ips.read(extTagName);
offset += extTagName.length;
String name =newString(removeZero(extTagName));//Byte2Unicode(extTagName);
byte[] flagData =new byte[2];
mp3ips.read(flagData);
offset += flagData.length;
intflag =0;
for(intj =0;j < flagData.length;j++) {
flag |= (((int) flagData[j] &0xff) << (8* j));
}
byte[] exValueSizeData =new byte[2];
mp3ips.read(exValueSizeData);
offset += exValueSizeData.length;
intexValueSize =0;
for(intj =0;j < exValueSizeData.length;j++) {
exValueSize |= (((int) exValueSizeData[j] &0xff) << (8* j));
}
if(flag !=3) {
byte[] exValueData =new byte[exValueSize];
mp3ips.read(exValueData);
offset += exValueData.length;
if("WM/AlbumTitle".equals(name)) {
String album = Byte2Unicode(exValueData);
info.setTalb(album);
}else if("WM/AlbumArtist".equals(name)) {
String artist = Byte2Unicode(exValueData);
info.setTpe1(artist);
}else if("WM/Picture".equals(name)) {// 专辑图
LogUtils.d(TAG,"----专辑图----");
byte[] apicTag =new byte[APIC_TAG.length];
System.arraycopy(exValueData,5,apicTag,0,apicTag.length);
if(compareArrays(apicTag,APIC_TAG)) {
byte[] imgData =new byte[exValueData.length-5- apicTag.length];
System.arraycopy(exValueData,5+ apicTag.length,imgData,0,imgData.length);
info.setApic(imgData);
}
}
}else{
mp3ips.skip(4);
offset +=4;
}
}
}else{
mp3ips.skip(frameSize -16-8);
offset += (frameSize -16-8);
}
}
}catch(IOException e) {
e.printStackTrace();
}
}
returninfo;
}
// for (int j = 0; j < extTagName.length; j++) {
// String temp = Integer.toHexString(extTagName[j] &
// 0xff);
// if (temp.length() == 1) {
// temp = "0" + temp;
// }
// LogUtils.d(TAG, "扩展信息名 extTagName[" + j + "]:" +
// temp);
// }
public voidsetInfo(Id3v2Info info) {
this.info= info;
}
publicId3v2InfogetInfo() {
returninfo;
}
publicStringgetName() {
returngetInfo().getTit2();
}
publicStringgetAuthor() {
returngetInfo().getTpe1();
}
publicStringgetSpecial() {
returngetInfo().getTalb();
}
public byte[]getImg() {
returngetInfo().getApic();
}
public booleancompareArrays(byte[] data1, int[] data2) {
if(data1.length== data2.length) {
for(inti =0;i < data2.length;i++) {
if((data1[i] &0xff) != (data2[i] &0xff)) {
return false;
}
}
return true;
}else{
return false;
}
}
public byte[]removeZero(byte[] a) {
intj =0;
// 这个for循环计算出你传入的这个数组去掉0后的长度
for(inti =0;i < a.length;i++) {
if(a[i] !=0) {
j++;
}
}
// 定义数组的长度
byte[] newarr =new byte[j];
j =0;
// 将不为零的copy到新数组中去
for(inti =0;i < a.length;i++) {
if(a[i] !=0) {
newarr[j] = a[i];
j++;
}
}
returnnewarr;
}
publicStringByte2Unicode(byteabyte[], intst, intbEnd)// 不包含bEnd
{
StringBuffer sb =newStringBuffer("");
for(intj = st;j < bEnd;) {
intlw = abyte[j++];
if(lw <0)
lw +=256;
inthi = abyte[j++];
if(hi <0)
hi +=256;
charc = (char) (lw + (hi <<8));
sb.append(c);
}
returnsb.toString();
}
publicStringByte2Unicode(byteabyte[], intlen) {
returnByte2Unicode(abyte,0,len);
}
publicStringByte2Unicode(byteabyte[]) {
returnByte2Unicode(abyte,0,abyte.length);
}
public byte[]Unicode2Byte(String s) {
intlen = s.length();
byteabyte[] =new byte[len <<1];
intj =0;
for(inti =0;i < len;i++) {
charc = s.charAt(i);
abyte[j++] = (byte) (c &0xff);
abyte[j++] = (byte) (c >>8);
}
returnabyte;
}
}