如何从mp3文件中提取音频数据?

时间:2011-02-15 15:18:03

标签: c++ c mp3

我需要创建一个mp3文件的元数据独立散列(即在重拍后可以计算相同的散列)。如何将音频数据仅提取到内存中,而不是通过解压缩器实际运行?

MAD似乎是一个很好的起点 - http://www.underbit.com/products/mad/但似乎并没有明显地暴露出这样做的功能。

任何指示赞赏!

6 个答案:

答案 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的总和。

请参阅https://github.com/pepaslabs/mp3md5sum

答案 4 :(得分:1)

单独使用ffmpeg可以计算音频文件音频片段的MD5哈希值,即无元数据。

使用:

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;
      }
   }
}