二进制读取mp3文件的ID3标签

时间:2015-12-26 19:18:52

标签: c++ perl binary mp3 id3

我试图用c ++读取一个mp3文件,并显示该文件包含的id3信息。我遇到的问题是当我读取帧头时,它所持有的内容大小是错误的。而不是给我一个10字节的整数,它给了我167772160字节。 http://id3.org/id3v2.3.0#ID3v2_frame_overview

Header header;
ContentFrame contentFrame;

ifstream file(argv[1], fstream::binary);
//Read header 
file.read((char*)&header, FRAMESIZE);

//This will print out 699 which is the correct filesize
cout << "Size: " << ID3_sync_safe_to_int(header.hSize) << endl << endl;

//Read frame header
file.read((char*)&contentFrame, FRAMESIZE);
//This should print out the frame size. 
cout << "Frame size: " << int(contentFrame.contentSize) << endl;

上面的代码用于将二进制数据转换为ASCCI数据。 主要内部

my($tag, $ver, $rev, $flags, $size) = unpack("Z3 C C C N"), "header");
my($frameID, $FrameContentSize, $frameFlags) = unpack("Z4 N C2", "content");

我已经在 Perl 中为此任务编写了一个程序,它运行正常,使用unpack如:

<a id="search_btn" data-role="button" class="ui-btn ui-corner-all ui-shadow ui-btn-inline ui-icon-search ui-btn-icon-left">Show me the players</a>

sync_safe_to_int也用于使标题的大小正确,但对于竞争大小,它只打印无任何转换 N“网络”(大端)命令中的无符号长(32位)。
C无符号字符(八位字节)值。
Z以空值终止(ASCIZ)的字符串,将为空填充。

我程序的输出:
标题内容
标签:ID3
Ver:3
Rev:0
标志:0
尺寸:699

错误的输出! 框架内容
ID:TPE1
尺寸:167772160
旗帜:

更正Perl的输出! 框架内容
ID:TPE1
尺寸:10
标志:0

3 个答案:

答案 0 :(得分:1)

contentFrame.contentSize定义为uint32_t,但打印为(signed)int

此外,由于document 状态多字节数字为 Big Endian

  

ID3v2中的投注者是最重要的位(MSB)。的的   多字节数字中的字节顺序是最重要的字节(例如,   $ 12345678将编码$ 12 34 56 78)。

但是,contentFrame.contentSize没有进行转换。这些字节也应该反转,如ID3_sync_safe_to_int()中所示,但这次以8的倍数而不是7的倍数(或使用ntohl() - 网络到主机的顺序)。

你说你得到1677772160而不是18,但即使操纵上面的位/字节,它们似乎也没有意义。你确定这些是正确的数字吗?在帖子的顶部,您还有其他值:

  

而不是给我一个低于100字节的低整数,它给了我   140000字节。

调用file.read((char*)&contentFrame, FRAMESIZE);后你看过内存中的字节了吗?但是,如果您的ID显示TPE1,则该位置应该没问题。我只是想知道你提供的数字是否正确,因为它们没有意义。

使用nthol()转化更新:

//Read frame header
file.read((char*)&contentFrame, FRAMESIZE);
uint32_t frame_size = ntohl(contentFrame);
cout << "Frame size: " << frame_size << endl;

ntohl()将在BE系统上的LE系统上运行(在BE系统上,它根本不会这样做)。

答案 1 :(得分:1)

而不是您最初发布的1677772160,而您获得的值是167772160,即0x0A000000,它立即显示您的字节与您期望的0x0000000A(十进制10)相反

您已安排Perl使用N格式以big-endian格式读取此内容,但您的C代码使用简单的uint32_t,这是依赖于硬件的,可能是小端的

您需要为此字段编写一个字节反转子例程,其行为与标头字段的ID3_sync_safe_to_int相同,但使用该值的所有32位。像这样的东西

uint32_t reverse_endian(uint32_t val)
{
   typedef union {
      uint32_t val;
      uint8_t byte[4];
   } split;

   split *original = (split *) &val;
   split new;

   new.byte[0] = original->byte[3];
   new.byte[1] = original->byte[2];
   new.byte[2] = original->byte[1];
   new.byte[3] = original->byte[0];

   return new.val;
}

答案 2 :(得分:0)

好吧我不确定你是否正确地用{{1}}方法解释了你的帧大小。

编辑:我不知道导致此问题的原因,但您可以单独使用fread阅读框架大小或执行此操作:

{{1}}