简单的mp3 id3 信息读取

其实id3信息读取还算比较容易。特别是V1,V2就复杂一点。V1的信息放在文件的结尾128字节,V2放在文件开头,而且长度大小不确定。下面我把我写的和xly rics中带的ID3解析拿出给大家分析,相关的id3详细信息到id3的官方网站查询。我的了id3 v1和v2读取程序是分开的。

ID3 V1读取程序。
#include<stdio.h>
#include<string.h>
/*
Header           1-3                3                   标签头

Title              4-33               30                  标题

Artist             34-63             30                  艺术家

Album            64-93             30                  专辑

Year               94-97             4                   出品年代

Comment       98-127           30                  备注

Cenre             128                1                   类型
*/
struct id3_v1_info
{
    char head[3];
    char title[30];
    char artist[30];
    char album[30];
    char year[4];
    char comment[30];
    unsigned char genre;
};
char genre_name[256][32]={"Blues","ClassicRock","Country","Dance","Disco","Funk","Grunge","Hip-Hop","Jazz","Metal","NewAge",
"Oldies","Other","Pop","R&B","Rap","Reggae","Rock","Techno","Industrial","Alternative","Ska","DeathMetal","Pranks","Soundtrack",
"Euro-Techno","Ambient","Trip-Hop","Vocal","Jazz+Funk","Fusion","Trance","Classical","Instrumental","Acid","House","Game",
"SoundClip","Gospel","Noise","AlternRock","Bass","Soul","Punk","Space","Meditative","InstrumentalPop","InstrumentalRock",
"Ethnic","Gothic","Darkwave","Techno-Industrial","Electronic","Pop-Folk","Eurodance","Dream","SouthernRock","Comedy","Cult",
"Gangsta","Top40","ChristianRap","Pop/Funk","Jungle","NativeAmerican","Cabaret","NewWave","Psychadelic","Rave","Showtunes",
"Trailer","Lo-Fi","Tribal","AcidPunk","AcidJazz","Polka","Retro","Musical","Rock&Roll","HardRock","Folk","Folk-Rock",
"NationalFolk","Swing","FastFusion","Bebob","Latin","Revival","Celtic","Bluegrass","Avantgarde","GothicRock","ProgessiveRock",
"PsychedelicRock","SymphonicRock","SlowRock","BigBand","Chorus","EasyListening","Acoustic","Humour","Speech","Chanson","Opera",
"ChamberMusic","Sonata","Symphony","BootyBass","Primus","PornGroove","Satire","SlowJam","Club","Tango","Samba","Folklore",
"Ballad","PowerBallad","RhythmicSoul","Freestyle","Duet","PunkRock","DrumSolo","Acapella","Euro-House","DanceHall","Goa",
"Drum&Bass","Club-House","Hardcore","Terror","Indie","BritPop","Negerpunk","PolskPunk","Beat","ChristianGangstaRap",
"HeavyMetal","BlackMetal","Crossover","ContemporaryChristian","ChristianRock","Merengue","Salsa","TrashMetal","Anime","JPop",
"Synthpop"
};
int  get_mp3_v1_info(char* filename,struct id3_v1_info *id3_v1)
{
    int ret;
    FILE *mp3_file=NULL;
    size_t count_length=1;
    char *string=NULL;
    mp3_file=fopen(filename,"r+");
    if(mp3_file==NULL)
    {
        return -1;
    }
    ret=fseek(mp3_file,-128,SEEK_END);
    if(ret==-1)
    {
        fclose(mp3_file);
        return ret;
    }
    else if(ret==0)
    {
        ret=fread((void *)id3_v1,128,1,mp3_file);
        if(strncmp(id3_v1->head,"TAG",3))
        {
            fclose(mp3_file);
            return -1;
        }
        fclose(mp3_file);
        return ret;
    }
}
int main(int argc,char **argv)
{
    char *filename;
    int ret;
    char string[32];
    size_t length;
    struct id3_v1_info id3_info;
    if(argc!=2)
    {
        return 0;
    }
    filename=argv[1];
    ret=get_mp3_v1_info(filename,&id3_info);
    if(ret<0)
    {
        return 0;
    }
    memset(string,0,32);
    strncpy(string,id3_info.head,3);
    string[3]='/0';
    printf("head:%s/n",string);
    memset(string,0,32);
    strncpy(string,id3_info.title,30);
    string[30]='/0';
    printf("title:%s/n",string);
    memset(string,0,32);
    strncpy(string,id3_info.artist,30);
    string[30]='/0';
    printf("artist:%s/n",string);
    memset(string,0,32);
    strncpy(string,id3_info.album,30);
    string[30]='/0';
    printf("album:%s/n",string);
    memset(string,0,32);
    strncpy(string,id3_info.year,4);
    string[4]='/0';
    printf("year:%s/n",string);
    memset(string,0,32);
    strncpy(string,id3_info.comment,30);
    string[30]='/0';
    printf("comment:%s/n",string);
    printf("genre:%s/n",genre_name[id3_info.genre]);
    return 1;
}

