zoukankan      html  css  js  c++  java
  • 用python给MP3加封面图片,修改作者,专辑等信息

    如何给MP3加封面呢,当然用iturns,千千静听当然是可以的,但是如果用程序来自动加封面呢,研究linux的ffmpeg,发现用这个加专辑信息还是容易的,但是封面始终加不上,无法,去研究mp3文件的编码吧

    用来描述MP3信息的head有两个大版本,分别是idv2和idv3,idv2放在文件尾部,只能描述一些简单的信息,idv3就厉害了,可以添加图片,和其它各种信息,包括自定义信息

    我们只研究 idv3,它放在文件头


    idv3也有几个常用的版本,一个是idv3.3一个是idv3.4两者区别不大,就是frame中的长度idv3.4 改成了sync safe integer ,其它相同,但是idv3.4只有iturns和一些比较先进的播放器能识别出来,比如windows 8 的mediaplay 就无法识别出来,这样在windows的文件夹中,那张封面图就木有了,所以,我们主要研究idv3.3


    idv3.3分 header和frame ,header描述 整个idv3.3的长度啊,和一些常见信息 共10bytes,frame可以有多个,比如专辑名,作者名,就是两个frame


    每个frame也有它的头,也是10个字节,下面具体描述


    header 共10个字节


    1-3  字节 字符串

    ID3

    4     字节  整数 

    表示版本号 正常是03 或者 04,03就是idv3.3 ,04就是idv3.4

    5    字节 整数

    小版本号 不管它

    6    一个flags 

    不管它 用0即可

    7-10 字节 一个无符号整数

    表示整个id3头的长度,这里的长度是个synchsafe integer,具体这个是啥,你可以去百度搜,我这儿只提供个算法,将该数字转义成真正的长度(不包括这个头的长度)

    def decode(x):  #如果按照正常算法得到的是synchsafe integer,解析成 真正的整数大小
        a = x & 0xff;
        b = (x >> 8) & 0xff;
        c = (x >> 16) & 0xff;
        d = (x >> 24) & 0xff;
        x_final = 0x0;
        x_final = x_final | a;
        x_final = x_final | (b << 7);
        x_final = x_final | (c << 14);
        x_final = x_final | (d << 21);
        return x_final
    
    def encode(x): #和上边相反
        a = x & 0x7f;
        b = (x >> 7) & 0x7f;
        c = (x >> 14) & 0x7f;
        d = (x >> 21) & 0x7f;
        
        x_final = 0x0;
        x_final = x_final | a;
        x_final = x_final | (b << 8);
        x_final = x_final | (c << 16);
        x_final = x_final | (d << 24);
        return x_final


    首先将那个4个字节的无符号整形转成整数n,这个整数并不是真正的长度,然后调decode(n)

    如果你要将一个整数转化成syncsafe integer 那么调encode()函数即可

    一个header的例子    ID3| 0x03| 0x00 | 0x00 | 0x00000013

    那么意思是idv3的版本,decode(0x00000013)的长度,全部是大端编码 big-endian

    到这儿id3的头就说完啦


    下面讲frame,每个frame也有一个固定的格式,每个frame 都有一个头



    也是10个字节

    1-4 字节 字符串

    TPE1 ,TIT2 ,TALB 具体去查http://id3.org/id3v2.3.0

    5-8字节 一个无符号整形 大端编码

    表示这个frame的长度,不包括这个10个字节的头(v3.4的版本这儿也是sync safe integer 需要decode,v3.3就不用啦)

    9-10字节  两个 0 不管他

    每个frame还有一个体,体也是有格式滴复杂的咱不说,只说最常用的,第一个字节表示编码,0就是普通编码,在win上就是gbk,在linux系列就是utf8

    所以如果是在win上的能正确解析的到 linux上就是乱码咧,如果你默认用utf8,win上是解析不出来滴,

     那如果我们选择1呢,1就是unicode,unicode是啥编码,说是ucs-2 这个是神马东东,其实就是utf16,所以,第一个字节,咱们用1,然后内容用utf16编码,两个平台就兼容啦

    例子  

    TPE1|0x00000012|0x0000

    0x01|content 

    长度12的content编码是utf16


    普通的frame是这个样子,还有我们的关键 frame,图片

    图片的的frame头和上边一样,也是10个字节 ,但是体 稍微不同

    第一个字节还是编码,选0就成,然后是 mime type 就是图片格式比如 image/jpeg 或者是image/png 然后跟一个0x00 表示格式结束

    然后再来个一个字节表示图片用途,比如封面是03,但是用03 有问题,不知道为啥,所以都是用0

    然后一个是描述,没用 用0就行

    然后就是图片数据开始啦,将图片打开,然后read数据到这儿就成了

    例子

    APIC|0x00001234|0x0000   头

    0x00|image/jpeg0x00|0x00|0x00 content(比如一个jpg的图片 是0xFFD8打头)

    噢了,下边是我写的一个读写mp3 idv3信息的小python代码

     

    # -*- coding: utf8 -*-
    import struct
    
    def decode(x):  #如果按照正常算法得到的synchsafe integer,解析成 真正的整数大小
        a = x & 0xff;
        b = (x >> 8) & 0xff;
        c = (x >> 16) & 0xff;
        d = (x >> 24) & 0xff;
        x_final = 0x0;
        x_final = x_final | a;
        x_final = x_final | (b << 7);
        x_final = x_final | (c << 14);
        x_final = x_final | (d << 21);
        return x_final
    
    def encode(x): #和上边相反
        a = x & 0x7f;
        b = (x >> 7) & 0x7f;
        c = (x >> 14) & 0x7f;
        d = (x >> 21) & 0x7f;
        
        x_final = 0x0;
        x_final = x_final | a;
        x_final = x_final | (b << 8);
        x_final = x_final | (c << 16);
        x_final = x_final | (d << 24);
        return x_final
    
    class MP3:
        def __init__(self,path):
            self.path = path
            pass
        def getInfo(self):
            fp = open( self.path,'rb');
            head = fp.read(10)
            id3,ver,revision,flag,length  = struct.unpack("!3sBBBI",head);
            length = decode(length)
            data = []
            while True:
                frame = fp.read(10)
                fid,size,flag,flag2 = struct.unpack("!4sI2B",frame)
                if size==0: #有时候会留1024的白 不知道为啥
                    break
                if ver==4:   #就是这一点 4和3的不同之处,4的这儿也采用synchsafe integer 了,注意啊
                    size = decode(size)
                content = fp.read(size)
                data.append((fid,content))
                length-= (size+10)
                print length
                if length<=0:
                    break
            fp.close()
            return data
        def buildItem(self,flag,content):
            content = content.decode('utf8').encode("utf16")
            content = struct.pack('!B',1)+content
            length = len(content)
            head = struct.pack('!4sI2B',flag,length,0,0);
            return head + content
            
        def addImage(self,image,data):
            fp = open( self.path,'rb');
            head = fp.read(10)
            try:
                id3,ver,revision,flag,length  = struct.unpack("!3sBBBI",head);
            except:
                return False;
            if id3 != 'ID3':
                return False
            #新建立个文件
            fpNew = open(self.path+'.bak',"wb");
            fpImage = open(image,"rb")
            imageData = fpImage.read() #待用
            originLength = decode(length) #真实长度
            length = 0
            
            imageDataPre = struct.pack("!B10s2BB",0,'image/jpeg',0,0,0)
            imageData = imageDataPre+imageData
            apicLen = len(imageData)  #图片数据区域长度
            imageDataHead = struct.pack("!4sI2B",'APIC',apicLen,0,0)
            imageData  = imageDataHead+imageData
            
            
            TPE1 =  self.buildItem('TPE1', data[u'Artist'].encode("utf8"))
            TIT2 = self.buildItem('TIT2', data[u'Title'].encode("utf8"))
            TALB = self.buildItem('TALB', data[u'Album'].encode("utf8"))
            
            #新长度
            length += len(imageData)
            length += len(TPE1)
            length += len(TIT2)
            length += len(TALB)
            
            header = head[0:3]
            header += struct.pack('!B',3)
            header += struct.pack('!H',0)
            #1字节留白
            header += struct.pack("!I",encode(length+1))
            
            fpNew.write(header)
            fpNew.write(TPE1)
            fpNew.write(TIT2)
            fpNew.write(TALB)
            fpNew.write(imageData)
            fpNew.write(struct.pack('!B',0))
            
            fp.seek(originLength,1) #跳
            fpNew.write(fp.read())
            fpNew.close()
            fp.close()
            fpImage.close()












  • 相关阅读:
    C语言随笔_printf输出多行
    C语言随笔_return答疑
    《疯狂Java讲义》(二十八)---- 异常
    《疯狂Java讲义》(二十七)----泛型
    《疯狂Java讲义》(二十七)---- Collections
    《疯狂Java讲义》(二十六)---- Map
    《疯狂Java讲义》(二十五)---- List 集合
    《疯狂Java讲义》(二十四)---- Set集合
    Problem(2)----How to set eclipse console locale/language
    Problem(1)----Eclipse hangs on copy/cut for JavaScript files
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/3074839.html
Copyright © 2011-2022 走看看