Python 实现 MP3 格式真伪校验:基于 ID3 与帧头解析
本文介绍如何使用 Python 判断音频文件是否为真正的 MP3 格式。由于仅靠后缀名不可靠,需通过解析文件结构来验证。MP3 文件通常包含 ID3V2 标签、音频帧和 ID3V1 标签。通过检测文件头部的 'ID3' 标识、结尾的 'TAG' 标识或帧头以 'FFF'(ASCII 255)开头,可有效区分真实 MP3 与伪装文件。
在项目中利用 MP3 格式播放音效时,曾遇到一个文件虽然以 .mp3 结尾,但在程序中无法播放声音。经排查发现,该文件实际为 WAV 格式,仅被错误地重命名。为了对资源进行准确的 MP3 格式判断,仅依赖文件后缀名是不可靠的,必须从编码格式层面进行判断。具体方法如下:
1. MP3 编码结构分析
MP3 文件属于流媒体文件格式,因此它没有像 AVI 或 WAV 那样统一的文件头(RIFF 结构)。对于 AVI 和 WAV 格式,可以通过对比 RIFF 字符串来快速识别,但 MP3 只能通过分析其内部编码结构来判断。这里简要说明 MP3 的编码规则,如需详细了解可参考相关技术文章。
一个标准的 MP3 文件大体由三个部分组成:TAG_V2(即 ID3V2)、音频数据帧、以及 TAG_V1(即 ID3V1)。
a). ID3V2 位于文件起始位置,以 'ID3' 字符串开头,存储了作者、作曲者、专辑等元数据。其长度不固定,是对 ID3V1 信息量的扩展,但并非所有 MP3 文件都包含此部分。
b). 一系列音频数据帧位于文件中间,帧的数量由文件大小和单帧长度决定。每个帧以 'FFF' 开头(对应 ASCII 码 255),帧长度可能固定也可能不固定,具体取决于位率(bitrate)。每个帧由帧头和数据实体两部分组成,帧头记录了 MP3 的位率、采样率和版本等信息,且各帧之间相互独立。
c). ID3V1 位于文件末尾,以 'TAG' 字符串开头,同样包含作者、作曲者、专辑等信息,其长度固定为 128 Byte。此部分也是可选的。
|
ID3V2 |
包含了作者,作曲,专辑等信息,长度不固定,扩展了ID3V1的信息量。 |
|
Frame . . . Frame |
一系列的帧,个数由文件大小和帧长决定 每个FRAME的长度可能不固定,也可能固定,由位率bitrate决定 每个FRAME又分为帧头和数据实体两部分 帧头记录了mp3的位率,采样率,版本等信息,每个帧之间相互独立。 |
|
ID3V1 |
包含了作者,作曲,专辑等信息,长度为128BYTE。 |
综上所述,通过检测上述三个结构(TAG_V2、音频数据帧、TAG_V1)中的特定开头标识,即可判断文件是否具备 MP3 编码特征。
2. Python 实现代码
# coding: utf-8
'''
@author: BigFengFeng
@time: 16/12/21 下午6:10
@license: Apache Licence
@description:
'''
import os
#mp3filePath是否是mp3格式的
def isMp3Format(mp3filePath):
#读取文件内字符串
f = open(mp3filePath, "r");
fileStr = f.read();
f.close();
head3Str = fileStr[:3];
#判断开头是不是ID3
if head3Str == "ID3":
return True;
#判断结尾有没有TAG
last32Str = fileStr[-32:];
if last32Str[:3] == "TAG":
return True;
#判断第一帧是不是FFF开头, 转成数字
# fixme 应该循环遍历每个帧头,这样才能100%判断是不是mp3
ascii = ord(fileStr[:1]);
if ascii == 255:
return True;
return False;
#遍历folderPath看看是不是都是mp3格式的,
#是就true,不是就是false, 并返回是mp3的list,不是MP3的list
def isMp3FolderTraverse(folderPath):
mp3List = [];
notMp3List = [];
isAllMpFormat = True;
for dirpath, dirnames, filenames in os.walk(folderPath):
for filename in filenames:
path = dirpath + os.sep + filename;
isMp3 = isMp3Format(path);
#判断是不是mp3结尾的 并且 是mp3格式的
if isMp3 == False and str.endswith(path, ".mp3") == True:
# print("--warning: file " + path + " is not mp3 format!--");
notMp3List.append(path);
isAllMpFormat = False;
else:
mp3List.append(path);
return isAllMpFormat, mp3List, notMp3List;
if __name__ == '__main__':
isMp3Format("s_com_click1.mp3");
isAllMp3, mp3List, notMp3List = isMp3FolderTraverse("sound");
print isAllMp3;
print mp3List;
print notMp3List;
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