ID3 V2读取程序,没有做完整的支持。缺乏对扩展信息的支持,一般mp3的id3信息也没有。
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>

typedef struct id3_v2_head
{
    char Header[3];    /*必须为"ID3"否则认为标签不存在*/
    unsigned char Ver;    /*版本号ID3V2.3就记录3*/
    unsigned char Revision;    /*副版本号此版本记录为0*/
    unsigned char Flag;    /*存放标志的字节,这个版本只定义了三位,稍后详细解说*/
    unsigned char Size[4];    /*标签大小,包括标签头的10个字节和所有的标签帧的大小*/
}id3_v2_head_t;

typedef struct id3_v2_frame
{
    char FrameID[4];  /*用四个字符标识一个帧,说明其内容,稍后有常用的标识对照表*/
    unsigned char Size[4];   /*帧内容的大小,不包括帧头,不得小于1*/
    unsigned char Flags[2];   /*存放标志,只定义了6位,稍后详细解说*/
}id3_v2_frame_t;

int get_id3_v2_info(char *string)
{
    char *filename=NULL;
    int ret;
    id3_v2_head_t * id3_v2_head=NULL;
    id3_v2_frame_t* id3_v2_frame=NULL;
    int fd;
    char *hdr = NULL;
    long int id3_v2_head_length=0;
    long int id3_v2_frame_length=0;
    long int total_read=0;
    struct stat sb;
    char *info;
    filename=string;
    fd=open(filename, O_RDONLY);
    if(fd==-1)
    {
        return -1;
    }
    fstat(fd,&sb); /*取得文件大小*/
    hdr=mmap(NULL,sb.st_size,PROT_READ,MAP_PRIVATE,fd,0);
    if(hdr== MAP_FAILED) /*判断是否映射成功*/
    {
        return 0;
        close(fd);
    }
    id3_v2_head=(id3_v2_head_t *)malloc(sizeof(id3_v2_head_t));
    if(id3_v2_head==NULL)
    {
        munmap(hdr,sb.st_size);
        close(fd);
        return -1;
    }
    memcpy(id3_v2_head,hdr,sizeof(id3_v2_head_t));
    if(strncmp(id3_v2_head->Header,"ID3",3))
    {
        printf("have no id3 v2 info/n");
        munmap(hdr,sb.st_size);
        close(fd);
        return -1;
    }
    printf("have  id3 v%d.%d info/n",id3_v2_head->Ver,id3_v2_head->Revision);
    id3_v2_head_length=(id3_v2_head->Size[0]&0x7F)*0x200000+(id3_v2_head->Size[1]&0x7F)*0x400+(id3_v2_head->Size[2]&0x7F)*0x80+(id3_v2_head->Size[3]&0x7F);
    printf("head length:%ld/n",id3_v2_head_length);
    total_read+=sizeof(id3_v2_head_t);
    while(total_read<id3_v2_head_length)
    {
        id3_v2_frame=(id3_v2_frame_t *)malloc(sizeof(id3_v2_frame_t));
        if(id3_v2_frame==NULL)
        {
            close(fd);
            munmap(hdr,sb.st_size);
            return -1;
        }
        memcpy(id3_v2_frame,hdr+total_read,sizeof(id3_v2_frame_t));
        id3_v2_frame_length=id3_v2_frame->Size[0]*0x100000000+id3_v2_frame->Size[1]*0x10000+id3_v2_frame->Size[2]*0x100+id3_v2_frame->Size[3];
        if(id3_v2_frame_length==0)
        {
            break;
        }
        total_read+=sizeof(id3_v2_frame_t);
        info=(char*)malloc(id3_v2_frame_length);
        if(info==NULL)
        {
            close(fd);
            munmap(hdr,sb.st_size);
            return -1;
        }
        memcpy(info,hdr+total_read,id3_v2_frame_length);
        printf("%s:%s/n",id3_v2_frame->FrameID,info+1);
        total_read+=id3_v2_frame_length;
        id3_v2_frame_length=0;
    }
    munmap(hdr,sb.st_size);
    close(fd);
    return 0;
}
int main(int argc,char **argv)
{
    char *filename;
    if(argc!=2)
    {
        return 0;
    }
    filename=argv[1];
    get_id3_v2_info(filename);
    return 1;
}

