读取 MP3 IDV2 标签大小
Reading the MP3 IDV2 tag size
我正在尝试读取 ID3V2 标签的大小。我的代码应该存储第一个 header ,它包含此结构中的标识、版本、标志和大小。代码从第0位读取到第9位并存储在这里
typedef struct
{
uint32_t id:24; //"ID3"
uint16_t version; // 00
uint8_t flags; // %abcd0000
uint32_t size; //4 * %0xxxxxxx
}__attribute__((__packed__))
ID3TAG;
阅读:
fread(tag, sizeof(ID3TAG), 1, media);
然后将 tag.size 的值传递给此函数,该函数取消同步大小的位:
int unsynchsafe(uint32_t in)
{
int out = 0, mask = 0x7F000000;
while (mask) {
out >>= 1;
out |= in & mask;
mask >>= 8;
}
return out;
}
然而,synchsafe 的返回值永远不可能是 header 的正确大小。我得到了 248627840。我用 exif 工具仔细检查了一下,结果不正确。如果能提供任何帮助,我将不胜感激
您遇到的问题与字节顺序有关。我假设您在 x86 系统上工作,或者在另一个小端系统上工作。 ID3 documentation 指出:
The byteorder in multibyte numbers is most significant byte first (e.g. 345678 would be encoded 34 56 78).
因此 size
在文件中存储为大端数字。将文件的字节读入 struct
后,您需要将此字节顺序转换为小端序,然后去除四个零位以获得 size
的最终 28 位表示。这也是您必须将 tag->id
与 0x334449
而不是 0x494433
进行比较的原因——存储在 tag->id
中的字节被作为多字节值访问,并以小端字节序进行解释订单。
以下是我为完成这项工作所做的更改。我稍微更改了 struct
,使用 uint8_t
的数组来获取正确的字节数。我还使用 memcmp()
来验证 tag->id
。我自由使用了 unsigned
和 unsigned long
类型,以避免移位问题。到 little-endian 的转换是原始的,并假定 8 位字节。
这是您在第一个 post 中链接到的完整文件,其中包含我的更改。我将 mp3 文件更改为我可以测试的文件。
#include <stdint.h>
#include <stdio.h>
#include <string.h> // for memcmp()
/**
** TAG is always present at the beggining of a ID3V2 MP3 file
** Constant size 10 bytes
**/
typedef struct
{
uint8_t id[3]; //"ID3"
uint8_t version[2]; // 00
uint8_t flags; // %abcd0000
uint32_t size; //4 * %0xxxxxxx
}__attribute__((__packed__))
ID3TAG;
unsigned int unsynchsafe(uint32_t be_in)
{
unsigned int out = 0ul, mask = 0x7F000000ul;
unsigned int in = 0ul;
/* be_in is now big endian */
/* convert to little endian */
in = ((be_in >> 24) | ((be_in >> 8) & 0xFF00ul) |
((be_in << 8) & 0xFF0000ul) | (be_in << 24));
while (mask) {
out >>= 1;
out |= (in & mask);
mask >>= 8;
}
return out;
}
/**
** Makes sure the file is supported and return the correct size
**/
int mp3Header(FILE* media, ID3TAG* tag)
{
unsigned int tag_size;
fread(tag, sizeof(ID3TAG), 1, media);
if(memcmp ((tag->id), "ID3", 3))
{
return -1;
}
tag_size = unsynchsafe(tag->size);
printf("tag_size = %u\n", tag_size);
return 0;
}
// main function
int main(void)
{
// opens the file
FILE* media = fopen("cognicast-049-carin-meier.mp3", "r");
//checks if the file exists
if(media == NULL)
{
printf("Couldn't read file\n");
return -1;
}
ID3TAG mp3_tag;
// check for the format of the file
if(mp3Header(media, &mp3_tag) != 0)
{
printf("Unsupported File Format\n");
fclose(media);
return -2;
}
fclose(media);
return 0;
}
顺便说一下,C 标准库中已经有一个函数可以进行这种转换。 ntohl()
在 netinet/in.h
头文件中,它将 uint32_t
数字从网络字节顺序(大端字节序)转换为主机字节顺序。如果你的系统是big-endian,函数returns的输入值不变。但是,如果您的系统是小端,则输入将转换为小端表示。这对于使用不同字节顺序约定的计算机之间传递数据很有用。还有相关函数 htonl()
、htons()
和 ntohs()
.
通过将我的原始转换代码替换为:
,可以更改(更好地)以上代码以使用 ntohl()
#include <netinet/in.h> // for ntohl()
...
/* convert to host-byte-order (little-endian for x86) */
in = ntohl(be_in);
我正在尝试读取 ID3V2 标签的大小。我的代码应该存储第一个 header ,它包含此结构中的标识、版本、标志和大小。代码从第0位读取到第9位并存储在这里
typedef struct
{
uint32_t id:24; //"ID3"
uint16_t version; // 00
uint8_t flags; // %abcd0000
uint32_t size; //4 * %0xxxxxxx
}__attribute__((__packed__))
ID3TAG;
阅读:
fread(tag, sizeof(ID3TAG), 1, media);
然后将 tag.size 的值传递给此函数,该函数取消同步大小的位:
int unsynchsafe(uint32_t in)
{
int out = 0, mask = 0x7F000000;
while (mask) {
out >>= 1;
out |= in & mask;
mask >>= 8;
}
return out;
}
然而,synchsafe 的返回值永远不可能是 header 的正确大小。我得到了 248627840。我用 exif 工具仔细检查了一下,结果不正确。如果能提供任何帮助,我将不胜感激
您遇到的问题与字节顺序有关。我假设您在 x86 系统上工作,或者在另一个小端系统上工作。 ID3 documentation 指出:
The byteorder in multibyte numbers is most significant byte first (e.g. 345678 would be encoded 34 56 78).
因此 size
在文件中存储为大端数字。将文件的字节读入 struct
后,您需要将此字节顺序转换为小端序,然后去除四个零位以获得 size
的最终 28 位表示。这也是您必须将 tag->id
与 0x334449
而不是 0x494433
进行比较的原因——存储在 tag->id
中的字节被作为多字节值访问,并以小端字节序进行解释订单。
以下是我为完成这项工作所做的更改。我稍微更改了 struct
,使用 uint8_t
的数组来获取正确的字节数。我还使用 memcmp()
来验证 tag->id
。我自由使用了 unsigned
和 unsigned long
类型,以避免移位问题。到 little-endian 的转换是原始的,并假定 8 位字节。
这是您在第一个 post 中链接到的完整文件,其中包含我的更改。我将 mp3 文件更改为我可以测试的文件。
#include <stdint.h>
#include <stdio.h>
#include <string.h> // for memcmp()
/**
** TAG is always present at the beggining of a ID3V2 MP3 file
** Constant size 10 bytes
**/
typedef struct
{
uint8_t id[3]; //"ID3"
uint8_t version[2]; // 00
uint8_t flags; // %abcd0000
uint32_t size; //4 * %0xxxxxxx
}__attribute__((__packed__))
ID3TAG;
unsigned int unsynchsafe(uint32_t be_in)
{
unsigned int out = 0ul, mask = 0x7F000000ul;
unsigned int in = 0ul;
/* be_in is now big endian */
/* convert to little endian */
in = ((be_in >> 24) | ((be_in >> 8) & 0xFF00ul) |
((be_in << 8) & 0xFF0000ul) | (be_in << 24));
while (mask) {
out >>= 1;
out |= (in & mask);
mask >>= 8;
}
return out;
}
/**
** Makes sure the file is supported and return the correct size
**/
int mp3Header(FILE* media, ID3TAG* tag)
{
unsigned int tag_size;
fread(tag, sizeof(ID3TAG), 1, media);
if(memcmp ((tag->id), "ID3", 3))
{
return -1;
}
tag_size = unsynchsafe(tag->size);
printf("tag_size = %u\n", tag_size);
return 0;
}
// main function
int main(void)
{
// opens the file
FILE* media = fopen("cognicast-049-carin-meier.mp3", "r");
//checks if the file exists
if(media == NULL)
{
printf("Couldn't read file\n");
return -1;
}
ID3TAG mp3_tag;
// check for the format of the file
if(mp3Header(media, &mp3_tag) != 0)
{
printf("Unsupported File Format\n");
fclose(media);
return -2;
}
fclose(media);
return 0;
}
顺便说一下,C 标准库中已经有一个函数可以进行这种转换。 ntohl()
在 netinet/in.h
头文件中,它将 uint32_t
数字从网络字节顺序(大端字节序)转换为主机字节顺序。如果你的系统是big-endian,函数returns的输入值不变。但是,如果您的系统是小端,则输入将转换为小端表示。这对于使用不同字节顺序约定的计算机之间传递数据很有用。还有相关函数 htonl()
、htons()
和 ntohs()
.
通过将我的原始转换代码替换为:
,可以更改(更好地)以上代码以使用ntohl()
#include <netinet/in.h> // for ntohl()
...
/* convert to host-byte-order (little-endian for x86) */
in = ntohl(be_in);