Mercurial > audlegacy
view Plugins/Input/mpg123/id3_frame.c @ 61:fa848bd484d8 trunk
[svn] Move plugins to Plugins/
author | nenolod |
---|---|
date | Fri, 28 Oct 2005 22:58:11 -0700 |
parents | |
children | d539e5c5f730 |
line wrap: on
line source
/********************************************************************* * * Copyright (C) 1999-2000, 2001, Espen Skoglund * Department of Computer Science, University of Tromsų * * Filename: id3_frame.c * Description: Code for handling ID3 frames. * Author: Espen Skoglund <espensk@stud.cs.uit.no> * Created at: Fri Feb 5 23:47:08 1999 * * $Id: id3_frame.c,v 1.5 2004/07/20 21:47:22 descender Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ********************************************************************/ #include "config.h" #ifdef HAVE_LIBZ #include <zlib.h> #endif #include <glib.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <stdarg.h> #include "xmms-id3.h" #include "id3_header.h" static void *id3_frame_get_dataptr(struct id3_frame *frame); static int id3_frame_get_size(struct id3_frame *frame); static int id3_read_frame_v22(struct id3_tag *id3); /* * Description of all valid ID3v2 frames. */ static struct id3_framedesc framedesc[] = { {ID3_AENC, "AENC", "Audio encryption"}, {ID3_APIC, "APIC", "Attached picture"}, {ID3_ASPI, "ASPI", "Audio seek point index"}, /* v4 only */ {ID3_COMM, "COMM", "Comments"}, {ID3_COMR, "COMR", "Commercial frame"}, {ID3_ENCR, "ENCR", "Encryption method registration"}, {ID3_EQUA, "EQUA", "Equalization"}, /* v3 only */ {ID3_EQU2, "EQU2", "Equalization (2)"}, /* v4 only */ {ID3_ETCO, "ETCO", "Event timing codes"}, {ID3_GEOB, "GEOB", "General encapsulated object"}, {ID3_GRID, "GRID", "Group identification registration"}, {ID3_IPLS, "IPLS", "Involved people list"}, /* v3 only */ {ID3_LINK, "LINK", "Linked information"}, {ID3_MCDI, "MCDI", "Music CD identifier"}, {ID3_MLLT, "MLLT", "MPEG location lookup table"}, {ID3_OWNE, "OWNE", "Ownership frame"}, {ID3_PRIV, "PRIV", "Private frame"}, {ID3_PCNT, "PCNT", "Play counter"}, {ID3_POPM, "POPM", "Popularimeter"}, {ID3_POSS, "POSS", "Position synchronisation frame"}, {ID3_RBUF, "RBUF", "Recommended buffer size"}, {ID3_RVAD, "RVAD", "Relative volume adjustment"}, /* v3 only */ {ID3_RVA2, "RVA2", "RVA2 Relative volume adjustment (2)"}, /* v4 only */ {ID3_RVRB, "RVRB", "Reverb"}, {ID3_SEEK, "SEEK", "Seek frame"}, /* v4 only */ {ID3_SIGN, "SIGN", "Signature frame"}, /* v4 only */ {ID3_SYLT, "SYLT", "Synchronized lyric/text"}, {ID3_SYTC, "SYTC", "Synchronized tempo codes"}, {ID3_TALB, "TALB", "Album/Movie/Show title"}, {ID3_TBPM, "TBPM", "BPM (beats per minute)"}, {ID3_TCOM, "TCOM", "Composer"}, {ID3_TCON, "TCON", "Content type"}, {ID3_TCOP, "TCOP", "Copyright message"}, {ID3_TDAT, "TDAT", "Date"}, /* v3 only */ {ID3_TDEN, "TDEN", "Encoding time"}, /* v4 only */ {ID3_TDLY, "TDLY", "Playlist delay"}, {ID3_TDOR, "TDOR", "Original release time"}, /* v4 only */ {ID3_TDRC, "TDRC", "Recording time"}, /* v4 only */ {ID3_TDRL, "TDRL", "Release time"}, /* v4 only */ {ID3_TDTG, "TDTG", "Tagging time"}, /* v4 only */ {ID3_TENC, "TENC", "Encoded by"}, {ID3_TEXT, "TEXT", "Lyricist/Text writer"}, {ID3_TFLT, "TFLT", "File type"}, {ID3_TIME, "TIME", "Time"}, /* v3 only */ {ID3_TIPL, "TIPL", "Involved people list"}, /* v4 only */ {ID3_TIT1, "TIT1", "Content group description"}, {ID3_TIT2, "TIT2", "Title/songname/content description"}, {ID3_TIT3, "TIT3", "Subtitle/Description refinement"}, {ID3_TKEY, "TKEY", "Initial key"}, {ID3_TLAN, "TLAN", "Language(s)"}, {ID3_TLEN, "TLEN", "Length"}, {ID3_TMCL, "TMCL", "Musician credits list"}, /* v4 only */ {ID3_TMOO, "TMOO", "Mood"}, /* v4 only */ {ID3_TMED, "TMED", "Media type"}, {ID3_TOAL, "TOAL", "Original album/movie/show title"}, {ID3_TOFN, "TOFN", "Original filename"}, {ID3_TOLY, "TOLY", "Original lyricist(s)/text writer(s)"}, {ID3_TOPE, "TOPE", "Original artist(s)/performer(s)"}, {ID3_TORY, "TORY", "Original release year"}, /* v3 only */ {ID3_TOWN, "TOWN", "File owner/licensee"}, {ID3_TPE1, "TPE1", "Lead performer(s)/Soloist(s)"}, {ID3_TPE2, "TPE2", "Band/orchestra/accompaniment"}, {ID3_TPE3, "TPE3", "Conductor/performer refinement"}, {ID3_TPE4, "TPE4", "Interpreted, remixed, or otherwise modified by"}, {ID3_TPOS, "TPOS", "Part of a set"}, {ID3_TPRO, "TPRO", "Produced notice"}, /* v4 only */ {ID3_TPUB, "TPUB", "Publisher"}, {ID3_TRCK, "TRCK", "Track number/Position in set"}, {ID3_TRDA, "TRDA", "Recording dates"}, /* v3 only */ {ID3_TRSN, "TRSN", "Internet radio station name"}, {ID3_TRSO, "TRSO", "Internet radio station owner"}, {ID3_TSIZ, "TSIZ", "Size"}, /* v3 only */ {ID3_TSOA, "TSOA", "Album sort order"}, /* v4 only */ {ID3_TSOP, "TSOP", "Performer sort order"}, /* v4 only */ {ID3_TSOT, "TSOT", "Title sort order"}, /* v4 only */ {ID3_TSRC, "TSRC", "ISRC (international standard recording code)"}, {ID3_TSSE, "TSSE", "Software/Hardware and settings used for encoding"}, {ID3_TSST, "TSST", "Set subtitle"}, /* v4 only */ {ID3_TYER, "TYER", "Year"}, /* v3 only */ {ID3_TXXX, "TXXX", "User defined text information frame"}, {ID3_UFID, "UFID", "Unique file identifier"}, {ID3_USER, "USER", "Terms of use"}, {ID3_USLT, "USLT", "Unsychronized lyric/text transcription"}, {ID3_WCOM, "WCOM", "Commercial information"}, {ID3_WCOP, "WCOP", "Copyright/Legal information"}, {ID3_WOAF, "WOAF", "Official audio file webpage"}, {ID3_WOAR, "WOAR", "Official artist/performer webpage"}, {ID3_WOAS, "WOAS", "Official audio source webpage"}, {ID3_WORS, "WORS", "Official internet radio station homepage"}, {ID3_WPAY, "WPAY", "Payment"}, {ID3_WPUB, "WPUB", "Publishers official webpage"}, {ID3_WXXX, "WXXX", "User defined URL link frame"}, }; struct id3_framedesc22 { guint32 fd_v22, fd_v24; }; static struct id3_framedesc22 framedesc22[] = { {ID3_BUF, ID3_RBUF}, /* Recommended buffer size */ {ID3_CNT, ID3_PCNT}, /* Play counter */ {ID3_COM, ID3_COMM}, /* Comments */ {ID3_CRA, ID3_AENC}, /* Audio encryption */ {ID3_CRM, 0}, /* Encrypted meta frame */ {ID3_ETC, ID3_ETCO}, /* Event timing codes */ /* Could be converted to EQU2 */ {ID3_EQU, 0}, /* Equalization */ {ID3_GEO, ID3_GEOB}, /* General encapsulated object */ /* Would need conversion to TIPL */ {ID3_IPL, 0}, /* Involved people list */ /* This is so fragile it's not worth trying to save */ {ID3_LNK, 0}, /* Linked information */ {ID3_MCI, ID3_MCDI}, /* Music CD Identifier */ {ID3_MLL, ID3_MLLT}, /* MPEG location lookup table */ /* Would need to convert header for APIC */ {ID3_PIC, 0}, /* Attached picture */ {ID3_POP, ID3_POPM}, /* Popularimeter */ {ID3_REV, ID3_RVRB}, /* Reverb */ /* Could be converted to RVA2 */ {ID3_RVA, 0}, /* Relative volume adjustment */ {ID3_SLT, ID3_SYLT}, /* Synchronized lyric/text */ {ID3_STC, ID3_SYTC}, /* Synced tempo codes */ {ID3_TAL, ID3_TALB}, /* Album/Movie/Show title */ {ID3_TBP, ID3_TBPM}, /* BPM (Beats Per Minute) */ {ID3_TCM, ID3_TCOM}, /* Composer */ {ID3_TCO, ID3_TCON}, /* Content type */ {ID3_TCR, ID3_TCOP}, /* Copyright message */ /* This could be incorporated into TDRC */ {ID3_TDA, 0}, /* Date */ {ID3_TDY, ID3_TDLY}, /* Playlist delay */ {ID3_TEN, ID3_TENC}, /* Encoded by */ {ID3_TFT, ID3_TFLT}, /* File type */ /* This could be incorporated into TDRC */ {ID3_TIM, 0}, /* Time */ {ID3_TKE, ID3_TKEY}, /* Initial key */ {ID3_TLA, ID3_TLAN}, /* Language(s) */ {ID3_TLE, ID3_TLEN}, /* Length */ {ID3_TMT, ID3_TMED}, /* Media type */ {ID3_TOA, ID3_TOPE}, /* Original artist(s)/performer(s) */ {ID3_TOF, ID3_TOFN}, /* Original filename */ {ID3_TOL, ID3_TOLY}, /* Original Lyricist(s)/text writer(s) */ /* * The docs says that original release year should be in * milliseconds! Hopefully that is a typo. */ {ID3_TOR, ID3_TDOR}, /* Original release year */ {ID3_TOT, ID3_TOAL}, /* Original album/Movie/Show title */ {ID3_TP1, ID3_TPE1}, /* Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group */ {ID3_TP2, ID3_TPE2}, /* Band/Orchestra/Accompaniment */ {ID3_TP3, ID3_TPE3}, /* Conductor/Performer refinement */ {ID3_TP4, ID3_TPE4}, /* Interpreted, remixed, or otherwise modified by */ {ID3_TPA, ID3_TPOS}, /* Part of a set */ {ID3_TPB, ID3_TPUB}, /* Publisher */ {ID3_TRC, ID3_TSRC}, /* ISRC (International Standard Recording Code) */ {ID3_TRD, 0}, /* Recording dates */ {ID3_TRK, ID3_TRCK}, /* Track number/Position in set */ {ID3_TSI, 0}, /* Size */ {ID3_TSS, ID3_TSSE}, /* Software/hardware and settings used for encoding */ {ID3_TT1, ID3_TIT1}, /* Content group description */ {ID3_TT2, ID3_TIT2}, /* Title/Songname/Content description */ {ID3_TT3, ID3_TIT3}, /* Subtitle/Description refinement */ {ID3_TXT, ID3_TEXT}, /* Lyricist/text writer */ {ID3_TXX, ID3_TXXX}, /* User defined text information frame */ {ID3_TYE, ID3_TDRC}, /* Year */ {ID3_UFI, ID3_UFID}, /* Unique file identifier */ {ID3_ULT, ID3_USLT}, /* Unsychronized lyric/text transcription */ {ID3_WAF, ID3_WOAF}, /* Official audio file webpage */ {ID3_WAR, ID3_WOAR}, /* Official artist/performer webpage */ {ID3_WAS, ID3_WOAS}, /* Official audio source webpage */ {ID3_WCM, ID3_WCOM}, /* Commercial information */ {ID3_WCP, ID3_WCOP}, /* Copyright/Legal information */ {ID3_WPB, ID3_WPUB}, /* Publishers official webpage */ {ID3_WXX, ID3_WXXX}, /* User defined URL link frame */ }; static struct id3_framedesc * find_frame_description(guint32 id) { int i; for (i = 0; i < sizeof(framedesc) / sizeof(struct id3_framedesc); i++) if (framedesc[i].fd_id == id) return &framedesc[i]; return NULL; } /* * Function id3_read_frame (id3) * * Read next frame from the indicated ID3 tag. Return 0 upon * success, or -1 if an error occured. * */ int id3_read_frame(struct id3_tag *id3) { struct id3_frame *frame; guint32 id; char *buf; if (id3->id3_version == 2) return id3_read_frame_v22(id3); /* * Read frame header. */ buf = id3->id3_read(id3, NULL, ID3_FRAMEHDR_SIZE); if (buf == NULL) return -1; /* * If we encounter an invalid frame id, we assume that there is * some padding in the header. We just skip the rest of the ID3 * tag. */ if (!((buf[0] >= '0' && buf[0] <= '9') || (buf[0] >= 'A' && buf[0] <= 'Z'))) { id3->id3_seek(id3, id3->id3_tagsize - id3->id3_pos); return 0; } id = ID3_FRAME_ID(buf[0], buf[1], buf[2], buf[3]); /* * Allocate frame. */ frame = g_malloc0(sizeof(*frame)); frame->fr_owner = id3; /* FIXME v2.4.0 */ frame->fr_raw_size = buf[4] << 24 | buf[5] << 16 | buf[6] << 8 | buf[7]; if (frame->fr_raw_size < 0 || frame->fr_raw_size > 1000000) { g_free(frame); return -1; } frame->fr_flags = buf[8] << 8 | buf[9]; /* * Determine the type of the frame. */ frame->fr_desc = find_frame_description(id); /* * Check if frame had a valid id. */ if (frame->fr_desc == NULL) { /* * No. Ignore the frame. */ if (id3->id3_seek(id3, frame->fr_raw_size) < 0) { g_free(frame); return -1; } return 0; } /* * Initialize frame. */ /* * We allocate 2 extra bytes. This simplifies retrieval of * text strings. */ frame->fr_raw_data = g_malloc0(frame->fr_raw_size + 2); if (id3->id3_read(id3, frame->fr_raw_data, frame->fr_raw_size) == NULL) { g_free(frame->fr_raw_data); g_free(frame); return -1; } /* * Insert frame into linked list. */ id3->id3_frame = g_list_append(id3->id3_frame, frame); /* * Check if frame is compressed using zlib. */ if (frame->fr_flags & ID3_FHFLAG_COMPRESS) return 0; frame->fr_data = id3_frame_get_dataptr(frame); frame->fr_size = id3_frame_get_size(frame); return 0; } /* * Function id3_get_frame (id3, type, num) * * Search in the list of frames for the ID3-tag, and return a frame * of the indicated type. If tag contains several frames of the * indicated type, the third argument tells which of the frames to * return. * */ struct id3_frame * id3_get_frame(struct id3_tag *id3, guint32 type, int num) { GList *node; for (node = id3->id3_frame; node != NULL; node = node->next) { struct id3_frame *fr = node->data; if (fr->fr_desc && fr->fr_desc->fd_id == type) { if (--num <= 0) return fr; } } return NULL; } /* * Function decompress_frame(frame) * * Uncompress the indicated frame. Return 0 upon success, or -1 if * an error occured. * */ static int decompress_frame(struct id3_frame *frame) { #ifdef HAVE_LIBZ z_stream z; int r; /* * Fetch the size of the decompressed data. */ frame->fr_size_z = g_ntohl(*((guint32 *) frame->fr_raw_data)); if (frame->fr_size_z < 0 || frame->fr_size_z > 1000000) return -1; /* * Allocate memory to hold uncompressed frame. */ frame->fr_data_z = g_malloc(frame->fr_size_z + (id3_frame_is_text(frame) ? 2 : 0)); /* * Initialize zlib. */ z.next_in = id3_frame_get_dataptr(frame); z.avail_in = id3_frame_get_size(frame); z.zalloc = NULL; z.zfree = NULL; z.opaque = NULL; r = inflateInit(&z); switch (r) { case Z_OK: break; case Z_MEM_ERROR: id3_error(frame->fr_owner, "zlib - no memory"); goto Error_init; case Z_VERSION_ERROR: id3_error(frame->fr_owner, "zlib - invalid version"); goto Error_init; default: id3_error(frame->fr_owner, "zlib - unknown error"); goto Error_init; } /* * Decompress frame. */ z.next_out = frame->fr_data_z; z.avail_out = frame->fr_size_z; r = inflate(&z, Z_SYNC_FLUSH); switch (r) { case Z_STREAM_END: break; case Z_OK: if (z.avail_in == 0) /* * This should not be possible with a correct stream. * We will be nice however, and try to go on. */ break; id3_error(frame->fr_owner, "zlib - buffer exhausted"); goto Error_inflate; default: id3_error(frame->fr_owner, "zlib - unknown error"); goto Error_inflate; } r = inflateEnd(&z); if (r != Z_OK) id3_error(frame->fr_owner, "zlib - inflateEnd error"); /* * Null-terminate text frames. */ if (id3_frame_is_text(frame)) { ((char *) frame->fr_data_z)[frame->fr_size_z] = 0; ((char *) frame->fr_data_z)[frame->fr_size_z + 1] = 0; } frame->fr_data = frame->fr_data_z; frame->fr_size = frame->fr_size_z + (id3_frame_is_text(frame) ? 2 : 0); return 0; /* * Cleanup code. */ Error_inflate: r = inflateEnd(&z); Error_init: g_free(frame->fr_data_z); frame->fr_data_z = NULL; #endif return -1; } /* * Function id3_decompress_frame(frame) * * Check if frame is compressed, and uncompress if necessary. * Return 0 upon success, or -1 if an error occured. * */ int id3_decompress_frame(struct id3_frame *frame) { if (!(frame->fr_flags & ID3_FHFLAG_COMPRESS)) /* Frame not compressed */ return 0; if (frame->fr_data_z) /* Frame already decompressed */ return 0; /* Do decompression */ return decompress_frame(frame); } /* * Function id3_delete_frame (frame) * * Remove frame from ID3 tag and release memory ocupied by it. * */ int id3_delete_frame(struct id3_frame *frame) { GList *list = frame->fr_owner->id3_frame; int ret; /* * Search for frame in list. */ if (g_list_find(list, frame) != NULL) { /* * Frame does not exist in frame list. */ ret = -1; } else { /* * Remove frame from frame list. */ list = g_list_remove(list, frame); frame->fr_owner->id3_altered = 1; ret = 0; } /* * Release memory occupied by frame. */ if (frame->fr_raw_data) g_free(frame->fr_raw_data); if (frame->fr_data_z) g_free(frame->fr_data_z); g_free(frame); return ret; } /* * Function id3_add_frame (id3, type) * * Add a new frame to the ID3 tag. Return a pointer to the new * frame, or NULL if an error occured. * */ struct id3_frame * id3_add_frame(struct id3_tag *id3, guint32 type) { struct id3_frame *frame; int i; /* * Allocate frame. */ frame = g_malloc0(sizeof(*frame)); /* * Initialize frame */ frame->fr_owner = id3; /* * Try finding the correct frame descriptor. */ for (i = 0; i < sizeof(framedesc) / sizeof(struct id3_framedesc); i++) { if (framedesc[i].fd_id == type) { frame->fr_desc = &framedesc[i]; break; } } /* * Insert frame into linked list. */ id3->id3_frame = g_list_append(id3->id3_frame, frame); id3->id3_altered = 1; return frame; } /* * Destroy all frames in an id3 tag, and free all data */ void id3_destroy_frames(struct id3_tag *id) { GList *node; for (node = id->id3_frame; node != NULL; node = node->next) { struct id3_frame *frame = node->data; /* * Release memory occupied by frame. */ if (frame->fr_raw_data) g_free(frame->fr_raw_data); if (frame->fr_data_z) g_free(frame->fr_data_z); g_free(frame); } g_list_free(id->id3_frame); id->id3_frame = NULL; } static int id3_frame_extra_headers(struct id3_frame *frame) { int retv = 0; /* * If frame is encrypted, we have four extra bytes in the * header. */ if (frame->fr_flags & ID3_FHFLAG_COMPRESS) retv += 4; /* * If frame is encrypted, we have one extra byte in the * header. */ if (frame->fr_flags & ID3_FHFLAG_ENCRYPT) retv += 1; /* * If frame has grouping identity, we have one extra byte in * the header. */ if (frame->fr_flags & ID3_FHFLAG_GROUP) retv += 1; return retv; } static void * id3_frame_get_dataptr(struct id3_frame *frame) { char *ptr = frame->fr_raw_data; ptr += id3_frame_extra_headers(frame); return ptr; } static int id3_frame_get_size(struct id3_frame *frame) { return frame->fr_raw_size - id3_frame_extra_headers(frame); } void id3_frame_clear_data(struct id3_frame *frame) { if (frame->fr_raw_data) g_free(frame->fr_raw_data); if (frame->fr_data_z) g_free(frame->fr_data_z); frame->fr_raw_data = NULL; frame->fr_raw_size = 0; frame->fr_data = NULL; frame->fr_size = 0; frame->fr_data_z = NULL; frame->fr_size_z = 0; } static guint32 find_v24_id(guint32 v22) { int i; for (i = 0; i < sizeof(framedesc22) / sizeof(framedesc22[0]); i++) if (framedesc22[i].fd_v22 == v22) return framedesc22[i].fd_v24; return 0; } static int id3_read_frame_v22(struct id3_tag *id3) { struct id3_frame *frame; guint32 id, idv24; char *buf; int size; /* * Read frame header. */ buf = id3->id3_read(id3, NULL, ID3_FRAMEHDR_SIZE_22); if (buf == NULL) return -1; /* * If we encounter an invalid frame id, we assume that there * is some. We just skip the rest of the ID3 tag. */ if (!((buf[0] >= '0' && buf[0] <= '9') || (buf[0] >= 'A' && buf[0] <= 'Z'))) { id3->id3_seek(id3, id3->id3_tagsize - id3->id3_pos); return 0; } id = ID3_FRAME_ID_22(buf[0], buf[1], buf[2]); size = buf[3] << 16 | buf[4] << 8 | buf[5]; if ((idv24 = find_v24_id(id)) == 0) { if (id3->id3_seek(id3, size) < 0) return -1; return 0; } /* * Allocate frame. */ frame = g_malloc0(sizeof(*frame)); frame->fr_owner = id3; frame->fr_raw_size = size; if (frame->fr_raw_size < 0 || frame->fr_raw_size > 1000000) { g_free(frame); return -1; } /* * Initialize frame. */ frame->fr_desc = find_frame_description(idv24); /* * We allocate 2 extra bytes. This simplifies retrieval of * text strings. */ frame->fr_raw_data = g_malloc0(frame->fr_raw_size + 2); if (id3->id3_read(id3, frame->fr_raw_data, frame->fr_raw_size) == NULL) { g_free(frame->fr_raw_data); g_free(frame); return -1; } /* * Insert frame into linked list. */ id3->id3_frame = g_list_append(id3->id3_frame, frame); frame->fr_data = frame->fr_raw_data; frame->fr_size = frame->fr_raw_size; return 0; }