XLYRICS的ID3信息读取
id.h头文件如下
/*
 * id3 include stuff
 */

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>

#define VERSION_SIZE 8

typedef struct {
    char version[VERSION_SIZE]; //It will never be this large!
    char *title;
    char *artist;
    char *album;
    char *year;
    char *comment;
    char *track;
    char *genre;
    char *buffer;
    char *ptr;
    size_t length;
    size_t size;
    size_t tag_length;
} ID3;

#define GENRE_MAX 148
#define ID3_INIT_SIZE 8192

#define isframeid(a) (isupper(a) || isdigit(a))
#define HUGE_STRING_LEN 8192
#define WATCHPOINT printf("WATCHPOINT %s %d/n", __FILE__, __LINE__);

extern unsigned char *blob;
extern size_t filesize;

ID3 * create_ID3();
int destroy_ID3(ID3 *blob);
int getID3ByFile(ID3 *info, const unsigned char *filename);
int getID3ByBlob(ID3 *info, const unsigned char *blob, size_t blob_length);

#ifdef NDEBUG
#define debug() fprintf(stderr, "%d: %d/n", __LINE__, rc);
#else
#define debug()
#endif

id3.c函数文件如下
/*
 **  id3.c
 **  $Id: id3.c,v 1.2 2006/05/05 12:08:57 xiaosuo Exp $
 **  This is something I really did not want to write.
 **
 **  It was pretty annoying that I had to write this
 **  but I could not find one single ID3 library
 **  that had a BSD license and was written in C.
 */

#include "id3.h"
#include <wchar.h>
unsigned char *blob;
size_t filesize;

const char *id3_genres[GENRE_MAX] =
{
    "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop",
    "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae",
    "Rock", "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack",
    "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical",
    "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "Alt",
    "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop",
    "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic",
    "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult",
    "Gangsta Rap", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American",
    "Cabaret", "New Wave", "Psychedelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal",
    "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk",
    "Folk/Rock", "National Folk", "Swing", "Fast-Fusion", "Bebob", "Latin", "Revival",
    "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock",
    "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening",
    "Acoustic", "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony",
    "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango",
    "Samba", "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet",
    "Punk Rock", "Drum Solo", "A Cappella", "Euro-House", "Dance Hall", "Goa",
    "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie", "BritPop", "Negerpunk",
    "Polsk Punk", "Beat", "Christian Gangsta Rap", "Heavy Metal", "Black Metal", "Crossover",
    "Contemporary Christian", "Christian Rock", "Merengue", "Salsa", "Thrash Metal",
    "Anime", "JPop", "Synthpop"
};

void print_char (const unsigned char *foo, int length)
{
    int x=0;

    printf("String %d/n", length);
    for (x; x < length; x++ ) {
        printf("%d: %c:%d/n", x, foo[x], foo[x]);
    }
    putchar('/n');
}

const char * genre_string(int genre)
{
    if(genre < GENRE_MAX && genre > -1)
        return id3_genres[genre];

    return NULL;
}

void clean_string(unsigned char *string, int length)
{
    unsigned char *ptr = string;

    for(; (ptr - string) < length && *ptr; ptr++);
    if(ptr - string == length){
        *(char*)(string) = '/0';
        return;
    }
    size_t len = length - (ptr - string) - 1;
    memmove(string, ptr+1, len);
    *(char *)(string + len) = '/0';
}

char * add_tag(ID3 *info, const unsigned char *tag, size_t length)
{
    char *begin = info->ptr;

    if (length > info->size) {
        if ( length > info->tag_length  ) {
            info->tag_length = length + 1;
            info->buffer = realloc(info->buffer, info->tag_length);
            info->ptr = info->buffer + info->length;
            info->size = info->tag_length;
        } else {
            //This should never, ever happen
            return NULL;
        }
    }
    memcpy(info->ptr, tag, length);   
    clean_string(info->ptr, length);
    info->length += length + 1;
    info->ptr += length;
    *info->ptr++ = '/0';

    // Now return the position of the last string
    return begin;
}

