configure.ac: Check for optional dependency on zlib for id3demux

Original commit message from CVS:
* configure.ac:
Check for optional dependency on zlib for id3demux

* gst/id3demux/Makefile.am:
* gst/id3demux/gstid3demux.c: (gst_gst_id3demux_get_type),
(gst_id3demux_base_init), (gst_id3demux_class_init),
(gst_id3demux_reset), (gst_id3demux_init), (gst_id3demux_dispose),
(gst_id3demux_add_srcpad), (gst_id3demux_remove_srcpad),
(gst_id3demux_trim_buffer), (gst_id3demux_chain),
(gst_id3demux_set_property), (gst_id3demux_get_property),
(id3demux_get_upstream_size), (gst_id3demux_srcpad_event),
(gst_id3demux_read_id3v1), (gst_id3demux_read_id3v2),
(gst_id3demux_sink_activate), (gst_id3demux_src_activate_pull),
(gst_id3demux_src_checkgetrange), (gst_id3demux_read_range),
(gst_id3demux_src_getrange), (gst_id3demux_change_state),
(gst_id3demux_pad_query), (gst_id3demux_get_query_types),
(simple_find_peek), (simple_find_suggest),
(gst_id3demux_do_typefind), (gst_id3demux_send_tag_event),
(plugin_init):
* gst/id3demux/gstid3demux.h:
* gst/id3demux/id3tags.c: (read_synch_uint),
(id3demux_read_id3v1_tag), (id3demux_read_id3v2_tag),
(id3demux_id3v2_frame_hdr_size), (convert_fid_to_v240),
(id3demux_id3v2_frames_to_tag_list):
* gst/id3demux/id3tags.h:
* gst/id3demux/id3v2.4.0-frames.txt:
* gst/id3demux/id3v2.4.0-structure.txt:
* gst/id3demux/id3v2frames.c: (id3demux_id3v2_parse_frame),
(parse_comment_frame), (parse_text_identification_frame),
(id3v2_tag_to_taglist), (parse_split_strings):
All new LGPL id3 demuxer. Can use zlib for compressed frames,
otherwise it discards them. Works on my test files.

* gst/wavparse/gstwavparse.c: (gst_wavparse_loop):
Don't send EOS to a non-existing srcpad
The debug category can be static
This commit is contained in:
Jan Schmidt 2005-12-18 15:14:44 +00:00
parent 3ba78f0193
commit a05fc62783
11 changed files with 4620 additions and 2 deletions

View file

@ -1,3 +1,42 @@
2005-12-18 Jan Schmidt <thaytan@mad.scientist.com>
* configure.ac:
Check for optional dependency on zlib for id3demux
* gst/id3demux/Makefile.am:
* gst/id3demux/gstid3demux.c: (gst_gst_id3demux_get_type),
(gst_id3demux_base_init), (gst_id3demux_class_init),
(gst_id3demux_reset), (gst_id3demux_init), (gst_id3demux_dispose),
(gst_id3demux_add_srcpad), (gst_id3demux_remove_srcpad),
(gst_id3demux_trim_buffer), (gst_id3demux_chain),
(gst_id3demux_set_property), (gst_id3demux_get_property),
(id3demux_get_upstream_size), (gst_id3demux_srcpad_event),
(gst_id3demux_read_id3v1), (gst_id3demux_read_id3v2),
(gst_id3demux_sink_activate), (gst_id3demux_src_activate_pull),
(gst_id3demux_src_checkgetrange), (gst_id3demux_read_range),
(gst_id3demux_src_getrange), (gst_id3demux_change_state),
(gst_id3demux_pad_query), (gst_id3demux_get_query_types),
(simple_find_peek), (simple_find_suggest),
(gst_id3demux_do_typefind), (gst_id3demux_send_tag_event),
(plugin_init):
* gst/id3demux/gstid3demux.h:
* gst/id3demux/id3tags.c: (read_synch_uint),
(id3demux_read_id3v1_tag), (id3demux_read_id3v2_tag),
(id3demux_id3v2_frame_hdr_size), (convert_fid_to_v240),
(id3demux_id3v2_frames_to_tag_list):
* gst/id3demux/id3tags.h:
* gst/id3demux/id3v2.4.0-frames.txt:
* gst/id3demux/id3v2.4.0-structure.txt:
* gst/id3demux/id3v2frames.c: (id3demux_id3v2_parse_frame),
(parse_comment_frame), (parse_text_identification_frame),
(id3v2_tag_to_taglist), (parse_split_strings):
All new LGPL id3 demuxer. Can use zlib for compressed frames,
otherwise it discards them. Works on my test files.
* gst/wavparse/gstwavparse.c: (gst_wavparse_loop):
Don't send EOS to a non-existing srcpad
The debug category can be static
2005-12-16 Stefan Kost <ensonic@users.sf.net>
* ext/esd/esdmon.c: (gst_esdmon_open_audio):

View file

@ -73,6 +73,7 @@ GST_PLUGINS_ALL="\
cutter \
debug \
effectv \
id3demux \
flx \
goom \
law \
@ -472,6 +473,17 @@ GST_CHECK_FEATURE(SPEEX, [speex plug-in], speex, [
])
])
dnl *** id3demux prefers to have zlib ***
translit(dnm, m, l) AM_CONDITIONAL(USE_ZLIB, true)
GST_CHECK_FEATURE(ZLIB, [zlib support for id3demux],, [
GST_CHECK_LIBHEADER(ZLIB,
z, uncompress,, zlib.h, [
HAVE_ZLIB="yes"
ZLIB_LIBS="-lz"
AC_SUBST(ZLIB_LIBS)
])
])
fi dnl of EXT plugins
dnl *** finalize CFLAGS, LDFLAGS, LIBS
@ -531,6 +543,7 @@ gst/avi/Makefile
gst/cutter/Makefile
gst/debug/Makefile
gst/effectv/Makefile
gst/id3demux/Makefile
gst/goom/Makefile
gst/law/Makefile
gst/level/Makefile

8
gst/id3demux/Makefile.am Normal file
View file

@ -0,0 +1,8 @@
plugin_LTLIBRARIES = libgstid3demux.la
libgstid3demux_la_SOURCES = gstid3demux.c id3tags.c id3v2frames.c
libgstid3demux_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
libgstid3demux_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgsttag-@GST_MAJORMINOR@ $(ZLIB_LIBS)
libgstid3demux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
noinst_HEADERS = gstid3demux.h id3tags.h

1086
gst/id3demux/gstid3demux.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,79 @@
/* Copyright 2005 Jan Schmidt <thaytan@mad.scientist.com>
* Copyright (C) 2003-2004 Benjamin Otte <otte@gnome.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_ID3DEMUX_H__
#define __GST_ID3DEMUX_H__
#include <gst/gst.h>
G_BEGIN_DECLS
#define GST_TYPE_ID3DEMUX \
(gst_gst_id3demux_get_type())
#define GST_ID3DEMUX(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ID3DEMUX,GstID3Demux))
#define GST_ID3DEMUX_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ID3DEMUX,GstID3Demux))
#define GST_IS_ID3DEMUX(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ID3DEMUX))
#define GST_IS_ID3DEMUX_CLASS(obj) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ID3DEMUX))
typedef struct _GstID3Demux GstID3Demux;
typedef struct _GstID3DemuxClass GstID3DemuxClass;
typedef enum {
GST_ID3DEMUX_READID3V2,
GST_ID3DEMUX_TYPEFINDING,
GST_ID3DEMUX_STREAMING
} GstID3DemuxState;
struct _GstID3Demux
{
GstElement element;
GstPad *sinkpad, *srcpad;
/* Number of bytes to remove from the start of file (ID3v2) */
guint strip_start;
/* Number of bytes to remove from the end of file (ID3v1) */
guint strip_end;
gint64 upstream_size;
GstID3DemuxState state;
GstBuffer *collect;
GstCaps *src_caps;
gboolean prefer_v1;
GstTagList *event_tags;
GstTagList *parsed_tags;
gboolean send_tag_event;
};
struct _GstID3DemuxClass
{
GstElementClass parent_class;
};
GType gst_gst_id3demux_get_type (void);
G_END_DECLS
#endif /* __GST_ID3DEMUX_H__ */

