summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThibaut Mattern <tmattern@users.sourceforge.net>2003-12-07 23:05:41 +0000
committerThibaut Mattern <tmattern@users.sourceforge.net>2003-12-07 23:05:41 +0000
commit317f2d1fd878b83c3ccf7412929b229e8b48260c (patch)
treed6d5c62245ddd00b522bd037cf29190c249bfffa /src
parent7f609e9d3432a7342e17fb564587392185d5562b (diff)
downloadxine-lib-317f2d1fd878b83c3ccf7412929b229e8b48260c.tar.gz
xine-lib-317f2d1fd878b83c3ccf7412929b229e8b48260c.tar.bz2
- 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
Diffstat (limited to 'src')
-rw-r--r--src/demuxers/Makefile.am4
-rw-r--r--src/demuxers/demux_mpgaudio.c225
-rw-r--r--src/demuxers/id3.c381
-rw-r--r--src/demuxers/id3.h104
4 files changed, 501 insertions, 213 deletions
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 */