From 317f2d1fd878b83c3ccf7412929b229e8b48260c Mon Sep 17 00:00:00 2001 From: Thibaut Mattern Date: Sun, 7 Dec 2003 23:05:41 +0000 Subject: - Move ID3 parsing code to id3.h/id3.c - Add ID3v2.3 parser ID3v2.4 is not done yet. CVS patchset: 5863 CVS date: 2003/12/07 23:05:41 --- src/demuxers/Makefile.am | 4 +- src/demuxers/demux_mpgaudio.c | 225 ++----------------------- src/demuxers/id3.c | 381 ++++++++++++++++++++++++++++++++++++++++++ src/demuxers/id3.h | 104 ++++++++++++ 4 files changed, 501 insertions(+), 213 deletions(-) create mode 100644 src/demuxers/id3.c create mode 100644 src/demuxers/id3.h diff --git a/src/demuxers/Makefile.am b/src/demuxers/Makefile.am index 661b46923..ff73b32e7 100644 --- a/src/demuxers/Makefile.am +++ b/src/demuxers/Makefile.am @@ -116,7 +116,7 @@ xineplug_dmx_games_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@ xineplug_dmx_audio_la_SOURCES = group_audio.c demux_aud.c demux_aiff.c \ demux_cdda.c demux_mpgaudio.c demux_nsf.c \ demux_realaudio.c demux_snd.c demux_voc.c \ - demux_vox.c demux_wav.c demux_ac3.c + demux_vox.c demux_wav.c demux_ac3.c id3.c xineplug_dmx_audio_la_LIBADD = $(XINE_LIB) xineplug_dmx_audio_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@ @@ -137,4 +137,4 @@ xineplug_dmx_nsv_la_LIBADD = $(XINE_LIB) xineplug_dmx_nsv_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@ include_HEADERS = demux.h -noinst_HEADERS = asfheader.h qtpalette.h group_games.h group_audio.h +noinst_HEADERS = asfheader.h qtpalette.h group_games.h group_audio.h id3.h diff --git a/src/demuxers/demux_mpgaudio.c b/src/demuxers/demux_mpgaudio.c index 2ab7a4323..2b73cbc9e 100644 --- a/src/demuxers/demux_mpgaudio.c +++ b/src/demuxers/demux_mpgaudio.c @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: demux_mpgaudio.c,v 1.131 2003/12/06 19:06:31 tmattern Exp $ + * $Id: demux_mpgaudio.c,v 1.132 2003/12/07 23:05:41 tmattern Exp $ * * demultiplexer for mpeg audio (i.e. mp3) streams * @@ -47,6 +47,7 @@ #include "compat.h" #include "bswap.h" #include "group_audio.h" +#include "id3.h" #define NUM_PREVIEW_BUFFERS 10 #define SNIFF_BUFFER_LENGTH 1024 @@ -68,23 +69,6 @@ #define XING_TOC_FLAG 0x0004 #define XING_VBR_SCALE_FLAG 0x0008 -/* id3v2 */ -#define ID3V22_TAG FOURCC_TAG('I', 'D', '3', 2) /* id3 v2.2 tags */ -#define ID3V23_TAG FOURCC_TAG('I', 'D', '3', 3) /* id3 v2.3 tags */ -#define ID3V24_TAG FOURCC_TAG('I', 'D', '3', 4) /* id3 v2.4 tags */ -#define ID3V2_UNSYNCH_FLAG 0x8000 - -/* id2v2.2 */ -#define ID3V2_COMPRESS_FLAG 0x4000 - -/* id2v2.3 */ -#define ID3V2_EXTHEAD_FLAG 0x4000 -#define ID3V2_EXP_FLAG 0x2000 - -/* id2v2.4 */ -#define ID3V2_FOOTER_FLAG 0x1000 - - typedef struct { /* header */ uint16_t frame_sync; @@ -109,33 +93,6 @@ typedef struct { double duration; /* in 1/90000 s */ } mpg_audio_frame_t; -typedef struct { - uint32_t id; - uint8_t revision; - uint8_t flags; - uint32_t size; -} id3v2_header_t; - -typedef struct { - uint32_t id; - uint32_t size; -} id3v22_frame_header_t; - -typedef struct { - uint32_t id; - uint32_t size; - uint16_t flags; -} id3v23_frame_header_t; - -typedef struct { - char tag[3]; - char title[30]; - char artist[30]; - char album[30]; - char year[4]; - char comment[30]; - char genre; -} id3v1_tag_t; typedef struct { @@ -533,165 +490,6 @@ static int sniff_buffer_looks_like_mp3 (input_plugin_t *input) return 0; } -static void read_id3_tags (demux_mpgaudio_t *this) { - - off_t len; - id3v1_tag_t tag; - - /* id3v1 */ - len = this->input->read (this->input, (char *)&tag, 128); - - if (len > 0) { - - if ( (tag.tag[0]=='T') && (tag.tag[1]=='A') && (tag.tag[2]=='G') ) { - - lprintf("id3v1 tag found\n"); - _x_meta_info_n_set(this->stream, XINE_META_INFO_TITLE, tag.title, 30); - _x_meta_info_n_set(this->stream, XINE_META_INFO_ARTIST, tag.artist, 30); - _x_meta_info_n_set(this->stream, XINE_META_INFO_ALBUM, tag.album, 30); - _x_meta_info_n_set(this->stream, XINE_META_INFO_COMMENT, tag.comment, 30); - } - } -} - -static int id3v2_parse_header(input_plugin_t *input, uint8_t *mp3_frame_header, - id3v2_header_t *tag_header) { - uint8_t buf[6]; - - tag_header->id = BE_32(mp3_frame_header); - if (input->read (input, buf, 6) == 6) { - tag_header->revision = buf[0]; - tag_header->flags = buf[1]; - - /* only 7 bits per byte */ - tag_header->size = (buf[2] << 21) + (buf[3] << 14) + (buf[4] << 7) + buf[5]; - lprintf("tag: ID3 v2.%d.%d\n", mp3_frame_header[3], tag_header->revision); - lprintf("flags: %d\n", tag_header->flags); - lprintf("size: %d\n", tag_header->size); - return 1; - } else { - return 0; - } -} - -static int id3v22_parse_frame_header(input_plugin_t *input, - id3v22_frame_header_t *frame_header) { - uint8_t buf[6]; - - if (input->read (input, buf, 6) == 6) { - frame_header->id = (buf[0] << 16) + (buf[1] << 8) + buf[2]; - - /* only 7 bits per byte */ - frame_header->size = (buf[3] << 14) + (buf[4] << 7) + buf[5]; - - lprintf("frame: %c%c%c: size: %d\n", buf[0], buf[1], buf[2], - frame_header->size); - - return 1; - } else { - return 0; - } -} - -static int id3v22_interp_frame(demux_mpgaudio_t *this, - id3v22_frame_header_t *frame_header) { - /* - * FIXME: supports unicode - */ - char buf[4096]; - - if (frame_header->size > 4096) { - lprintf("too long\n"); - return 1; - } - - if (this->input->read (this->input, buf, frame_header->size) == frame_header->size) { - buf[frame_header->size] = 0; - - switch (frame_header->id) { - case (FOURCC_TAG(0, 'T', 'T', '1')): - _x_meta_info_set(this->stream, XINE_META_INFO_GENRE, buf + 1); - break; - - case (FOURCC_TAG(0, 'T', 'T', '2')): - _x_meta_info_set(this->stream, XINE_META_INFO_TITLE, buf + 1); - break; - - case (FOURCC_TAG(0, 'T', 'P', '1')): - _x_meta_info_set(this->stream, XINE_META_INFO_ARTIST, buf + 1); - break; - - case (FOURCC_TAG(0, 'T', 'A', 'L')): - _x_meta_info_set(this->stream, XINE_META_INFO_ALBUM, buf + 1); - break; - - case (FOURCC_TAG(0, 'T', 'Y', 'E')): - _x_meta_info_set(this->stream, XINE_META_INFO_YEAR, buf + 1); - break; - - case (FOURCC_TAG(0, 'C', 'O', 'M')): - _x_meta_info_set(this->stream, XINE_META_INFO_COMMENT, buf + 1 + 3); - break; - - default: - lprintf("unhandled frame\n"); - } - - return 1; - } else { - lprintf("read error\n"); - return 0; - } -} - - -static int id3v22_parse_tag(demux_mpgaudio_t *this, int8_t *mp3_frame_header) { - id3v2_header_t tag_header; - id3v22_frame_header_t tag_frame_header; - int pos = 0; - - if (id3v2_parse_header(this->input, mp3_frame_header, &tag_header)) { - - if (tag_header.flags & ID3V2_COMPRESS_FLAG) { - /* compressed tag ? just skip it */ - this->input->seek (this->input, tag_header.size - pos, SEEK_CUR); - } else { - - while ((pos + 6) <= tag_header.size) { - if (id3v22_parse_frame_header(this->input, &tag_frame_header)) { - pos += 6; - if (tag_frame_header.id && tag_frame_header.size) { - if ((pos + tag_frame_header.size) <= tag_header.size) { - if (!id3v22_interp_frame(this, &tag_frame_header)) { - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, - "id2v22: invalid frame content\n"); - } - } else { - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, - "id3v22: invalid frame header\n"); - this->input->seek (this->input, tag_header.size - pos, SEEK_CUR); - return 1; - } - pos += tag_frame_header.size; - } else { - /* end of frames, the rest is padding */ - this->input->seek (this->input, tag_header.size - pos, SEEK_CUR); - return 1; - } - } else { - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, - "id2v22: id3v2_parse_frame_header problem\n"); - return 0; - } - } - } - return 1; - } else { - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "id3v22: id3v2_parse_header problem\n"); - return 0; - } -} - static int mpg123_read_frame_header(demux_mpgaudio_t *this, uint8_t *header_buf, int bytes) { off_t len; int i; @@ -725,19 +523,24 @@ static int demux_mpgaudio_next (demux_mpgaudio_t *this, int decoder_flags) { return mpg123_parse_frame_payload(this, header_buf, decoder_flags); } else if ((BE_32(header_buf)) == ID3V22_TAG) { - lprintf("ID3V2.2 tag\n"); - if (!id3v22_parse_tag(this, header_buf)) { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + "demux_mpgaudio: ID3V2.2 tag\n"); + if (!id3v22_parse_tag(this->input, this->stream, header_buf)) { return 0; } bytes = 4; } else if ((BE_32(header_buf)) == ID3V23_TAG) { - lprintf("ID3V2.3 tag\n"); - /* TODO: add parsing here */ - bytes = 1; /* resync */ + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + "demux_mpgaudio: ID3V2.3 tag\n"); + if (!id3v23_parse_tag(this->input, this->stream, header_buf)) { + return 0; + } + bytes = 4; } else if ((BE_32(header_buf)) == ID3V24_TAG) { - lprintf("ID3V2.4 tag\n"); + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + "demux_mpgaudio: ID3V2.4 tag\n"); /* TODO: add parsing here */ bytes = 1; /* resync */ @@ -832,7 +635,7 @@ static void demux_mpgaudio_send_headers (demux_plugin_t *this_gen) { pos = this->input->get_length(this->input) - 128; if(pos > 0) { if (pos == this->input->seek (this->input, pos, SEEK_SET)) - read_id3_tags (this); + id3v1_parse_tag (this->input, this->stream); } } diff --git a/src/demuxers/id3.c b/src/demuxers/id3.c new file mode 100644 index 000000000..5eb763e7d --- /dev/null +++ b/src/demuxers/id3.c @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2000-2003 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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 + * + * ID3 tag parser + * + * Supported versions: v1, v2.2 + * TODO: v2.3, v2.4 + * + * $Id: id3.c,v 1.1 2003/12/07 23:05:41 tmattern Exp $ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define LOG_MODULE "id3" +#define LOG_VERBOSE +/* +#define LOG +*/ + +#include "xine_internal.h" +#include "xineutils.h" +#include "bswap.h" +#include "id3.h" + +int id3v1_parse_tag (input_plugin_t *input, xine_stream_t *stream) { + + off_t len; + id3v1_tag_t tag; + + /* id3v1 */ + len = input->read (input, (char *)&tag, 128); + + if (len == 128) { + + if ( (tag.tag[0]=='T') && (tag.tag[1]=='A') && (tag.tag[2]=='G') ) { + + lprintf("id3v1 tag found\n"); + _x_meta_info_n_set(stream, XINE_META_INFO_TITLE, tag.title, 30); + _x_meta_info_n_set(stream, XINE_META_INFO_ARTIST, tag.artist, 30); + _x_meta_info_n_set(stream, XINE_META_INFO_ALBUM, tag.album, 30); + _x_meta_info_n_set(stream, XINE_META_INFO_COMMENT, tag.comment, 30); + } + return 1; + } else { + return 0; + } +} + + + +static int id3v2_parse_header(input_plugin_t *input, uint8_t *mp3_frame_header, + id3v2_header_t *tag_header) { + uint8_t buf[6]; + + tag_header->id = BE_32(mp3_frame_header); + if (input->read (input, buf, 6) == 6) { + tag_header->revision = buf[0]; + tag_header->flags = buf[1]; + + /* only 7 bits per byte (unsynch) */ + tag_header->size = (buf[2] << 21) + (buf[3] << 14) + (buf[4] << 7) + buf[5]; + lprintf("tag: ID3 v2.%d.%d\n", mp3_frame_header[3], tag_header->revision); + lprintf("flags: %d\n", tag_header->flags); + lprintf("size: %d\n", tag_header->size); + return 1; + } else { + return 0; + } +} + +/* id3 v2.2 */ + +static int id3v22_parse_frame_header(input_plugin_t *input, + id3v22_frame_header_t *frame_header) { + uint8_t buf[ID3V22_FRAME_HEADER_SIZE]; + int len; + + len = input->read (input, buf, ID3V22_FRAME_HEADER_SIZE); + if (len == ID3V22_FRAME_HEADER_SIZE) { + frame_header->id = (buf[0] << 16) + (buf[1] << 8) + buf[2]; + + /* only 7 bits per byte (unsynch) */ + frame_header->size = (buf[3] << 14) + (buf[4] << 7) + buf[5]; + + lprintf("frame: %c%c%c: size: %d\n", buf[0], buf[1], buf[2], + frame_header->size); + + return 1; + } else { + return 0; + } +} + +static int id3v22_interp_frame(input_plugin_t *input, + xine_stream_t *stream, + id3v22_frame_header_t *frame_header) { + /* + * FIXME: supports unicode + */ + char buf[4096]; + + if (frame_header->size >= 4096) { + lprintf("too long\n"); + return 1; + } + + if (input->read (input, buf, frame_header->size) == frame_header->size) { + buf[frame_header->size] = 0; + + switch (frame_header->id) { + case ( FOURCC_TAG(0, 'T', 'T', '1') ): + _x_meta_info_set(stream, XINE_META_INFO_GENRE, buf + 1); + break; + + case ( FOURCC_TAG(0, 'T', 'T', '2') ): + _x_meta_info_set(stream, XINE_META_INFO_TITLE, buf + 1); + break; + + case ( FOURCC_TAG(0, 'T', 'P', '1') ): + _x_meta_info_set(stream, XINE_META_INFO_ARTIST, buf + 1); + break; + + case ( FOURCC_TAG(0, 'T', 'A', 'L') ): + _x_meta_info_set(stream, XINE_META_INFO_ALBUM, buf + 1); + break; + + case ( FOURCC_TAG(0, 'T', 'Y', 'E') ): + _x_meta_info_set(stream, XINE_META_INFO_YEAR, buf + 1); + break; + + case ( FOURCC_TAG(0, 'C', 'O', 'M') ): + _x_meta_info_set(stream, XINE_META_INFO_COMMENT, buf + 1 + 3); + break; + + default: + lprintf("unhandled frame\n"); + } + + return 1; + } else { + lprintf("read error\n"); + return 0; + } +} + + +int id3v22_parse_tag(input_plugin_t *input, + xine_stream_t *stream, + int8_t *mp3_frame_header) { + id3v2_header_t tag_header; + id3v22_frame_header_t tag_frame_header; + int pos = 0; + + if (id3v2_parse_header(input, mp3_frame_header, &tag_header)) { + + if (tag_header.flags & ID3V22_COMPRESS_FLAG) { + /* compressed tag ? just skip it */ + input->seek (input, tag_header.size - pos, SEEK_CUR); + } else { + /* frame parsing */ + while ((pos + ID3V22_FRAME_HEADER_SIZE) <= tag_header.size) { + if (id3v22_parse_frame_header(input, &tag_frame_header)) { + pos += ID3V22_FRAME_HEADER_SIZE; + if (tag_frame_header.id && tag_frame_header.size) { + if ((pos + tag_frame_header.size) <= tag_header.size) { + if (!id3v22_interp_frame(input, stream, &tag_frame_header)) { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + "id3: invalid frame content\n"); + } + } else { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + "id3: invalid frame header\n"); + input->seek (input, tag_header.size - pos, SEEK_CUR); + return 1; + } + pos += tag_frame_header.size; + } else { + /* end of frames, the rest is padding */ + input->seek (input, tag_header.size - pos, SEEK_CUR); + return 1; + } + } else { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + "id3: id3v2_parse_frame_header problem\n"); + return 0; + } + } + } + return 1; + } else { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, "id3: id3v2_parse_header problem\n"); + return 0; + } +} + +/* id3 v2.3 */ + +static int id3v23_parse_frame_header(input_plugin_t *input, + id3v23_frame_header_t *frame_header) { + uint8_t buf[ID3V23_FRAME_HEADER_SIZE]; + int len; + + len = input->read (input, buf, ID3V23_FRAME_HEADER_SIZE); + if (len == ID3V23_FRAME_HEADER_SIZE) { + frame_header->id = BE_32(buf); + + /* only 7 bits per byte (unsynch) */ + frame_header->size = (buf[4] << 21) + (buf[5] << 14) + (buf[6] << 7) + buf[7]; + + frame_header->flags = BE_16(buf + 8); + + lprintf("frame: %c%c%c%c, size: %d, flags: %X\n", buf[0], buf[1], buf[2], buf[3], + frame_header->size, frame_header->flags); + + return 1; + } else { + return 0; + } +} + +static int id3v23_parse_frame_ext_header(input_plugin_t *input, + id3v23_frame_ext_header_t *frame_ext_header) { + uint8_t buf[14]; + + if (input->read (input, buf, 4) == 4) { + /* only 7 bits per byte (unsynch) */ + frame_ext_header->size = (buf[0] << 21) + (buf[1] << 14) + (buf[2] << 7) + buf[3]; + + if (frame_ext_header->size == 6) { + if (input->read (input, buf + 4, 6) == 6) { + frame_ext_header->flags = BE_16(buf + 4); + frame_ext_header->padding_size = BE_32(buf + 6); + frame_ext_header->crc = 0; + } else { + return 0; + } + + } else if (frame_ext_header->size == 10) { + if (input->read (input, buf + 4, 10) == 10) { + frame_ext_header->flags = BE_16(buf + 4); + frame_ext_header->padding_size = BE_32(buf + 6); + frame_ext_header->crc = BE_32(buf + 10); + } else { + return 0; + } + + } else { + lprintf("invalid ext header size: %d\n", frame_ext_header->size); + return 0; + } + + lprintf("ext header: size: %d, flags: %X, padding_size: %d, crc: %d\n", + frame_ext_header->size, frame_ext_header->flags, + frame_ext_header->padding_size, frame_ext_header->crc); + return 1; + } else { + return 0; + } +} + +static int id3v23_interp_frame(input_plugin_t *input, + xine_stream_t *stream, + id3v23_frame_header_t *frame_header) { + /* + * FIXME: supports unicode + */ + char buf[4096]; + + if (frame_header->size >= 4096) { + lprintf("too long\n"); + return 1; + } + + if (input->read (input, buf, frame_header->size) == frame_header->size) { + buf[frame_header->size] = 0; + + switch (frame_header->id) { + case ( FOURCC_TAG('T', 'C', 'O', 'N') ): + _x_meta_info_set(stream, XINE_META_INFO_GENRE, buf + 1); + break; + + case ( FOURCC_TAG('T', 'I', 'T', '2') ): + _x_meta_info_set(stream, XINE_META_INFO_TITLE, buf + 1); + break; + + case ( FOURCC_TAG('T', 'P', 'E', '1') ): + _x_meta_info_set(stream, XINE_META_INFO_ARTIST, buf + 1); + break; + + case ( FOURCC_TAG('T', 'A', 'L', 'B') ): + _x_meta_info_set(stream, XINE_META_INFO_ALBUM, buf + 1); + break; + + case ( FOURCC_TAG('T', 'Y', 'E', 'R') ): + _x_meta_info_set(stream, XINE_META_INFO_YEAR, buf + 1); + break; + + case ( FOURCC_TAG('C', 'O', 'M', 'M') ): + _x_meta_info_set(stream, XINE_META_INFO_COMMENT, buf + 1 + 3); + break; + + default: + lprintf("unhandled frame\n"); + } + + return 1; + } else { + lprintf("read error\n"); + return 0; + } +} + +int id3v23_parse_tag(input_plugin_t *input, + xine_stream_t *stream, + int8_t *mp3_frame_header) { + id3v2_header_t tag_header; + id3v23_frame_header_t tag_frame_header; + id3v23_frame_ext_header_t tag_frame_ext_header; + int pos = 0; + + if (id3v2_parse_header(input, mp3_frame_header, &tag_header)) { + + if (tag_header.flags & ID3V23_EXTHEAD_FLAG) { + /* extended header */ + if (!id3v23_parse_frame_ext_header(input, &tag_frame_ext_header)) { + return 0; + } + } + + /* frame parsing */ + while ((pos + ID3V23_FRAME_HEADER_SIZE) <= tag_header.size) { + if (id3v23_parse_frame_header(input, &tag_frame_header)) { + pos += ID3V23_FRAME_HEADER_SIZE; + if (tag_frame_header.id && tag_frame_header.size) { + if ((pos + tag_frame_header.size) <= tag_header.size) { + if (!id3v23_interp_frame(input, stream, &tag_frame_header)) { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + "id3: invalid frame content\n"); + } + } else { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + "id3: invalid frame header\n"); + input->seek (input, tag_header.size - pos, SEEK_CUR); + return 1; + } + pos += tag_frame_header.size; + } else { + /* end of frames, the rest is padding */ + input->seek (input, tag_header.size - pos, SEEK_CUR); + return 1; + } + } else { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + "id3: id3v2_parse_frame_header problem\n"); + return 0; + } + } + return 1; + } else { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, "id3v22: id3v2_parse_header problem\n"); + return 0; + } +} diff --git a/src/demuxers/id3.h b/src/demuxers/id3.h new file mode 100644 index 000000000..09fc950b0 --- /dev/null +++ b/src/demuxers/id3.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2000-2003 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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 + * + * ID3 tag parser + * + * Supported versions: v1, v2.2 + * TODO: v2.3, v2.4 + * + * $Id: id3.h,v 1.1 2003/12/07 23:05:41 tmattern Exp $ + */ + +#ifndef ID3_H +#define ID3_H + +#include "xine_internal.h" +#include "xineutils.h" +#include "bswap.h" + +/* id3v2 */ +#define FOURCC_TAG BE_FOURCC +#define ID3V22_TAG FOURCC_TAG('I', 'D', '3', 2) /* id3 v2.2 tags */ +#define ID3V23_TAG FOURCC_TAG('I', 'D', '3', 3) /* id3 v2.3 tags */ +#define ID3V24_TAG FOURCC_TAG('I', 'D', '3', 4) /* id3 v2.4 tags */ + +/* id2v2.2 */ +#define ID3V22_FRAME_HEADER_SIZE 6 +#define ID3V22_UNSYNCH_FLAG 0x8000 +#define ID3V22_COMPRESS_FLAG 0x4000 + +/* id2v2.3 */ +#define ID3V23_FRAME_HEADER_SIZE 10 +#define ID3V23_UNSYNCH_FLAG 0x8000 +#define ID3V23_EXTHEAD_FLAG 0x4000 +#define ID3V23_EXP_FLAG 0x2000 + +/* id2v2.4 */ +#define ID3V24_FRAME_HEADER_SIZE 10 +#define ID3V24_UNSYNCH_FLAG 0x8000 +#define ID3V24_EXTHEAD_FLAG 0x4000 +#define ID3V24_EXP_FLAG 0x2000 +#define ID3V24_FOOTER_FLAG 0x1000 + +typedef struct { + uint32_t id; + uint8_t revision; + uint8_t flags; + uint32_t size; +} id3v2_header_t; + +typedef struct { + uint32_t id; + uint32_t size; +} id3v22_frame_header_t; + +typedef struct { + uint32_t id; + uint32_t size; + uint16_t flags; +} id3v23_frame_header_t; + +typedef struct { + uint32_t size; + uint16_t flags; + uint32_t padding_size; + uint32_t crc; +} id3v23_frame_ext_header_t; + +typedef struct { + char tag[3]; + char title[30]; + char artist[30]; + char album[30]; + char year[4]; + char comment[30]; + char genre; +} id3v1_tag_t; + +int id3v1_parse_tag (input_plugin_t *input, xine_stream_t *stream); + +int id3v22_parse_tag(input_plugin_t *input, + xine_stream_t *stream, + int8_t *mp3_frame_header); + +int id3v23_parse_tag(input_plugin_t *input, + xine_stream_t *stream, + int8_t *mp3_frame_header); + +#endif /* ID3_H */ -- cgit v1.2.3