C-读取MP3 IDV2标签大小



我正在尝试读取ID3V2标签的大小。我的代码应该存储该结构中包含标识,版本,标志和大小的第一个标头。从位0到9位的代码曲线并在此处存储

typedef struct
{
   uint32_t id:24; //"ID3"
   uint16_t version; // $04 00
   uint8_t flags; // %abcd0000
   uint32_t size; //4 * %0xxxxxxx
}__attribute__((__packed__))
ID3TAG;

阅读:

fread(tag, sizeof(ID3TAG), 1, media);

然后将标签的值传递到该函数的尺寸。

int unsynchsafe(uint32_t in)
{
    int out = 0, mask = 0x7F000000;
    while (mask) {
        out >>= 1;
        out |= in & mask;
        mask >>= 8;
    }
    return out;
}

但是,同步的返回值永远不能仅仅是标头的正确尺寸。我得到了248627840。我使用exif工具进行了仔细检查,这是不正确的。我真的很感谢任何帮助

与endians有关的问题。我假设您正在使用X86系统或另一个小型系统的系统。ID3文档指出:

多键号中的字节顺序是最重要的字节(例如,$ 12345678将被编码为$ 12 34 56 78)。

因此,size在文件中存储为大型数字。将文件的字节读取到struct中后,您需要将此字节订单转换为Little-endian,然后再删除四个零位才能获得size的最后28位表示。这也是为什么您必须将tag->id0x334449而不是0x494433进行比较 - 存储在tag->id中的字节被作为多键值访问,并以小阶顺序解释。

这是我为完成这项工作的更改。我使用uint8_t的数组来获取正确数量的字节,我将struct更改了一点。我还使用memcmp()来验证tag->id。我自由地使用了unsignedunsigned long类型,以避免位移动困境。转换为Little-endian是原始的,并假设8位字节。

这是您在我的更改中链接到第一篇文章中的整个文件。我将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];  // $04 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 = %un", 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 filen");
        return -1;
    } 
    ID3TAG mp3_tag;
    // check for the format of the file
    if(mp3Header(media, &mp3_tag) != 0)
    {
        printf("Unsupported File Formatn");
        fclose(media);
        return -2;      
    }
    fclose(media);
    return 0;
}

顺便说一句,在C标准库中已经有一个函数可以进行此转换。ntohl()位于netinet/in.h标头文件中,并且它将网络字节顺序(大端)中的uint32_t数字转换为主机字节订单。如果您的系统是大端,则该函数将返回输入值不变。但是,如果您的系统是小型的,则将输入转换为小代表。这对于使用不同字节顺序约定在计算机之间传递数据很有用。还有相关功能htonl()htons()ntohs()

可以通过以下方式更改上述代码(更好)使用ntohl()

#include <netinet/in.h>  // for ntohl()
...
/* convert to host-byte-order (little-endian for x86) */
in = ntohl(be_in);

最新更新