赞
踩
以下来自百度百科。
MP3是一种音频压缩技术,其全称是动态影像专家压缩标准音频层面3(Moving Picture Experts Group Audio Layer III),简称为MP3。它被设计用来大幅度地降低音频数据量。利用 MPEG Audio Layer 3 的技术,将音乐以1:10 甚至 1:12 的压缩率,压缩成容量较小的文件,而对于大多数用户来说重放的音质与最初的不压缩音频相比没有明显的下降。它是在1991年由位于德国埃尔朗根的研究组织Fraunhofer-Gesellschaft的一组工程师发明和标准化的。用MP3形式存储的音乐就叫作MP3音乐,能播放MP3音乐的机器就叫作MP3播放器。
mp3 的tag一般情况下,指的是mp3的元数据,一个规则mp3文件通常包含有3个部分:
TAG_V2(ID3V2)
FRAME
TAG_V1(ID3V1)
标签 | 说明 |
---|---|
ID3V2 | 包含了作者、作曲、专辑等信息,长度不固定,拓展了ID3V1的信息量 |
FRAME | 一系列的帧,个数由文件大小和帧长决定 |
· | 每个FRAME的长度可能不固定,也可能是固定,由位率bitrate 决定 |
· | 每个FRAME又分为帧头和数据实体两部分 |
· | 帧头记录了mp3的位率,采样率,版本等信息,每个帧之间相互独立 |
· | |
ID3V1 | 包含了作者,作曲,专辑等信息,长度为128byte |
TAG_V1部分是mp3文件等最后128byte等内容,其中包含的信息如下:
ID3V2与ID3V1的作用差不多,也是记录mp3的有关信息,但ID3V2的结构比ID3V要复杂得多,而且可以伸缩和拓展。ID3V2到现在一共有4个版本,但流行的播放软件一般只支持第3版,即ID3V2.3。由于ID3V1记录在mp3文件的末尾,ID3V2就只好记录在mp3文件的首部了。
每个ID3V2.3的标签都是由一个标签头和热干个标签帧或一个拓展标签头组成。歌曲的信息如标题、作者等都存放在不同的标签帧中,拓展标签头和标签并不是必须的,但每个标签至少要有一个标签帧。
一个mp3文件如果有ID3V2的话,那么ID3V2的标签头占用文件最前面的10个byte,其数据说明如下:
名称 | 字节 | 说明 |
---|---|---|
Header | 3 | ID3V2标示符“ID3”的ascii编码,否则认为没有ID3V2 |
Ver | 1 | 版本号,=03 |
Reveision | 1 | 副版本号,=00 |
flag | 1 | 标志位,一般没意义,=00 |
Size | 4 | 标签内容长度,高位在前,不包括标签头的10个字节 |
表说明
标签内容由若干个标签帧组成,每个标签帧都由一个10字节的枕头和至少1个字节的不固定长度的帧内容组成,她们顺序存放在文件中。每个帧都由帧头和帧内容组成,数据说明如下:
名称 | 字节 | 说明 |
---|---|---|
FrameID | 4 | 帧标识符的ascii编码,常用标识符见【常见标识符表】说明 |
Size | 4 | 帧内容及编码方式的合计长度,高位在前 |
Flags | 2 | 标志位,只使用了6位,详细见【标志位表】,一般均=0 |
encode | 4 | 帧内容所用的编码方式,一般没有此项 |
帧内容 | 0 | 至少1个字节 |
表说明
类型 | 说明 |
---|---|
AENC | 音频加密技术 |
APIC | 附加描述 |
COMM | 注释,相当于ID3V1的Comment |
COMR | 广告 |
ENCR | 加密方法注册 |
ETC0 | 事件时间编码 |
GEOB | 常规压缩对象 |
GRID | 组识别注册 |
IPLS | 复杂类别列表 |
MCDI | 音乐CD标识符 |
MLLT | MPEG位置查找表格 |
OWNE | 所有权 |
PRIV | 私有 |
PCNT | 播放计数 |
POPM | 普通仪表 |
POSS | 位置同步 |
RBUF | 推荐缓冲区大小 |
RVAD | 音量调节器 |
RVRB | 混响 |
SYLT | 同步歌词或文本 |
SYTC | 同步节拍编码 |
TALB | 专辑,相当于ID3V1的Album |
TBPM | 每分钟节拍数 |
TCOM | 作曲家 |
TCON | 流派风 |
TCOP | 版权 |
TDAT | 日期 |
TDLY | 播放列表返录 |
TENC | 编码 |
TEXT | 歌词作者 |
TFLT | 文件类型 |
TIME | 时间 |
TIT1 | 内容组描述 |
TIT2 | 标题,相当于ID3V1的Title |
TIT3 | 副标题 |
TKEY | 最初关键字 |
TLAN | 语言 |
TLEN | 长度 |
TMED | 媒体类型 |
TOAL | 原唱片集 |
TOFN | 原文件名 |
TOLY | 原歌词作者 |
TOPE | 原艺术家 |
TORY | 最初发行年份 |
TOWM | 文件所有者 |
TPE1 | 艺术家,相当于ID3V1的Artist |
TPUB | 发行人 |
TACK | 音轨,相当于ID3V1的Track |
TRDA | 录制日期 |
TSIZ | 大小 |
TSSE | 编码使用的软件 |
TYER | 年代,相当于ID3V1的Year |
TXXX | 年度 |
UFID | 唯一文件标识符 |
USLT | 歌词 |
位址 | 说明 |
---|---|
0 | 标签保护标志,如设置表示此帧作废 |
1 | 文件保护标志,如设置表示此帧作废 |
2 | 只读标志,如设置表示此帧不能修改 |
3 | 压缩标志,如设置表示1个字节存放2个BCD码表示数字 |
4 | 加密标志 |
5 | 组标志,如设置表示此帧和其他的某帧是一组 |
下面举例说明python读取mp3元数据。
python读取mp3的ID3V1(TAG V1)。
使用如下命令查看python是否已经安装chardet.
pip list
输出结果如下:
Package Version
---------- ---------
certifi 2020.12.5
chardet 4.0.0
idna 2.10
pip 20.2.2
pyexiv2 2.4.1
requests 2.25.1
setuptools 49.2.1
urllib3 1.26.2
wheel 0.34.2
#coding: UTF-8 import os import string import base64 import chardet ''' 解析mp3,获取TAG_V1 ''' def parse(fileObj,version='v1'): fileObj.seek(0,2) if(fileObj.tell()<128): return False fileObj.seek(-128,2) tag_data=fileObj.read() if(tag_data[0:3] != b'TAG'): return False return getTag(tag_data) def decodeData(bin_seq): result=chardet.detect(bin_seq) # print(result) if(result['confidence']>0): try: return bin_seq.decode(result['encoding']) except: return 'Decode fail' def getTag(tag_data): STRIP_CHARS=b'\x00' tags={} tags['title']=tag_data[3:33].strip(STRIP_CHARS) if(tags['title']): tags['title'] = decodeData(tags['title']) tags['artist']=tag_data[33:63].strip(STRIP_CHARS) if(tags['artist']): tags['artist']=decodeData(tags['artist']) tags['genre'] = ord(tag_data[127:128]) return tags f=open('/Users/michaelkoo/work/env/csdn/nanerzhi.mp3','rb') t=parse(f) print(t)
实例运行结果如下:
{'title': '男儿志(《少林足球》电影插曲)', 'artist': 'Decode fail', 'genre': 255}
如下:
""" 处理mp3解析,获取ID3V2信息 """ import struct encodings = ['GBK', 'UTF-16', 'UTF-16BE', 'UTF-8'] def parse_ID3V2_frames(frames_bin): """ 解析帧数据 :param frames_bin: :return: """ pointer = 0 frames_bin_size = len(frames_bin) frames = {} while pointer < frames_bin_size - 10: frame_header_bin = frames_bin[pointer:pointer + 10] frame_header = struct.unpack('>4sI2s', frame_header_bin) frame_body_size = frame_header[1] if frame_body_size == 0: break pointer += 10 frames[frame_header[0]] = frames_bin[pointer:pointer + frame_body_size] pointer += frame_body_size TIT2_bin = frames.get(b'TIT2', None) TPE1_bin = frames.get(b'TPE1', None) TALB_bin = frames.get(b'TALB', None) if TALB_bin: encoding = encodings[TALB_bin[0]] frames[b'TALB'] = TALB_bin[1:].decode(encoding) if TIT2_bin: encoding = encodings[TIT2_bin[0]] frames[b'TIT2'] = TIT2_bin[1:].decode(encoding) if TPE1_bin: encoding = encodings[TPE1_bin[0]] frames[b'TPE1'] = TPE1_bin[1:].decode(encoding) return frames def parse_ID3V2_head(head_bin): """ 获取数据头位置 :param head_bin: :return: """ if head_bin[:3] != b'ID3': return None frames_bin_size = (head_bin[6] << 21 | head_bin[7] << 14 | head_bin[8] << 7 | head_bin[9]) return frames_bin_size def read_mp3_tag_v2(): """ :return: """ f = open('/Users/michaelkoo/work/env/csdn/dream.mp3', 'rb') frames_bin_size = parse_ID3V2_head(f.read(10)) if frames_bin_size is None: f.close() print('it not contain ID3V2') return frames_bin = f.read(frames_bin_size - 10) f.close() frames = parse_ID3V2_frames(frames_bin) tit2 = frames[b'TIT2']#标题 print('标题:', tit2) tpe1 = frames[b'TPE1']#作者 print('艺术家:',tpe1) talb = frames[b'TALB']#专辑 print('专辑:', talb) pass
实例运行结果如下:
标题: 1-白日梦
艺术家: 1-白日梦
专辑: 配乐大师
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。