431
gst/id3demux/id3tags.c Normal file
View file

@ -0,0 +1,431 @@
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
/* Copyright 2005 Jan Schmidt <thaytan@mad.scientist.com>
* Copyright 2002,2003 Scott Wheeler <wheeler@kde.org> (portions from taglib)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <gst/tag/tag.h>
#include "id3tags.h"
GST_DEBUG_CATEGORY_EXTERN (id3demux_debug);
#define GST_CAT_DEFAULT (id3demux_debug)
#define HANDLE_INVALID_SYNCSAFE
static ID3TagsResult
id3demux_id3v2_frames_to_tag_list (ID3TagsWorking * work, guint size);
guint
read_synch_uint (guint8 * data, guint size)
{
gint i;
guint result = 0;
gint invalid = 0;
g_assert (size <= 4);
size--;
for (i = 0; i <= size; i++) {
invalid |= data[i] & 0x80;
result |= (data[i] & 0x7f) << ((size - i) * 7);
}
#ifdef HANDLE_INVALID_SYNCSAFE
if (invalid) {
GST_WARNING ("Invalid synch-safe integer in ID3v2 frame "
"- using the actual value instead");
result = 0;
for (i = 0; i <= size; i++) {
result |= data[i] << ((size - i) * 8);
}
}
#endif
return result;
}
ID3TagsResult
id3demux_read_id3v1_tag (GstBuffer * buffer, guint * id3v1_size,
GstTagList ** tags)
{
GstTagList *new_tags;
guint8 *data;
g_return_val_if_fail (buffer != NULL, ID3TAGS_V1_BAD_SIZE);
data = GST_BUFFER_DATA (buffer);
if (GST_BUFFER_SIZE (buffer) != ID3V1_TAG_SIZE)
return ID3TAGS_V1_BAD_SIZE;
/* Check that buffer starts with 'TAG' */
if (data[0] != 'T' || data[1] != 'A' || data[2] != 'G') {
if (id3v1_size)
*id3v1_size = 0;
GST_DEBUG ("No ID3v1 tag in data");
return ID3TAGS_READ_TAG;
}
g_return_val_if_fail (tags != NULL, ID3TAGS_READ_TAG);
new_tags = gst_tag_list_new_from_id3v1 (GST_BUFFER_DATA (buffer));
if (new_tags == NULL)
return ID3TAGS_BROKEN_TAG;
if (*tags) {
GstTagList *merged;
merged = gst_tag_list_merge (*tags, new_tags, GST_TAG_MERGE_REPLACE);
gst_tag_list_free (*tags);
gst_tag_list_free (new_tags);
*tags = merged;
} else
*tags = new_tags;
return ID3TAGS_READ_TAG;
}
ID3TagsResult
id3demux_read_id3v2_tag (GstBuffer * buffer, guint * id3v2_size,
GstTagList ** tags)
{
guint8 *data;
guint read_size;
ID3TagsWorking work;
guint8 flags;
ID3TagsResult result;
guint16 version;
g_return_val_if_fail (buffer != NULL, ID3TAGS_MORE_DATA);
if (GST_BUFFER_SIZE (buffer) < ID3V2_MARK_SIZE)
return ID3TAGS_MORE_DATA; /* Need more data to decide with */
data = GST_BUFFER_DATA (buffer);
/* Check for 'ID3' string at start of buffer */
if (data[0] != 'I' || data[1] != 'D' || data[2] != '3') {
if (id3v2_size)
*id3v2_size = 0;
GST_DEBUG ("No ID3v2 tag in data");
return ID3TAGS_READ_TAG;
}
/* OK, get enough data to read the entire header */
if (GST_BUFFER_SIZE (buffer) < ID3V2_HDR_SIZE)
return ID3TAGS_MORE_DATA; /* Need more data to decide with */
/* Read the version */
version = GST_READ_UINT16_BE (data + 3);
/* Read the flags */
flags = data[5];
/* Read the size from the header */
read_size = read_synch_uint (data + 6, 4);
if (read_size == 0) {
return ID3TAGS_BROKEN_TAG;
}
read_size += 10;
/* Expand the read size to include a footer if there is one */
if (flags & ID3V2_HDR_FLAG_FOOTER) {
read_size += 10;
}
if (id3v2_size)
*id3v2_size = read_size;
/* Validate the version. At the moment, we only support up to 2.4.0 */
if (ID3V2_VER_MAJOR (version) > 4 || ID3V2_VER_MINOR (version) > 0) {
GST_WARNING ("ID3v2 tag is from revision 2.%d.%d, "
"but decoder only supports 2.%d.%d. Ignoring as per spec.",
version >> 8, version & 0xff, ID3V2_VERSION >> 8, ID3V2_VERSION & 0xff);
return ID3TAGS_READ_TAG;
}
GST_DEBUG ("ID3v2 tag with revision 2.%d.%d\n", version >> 8, version & 0xff);
if (GST_BUFFER_SIZE (buffer) < read_size)
return ID3TAGS_MORE_DATA; /* Need more data to decode with */
g_return_val_if_fail (tags != NULL, ID3TAGS_READ_TAG);
memset (&work, 0, sizeof (ID3TagsWorking));
work.buffer = buffer;
work.hdr.version = version;
work.hdr.size = read_size;
work.hdr.flags = flags;
work.hdr.frame_data = GST_BUFFER_DATA (buffer) + ID3V2_HDR_SIZE;
if (flags & ID3V2_HDR_FLAG_FOOTER)
work.hdr.frame_data_size = read_size - ID3V2_HDR_SIZE - 10;
else
work.hdr.frame_data_size = read_size - ID3V2_HDR_SIZE;
result = id3demux_id3v2_frames_to_tag_list (&work, read_size);
/* Actually read the tags */
if (work.tags != NULL) {
if (*tags) {
GstTagList *merged;
merged = gst_tag_list_merge (*tags, work.tags, GST_TAG_MERGE_REPLACE);
gst_tag_list_free (*tags);
gst_tag_list_free (work.tags);
*tags = merged;
} else
*tags = work.tags;
}
return result;
}
static guint
id3demux_id3v2_frame_hdr_size (guint id3v2ver)
{
/* ID3v2 < 2.3.0 only had 6 byte header */
switch (ID3V2_VER_MAJOR (id3v2ver)) {
case 0:
case 1:
case 2:
return 6;
case 3:
case 4:
default:
return 10;
}
}
static const gchar *obsolete_frame_ids[] = {
"CRM", "EQU", "LNK", "RVA", "TIM", "TSI", /* From 2.2 */
"EQUA", "RVAD", "TIME", "TRDA", "TSIZ", /* From 2.3 */
NULL
};
const struct ID3v2FrameIDConvert
{
gchar *orig;
gchar *new;
} frame_id_conversions[] = {
/* 2.3.x frames */
{
"TDAT", "TDRC"}, {
"TORY", "TDOR"}, {
"TYER", "TDRC"},
/* 2.2.x frames */
{
"BUF", "RBUF"}, {
"CNT", "PCNT"}, {
"COM", "COMM"}, {
"CRA", "AENC"}, {
"ETC", "ETCO"}, {
"GEO", "GEOB"}, {
"IPL", "TIPL"}, {
"MCI", "MCDI"}, {
"MLL", "MLLT"}, {
"PIC", "APIC"}, {
"POP", "POPM"}, {
"REV", "RVRB"}, {
"SLT", "SYLT"}, {
"STC", "SYTC"}, {
"TAL", "TALB"}, {
"TBP", "TBPM"}, {
"TCM", "TCOM"}, {
"TCR", "TCOP"}, {
"TDA", "TDRC"}, {
"TDY", "TDLY"}, {
"TEN", "TENC"}, {
"TFT", "TFLT"}, {
"TKE", "TKEY"}, {
"TLA", "TLAN"}, {
"TLE", "TLEN"}, {
"TMT", "TMED"}, {
"TOA", "TOAL"}, {
"TOF", "TOFN"}, {
"TOL", "TOLY"}, {
"TOR", "TDOR"}, {
"TOT", "TOAL"}, {
"TP1", "TPE1"}, {
"TP2", "TPE2"}, {
"TP3", "TPE3"}, {
"TP4", "TPE4"}, {
"TPA", "TPOS"}, {
"TPB", "TPUB"}, {
"TRC", "TSRC"}, {
"TRD", "TDRC"}, {
"TRK", "TRCK"}, {
"TSS", "TSSE"}, {
"TT1", "TIT1"}, {
"TT2", "TIT2"}, {
"TT3", "TIT3"}, {
"TXT", "TOLY"}, {
"TXX", "TXXX"}, {
"TYE", "TDRC"}, {
"UFI", "UFID"}, {
"ULT", "USLT"}, {
"WAF", "WOAF"}, {
"WAR", "WOAR"}, {
"WAS", "WOAS"}, {
"WCM", "WCOM"}, {
"WCP", "WCOP"}, {
"WPB", "WPUB"}, {
"WXX", "WXXX"}, {
NULL, NULL}
};
static gboolean
convert_fid_to_v240 (gchar * frame_id)
{
gint i = 0;
while (obsolete_frame_ids[i] != NULL) {
if (strncmp (frame_id, obsolete_frame_ids[i], 5) == 0)
return TRUE;
i++;
}
i = 0;
while (frame_id_conversions[i].orig != NULL) {
if (strncmp (frame_id, frame_id_conversions[i].orig, 5) == 0) {
strcpy (frame_id, frame_id_conversions[i].new);
return FALSE;
}
i++;
}
return FALSE;
}
static ID3TagsResult
id3demux_id3v2_frames_to_tag_list (ID3TagsWorking * work, guint size)
{
guint frame_hdr_size;
gboolean read_a_frame = FALSE;
guint8 *start;
/* Extended header if present */
if (work->hdr.flags & ID3V2_HDR_FLAG_EXTHDR) {
work->hdr.ext_hdr_size = read_synch_uint (work->hdr.frame_data, 4);
if (work->hdr.ext_hdr_size < 6 ||
(work->hdr.ext_hdr_size) > work->hdr.frame_data_size) {
return ID3TAGS_BROKEN_TAG;
}
work->hdr.ext_flag_bytes = work->hdr.frame_data[4];
if (5 + work->hdr.ext_flag_bytes > work->hdr.frame_data_size) {
GST_DEBUG
("Tag claims extended header, but doesn't have enough bytes. Broken tag");
return ID3TAGS_BROKEN_TAG;
}
work->hdr.ext_flag_data = work->hdr.frame_data + 5;
work->hdr.frame_data += work->hdr.ext_hdr_size;
work->hdr.frame_data_size -= work->hdr.ext_hdr_size;
}
start = GST_BUFFER_DATA (work->buffer);
frame_hdr_size = id3demux_id3v2_frame_hdr_size (work->hdr.version);
if (work->hdr.frame_data_size <= frame_hdr_size) {
GST_DEBUG ("Tag has no data frames. Broken tag");
return ID3TAGS_BROKEN_TAG; /* Must have at least one frame */
}
work->tags = gst_tag_list_new ();
g_return_val_if_fail (work->tags != NULL, ID3TAGS_READ_TAG);
while (work->hdr.frame_data_size > frame_hdr_size) {
guint frame_size = 0;
gchar frame_id[5] = "";
guint16 frame_flags = 0x0;
gboolean obsolete_id = FALSE;
/* Read the header */
switch (ID3V2_VER_MAJOR (work->hdr.version)) {
case 0:
case 1:
case 2:
frame_id[0] = work->hdr.frame_data[0];
frame_id[1] = work->hdr.frame_data[1];
frame_id[2] = work->hdr.frame_data[2];
frame_id[3] = 0;
frame_id[4] = 0;
obsolete_id = convert_fid_to_v240 (frame_id);
frame_size = read_synch_uint (work->hdr.frame_data + 3, 3);
frame_flags = 0;
break;
case 3:
case 4:
default:
frame_id[0] = work->hdr.frame_data[0];
frame_id[1] = work->hdr.frame_data[1];
frame_id[2] = work->hdr.frame_data[2];
frame_id[3] = work->hdr.frame_data[3];
frame_id[4] = 0;
frame_size = read_synch_uint (work->hdr.frame_data + 4, 4);
frame_flags = GST_READ_UINT16_BE (work->hdr.frame_data + 8);
if (ID3V2_VER_MAJOR (work->hdr.version) == 3) {
frame_flags &= ID3V2_3_FRAME_FLAGS_MASK;
obsolete_id = convert_fid_to_v240 (frame_id);
}
break;
}
work->hdr.frame_data += frame_hdr_size;
work->hdr.frame_data_size -= frame_hdr_size;
if (frame_size > work->hdr.frame_data_size ||
frame_size == 0 || strcmp (frame_id, "") == 0)
break; /* No more frames to read */
#if 0
g_print
("Frame @ %d (0x%02x) id %s size %d, next=%d (0x%02x) obsolete=%d\n",
work->hdr.frame_data - start, work->hdr.frame_data - start, frame_id,
frame_size, work->hdr.frame_data + frame_hdr_size + frame_size - start,
work->hdr.frame_data + frame_hdr_size + frame_size - start,
obsolete_id);
#endif
if (!obsolete_id) {
/* Now, read, decompress etc the contents of the frame
* into a TagList entry */
work->cur_frame_size = frame_size;
work->frame_id = frame_id;
work->frame_flags = frame_flags;
if (id3demux_id3v2_parse_frame (work)) {
read_a_frame = TRUE;
GST_LOG ("Extracted frame with id %s", frame_id);
}
}
work->hdr.frame_data += frame_size;
work->hdr.frame_data_size -= frame_size;
}
if (!read_a_frame) {
GST_DEBUG ("Could not extract any frames from tag. Broken tag");
gst_tag_list_free (work->tags);
work->tags = NULL;
return ID3TAGS_BROKEN_TAG;
}
return ID3TAGS_READ_TAG;
}