size_t id3_size(const unsigned char* buffer)
{
    size_t size  = 0;
    size =  (buffer[3] & 0x7f) +
        ((buffer[2] & 0x7f) << 7) +
        ((buffer[1] & 0x7f) << 14) +
        ((buffer[0] & 0x7f) << 21);

    return size;
}

size_t id3_size2(const unsigned char * buffer)
{
    size_t size  = 0;
    size =  (buffer[2] & 0x7f) +
        ((buffer[1] & 0x7f) << 7) +
        ((buffer[0] & 0x7f) << 14);

    return size;
}

size_t get_framesize(const char *buffer)
{
    return ((buffer[6] << 8) + (buffer[7]));
}

int get_id3v1_tag (ID3 *info, const unsigned char *blob, size_t blob_length)
{
    const unsigned char *ptr_buffer = blob;
    const char *genre = NULL;

    ptr_buffer += (blob_length - 128);

    if(!strncmp(ptr_buffer, "TAG", 3)) {
        /* Paranoid, not all systems are made equally */
        ptr_buffer +=3;

        info->title = add_tag(info, ptr_buffer, 30);
        ptr_buffer +=30;

        info->artist = add_tag(info, ptr_buffer, 30);
        ptr_buffer +=30;

        info->album = add_tag(info, ptr_buffer, 30);
        ptr_buffer +=30;

        info->year = add_tag(info, ptr_buffer, 4);
        ptr_buffer +=4;

        info->comment = add_tag(info, ptr_buffer, 30);
        ptr_buffer +=30;

        genre = genre_string(ptr_buffer[0]);
        if (genre)
            info->genre = add_tag(info, genre, strlen(genre));

        return 0;
    }

    return 1;
}

int id_2_2(ID3 *info, const unsigned char *blob)
{
    const unsigned char *ptr = blob;

#ifdef NDEBUG
    printf("BEGIN %d : %d /n", info->tag_length, (ptr - blob));
#endif
    while (info->tag_length > (ptr - blob)) {
        size_t size = id3_size2(ptr+3);
        if(size <= 0) break;

        if (size + (ptr - blob) > info->tag_length)
            break;
        /*
        printf("TAG %s/n", ptr);
        printf("String %s/n", ptr + 6 + 1);
        printf("Size %d/n", size);
        printf("OFF_SET %d /n ", off_set);
        */
        if(!strncmp("TP1", ptr, 3)){
            info->artist = add_tag(info, ptr + 6, size);
        } else if (!strncmp("TT2", ptr, 3)) {
            info->title = add_tag(info, ptr + 6, size);
        } else if (!strncmp("TAL", ptr, 3)) {
            info->album = add_tag(info, ptr + 6, size);
        } else if (!strncmp("TRK", ptr, 3)) {
            info->track = add_tag(info, ptr + 6, size);
        } else if (!strncmp("TYE", ptr, 3)) {
            info->year = add_tag(info, ptr + 6, size);
        } else if (!strncmp("COM", ptr, 3)) {
            info->comment = add_tag(info, ptr + 6, size);
        } else if (!strncmp("TCO", ptr, 3)) {
            info->genre = add_tag(info, ptr + 6, size);
        }
        ptr += size + 6;
    }

    return 0;
}

int id_2_3(ID3 *info, const char *blob)
{
    //size_t processedsize = 0;
    const char *ptr = blob;

#ifdef NDEBUG
    printf("BEGIN %d : %d /n", info->tag_length, (ptr - blob));
#endif
    while (info->tag_length > (ptr - blob)) {
        size_t size = get_framesize(ptr);
        if(size <= 0) break;

        if (size + (ptr - blob) > info->tag_length)
            break;

#ifdef NDEBUG
            printf("SIZE %d : %0.4s : %s /n", size, ptr, ptr + 11);
#endif
        if (!strncmp("TPE1",ptr,4)) {
            info->artist = add_tag(info, ptr +10, size);
        } else if (!strncmp("TIT2",ptr,4)) {
            info->title = add_tag(info, ptr +10, size);
        } else if (!strncmp("TALB",ptr,4)) {
            info->album = add_tag(info, ptr +10, size);
        } else if (!strncmp("TRCK",ptr,4)) {
            //info->track = add_tag(info, ptr +10, size);
        } else if (!strncmp("TYER",ptr,4)) {
            info->year = add_tag(info, ptr +10, size);
        } else if (!strncmp("COMM",ptr,4)) {
            // Was originally 14
            info->comment = add_tag(info, ptr +10, size);
        } else if (!strncmp("TCON",ptr,4)) {
        }
        ptr += 10 + size;
        //processedsize += 10 + size;
    }

    return 0;
}

