我需要创建一个mp3文件的元数据独立散列(即在重拍后可以计算相同的散列)。如何将音频数据仅提取到内存中,而不是通过解压缩器实际运行?
MAD似乎是一个很好的起点 - http://www.underbit.com/products/mad/但似乎并没有明显地暴露出这样做的功能。
任何指示赞赏!
答案 0 :(得分:6)
如何将音频数据仅提取到内存中,而不是通过解压缩器实际运行?
您无法解压缩音频数据 - 它已被压缩!但是,如果您只想要原始压缩流,请继续阅读!
典型的mp3音频文件将分为几个部分:
[很可能是metatag]
[可能的垃圾]
[可能的XING / LAME标签[可能更多垃圾]]
[mp3音频帧]
[可能的元标记]
可能是metatag:大多数mp3音频文件都会有id3标记。请注意,某些用户可能会使用不同的标记格式标记其mp3文件,例如APE,因此您也需要考虑这一点。
可能的垃圾:一些mp3音频文件已被标记,重新标记和转换,因此metatag标头可能无法为第一个音频帧提供准确的偏移量,因为之前标签的残留物可以留下来。 foobar2000可以选择解决此问题。
可能的XING / LAME标记:这些标记包含在mp3音频帧中,但它们不包含实际音频。 madplay有代码向您展示如何阅读和解析这些框架。 XING / LAME标头可能有帧数,因此值得解析这些标头。同样,如果文件已经通过许多不同的标记器和编辑器,则可能存在几个格式错误,无效的音频帧。
MP3音频帧:实际压缩流,分为“帧”。每帧将以同步位模式0xFFE开始。
可能的元标记:在文件末尾找到更多的元标记并不罕见。 id3v1,APE,歌词都可以在这里找到。
要查找音频帧偏移量,您需要解析任何元标记头,然后开始寻找同步位模式。您不能只从文件的开头开始查找同步模式,因为并非所有标记都正确支持unsynchronization,因此元标记本身可能包含0xFFE模式。
获得第一个音频帧的偏移后,您应该查看文件的末尾并计算有多少非音频数据,以便您知道何时停止解析音频。一旦你有音频数据开头的偏移量和音频数据末尾的偏移量,你就可以通过你的散列/校验和函数传递音频数据!
答案 1 :(得分:3)
您可以使用ffmpeg 直接使用复制模式访问音频内容。什么格式无关紧要,因为API会为您提供一个包含原始数据的容器(仅在复制模式下)。如果您有视频,或者想要处理已解码的音频数据,也可以进行解复用和解码。
查看ffmpeg的示例,快速介绍如何执行此操作。通过使用ffmpeg,我的意思是不使用该工具,而是使用c ++ / c中的libffmpeg(libavformat,libavcodec),尽管我认为您也可以使用ffmpeg工具从cmdline执行此操作,方法是将输出发送到stdout并将其传输到md5sum或等价的东西(如果你是unix用户,那就是)。
特殊情况“-acodec copy”告诉ffmpeg使用与用于解码的编码相同的编解码器。换句话说,不会发生音频转码。
答案 2 :(得分:2)
什么样的音频数据?原始解码的PCM流?个人MP3相框?如果它是一个封装在.wav中的MP3怎么办?它仍然可以有.mp3扩展名,但在它周围有完整的.wav包装。
剥离ID3v1标签很简单 - 文件末尾只有128个字节。 ID3v2有点难 - 它的长度可变,并且在MP3的开头之前,你必须解析长度字段(这是4字节,其中只使用最低的7位,为标签提供28位的最大长度)。 .wav包装器会更难 - 我不知道.wav强加给元数据的任何细节。
答案 3 :(得分:1)
我最近也需要解决这个问题(检测具有不同ID3标签的重复mp3文件)。最简单的方法是使用ffmpeg制作一个mp3文件的副本,删除所有ID3标签,然后取md5的总和。
答案 4 :(得分:1)
使用:
ffmpeg -v -i $file -acodec copy -f md5 -
请注意,FLAC已将MD5哈希存储为元数据。
答案 5 :(得分:0)
我为一个Linux手机盒写了这个简单的小片段,里面有一个无法处理标签的旧MP3播放器。剩下的就是mp3标题和数据(在编码的stdout上)。您可以将它用于md5。
#include <fcntl.h>
#define DUMPTAGS
int main(int argc, char **argv){
unsigned char buf[4096];
int len,fd = open(argv[1],O_RDONLY);
while (len=read(fd,buf,10)){ // handle ID3v2 tags (maybe multiple)
if (buf[0]=='I' && buf[1]=='D' && buf[2]=='3'){
len=read(fd,buf,buf[9]|(buf[8] << 7)|(buf[7] << 14)|(buf[6] << 21));
#ifdef DUMPTAGS
write(2,buf,len);
#endif
} else break;
}
while (write(1,buf,len)){
unsigned char tag[3] = {'T','A','G'}, *end;
len=read(fd,buf,4096);
end=(unsigned char *)memmem(buf,len,&tag,3);
if (end){ //handle ID3v1 tag (should only be 1)
write(1,buf,end-buf);
#ifdef DUMPTAGS
write(2,end,len-(end-buf));
#endif
break;
}
}
}