Проблема, с которой вы сталкиваетесь, связана с контентом. Я предполагаю, что вы работаете с системой x86 или с другой системой, которая малозначительна. ID3 documentation утверждает, что:
в числах порядком байтов многобайтовых наиболее значащий байт первый (например, $ 12345678 будет закодирован $ 12 34 56 78).
Таким образом, size
хранится в виде большого числа символов в файле. После того, как вы прочитаете байты файла в своем struct
, вам необходимо преобразовать этот байтодер в little-endian, прежде чем удалять четыре нулевых бита, чтобы получить окончательное 28-битное представление size
. Вот почему вам пришлось сравнивать tag->id
с 0x334449
вместо 0x494433
- байты, хранящиеся в tag->id
, были доступны как многобайтовые значения и интерпретировались в порядке юнитов.
Вот изменения, которые я сделал для выполнения этой работы. Я немного изменил ваш struct
, используя массивы uint8_t
, чтобы получить правильное количество байтов. Я также использовал memcmp()
для проверки tag->id
. Я использовал либеральные типы unsigned
и unsigned 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 = %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) для размещения байтового байта. Если ваша система является big-endian, функция возвращает неизменное значение ввода. Но если ваша система малозначна, вход преобразуется в представление little-endian. Это полезно для передачи данных между компьютерами с использованием различных соглашений о порядке байтов. Существуют также связанные функции htonl()
, htons()
и ntohs()
.
Приведенный выше код может быть изменен (в лучшую сторону), чтобы использовать ntohl()
, заменив мой примитивный код преобразования с:
#include <netinet/in.h> // for ntohl()
...
/* convert to host-byte-order (little-endian for x86) */
in = ntohl(be_in);
Пожалуйста, включите [mcve] в вашем вопросе. См. Также [ask]. Это не сайт проверки кода. – e0k
Вы должны поместить код в свой вопрос, чтобы люди могли его увидеть и скопировать. Не то, чтобы это ваша проблема, но обычно это плохая идея для основных типов typedef, например 'typedef uint32_t LONG'. –
для всех, жалующихся на формат моего вопроса, я полностью изменил его. Пожалуйста, взгляните на мой код. –