int process_extended_header(ID3 *info, const char *blob)
{
    unsigned long CRC = 0;
    unsigned long paddingsize = 0;

    /*Shortcut. The extended header size will be either 6 or 10 bytes.
      If it's ten bytes, it means that there's CRC data (though we check
      the flag anyway). I'm gonna save it, though I'll be damned if I
      know what to do with it.*/
    if ((blob[3] == 0x0A) && (blob[4])) {
        CRC = (blob[10] << 24) + (blob[11] << 16) +
            (blob[12] << 8) + (blob[13]);
    }

    paddingsize = (blob[6] << 24) + (blob[7] << 16) +
        (blob[8] << 8) + (blob[9]);

    /*subtract the size of the padding from the size of the tag */
    info->tag_length -= paddingsize;

    /*continue decoding the frames */
    return id_2_3(info, blob);
}

int get_id3v2_tag (ID3 *info, const unsigned char *blob, size_t blob_length)
{
    int unsynchronized = 0;
    int hasExtendedHeader = 0;
    int experimental = 0;
    const char *ptr_buffer = blob;

    if(!strncmp(ptr_buffer, "ID3", 3)) {
        info->tag_length = id3_size(ptr_buffer+6);
#ifdef NDEBUG
        printf("TAG SIZE %d/n", info->tag_length);
#endif
        snprintf(info->version, VERSION_SIZE, "%d.%d", blob[3], blob[4]);

        if ((blob[5] & 0x40) >> 6) {
            hasExtendedHeader = 1;
        }
        if ((blob[5] & 0x80) >> 7) {
            unsynchronized = 1;
        }

#ifdef UNUSED
        /*Present, but not very useful*/
        if ((blob[5] & 0x20) >> 5) {
            experimental = 1;
        }

        if (unsynchronized) {
            register int i,j; /*index*/
            /*Replace every instance of '0xFF 0x00'
              with '0xFF'*/
            for(i=0; i < tagsize; i++) {
                if (buffer[i] == 0xFF && buffer[i+1] == 0x00) {
                    for(j=i+1; i < tagsize; i++) {
                        buffer[j] = buffer[j+1];
                    }
                }
            }
        }
#endif

        /* Move past the above */
        ptr_buffer += 10;
        /*If the tag has an extended header, parse it*/
        if (hasExtendedHeader) {
            return process_extended_header(info, blob);
        } else if (blob[3] == 2) {
            return id_2_2(info, ptr_buffer);
        } else if (blob[3] == 3) {
            return id_2_3(info, ptr_buffer);
        }

    }

    return 1;
}

int getID3ByFile(ID3 *info, const unsigned char *filename)
{
    int rc;
    struct stat buf;
    int fd = -1;

    if(stat(filename, &buf)) {
        return -1;
    }

    if((fd = open(filename, O_RDONLY)) ==  -1) {
        return -1;
    }

    lseek(fd, 0, SEEK_SET);
    filesize = buf.st_size;
    blob = mmap(NULL, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if((blob ==(void *)-1)) {
        fprintf(stderr, "Errno %s(%d)/n",strerror(errno), errno);
        return -1;
    }

    close(fd);
    rc = getID3ByBlob(info, blob, filesize);
    return rc;
}

int getID3ByBlob(ID3 *info, const unsigned char *blob, size_t blob_length)
{
    int rc = -1;

    // Sometimes people encode both headers, so we test for both

    /*    if ((blob_length > 128) && !get_id3v1_tag(info, blob, blob_length)) {
        sprintf(info->version, "1");
        rc = 0;
        goto out;
        }
        */

    if (!get_id3v2_tag(info, blob, blob_length))
        rc = 0;

out:
    return rc;
}

int destroy_ID3(ID3 *info)
{
    free(info->buffer);
    free(info);

    return 0; //Eventually we should do more here
}

ID3 * create_ID3(void)
{
    //At the moment ID3 is just a structure
    ID3 *info = (ID3 *)malloc(sizeof(ID3));
    if (info)
        memset(info, 0, sizeof(ID3));
    else
        return NULL;

    info->buffer = (char *)malloc(ID3_INIT_SIZE);
    info->size = ID3_INIT_SIZE;
    info->ptr = info->buffer;

    return info;
}
ID3越来越强大了,可以包含一些图片信息了,结构有点想wma的文件结构了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值