2016-12-11 5 views
0

Я пытаюсь прочитать размер тега ID3V2. мой код должен хранить первый заголовок, который содержит идентификатор, версию, флаги и размер в этой структуре. Кодовый freads от бита 0 до бита 9 и сохраняет его здесьЧтение размера ID ID ID2 ID

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 к этой функции, которая unsyncsafe бит размера:

int unsynchsafe(uint32_t in) 
{ 
    int out = 0, mask = 0x7F000000; 

    while (mask) { 
     out >>= 1; 
     out |= in & mask; 
     mask >>= 8; 
    } 

    return out; 
} 

Однако возвращаемое значение synchsafe никогда не может быть правильным размером только заголовка. Я получил 248627840. Я дважды проверил с помощью exif-инструмента, и это было неправильно. Я бы очень признателен за любую помощь.

+0

Пожалуйста, включите [mcve] в вашем вопросе. См. Также [ask]. Это не сайт проверки кода. – e0k

+0

Вы должны поместить код в свой вопрос, чтобы люди могли его увидеть и скопировать. Не то, чтобы это ваша проблема, но обычно это плохая идея для основных типов typedef, например 'typedef uint32_t LONG'. –

+0

для всех, жалующихся на формат моего вопроса, я полностью изменил его. Пожалуйста, взгляните на мой код. –

ответ

1

Проблема, с которой вы сталкиваетесь, связана с контентом. Я предполагаю, что вы работаете с системой 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); 
+0

Это очень хороший информация. Это действительно помогло мне прояснить ситуацию. Большое спасибо за ваше время и силы: D –

+0

@ AbdullahEmad-- Конечно; Я добавил несколько комментариев к моему ответу о функции стандартной библиотеки 'ntohl()', которая может использоваться для преобразования из big-endian в то, что подходит для вашей системы. –

+0

Спасибо, миллион раз. Я хочу убедиться, что получаю это. Согласно документации id3 размер делится на 3 байта. Каждый байт имеет значение 0 на 7-м бите. Означает ли это, что мне нужно избавиться от этого 7-го нуля, прежде чем передать его функции ntohl(), которая преобразует число в little endian или эта функция позаботится об этом для меня? –

Смежные вопросы