正确读取被 ID3 标签破坏的 MP3 文件
Posted
技术标签:
【中文标题】正确读取被 ID3 标签破坏的 MP3 文件【英文标题】:Proper reading of MP3 file disrupted by ID3 tags 【发布时间】:2016-06-12 18:20:08 【问题描述】:我的学期项目将于本周四到期,我在阅读 MP3 文件时遇到了重大问题(该项目是关于声音分析的,不要问我到底是什么以及为什么我这么晚才做)。
首先,我读取前 10 个字节来检查 ID3 标签。如果它们存在,我将直接跳到第一个 MP3 标题 - 或者至少这是个大主意。下面是我计算 ID3 标签大小的方法:
if (inbuf[0] == 'I' && inbuf[1] == 'D' && inbuf[2] == '3') //inbuf contains first 10 bytes from file
int size = inbuf[3] * 2097152 + inbuf[4] * 16384 + inbuf[5] * 128 + inbuf[6]; //Will change to binary shifts later
//Do something else with it - skip rest of ID3 tags etc
它适用于没有 ID3 标签的文件和一些带有它们的文件,但对于其他一些文件 ffmpeg(我用于解码)返回“无标题”错误,这意味着它没有正确捕获 MP3 标题。我知道,因为如果我从该 .mp3 文件中删除 ID3(例如使用 Winamp),则不会发生错误。结论是大小计数算法并不总是有效的。
所以问题是:我如何才能知道 .mp3 文件的整个 ID3 部分(所有可能的标签、专辑图片等等)到底有多大?我到处都在寻找它,但我一直在寻找我在上面发布的这个算法。有时我还需要考虑大约 10 个字节的页脚,但它似乎经常需要超过 10 个字节才能最终捕获正确的 MP3 帧。
【问题讨论】:
当您读取 ID3 的大小时,它表示 ID3 标头之后的数据量,因此为了说明标头的 10 个字节,我们将+ 10
添加到大小。如果大小不正确(在十六进制编辑器中检查?)然后考虑查看 sync safe integers 并通过这样的函数传递您的值以获得返回的正确大小。
PS:从十六进制编辑器复制并粘贴到此处...仅显示有问题的 ID3 标头的 10 个字节。您通过代码获得了什么尺寸?并且(来自十六进制编辑器)还告诉我们显示的字节 FF FB
第一个 mp3 帧的偏移量。
【参考方案1】:
ID3v1 标记的大小始终固定为 128 字节。
我会找到下面的描述
如果将所有这些字段的大小相加,我们会发现 30+30+30+4+30+1 等于 125 字节而不是 128 字节。缺失的三个字节可以在标签的最开始,歌曲标题之前找到。这三个字节始终是“TAG”并且是这确实是ID3标签的标识。查找 ID3v1/1.1 标记的最简单方法是查找文件末尾 128 个字节的单词“TAG”。
来源:http://id3.org/ID3v1
还有另一个版本,叫做 ID3v2:
其中一个设计目标是 ID3v2 应该非常灵活和可扩展...... 由于每帧可以是 16MB,而整个标签可以是 256MB,因此您可能再也不会遇到与尝试在限制为 30 个字符的旧 ID3 中编写有用注释时相同的情况了。
此 ID3v2 始终从音频文件的开头开始,您可以在此处阅读:http://id3.org/ID3v2Easy
ID3v2/file identifier "ID3"
ID3v2 version $03 00
ID3v2 flags %abc00000
ID3v2 size 4 * %0xxxxxxx
ID3v2 标记大小用四个字节编码,其中每个字节的最高有效位(第 7 位)设置为零,总共 28 位。零位被忽略,因此一个 257 字节长的标签表示为 $00 00 02 01。
【讨论】:
我已经知道这一切了。问题是,即使我以这种方式实现读取 ID3v2 标记大小并将其保存到某个 x 变量,在读取这些 x 字节后,解码器也几乎无法将文件中的下一个内容识别为正确的 MP3 帧。我的问题不是关于如何计算这个大小,而是为什么在计算它并读取 ID3 的其余部分之后,文件中似乎仍然有一些数据。【参考方案2】:bool LameDecoder::skipDataIfRequired()
auto data = m_file.read(3);
Q_ASSERT(data.size() == 3);
if (data.size() != 3)
return false;
if (memcmp(data.constData(), "ID3", 3))
m_file.seek(0);
return true;
// ID3v2 tag is detected; skip it
m_file.seek(3+2+1);
data = m_file.read(4);
if (data.size() != 4)
return false;
qint32 size = (data[0] << (7*3)) | (data[1] << (7*2)) |
(data[2] << 7) | data[3];
m_file.seek(3+2+1+4+size);
return true;
【讨论】:
为什么要使用 7 进行位移? Draykoon D,答案在上面的答案中。使用 28 (4*7) 位而不是 32 位。以上是关于正确读取被 ID3 标签破坏的 MP3 文件的主要内容,如果未能解决你的问题,请参考以下文章
通过javascript从文件夹中的mp3文件中读取id3标签
使用 JavaFX MediaPlayer 从 MP3 读取 ID3v2 标签