Чтение размера тега 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);
затем передает значение tag.size этой функции, которая не синхронизирует биты размера:
int unsynchsafe(uint32_t in)
{
int out = 0, mask = 0x7F000000;
while (mask) {
out >>= 1;
out |= in & mask;
mask >>= 8;
}
return out;
}
Однако возвращаемое значение synchsafe никогда не может иметь правильный размер только заголовка. Я получил 248627840. Я дважды проверил, используя инструмент EXIF, и это было не правильно. Я был бы очень признателен за любую помощь
1 ответ
Проблема, с которой вы столкнулись, связана с порядком байтов. Я предполагаю, что вы работаете в системе x86 или в другой системе с прямым порядком байтов. Документация ID3 гласит, что:
Метеоритель в многобайтовых числах является старшим значащим байтом первым (например, 12345678 долларов будет закодировано в 12 34 56 78 долларов).
Итак size
хранится как порядковый номер в файле. После того, как вы прочитали байты файла в свой struct
Вам нужно преобразовать этот метеорометр в младший порядок, прежде чем вычеркнуть четыре нулевых бита, чтобы получить окончательное 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
число от сетевого байтового порядка (который является прямым порядком байтов) до хостового байтового порядка. Если ваша система имеет порядок байтов, функция возвращает входное значение без изменений. Но если ваша система имеет порядок с прямым порядком байтов, входные данные преобразуются в представление с прямым порядком байтов. Это полезно для передачи данных между компьютерами с использованием различных соглашений о порядке байтов. Есть также связанные функции htonl()
, htons()
, а также ntohs()
,
Приведенный выше код можно изменить (в лучшую сторону), чтобы использовать ntohl()
заменив мой примитивный код преобразования на:
#include <netinet/in.h> // for ntohl()
...
/* convert to host-byte-order (little-endian for x86) */
in = ntohl(be_in);