112
gst/id3demux/id3tags.h Normal file
View file

@ -0,0 +1,112 @@
/* Copyright 2005 Jan Schmidt <thaytan@mad.scientist.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __ID3TAGS_H__
#define __ID3TAGS_H__
#include <gst/gst.h>
G_BEGIN_DECLS
#define ID3V1_TAG_SIZE 128
#define ID3V2_MARK_SIZE 3
#define ID3V2_HDR_SIZE 10
typedef enum {
ID3TAGS_V1_BAD_SIZE,
ID3TAGS_MORE_DATA,
ID3TAGS_READ_TAG,
ID3TAGS_BROKEN_TAG
} ID3TagsResult;
/* From id3tags.c */
ID3TagsResult id3demux_read_id3v1_tag (GstBuffer *buffer, guint *id3v1_size,
GstTagList **tags);
ID3TagsResult id3demux_read_id3v2_tag (GstBuffer *buffer, guint *id3v2_size,
GstTagList **tags);
G_END_DECLS
/* Things shared by id3tags.c and id3v2frames.c */
#define ID3V2_VERSION 0x0400
#define ID3V2_VER_MAJOR(v) ((v) >> 8)
#define ID3V2_VER_MINOR(v) ((v) & 0xff)
typedef struct {
guint16 version;
guint8 flags;
guint32 size;
guint8 *frame_data;
guint32 frame_data_size;
guint32 ext_hdr_size;
guint8 ext_flag_bytes;
guint8 *ext_flag_data;
} ID3v2Header;
typedef struct {
ID3v2Header hdr;
GstBuffer *buffer;
GstTagList *tags;
/* Current frame decoding */
guint cur_frame_size;
gchar *frame_id;
guint16 frame_flags;
guint8 *parse_data;
guint parse_size;
} ID3TagsWorking;
enum {
ID3V2_HDR_FLAG_UNSYNC = 0x80,
ID3V2_HDR_FLAG_EXTHDR = 0x40,
ID3V2_HDR_FLAG_EXPERIMENTAL = 0x20,
ID3V2_HDR_FLAG_FOOTER = 0x10
};
enum {
ID3V2_EXT_FLAG_UPDATE = 0x80,
ID3V2_EXT_FLAG_CRC = 0x40,
ID3V2_EXT_FLAG_RESTRICTED = 0x20
};
enum {
ID3V2_FRAME_STATUS_FRAME_ALTER_PRESERVE = 0x4000,
ID3V2_FRAME_STATUS_FILE_ALTER_PRESERVE = 0x2000,
ID3V2_FRAME_STATUS_READONLY = 0x1000,
ID3V2_FRAME_FORMAT_GROUPING_ID = 0x0040,
ID3V2_FRAME_FORMAT_COMPRESSION = 0x0008,
ID3V2_FRAME_FORMAT_ENCRYPTION = 0x0004,
ID3V2_FRAME_FORMAT_UNSYNCHRONISATION = 0x0002,
ID3V2_FRAME_FORMAT_DATA_LENGTH_INDICATOR = 0x0001
};
#define ID3V2_3_FRAME_FLAGS_MASK \
(ID3V2_FRAME_STATUS_FRAME_ALTER_PRESERVE | \
ID3V2_FRAME_STATUS_FILE_ALTER_PRESERVE | \
ID3V2_FRAME_STATUS_READONLY | \
ID3V2_FRAME_FORMAT_GROUPING_ID | \
ID3V2_FRAME_FORMAT_COMPRESSION | \
ID3V2_FRAME_FORMAT_ENCRYPTION)
/* From id3v2frames.c */
gboolean id3demux_id3v2_parse_frame (ID3TagsWorking *work);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,733 @@
Informal standard M. Nilsson
Document: id3v2.4.0-structure.txt 16 September 2001
ID3 tag version 2.4.0 - Main Structure
Status of this document
This document is an informal standard and replaces the ID3v2.3.0
standard [ID3v2]. A formal standard will use another revision number
even if the content is identical to document. The contents in this
document may change for clarifications but never for added or altered
functionallity.
Distribution of this document is unlimited.
Abstract
This document describes the main structure of ID3v2.4.0, which is a
revised version of the ID3v2 informal standard [ID3v2] version
2.3.0. The ID3v2 offers a flexible way of storing audio meta
information within the audio file itself. The information may be
technical information, such as equalisation curves, as well as
title, performer, copyright etc.
ID3v2.4.0 is meant to be as close as possible to ID3v2.3.0 in order
to allow for implementations to be revised as easily as possible.
1. Table of contents
Status of this document
Abstract
1. Table of contents
2. Conventions in this document
2. Standard overview
3. ID3v2 overview
3.1. ID3v2 header
3.2. ID3v2 extended header
3.3. Padding
3.4. ID3v2 footer
4. ID3v2 frames overview
4.1. Frame header flags
4.1.1. Frame status flags
4.1.2. Frame format flags
5. Tag location
6. Unsynchronisation
6.1. The unsynchronisation scheme
6.2. Synchsafe integers
7. Copyright
8. References
9. Author's Address
2. Conventions in this document
Text within "" is a text string exactly as it appears in a tag.
Numbers preceded with $ are hexadecimal and numbers preceded with %
are binary. $xx is used to indicate a byte with unknown content. %x
is used to indicate a bit with unknown content. The most significant
bit (MSB) of a byte is called 'bit 7' and the least significant bit
(LSB) is called 'bit 0'.
A tag is the whole tag described in this document. A frame is a block
of information in the tag. The tag consists of a header, frames and
optional padding. A field is a piece of information; one value, a
string etc. A numeric string is a string that consists of the
characters "0123456789" only.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in RFC 2119 [KEYWORDS].
3. ID3v2 overview
ID3v2 is a general tagging format for audio, which makes it possible
to store meta data about the audio inside the audio file itself. The
ID3 tag described in this document is mainly targeted at files
encoded with MPEG-1/2 layer I, MPEG-1/2 layer II, MPEG-1/2 layer III
and MPEG-2.5, but may work with other types of encoded audio or as a
stand alone format for audio meta data.
ID3v2 is designed to be as flexible and expandable as possible to
meet new meta information needs that might arise. To achieve that
ID3v2 is constructed as a container for several information blocks,
called frames, whose format need not be known to the software that
encounters them. At the start of every frame is an unique and
predefined identifier, a size descriptor that allows software to skip
unknown frames and a flags field. The flags describes encoding
details and if the frame should remain in the tag, should it be
unknown to the software, if the file is altered.
The bitorder in ID3v2 is most significant bit first (MSB). The
byteorder in multibyte numbers is most significant byte first (e.g.
$12345678 would be encoded $12 34 56 78), also known as big endian
and network byte order.
Overall tag structure:
+-----------------------------+
| Header (10 bytes) |
+-----------------------------+
| Extended Header |
| (variable length, OPTIONAL) |
+-----------------------------+
| Frames (variable length) |
+-----------------------------+
| Padding |
| (variable length, OPTIONAL) |
+-----------------------------+
| Footer (10 bytes, OPTIONAL) |
+-----------------------------+
In general, padding and footer are mutually exclusive. See details in
sections 3.3, 3.4 and 5.
3.1. ID3v2 header
The first part of the ID3v2 tag is the 10 byte tag header, laid out
as follows:
ID3v2/file identifier "ID3"
ID3v2 version $04 00
ID3v2 flags %abcd0000
ID3v2 size 4 * %0xxxxxxx
The first three bytes of the tag are always "ID3", to indicate that
this is an ID3v2 tag, directly followed by the two version bytes. The
first byte of ID3v2 version is its major version, while the second
byte is its revision number. In this case this is ID3v2.4.0. All
revisions are backwards compatible while major versions are not. If
software with ID3v2.4.0 and below support should encounter version
five or higher it should simply ignore the whole tag. Version or
revision will never be $FF.
The version is followed by the ID3v2 flags field, of which currently
four flags are used.
a - Unsynchronisation
Bit 7 in the 'ID3v2 flags' indicates whether or not
unsynchronisation is applied on all frames (see section 6.1 for
details); a set bit indicates usage.
b - Extended header
The second bit (bit 6) indicates whether or not the header is
followed by an extended header. The extended header is described in
section 3.2. A set bit indicates the presence of an extended
header.
c - Experimental indicator
The third bit (bit 5) is used as an 'experimental indicator'. This
flag SHALL always be set when the tag is in an experimental stage.
d - Footer present
Bit 4 indicates that a footer (section 3.4) is present at the very
end of the tag. A set bit indicates the presence of a footer.
All the other flags MUST be cleared. If one of these undefined flags
are set, the tag might not be readable for a parser that does not
know the flags function.
The ID3v2 tag size is stored as a 32 bit synchsafe integer (section
6.2), making a total of 28 effective bits (representing up to 256MB).
The ID3v2 tag size is the sum of the byte length of the extended
header, the padding and the frames after unsynchronisation. If a
footer is present this equals to ('total size' - 20) bytes, otherwise
('total size' - 10) bytes.
An ID3v2 tag can be detected with the following pattern:
$49 44 33 yy yy xx zz zz zz zz
Where yy is less than $FF, xx is the 'flags' byte and zz is less than
$80.
3.2. Extended header
The extended header contains information that can provide further
insight in the structure of the tag, but is not vital to the correct
parsing of the tag information; hence the extended header is
optional.
Extended header size 4 * %0xxxxxxx
Number of flag bytes $01
Extended Flags $xx
Where the 'Extended header size' is the size of the whole extended
header, stored as a 32 bit synchsafe integer. An extended header can
thus never have a size of fewer than six bytes.
The extended flags field, with its size described by 'number of flag
bytes', is defined as:
%0bcd0000
Each flag that is set in the extended header has data attached, which
comes in the order in which the flags are encountered (i.e. the data
for flag 'b' comes before the data for flag 'c'). Unset flags cannot
have any attached data. All unknown flags MUST be unset and their
corresponding data removed when a tag is modified.
Every set flag's data starts with a length byte, which contains a
value between 0 and 127 ($00 - $7f), followed by data that has the
field length indicated by the length byte. If a flag has no attached
data, the value $00 is used as length byte.
b - Tag is an update
If this flag is set, the present tag is an update of a tag found
earlier in the present file or stream. If frames defined as unique
are found in the present tag, they are to override any
corresponding ones found in the earlier tag. This flag has no
corresponding data.
Flag data length $00
c - CRC data present
If this flag is set, a CRC-32 [ISO-3309] data is included in the
extended header. The CRC is calculated on all the data between the
header and footer as indicated by the header's tag length field,
minus the extended header. Note that this includes the padding (if
there is any), but excludes the footer. The CRC-32 is stored as an
35 bit synchsafe integer, leaving the upper four bits always
zeroed.
Flag data length $05
Total frame CRC 5 * %0xxxxxxx
d - Tag restrictions
For some applications it might be desired to restrict a tag in more
ways than imposed by the ID3v2 specification. Note that the
presence of these restrictions does not affect how the tag is
decoded, merely how it was restricted before encoding. If this flag
is set the tag is restricted as follows:
Flag data length $01
Restrictions %ppqrrstt
p - Tag size restrictions
00 No more than 128 frames and 1 MB total tag size.
01 No more than 64 frames and 128 KB total tag size.
10 No more than 32 frames and 40 KB total tag size.
11 No more than 32 frames and 4 KB total tag size.
q - Text encoding restrictions
0 No restrictions
1 Strings are only encoded with ISO-8859-1 [ISO-8859-1] or
UTF-8 [UTF-8].
r - Text fields size restrictions
00 No restrictions
01 No string is longer than 1024 characters.
10 No string is longer than 128 characters.
11 No string is longer than 30 characters.
Note that nothing is said about how many bytes is used to
represent those characters, since it is encoding dependent. If a
text frame consists of more than one string, the sum of the
strungs is restricted as stated.
s - Image encoding restrictions
0 No restrictions
1 Images are encoded only with PNG [PNG] or JPEG [JFIF].
t - Image size restrictions
00 No restrictions
01 All images are 256x256 pixels or smaller.
10 All images are 64x64 pixels or smaller.
11 All images are exactly 64x64 pixels, unless required
otherwise.
3.3. Padding
It is OPTIONAL to include padding after the final frame (at the end
of the ID3 tag), making the size of all the frames together smaller
than the size given in the tag header. A possible purpose of this
padding is to allow for adding a few additional frames or enlarge
existing frames within the tag without having to rewrite the entire
file. The value of the padding bytes must be $00. A tag MUST NOT have
any padding between the frames or between the tag header and the
frames. Furthermore it MUST NOT have any padding when a tag footer is
added to the tag.
3.4. ID3v2 footer
To speed up the process of locating an ID3v2 tag when searching from
the end of a file, a footer can be added to the tag. It is REQUIRED
to add a footer to an appended tag, i.e. a tag located after all
audio data. The footer is a copy of the header, but with a different
identifier.
ID3v2 identifier "3DI"
ID3v2 version $04 00
ID3v2 flags %abcd0000
ID3v2 size 4 * %0xxxxxxx
4. ID3v2 frame overview
All ID3v2 frames consists of one frame header followed by one or more
fields containing the actual information. The header is always 10
bytes and laid out as follows:
Frame ID $xx xx xx xx (four characters)
Size 4 * %0xxxxxxx
Flags $xx xx
The frame ID is made out of the characters capital A-Z and 0-9.
Identifiers beginning with "X", "Y" and "Z" are for experimental
frames and free for everyone to use, without the need to set the
experimental bit in the tag header. Bear in mind that someone else
might have used the same identifier as you. All other identifiers are
either used or reserved for future use.
The frame ID is followed by a size descriptor containing the size of
the data in the final frame, after encryption, compression and
unsynchronisation. The size is excluding the frame header ('total
frame size' - 10 bytes) and stored as a 32 bit synchsafe integer.
In the frame header the size descriptor is followed by two flag
bytes. These flags are described in section 4.1.
There is no fixed order of the frames' appearance in the tag,
although it is desired that the frames are arranged in order of
significance concerning the recognition of the file. An example of
such order: UFID, TIT2, MCDI, TRCK ...
A tag MUST contain at least one frame. A frame must be at least 1
byte big, excluding the header.
If nothing else is said, strings, including numeric strings and URLs
[URL], are represented as ISO-8859-1 [ISO-8859-1] characters in the
range $20 - $FF. Such strings are represented in frame descriptions
as <text string>, or <full text string> if newlines are allowed. If
nothing else is said newline character is forbidden. In ISO-8859-1 a
newline is represented, when allowed, with $0A only.
Frames that allow different types of text encoding contains a text
encoding description byte. Possible encodings:
$00 ISO-8859-1 [ISO-8859-1]. Terminated with $00.
$01 UTF-16 [UTF-16] encoded Unicode [UNICODE] with BOM. All
strings in the same frame SHALL have the same byteorder.
Terminated with $00 00.
$02 UTF-16BE [UTF-16] encoded Unicode [UNICODE] without BOM.
Terminated with $00 00.
$03 UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00.
Strings dependent on encoding are represented in frame descriptions
as <text string according to encoding>, or <full text string
according to encoding> if newlines are allowed. Any empty strings of
type $01 which are NULL-terminated may have the Unicode BOM followed
by a Unicode NULL ($FF FE 00 00 or $FE FF 00 00).
The timestamp fields are based on a subset of ISO 8601. When being as
precise as possible the format of a time string is
yyyy-MM-ddTHH:mm:ss (year, "-", month, "-", day, "T", hour (out of
24), ":", minutes, ":", seconds), but the precision may be reduced by
removing as many time indicators as wanted. Hence valid timestamps
are
yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm and
yyyy-MM-ddTHH:mm:ss. All time stamps are UTC. For durations, use
the slash character as described in 8601, and for multiple non-
contiguous dates, use multiple strings, if allowed by the frame
definition.
The three byte language field, present in several frames, is used to
describe the language of the frame's content, according to ISO-639-2
[ISO-639-2]. The language should be represented in lower case. If the
language is not known the string "XXX" should be used.
All URLs [URL] MAY be relative, e.g. "picture.png", "../doc.txt".
If a frame is longer than it should be, e.g. having more fields than
specified in this document, that indicates that additions to the
frame have been made in a later version of the ID3v2 standard. This
is reflected by the revision number in the header of the tag.
4.1. Frame header flags
In the frame header the size descriptor is followed by two flag
bytes. All unused flags MUST be cleared. The first byte is for
'status messages' and the second byte is a format description. If an
unknown flag is set in the first byte the frame MUST NOT be changed
without that bit cleared. If an unknown flag is set in the second
byte the frame is likely to not be readable. Some flags in the second
byte indicates that extra information is added to the header. These
fields of extra information is ordered as the flags that indicates
them. The flags field is defined as follows (l and o left out because
ther resemblence to one and zero):
%0abc0000 %0h00kmnp
Some frame format flags indicate that additional information fields
are added to the frame. This information is added after the frame
header and before the frame data in the same order as the flags that
indicates them. I.e. the four bytes of decompressed size will precede
the encryption method byte. These additions affects the 'frame size'
field, but are not subject to encryption or compression.
The default status flags setting for a frame is, unless stated
otherwise, 'preserved if tag is altered' and 'preserved if file is
altered', i.e. %00000000.
4.1.1. Frame status flags
a - Tag alter preservation
This flag tells the tag parser what to do with this frame if it is
unknown and the tag is altered in any way. This applies to all
kinds of alterations, including adding more padding and reordering
the frames.
0 Frame should be preserved.
1 Frame should be discarded.
b - File alter preservation
This flag tells the tag parser what to do with this frame if it is
unknown and the file, excluding the tag, is altered. This does not
apply when the audio is completely replaced with other audio data.
0 Frame should be preserved.
1 Frame should be discarded.
c - Read only
This flag, if set, tells the software that the contents of this
frame are intended to be read only. Changing the contents might
break something, e.g. a signature. If the contents are changed,
without knowledge of why the frame was flagged read only and
without taking the proper means to compensate, e.g. recalculating
the signature, the bit MUST be cleared.
4.1.2. Frame format flags
h - Grouping identity
This flag indicates whether or not this frame belongs in a group
with other frames. If set, a group identifier byte is added to the
frame. Every frame with the same group identifier belongs to the
same group.
0 Frame does not contain group information
1 Frame contains group information
k - Compression
This flag indicates whether or not the frame is compressed.
A 'Data Length Indicator' byte MUST be included in the frame.
0 Frame is not compressed.
1 Frame is compressed using zlib [zlib] deflate method.
If set, this requires the 'Data Length Indicator' bit
to be set as well.
m - Encryption
This flag indicates whether or not the frame is encrypted. If set,
one byte indicating with which method it was encrypted will be
added to the frame. See description of the ENCR frame for more
information about encryption method registration. Encryption
should be done after compression. Whether or not setting this flag
requires the presence of a 'Data Length Indicator' depends on the
specific algorithm used.
0 Frame is not encrypted.
1 Frame is encrypted.
n - Unsynchronisation
This flag indicates whether or not unsynchronisation was applied
to this frame. See section 6 for details on unsynchronisation.
If this flag is set all data from the end of this header to the
end of this frame has been unsynchronised. Although desirable, the
presence of a 'Data Length Indicator' is not made mandatory by
unsynchronisation.
0 Frame has not been unsynchronised.
1 Frame has been unsyrchronised.
p - Data length indicator
This flag indicates that a data length indicator has been added to
the frame. The data length indicator is the value one would write
as the 'Frame length' if all of the frame format flags were
zeroed, represented as a 32 bit synchsafe integer.
0 There is no Data Length Indicator.
1 A data length Indicator has been added to the frame.
5. Tag location
The default location of an ID3v2 tag is prepended to the audio so
that players can benefit from the information when the data is
streamed. It is however possible to append the tag, or make a
prepend/append combination. When deciding upon where an unembedded
tag should be located, the following order of preference SHOULD be
considered.
1. Prepend the tag.
2. Prepend a tag with all vital information and add a second tag at
the end of the file, before tags from other tagging systems. The
first tag is required to have a SEEK frame.
3. Add a tag at the end of the file, before tags from other tagging
systems.
In case 2 and 3 the tag can simply be appended if no other known tags
are present. The suggested method to find ID3v2 tags are:
1. Look for a prepended tag using the pattern found in section 3.1.
2. If a SEEK frame was found, use its values to guide further
searching.
3. Look for a tag footer, scanning from the back of the file.
For every new tag that is found, the old tag should be discarded
unless the update flag in the extended header (section 3.2) is set.
6. Unsynchronisation
The only purpose of unsynchronisation is to make the ID3v2 tag as
compatible as possible with existing software and hardware. There is
no use in 'unsynchronising' tags if the file is only to be processed
only by ID3v2 aware software and hardware. Unsynchronisation is only
useful with tags in MPEG 1/2 layer I, II and III, MPEG 2.5 and AAC
files.
6.1. The unsynchronisation scheme
Whenever a false synchronisation is found within the tag, one zeroed
byte is inserted after the first false synchronisation byte. The
format of synchronisations that should be altered by ID3 encoders is
as follows:
%11111111 111xxxxx
and should be replaced with:
%11111111 00000000 111xxxxx
This has the side effect that all $FF 00 combinations have to be
altered, so they will not be affected by the decoding process.
Therefore all the $FF 00 combinations have to be replaced with the
$FF 00 00 combination during the unsynchronisation.
To indicate usage of the unsynchronisation, the unsynchronisation
flag in the frame header should be set. This bit MUST be set if the
frame was altered by the unsynchronisation and SHOULD NOT be set if
unaltered. If all frames in the tag are unsynchronised the
unsynchronisation flag in the tag header SHOULD be set. It MUST NOT
be set if the tag has a frame which is not unsynchronised.
Assume the first byte of the audio to be $FF. The special case when
the last byte of the last frame is $FF and no padding nor footer is
used will then introduce a false synchronisation. This can be solved
by adding a footer, adding padding or unsynchronising the frame and
add $00 to the end of the frame data, thus adding more byte to the
frame size than a normal unsynchronisation would. Although not
preferred, it is allowed to apply the last method on all frames
ending with $FF.
It is preferred that the tag is either completely unsynchronised or
not unsynchronised at all. A completely unsynchronised tag has no
false synchonisations in it, as defined above, and does not end with
$FF. A completely non-unsynchronised tag contains no unsynchronised
frames, and thus the unsynchronisation flag in the header is cleared.
Do bear in mind, that if compression or encryption is used, the
unsynchronisation scheme MUST be applied afterwards. When decoding an
unsynchronised frame, the unsynchronisation scheme MUST be reversed
first, encryption and decompression afterwards.
6.2. Synchsafe integers
In some parts of the tag it is inconvenient to use the
unsychronisation scheme because the size of unsynchronised data is
not known in advance, which is particularly problematic with size
descriptors. The solution in ID3v2 is to use synchsafe integers, in
which there can never be any false synchs. Synchsafe integers are
integers that keep its highest bit (bit 7) zeroed, making seven bits
out of eight available. Thus a 32 bit synchsafe integer can store 28
bits of information.
Example:
255 (%11111111) encoded as a 16 bit synchsafe integer is 383
(%00000001 01111111).
7. Copyright
Copyright (C) Martin Nilsson 2000. All Rights Reserved.
This document and translations of it may be copied and furnished to
others, and derivative works that comment on or otherwise explain it
or assist in its implementation may be prepared, copied, published
and distributed, in whole or in part, without restriction of any
kind, provided that a reference to this document is included on all
such copies and derivative works. However, this document itself may
not be modified in any way and reissued as the original document.
The limited permissions granted above are perpetual and will not be
revoked.
This document and the information contained herein is provided on an
'AS IS' basis and THE AUTHORS DISCLAIMS ALL WARRANTIES, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
8. References
[ID3v2] Martin Nilsson, 'ID3v2 informal standard'.
<url:http://www.id3.org/id3v2.3.0.txt>
[ISO-639-2] ISO/FDIS 639-2.
'Codes for the representation of names of languages, Part 2: Alpha-3
code.' Technical committee / subcommittee: TC 37 / SC 2
[ISO-3309] ISO 3309
'Information Processing Systems--Data Communication High-Level Data
Link Control Procedure--Frame Structure', IS 3309, October 1984, 3rd
Edition.
[ISO-8859-1] ISO/IEC DIS 8859-1.
'8-bit single-byte coded graphic character sets, Part 1: Latin
alphabet No. 1.' Technical committee / subcommittee: JTC 1 / SC 2
[JFIF] 'JPEG File Interchange Format, version 1.02'
<url:http://www.w3.org/Graphics/JPEG/jfif.txt>
[KEYWORDS] S. Bradner, 'Key words for use in RFCs to Indicate
Requirement Levels', RFC 2119, March 1997.
<url:ftp://ftp.isi.edu/in-notes/rfc2119.txt>
[MPEG] ISO/IEC 11172-3:1993.
'Coding of moving pictures and associated audio for digital storage
media at up to about 1,5 Mbit/s, Part 3: Audio.'
Technical committee / subcommittee: JTC 1 / SC 29
and
ISO/IEC 13818-3:1995
'Generic coding of moving pictures and associated audio information,
Part 3: Audio.'
Technical committee / subcommittee: JTC 1 / SC 29
and
ISO/IEC DIS 13818-3
'Generic coding of moving pictures and associated audio information,
Part 3: Audio (Revision of ISO/IEC 13818-3:1995)'
[PNG] 'Portable Network Graphics, version 1.0'
<url:http://www.w3.org/TR/REC-png-multi.html>
[UNICODE] The Unicode Consortium,
'The Unicode Standard Version 3.0', ISBN 0-201-61633-5.
<url:http://www.unicode.org/unicode/standard/versions/Unicode3.0.htm>
[URL] T. Berners-Lee, L. Masinter & M. McCahill, 'Uniform Resource
Locators (URL)', RFC 1738, December 1994.
<url:ftp://ftp.isi.edu/in-notes/rfc1738.txt>
[UTF-8] F. Yergeau, 'UTF-8, a transformation format of ISO 10646',
RFC 2279, January 1998.
<url:ftp://ftp.isi.edu/in-notes/rfc2279.txt>
[UTF-16] F. Yergeau, 'UTF-16, an encoding of ISO 10646', RFC 2781,
February 2000.
<url:ftp://ftp.isi.edu/in-notes/rfc2781.txt>
[ZLIB] P. Deutsch, Aladdin Enterprises & J-L. Gailly, 'ZLIB
Compressed Data Format Specification version 3.3', RFC 1950,
May 1996.
<url:ftp://ftp.isi.edu/in-notes/rfc1950.txt>
9. Author's Address
Written by
Martin Nilsson
Rydsvägen 246 C. 30
SE-584 34 Linköping
Sweden
Email: nilsson@id3.org

382
gst/id3demux/id3v2frames.c Normal file
View file

@ -0,0 +1,382 @@
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
/* Copyright 2005 Jan Schmidt <thaytan@mad.scientist.com>
* Copyright 2002,2003 Scott Wheeler <wheeler@kde.org> (portions from taglib)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <gst/tag/tag.h>
#ifdef HAVE_ZLIB
#include <zlib.h>
#endif
#include "id3tags.h"
GST_DEBUG_CATEGORY_EXTERN (id3demux_debug);
#define GST_CAT_DEFAULT (id3demux_debug)
static gchar *parse_comment_frame (ID3TagsWorking * work);
static gchar *parse_text_identification_frame (ID3TagsWorking * work);
static gboolean id3v2_tag_to_taglist (ID3TagsWorking * work,
const gchar * tag_name, gchar * tag_str);
static void parse_split_strings (ID3TagsWorking * work, guint8 encoding,
gchar ** field1, gchar ** field2);
#define ID3V2_ENCODING_ISO8859 0x00
#define ID3V2_ENCODING_UTF16 0x01
#define ID3V2_ENCODING_UTF16BE 0x02
#define ID3V2_ENCODING_UTF8 0x03
extern guint read_synch_uint (guint8 * data, guint size);
gboolean
id3demux_id3v2_parse_frame (ID3TagsWorking * work)
{
const gchar *tag_name;
gboolean result = FALSE;
gint i;
guint8 *frame_data = work->hdr.frame_data;
guint frame_data_size = work->cur_frame_size;
gchar *tag_str = NULL;
/* Check that the frame id is valid */
for (i = 0; i < 5 && work->frame_id[i] != '\0'; i++) {
if (!g_ascii_isalnum (work->frame_id[i])) {
GST_DEBUG ("Encountered invalid frame_id");
return FALSE;
}
}
/* Can't handle encrypted frames right now */
if (work->frame_flags & ID3V2_FRAME_FORMAT_ENCRYPTION) {
GST_WARNING ("Encrypted frames are not supported");
return FALSE;
}
if (work->frame_flags & ID3V2_FRAME_FORMAT_UNSYNCHRONISATION) {
GST_WARNING ("ID3v2 frame with unsupported unsynchronisation applied. "
"May fail badly");
}
tag_name = gst_tag_from_id3_tag (work->frame_id);
if (tag_name == NULL)
return FALSE;
if (work->frame_flags & (ID3V2_FRAME_FORMAT_COMPRESSION |
ID3V2_FRAME_FORMAT_DATA_LENGTH_INDICATOR)) {
if (work->hdr.frame_data_size <= 4)
return FALSE;
work->parse_size = read_synch_uint (frame_data, 4);
frame_data += 4;
frame_data_size -= 4;
} else
work->parse_size = frame_data_size;
if (work->frame_flags & ID3V2_FRAME_FORMAT_COMPRESSION) {
uLongf destSize = work->parse_size;
Bytef *dest, *src;
work->parse_data = g_malloc (work->parse_size);
g_return_val_if_fail (work->parse_data != NULL, FALSE);
dest = (Bytef *) work->parse_data;
src = (Bytef *) frame_data;
if (uncompress (dest, &destSize, src, frame_data_size) != Z_OK) {
g_free (work->parse_data);
return FALSE;
}
} else {
work->parse_data = work->hdr.frame_data;
}
if (work->frame_id[0] == 'T') {
if (strcmp (work->frame_id, "TXXX") != 0) {
/* Text identification frame */
tag_str = parse_text_identification_frame (work);
} else {
/* Handle user text frame */
}
} else if (!strcmp (work->frame_id, "COMM")) {
/* Comment */
tag_str = parse_comment_frame (work);
} else if (!strcmp (work->frame_id, "APIC")) {
/* Attached picture */
} else if (!strcmp (work->frame_id, "RVA2")) {
/* Relative volume */
} else if (!strcmp (work->frame_id, "UFID")) {
/* Unique file identifier */
}
if (work->frame_flags & ID3V2_FRAME_FORMAT_COMPRESSION) {
g_free (work->parse_data);
}
if (tag_str != NULL) {
/* g_print ("Tag %s value %s\n", tag_name, tag_str); */
result = id3v2_tag_to_taglist (work, tag_name, tag_str);
g_free (tag_str);
}
return result;
}
static gchar *
parse_comment_frame (ID3TagsWorking * work)
{
guint8 encoding;
gchar language[4];
gchar *description = NULL;
gchar *text = NULL;
gchar *out_str = NULL;
if (work->parse_size < 6)
return NULL;
encoding = work->parse_data[0];
language[0] = work->parse_data[1];
language[1] = work->parse_data[2];
language[2] = work->parse_data[3];
language[3] = 0;
parse_split_strings (work, encoding, &description, &text);
if (text == NULL || description == NULL) {
GST_ERROR ("Failed to decode comment frame");
goto fail;
}
if (!g_utf8_validate (text, -1, NULL)) {
GST_ERROR ("Converted string is not valid utf-8");
goto fail;
} else {
if (strlen (description) > 0 && g_utf8_validate (description, -1, NULL)) {
out_str = g_strdup_printf ("Description: %s\nComment: %s",
description, text);
} else {
out_str = g_strdup (text);
}
}
fail:
g_free (description);
g_free (text);
return out_str;
}
static gchar *
parse_text_identification_frame (ID3TagsWorking * work)
{
guchar encoding;
gchar *text = NULL;
if (work->parse_size < 2)
return NULL;
encoding = work->parse_data[0];
switch (encoding) {
case ID3V2_ENCODING_ISO8859:
text = g_convert ((gchar *) (work->parse_data + 1),
work->parse_size - 1, "UTF-8", "ISO-8859-1", NULL, NULL, NULL);
break;
case ID3V2_ENCODING_UTF8:
text = g_strndup ((gchar *) (work->parse_data + 1), work->parse_size - 1);
break;
case ID3V2_ENCODING_UTF16:
text = g_convert ((gchar *) (work->parse_data + 1),
work->parse_size - 1, "UTF-8", "UTF-16", NULL, NULL, NULL);
break;
case ID3V2_ENCODING_UTF16BE:
text = g_convert ((gchar *) (work->parse_data + 1),
work->parse_size - 1, "UTF-8", "UTF-16BE", NULL, NULL, NULL);
break;
}
if (text != NULL && !g_utf8_validate (text, -1, NULL)) {
GST_ERROR ("Converted string is not valid utf-8");
g_free (text);
text = NULL;
}
return text;
}
static gboolean
id3v2_tag_to_taglist (ID3TagsWorking * work, const gchar * tag_name,
gchar * tag_str)
{
GType tag_type = gst_tag_get_type (tag_name);
GstTagList *tag_list = work->tags;
switch (tag_type) {
case G_TYPE_UINT:
{
guint tmp;
gchar *check;
tmp = strtoul ((char *) tag_str, &check, 10);
if (strcmp (tag_name, GST_TAG_DATE) == 0) {
GDate *d;
if (*check != '\0')
break;
if (tmp == 0)
break;
d = g_date_new_dmy (1, 1, tmp);
tmp = g_date_get_julian (d);
g_date_free (d);
} else if (strcmp (tag_name, GST_TAG_TRACK_NUMBER) == 0) {
if (*check == '/') {
guint total;
check++;
total = strtoul (check, &check, 10);
if (*check != '\0')
break;
gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
GST_TAG_TRACK_COUNT, total, NULL);
}
} else if (strcmp (tag_name, GST_TAG_ALBUM_VOLUME_NUMBER) == 0) {
if (*check == '/') {
guint total;
check++;
total = strtoul (check, &check, 10);
if (*check != '\0')
break;
gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
GST_TAG_ALBUM_VOLUME_COUNT, total, NULL);
}
}
if (*check != '\0')
break;
gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND, tag_name, tmp, NULL);
break;
}
case G_TYPE_UINT64:
{
guint64 tmp;
g_assert (strcmp (tag_name, GST_TAG_DURATION) == 0);
tmp = strtoul ((char *) tag_str, NULL, 10);
if (tmp == 0) {
break;
}
gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
GST_TAG_DURATION, tmp * 1000 * 1000, NULL);
break;
}
case G_TYPE_STRING:{
gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
tag_name, (const gchar *) tag_str, NULL);
break;
}
/* handles GST_TYPE_DATE and anything else */
default:{
GValue src = { 0, };
GValue dest = { 0, };
g_value_init (&src, G_TYPE_STRING);
g_value_set_string (&src, (const gchar *) tag_str);
g_value_init (&dest, tag_type);
if (g_value_transform (&src, &dest)) {
gst_tag_list_add_values (tag_list, GST_TAG_MERGE_APPEND,
tag_name, &dest, NULL);
} else {
GST_WARNING ("Failed to transform tag from string to type '%s'",
g_type_name (tag_type));
}
g_value_unset (&src);
g_value_unset (&dest);
break;
}
}
return TRUE;
}
static void
parse_split_strings (ID3TagsWorking * work, guint8 encoding,
gchar ** field1, gchar ** field2)
{
guint text_pos;
*field1 = *field2 = NULL;
switch (encoding) {
case ID3V2_ENCODING_ISO8859:
for (text_pos = 4; text_pos < work->parse_size - 1; text_pos++) {
if (work->parse_data[text_pos] == 0) {
*field1 = g_convert ((gchar *) (work->parse_data + 4),
text_pos - 4, "UTF-8", "ISO-8859-1", NULL, NULL, NULL);
*field2 = g_convert ((gchar *) (work->parse_data + text_pos + 5),
work->parse_size - text_pos - 5,
"UTF-8", "ISO-8859-1", NULL, NULL, NULL);
break;
}
}
break;
case ID3V2_ENCODING_UTF8:
*field1 = g_strndup ((gchar *) (work->parse_data + 4),
work->parse_size - 4);
text_pos = 4 + strlen (*field1) + 1; /* Offset by one more for the null */
if (text_pos < work->parse_size) {
*field2 = g_strndup ((gchar *) (work->parse_data + text_pos),
work->parse_size - text_pos);
}
break;
case ID3V2_ENCODING_UTF16:
case ID3V2_ENCODING_UTF16BE:
{
/* Find '\0\0' terminator */
for (text_pos = 4; text_pos < work->parse_size - 2; text_pos++) {
if (work->parse_data[text_pos] == 0 &&
work->parse_data[text_pos + 1] == 0) {
/* found our delimiter */
if (encoding == ID3V2_ENCODING_UTF16) {
*field1 = g_convert ((gchar *) (work->parse_data + 4),
text_pos - 4, "UTF-8", "UTF-16", NULL, NULL, NULL);
*field2 = g_convert ((gchar *) (work->parse_data + text_pos + 6),
work->parse_size - text_pos - 6,
"UTF-8", "UTF-16", NULL, NULL, NULL);
} else {
*field1 = g_convert ((gchar *) (work->parse_data + 4),
text_pos - 4, "UTF-8", "UTF-16BE", NULL, NULL, NULL);
*field2 = g_convert ((gchar *) (work->parse_data + text_pos + 6),
work->parse_size - text_pos - 6,
"UTF-8", "UTF-16BE", NULL, NULL, NULL);
}
break;
}
}
break;
}
}
}

View file

@ -33,7 +33,7 @@
#define G_MAXUINT32 0xffffffff
#endif
GST_DEBUG_CATEGORY (wavparse_debug);
GST_DEBUG_CATEGORY_STATIC (wavparse_debug);
#define GST_CAT_DEFAULT (wavparse_debug)
static void gst_wavparse_base_init (gpointer g_class);
@ -1140,6 +1140,7 @@ pause:
GST_ELEMENT_ERROR (wav, STREAM, FAILED,
(_("Internal data stream error.")),
("streaming stopped, reason %s", gst_flow_get_name (ret)));
if (wav->srcpad != NULL)
gst_pad_push_event (wav->srcpad, gst_event_new_eos ());
}
}