id3demux: use -base provided id3 tag parsing
authorMark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
Thu, 14 Jul 2011 13:42:36 +0000 (15:42 +0200)
committerTim-Philipp Müller <tim.muller@collabora.co.uk>
Sat, 13 Aug 2011 22:19:32 +0000 (23:19 +0100)
https://bugzilla.gnome.org/show_bug.cgi?id=654388

configure.ac
gst/id3demux/Makefile.am
gst/id3demux/gstid3demux.c
gst/id3demux/id3tags.c [deleted file]
gst/id3demux/id3tags.h [deleted file]
gst/id3demux/id3v2frames.c [deleted file]

index 304fedd..eab345d 100644 (file)
@@ -993,9 +993,9 @@ AG_GST_CHECK_FEATURE(WAVPACK, [wavpack plug-in], wavpack, [
   AC_SUBST(WAVPACK_LIBS)
 ])
 
-dnl *** qtdemux & id3demux & matroska prefer to have zlib ***
+dnl *** qtdemux & matroska prefer to have zlib ***
 translit(dnm, m, l) AM_CONDITIONAL(USE_ZLIB, true)
-AG_GST_CHECK_FEATURE(ZLIB, [zlib support for id3demux/qtdemux/matroska],, [
+AG_GST_CHECK_FEATURE(ZLIB, [zlib support for qtdemux/matroska],, [
   AG_GST_CHECK_LIBHEADER(ZLIB,
     z, uncompress,, zlib.h, [
     HAVE_ZLIB="yes"
index 823cb77..cce8035 100644 (file)
@@ -1,13 +1,13 @@
 plugin_LTLIBRARIES = libgstid3demux.la
 
-libgstid3demux_la_SOURCES = gstid3demux.c id3tags.c id3v2frames.c
+libgstid3demux_la_SOURCES = gstid3demux.c
 libgstid3demux_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) 
 libgstid3demux_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgsttag-@GST_MAJORMINOR@ \
-       -lgstpbutils-@GST_MAJORMINOR@ $(GST_BASE_LIBS) $(ZLIB_LIBS)
+       -lgstpbutils-@GST_MAJORMINOR@ $(GST_BASE_LIBS)
 libgstid3demux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 libgstid3demux_la_LIBTOOLFLAGS = --tag=disable-static
 
-noinst_HEADERS = gstid3demux.h id3tags.h
+noinst_HEADERS = gstid3demux.h
 
 Android.mk: Makefile.am $(BUILT_SOURCES)
        androgenizer \
index 9368504..c98bb55 100644 (file)
@@ -55,7 +55,6 @@
 #include <string.h>
 
 #include "gstid3demux.h"
-#include "id3tags.h"
 
 enum
 {
@@ -68,6 +67,9 @@ enum
 GST_DEBUG_CATEGORY (id3demux_debug);
 #define GST_CAT_DEFAULT (id3demux_debug)
 
+#define ID3V1_TAG_SIZE 128
+#define ID3V2_HDR_SIZE GST_TAG_ID3V2_HEADER_SIZE
+
 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
     GST_PAD_ALWAYS,
@@ -142,7 +144,7 @@ gst_id3demux_identify_tag (GstTagDemux * demux, GstBuffer * buf,
     if (data[0] != 'I' || data[1] != 'D' || data[2] != '3')
       goto no_marker;
 
-    *tag_size = id3demux_calc_id3v2_tag_size (buf);
+    *tag_size = gst_tag_get_id3v2_tag_size (buf);
   } else {
     if (data[0] != 'T' || data[1] != 'A' || data[2] != 'G')
       goto no_marker;
@@ -178,11 +180,10 @@ gst_id3demux_parse_tag (GstTagDemux * demux, GstBuffer * buffer,
     gboolean start_tag, guint * tag_size, GstTagList ** tags)
 {
   if (start_tag) {
-    ID3TagsResult res;          /* FIXME: make id3tags.c return tagmuxresult values */
-
-    res = id3demux_read_id3v2_tag (buffer, tag_size, tags);
+    *tag_size = gst_tag_get_id3v2_tag_size (buffer);
+    *tags = gst_tag_list_from_id3v2_tag (buffer);
 
-    if (G_LIKELY (res == ID3TAGS_READ_TAG)) {
+    if (G_LIKELY (*tags != NULL)) {
       gst_id3demux_add_container_format (*tags);
       return GST_TAG_DEMUX_RESULT_OK;
     } else {
@@ -276,11 +277,6 @@ plugin_init (GstPlugin * plugin)
 
   gst_tag_register_musicbrainz_tags ();
 
-  /* ensure private tag is registered */
-  gst_tag_register (GST_ID3_DEMUX_TAG_ID3V2_FRAME, GST_TAG_FLAG_META,
-      GST_TYPE_BUFFER, "ID3v2 frame", "unparsed id3v2 tag frame",
-      gst_tag_merge_use_first);
-
   return gst_element_register (plugin, "id3demux",
       GST_RANK_PRIMARY, GST_TYPE_ID3DEMUX);
 }
diff --git a/gst/id3demux/id3tags.c b/gst/id3demux/id3tags.c
deleted file mode 100644 (file)
index d201d7c..0000000
+++ /dev/null
@@ -1,554 +0,0 @@
-/* -*- 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 (const 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;
-}
-
-guint
-id3demux_calc_id3v2_tag_size (GstBuffer * buf)
-{
-  guint8 *data, flags;
-  guint size;
-
-  g_assert (buf != NULL);
-  g_assert (GST_BUFFER_SIZE (buf) >= ID3V2_HDR_SIZE);
-
-  data = GST_BUFFER_DATA (buf);
-
-  /* Check for 'ID3' string at start of buffer */
-  if (data[0] != 'I' || data[1] != 'D' || data[2] != '3') {
-    GST_DEBUG ("No ID3v2 tag in data");
-    return 0;
-  }
-
-  /* Read the flags */
-  flags = data[5];
-
-  /* Read the size from the header */
-  size = read_synch_uint (data + 6, 4);
-  if (size == 0)
-    return ID3V2_HDR_SIZE;
-
-  size += ID3V2_HDR_SIZE;
-
-  /* Expand the read size to include a footer if there is one */
-  if ((flags & ID3V2_HDR_FLAG_FOOTER))
-    size += 10;
-
-  GST_DEBUG ("ID3v2 tag, size: %u bytes", size);
-  return size;
-}
-
-guint8 *
-id3demux_ununsync_data (const guint8 * unsync_data, guint32 * size)
-{
-  const guint8 *end;
-  guint8 *out, *uu;
-  guint out_size;
-
-  uu = out = g_malloc (*size);
-
-  for (end = unsync_data + *size; unsync_data < end - 1; ++unsync_data, ++uu) {
-    *uu = *unsync_data;
-    if (G_UNLIKELY (*unsync_data == 0xff && *(unsync_data + 1) == 0x00))
-      ++unsync_data;
-  }
-
-  /* take care of last byte (if last two bytes weren't 0xff 0x00) */
-  if (unsync_data < end) {
-    *uu = *unsync_data;
-    ++uu;
-  }
-
-  out_size = uu - out;
-  GST_DEBUG ("size after un-unsyncing: %u (before: %u)", out_size, *size);
-
-  *size = out_size;
-  return out;
-}
-
-/* caller must pass buffer with full ID3 tag */
-ID3TagsResult
-id3demux_read_id3v2_tag (GstBuffer * buffer, guint * id3v2_size,
-    GstTagList ** tags)
-{
-  guint8 *data, *uu_data = NULL;
-  guint read_size;
-  ID3TagsWorking work;
-  guint8 flags;
-  ID3TagsResult result;
-  guint16 version;
-
-  read_size = id3demux_calc_id3v2_tag_size (buffer);
-
-  if (id3v2_size)
-    *id3v2_size = read_size;
-
-  /* Ignore tag if it has no frames attached, but skip the header then */
-  if (read_size <= ID3V2_HDR_SIZE)
-    return ID3TAGS_BROKEN_TAG;
-
-  data = GST_BUFFER_DATA (buffer);
-
-  /* Read the version */
-  version = GST_READ_UINT16_BE (data + 3);
-
-  /* Read the flags */
-  flags = data[5];
-
-  /* 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_BROKEN_TAG;
-  }
-
-  GST_DEBUG ("ID3v2 header flags: %s %s %s %s",
-      (flags & ID3V2_HDR_FLAG_UNSYNC) ? "UNSYNC" : "",
-      (flags & ID3V2_HDR_FLAG_EXTHDR) ? "EXTENDED_HEADER" : "",
-      (flags & ID3V2_HDR_FLAG_EXPERIMENTAL) ? "EXPERIMENTAL" : "",
-      (flags & ID3V2_HDR_FLAG_FOOTER) ? "FOOTER" : "");
-
-  /* This shouldn't really happen! Caller should have checked first */
-  if (GST_BUFFER_SIZE (buffer) < read_size) {
-    GST_DEBUG
-        ("Found ID3v2 tag with revision 2.%d.%d - need %u more bytes to read",
-        version >> 8, version & 0xff,
-        (guint) (read_size - GST_BUFFER_SIZE (buffer)));
-    return ID3TAGS_MORE_DATA;   /* Need more data to decode with */
-  }
-
-  GST_DEBUG ("Reading ID3v2 tag with revision 2.%d.%d of size %u", version >> 8,
-      version & 0xff, read_size);
-
-  g_return_val_if_fail (tags != NULL, ID3TAGS_READ_TAG);
-
-  GST_MEMDUMP ("ID3v2 tag", GST_BUFFER_DATA (buffer), read_size);
-
-  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;
-
-  /* in v2.3 the frame sizes are not syncsafe, so the entire tag had to be
-   * unsynced. In v2.4 the frame sizes are syncsafe so it's just the frame
-   * data that needs un-unsyncing, but not the frame headers. */
-  if ((flags & ID3V2_HDR_FLAG_UNSYNC) != 0 && ID3V2_VER_MAJOR (version) <= 3) {
-    GST_DEBUG ("Un-unsyncing entire tag");
-    uu_data = id3demux_ununsync_data (work.hdr.frame_data,
-        &work.hdr.frame_data_size);
-    work.hdr.frame_data = uu_data;
-    GST_MEMDUMP ("ID3v2 tag (un-unsyced)", uu_data, work.hdr.frame_data_size);
-  }
-
-  result = id3demux_id3v2_frames_to_tag_list (&work, work.hdr.frame_data_size);
-
-  *tags = work.tags;
-
-  g_free (uu_data);
-
-  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
-{
-  const gchar *orig;
-  const gchar *new;
-} frame_id_conversions[] = {
-  /* 2.3.x frames */
-  {
-  "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"}, {
-  "TCO", "TCON"}, {
-  "TCR", "TCOP"}, {
-  "TDA", "TDAT"}, {             /* obsolete, but we need to parse it anyway */
-  "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;
-}
-
-
-/* add unknown or unhandled ID3v2 frames to the taglist as binary blobs */
-static void
-id3demux_add_id3v2_frame_blob_to_taglist (ID3TagsWorking * work, guint size)
-{
-  GstBuffer *blob;
-  GstCaps *caps;
-  guint8 *frame_data;
-  gchar *media_type;
-  guint frame_size, header_size;
-  guint i;
-
-  switch (ID3V2_VER_MAJOR (work->hdr.version)) {
-    case 1:
-    case 2:
-      header_size = 3 + 3;
-      break;
-    case 3:
-    case 4:
-      header_size = 4 + 4 + 2;
-      break;
-    default:
-      g_return_if_reached ();
-  }
-
-  frame_data = work->hdr.frame_data - header_size;
-  frame_size = size + header_size;
-
-  blob = gst_buffer_new_and_alloc (frame_size);
-  memcpy (GST_BUFFER_DATA (blob), frame_data, frame_size);
-
-  /* Sanitize frame id */
-  for (i = 0; i < 4; i++) {
-    if (!g_ascii_isalnum (frame_data[i]))
-      frame_data[i] = '_';
-  }
-
-  media_type = g_strdup_printf ("application/x-gst-id3v2-%c%c%c%c-frame",
-      g_ascii_tolower (frame_data[0]), g_ascii_tolower (frame_data[1]),
-      g_ascii_tolower (frame_data[2]), g_ascii_tolower (frame_data[3]));
-  caps = gst_caps_new_simple (media_type, "version", G_TYPE_INT,
-      (gint) ID3V2_VER_MAJOR (work->hdr.version), NULL);
-  gst_buffer_set_caps (blob, caps);
-  gst_caps_unref (caps);
-  g_free (media_type);
-
-  /* gst_util_dump_mem (GST_BUFFER_DATA (blob), GST_BUFFER_SIZE (blob)); */
-
-  gst_tag_list_add (work->tags, GST_TAG_MERGE_APPEND,
-      GST_ID3_DEMUX_TAG_ID3V2_FRAME, blob, NULL);
-  gst_buffer_unref (blob);
-}
-
-static ID3TagsResult
-id3demux_id3v2_frames_to_tag_list (ID3TagsWorking * work, guint size)
-{
-  guint frame_hdr_size;
-  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) {
-      GST_DEBUG ("Invalid extended header. Broken tag");
-      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;
-    gboolean read_synch_size = TRUE;
-    guint i;
-
-    /* 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);
-
-        /* 3 byte non-synchsafe size */
-        frame_size = work->hdr.frame_data[3] << 16 |
-            work->hdr.frame_data[4] << 8 | work->hdr.frame_data[5];
-        frame_flags = 0;
-        break;
-      case 3:
-        read_synch_size = FALSE;        /* 2.3 frame size is not synch-safe */
-      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;
-        if (read_synch_size)
-          frame_size = read_synch_uint (work->hdr.frame_data + 4, 4);
-        else
-          frame_size = GST_READ_UINT32_BE (work->hdr.frame_data + 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);
-          if (obsolete_id)
-            GST_DEBUG ("Ignoring v2.3 frame %s", 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 || strcmp (frame_id, "") == 0)
-      break;                    /* No more frames to read */
-
-    /* Sanitize frame id */
-    switch (ID3V2_VER_MAJOR (work->hdr.version)) {
-      case 0:
-      case 1:
-      case 2:
-        for (i = 0; i < 3; i++) {
-          if (!g_ascii_isalnum (frame_id[i]))
-            frame_id[i] = '_';
-        }
-        break;
-      default:
-        for (i = 0; i < 4; i++) {
-          if (!g_ascii_isalnum (frame_id[i]))
-            frame_id[i] = '_';
-        }
-    }
-#if 1
-    GST_LOG
-        ("Frame @ %ld (0x%02lx) id %s size %u, next=%ld (0x%02lx) obsolete=%d",
-        (glong) (work->hdr.frame_data - start),
-        (glong) (work->hdr.frame_data - start), frame_id, frame_size,
-        (glong) (work->hdr.frame_data + frame_hdr_size + frame_size - start),
-        (glong) (work->hdr.frame_data + frame_hdr_size + frame_size - start),
-        obsolete_id);
-#define flag_string(flag,str) \
-        ((frame_flags & (flag)) ? (str) : "")
-    GST_LOG ("Frame header flags: 0x%04x %s %s %s %s %s %s %s", frame_flags,
-        flag_string (ID3V2_FRAME_STATUS_FRAME_ALTER_PRESERVE, "ALTER_PRESERVE"),
-        flag_string (ID3V2_FRAME_STATUS_READONLY, "READONLY"),
-        flag_string (ID3V2_FRAME_FORMAT_GROUPING_ID, "GROUPING_ID"),
-        flag_string (ID3V2_FRAME_FORMAT_COMPRESSION, "COMPRESSION"),
-        flag_string (ID3V2_FRAME_FORMAT_ENCRYPTION, "ENCRYPTION"),
-        flag_string (ID3V2_FRAME_FORMAT_UNSYNCHRONISATION, "UNSYNC"),
-        flag_string (ID3V2_FRAME_FORMAT_DATA_LENGTH_INDICATOR, "LENGTH_IND"));
-#undef flag_str
-#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)) {
-        GST_LOG ("Extracted frame with id %s", frame_id);
-      } else {
-        GST_LOG ("Failed to extract frame with id %s", frame_id);
-        id3demux_add_id3v2_frame_blob_to_taglist (work, frame_size);
-      }
-    }
-    work->hdr.frame_data += frame_size;
-    work->hdr.frame_data_size -= frame_size;
-  }
-
-  if (gst_structure_n_fields (GST_STRUCTURE (work->tags)) == 0) {
-    GST_DEBUG ("Could not extract any frames from tag. Broken or empty tag");
-    gst_tag_list_free (work->tags);
-    work->tags = NULL;
-    return ID3TAGS_BROKEN_TAG;
-  }
-
-  /* Set day/month now if they were in a separate (obsolete) TDAT frame */
-  if (work->pending_day != 0 && work->pending_month != 0) {
-    GDate *date = NULL;
-
-    if (gst_tag_list_get_date (work->tags, GST_TAG_DATE, &date)) {
-      g_date_set_day (date, work->pending_day);
-      g_date_set_month (date, work->pending_month);
-      gst_tag_list_add (work->tags, GST_TAG_MERGE_REPLACE, GST_TAG_DATE,
-          date, NULL);
-      g_date_free (date);
-    }
-  }
-
-  return ID3TAGS_READ_TAG;
-}
diff --git a/gst/id3demux/id3tags.h b/gst/id3demux/id3tags.h
deleted file mode 100644 (file)
index 14a42de..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/* 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
-
-/* private tag for storing unprocessed ID3v2 frames */
-#define GST_ID3_DEMUX_TAG_ID3V2_FRAME "private-id3v2-frame"
-
-#define ID3V1_TAG_SIZE 128
-#define ID3V2_MARK_SIZE 3
-#define ID3V2_HDR_SIZE 10
-
-typedef enum {
-  ID3TAGS_MORE_DATA,
-  ID3TAGS_READ_TAG,
-  ID3TAGS_BROKEN_TAG
-} ID3TagsResult;
-
-/* From id3tags.c */
-guint id3demux_calc_id3v2_tag_size (GstBuffer * buf);
-ID3TagsResult id3demux_read_id3v2_tag (GstBuffer *buffer, guint *id3v2_size,
-  GstTagList **tags);
-
-guint read_synch_uint (const guint8 * data, guint size);
-
-/* 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;
-  
-  /* To collect day/month from obsolete TDAT frame if it exists */
-  guint pending_month;
-  guint pending_day;
-} 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);
-
-guint8 * id3demux_ununsync_data (const guint8 * unsync_data, guint32 * size);
-
-G_END_DECLS
-
-#endif
diff --git a/gst/id3demux/id3v2frames.c b/gst/id3demux/id3v2frames.c
deleted file mode 100644 (file)
index e51bbb7..0000000
+++ /dev/null
@@ -1,1167 +0,0 @@
-/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
-/* Copyright 2006-2008 Tim-Philipp Müller <tim centricular net>
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <gst/tag/tag.h>
-#include <gst/base/gsttypefindhelper.h>
-
-#ifdef HAVE_ZLIB
-#include <zlib.h>
-#endif
-
-#include "id3tags.h"
-
-GST_DEBUG_CATEGORY_EXTERN (id3demux_debug);
-#define GST_CAT_DEFAULT (id3demux_debug)
-
-static gboolean parse_comment_frame (ID3TagsWorking * work);
-static gchar *parse_url_link_frame (ID3TagsWorking * work,
-    const gchar ** tag_name);
-static GArray *parse_text_identification_frame (ID3TagsWorking * work);
-static gchar *parse_user_text_identification_frame (ID3TagsWorking * work,
-    const gchar ** tag_name);
-static gchar *parse_unique_file_identifier (ID3TagsWorking * work,
-    const gchar ** tag_name);
-static gboolean parse_relative_volume_adjustment_two (ID3TagsWorking * work);
-static void parse_obsolete_tdat_frame (ID3TagsWorking * work);
-static gboolean id3v2_tag_to_taglist (ID3TagsWorking * work,
-    const gchar * tag_name, const gchar * tag_str);
-/* Parse a single string into an array of gchar* */
-static void parse_split_strings (guint8 encoding, gchar * data, gint data_size,
-    GArray ** out_fields);
-static void free_tag_strings (GArray * fields);
-static gboolean
-id3v2_genre_fields_to_taglist (ID3TagsWorking * work, const gchar * tag_name,
-    GArray * tag_fields);
-static gboolean parse_picture_frame (ID3TagsWorking * work);
-
-#define ID3V2_ENCODING_ISO8859 0x00
-#define ID3V2_ENCODING_UTF16   0x01
-#define ID3V2_ENCODING_UTF16BE 0x02
-#define ID3V2_ENCODING_UTF8    0x03
-
-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;
-  GArray *tag_fields = NULL;
-  guint8 *uu_data = NULL;
-
-#ifdef HAVE_ZLIB
-  guint8 *uncompressed_data = NULL;
-#endif
-
-  /* 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 (in case we ever do, we'll have
-   * to do the decryption after the un-unsynchronisation and decompression,
-   * not here) */
-  if (work->frame_flags & ID3V2_FRAME_FORMAT_ENCRYPTION) {
-    GST_WARNING ("Encrypted frames are not supported");
-    return FALSE;
-  }
-
-  tag_name = gst_tag_from_id3_tag (work->frame_id);
-  if (tag_name == NULL &&
-      strncmp (work->frame_id, "RVA2", 4) != 0 &&
-      strncmp (work->frame_id, "TXXX", 4) != 0 &&
-      strncmp (work->frame_id, "TDAT", 4) != 0 &&
-      strncmp (work->frame_id, "UFID", 4) != 0) {
-    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;
-    if (ID3V2_VER_MAJOR (work->hdr.version) == 3) {
-      work->parse_size = GST_READ_UINT32_BE (frame_data);
-    } else {
-      work->parse_size = read_synch_uint (frame_data, 4);
-    }
-    frame_data += 4;
-    frame_data_size -= 4;
-    GST_LOG ("Un-unsynced data size %d (of %d)", work->parse_size,
-        frame_data_size);
-    if (work->parse_size > frame_data_size) {
-      GST_WARNING ("ID3v2 frame %s data has invalid size %d (>%d)",
-          work->frame_id, work->parse_size, frame_data_size);
-      return FALSE;
-    }
-  }
-
-  /* in v2.3 the frame sizes are not syncsafe, so the entire tag had to be
-   * unsynced. In v2.4 the frame sizes are syncsafe so it's just the frame
-   * data that needs un-unsyncing, but not the frame headers. */
-  if (ID3V2_VER_MAJOR (work->hdr.version) == 4) {
-    if ((work->hdr.flags & ID3V2_HDR_FLAG_UNSYNC) != 0 ||
-        ((work->frame_flags & ID3V2_FRAME_FORMAT_UNSYNCHRONISATION) != 0)) {
-      GST_DEBUG ("Un-unsyncing frame %s", work->frame_id);
-      uu_data = id3demux_ununsync_data (frame_data, &frame_data_size);
-      frame_data = uu_data;
-      GST_MEMDUMP ("ID3v2 frame (un-unsyced)", frame_data, frame_data_size);
-    }
-  }
-
-  work->parse_size = frame_data_size;
-
-  if (work->frame_flags & ID3V2_FRAME_FORMAT_COMPRESSION) {
-#ifdef HAVE_ZLIB
-    uLongf destSize = work->parse_size;
-    Bytef *dest, *src;
-
-    uncompressed_data = g_malloc (work->parse_size);
-
-    dest = (Bytef *) uncompressed_data;
-    src = (Bytef *) frame_data;
-
-    if (uncompress (dest, &destSize, src, frame_data_size) != Z_OK) {
-      g_free (uncompressed_data);
-      g_free (uu_data);
-      return FALSE;
-    }
-    if (destSize != work->parse_size) {
-      GST_WARNING
-          ("Decompressing ID3v2 frame %s did not produce expected size %d bytes (got %lu)",
-          tag_name, work->parse_size, destSize);
-      g_free (uncompressed_data);
-      g_free (uu_data);
-      return FALSE;
-    }
-    work->parse_data = uncompressed_data;
-#else
-    GST_WARNING ("Compressed ID3v2 tag frame could not be decompressed"
-        " because gstid3demux was compiled without zlib support");
-    g_free (uu_data);
-    return FALSE;
-#endif
-  } else {
-    work->parse_data = frame_data;
-  }
-
-  if (work->frame_id[0] == 'T') {
-    if (strcmp (work->frame_id, "TDAT") == 0) {
-      parse_obsolete_tdat_frame (work);
-      result = TRUE;
-    } else if (strcmp (work->frame_id, "TXXX") == 0) {
-      /* Handle user text frame */
-      tag_str = parse_user_text_identification_frame (work, &tag_name);
-    } else {
-      /* Text identification frame */
-      tag_fields = parse_text_identification_frame (work);
-    }
-  } else if (work->frame_id[0] == 'W' && strcmp (work->frame_id, "WXXX") != 0) {
-    /* URL link frame: ISO-8859-1 encoded, one frame per tag */
-    tag_str = parse_url_link_frame (work, &tag_name);
-  } else if (!strcmp (work->frame_id, "COMM")) {
-    /* Comment */
-    result = parse_comment_frame (work);
-  } else if (!strcmp (work->frame_id, "APIC")) {
-    /* Attached picture */
-    result = parse_picture_frame (work);
-  } else if (!strcmp (work->frame_id, "RVA2")) {
-    /* Relative volume */
-    result = parse_relative_volume_adjustment_two (work);
-  } else if (!strcmp (work->frame_id, "UFID")) {
-    /* Unique file identifier */
-    tag_str = parse_unique_file_identifier (work, &tag_name);
-  }
-#ifdef HAVE_ZLIB
-  if (work->frame_flags & ID3V2_FRAME_FORMAT_COMPRESSION) {
-    g_free (uncompressed_data);
-    uncompressed_data = NULL;
-    work->parse_data = frame_data;
-  }
-#endif
-
-  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);
-  }
-  if (tag_fields != NULL) {
-    if (strcmp (work->frame_id, "TCON") == 0) {
-      /* Genre strings need special treatment */
-      result |= id3v2_genre_fields_to_taglist (work, tag_name, tag_fields);
-    } else {
-      gint t;
-
-      for (t = 0; t < tag_fields->len; t++) {
-        tag_str = g_array_index (tag_fields, gchar *, t);
-        if (tag_str != NULL && tag_str[0] != '\0')
-          result |= id3v2_tag_to_taglist (work, tag_name, tag_str);
-      }
-    }
-    free_tag_strings (tag_fields);
-  }
-
-  g_free (uu_data);
-
-  return result;
-}
-
-static gboolean
-parse_comment_frame (ID3TagsWorking * work)
-{
-  guint dummy;
-  guint8 encoding;
-  gchar language[4];
-  GArray *fields = NULL;
-  gchar *description, *text;
-
-  if (work->parse_size < 6)
-    return FALSE;
-
-  encoding = work->parse_data[0];
-  language[0] = g_ascii_tolower (work->parse_data[1]);
-  language[1] = g_ascii_tolower (work->parse_data[2]);
-  language[2] = g_ascii_tolower (work->parse_data[3]);
-  language[3] = '\0';
-
-  parse_split_strings (encoding, (gchar *) work->parse_data + 4,
-      work->parse_size - 4, &fields);
-
-  if (fields == NULL || fields->len < 2) {
-    GST_WARNING ("Failed to decode comment frame");
-    goto fail;
-  }
-  description = g_array_index (fields, gchar *, 0);
-  text = g_array_index (fields, gchar *, 1);
-
-  if (!g_utf8_validate (text, -1, NULL)) {
-    GST_WARNING ("Converted string is not valid utf-8");
-    goto fail;
-  }
-
-  /* skip our own dummy descriptions (from id3v2mux) */
-  if (strlen (description) > 0 && g_utf8_validate (description, -1, NULL) &&
-      sscanf (description, "c%u", &dummy) != 1) {
-    gchar *s;
-
-    /* must be either an ISO-639-1 or ISO-639-2 language code */
-    if (language[0] != '\0' &&
-        g_ascii_isalpha (language[0]) &&
-        g_ascii_isalpha (language[1]) &&
-        (g_ascii_isalpha (language[2]) || language[2] == '\0')) {
-      const gchar *lang_code;
-
-      /* prefer two-letter ISO 639-1 code if we have a mapping */
-      lang_code = gst_tag_get_language_code (language);
-      s = g_strdup_printf ("%s[%s]=%s", description,
-          (lang_code) ? lang_code : language, text);
-    } else {
-      s = g_strdup_printf ("%s=%s", description, text);
-    }
-    gst_tag_list_add (work->tags, GST_TAG_MERGE_APPEND,
-        GST_TAG_EXTENDED_COMMENT, s, NULL);
-    g_free (s);
-  } else if (text != NULL && *text != '\0') {
-    gst_tag_list_add (work->tags, GST_TAG_MERGE_APPEND,
-        GST_TAG_COMMENT, text, NULL);
-  } else {
-    goto fail;
-  }
-
-  free_tag_strings (fields);
-  return TRUE;
-
-fail:
-  {
-    GST_WARNING ("failed to parse COMM frame");
-    free_tag_strings (fields);
-    return FALSE;
-  }
-}
-
-static GArray *
-parse_text_identification_frame (ID3TagsWorking * work)
-{
-  guchar encoding;
-  GArray *fields = NULL;
-
-  if (work->parse_size < 2)
-    return NULL;
-
-  encoding = work->parse_data[0];
-  parse_split_strings (encoding, (gchar *) work->parse_data + 1,
-      work->parse_size - 1, &fields);
-  if (fields) {
-    if (fields->len > 0) {
-      GST_LOG ("Read %d fields from Text ID frame of size %d with encoding %d"
-          ". First is '%s'", fields->len, work->parse_size - 1, encoding,
-          g_array_index (fields, gchar *, 0));
-    } else {
-      GST_LOG ("Read 0 fields from Text ID frame of size %d with encoding %d",
-          work->parse_size - 1, encoding);
-    }
-  }
-
-  return fields;
-}
-
-static gboolean
-link_is_known_license (const gchar * url)
-{
-  return g_str_has_prefix (url, "http://creativecommons.org/licenses/");
-}
-
-static gchar *
-parse_url_link_frame (ID3TagsWorking * work, const gchar ** tag_name)
-{
-  gsize len;
-  gchar *nul, *data, *link;
-
-  *tag_name = NULL;
-
-  if (work->parse_size == 0)
-    return NULL;
-
-  data = (gchar *) work->parse_data;
-  /* if there's more data then the string is long, we only want to parse the
-   * data up to the terminating zero to g_convert and ignore the rest, as
-   * per spec */
-  nul = memchr (data, '\0', work->parse_size);
-  if (nul != NULL) {
-    len = (gsize) (nul - data);
-  } else {
-    len = work->parse_size;
-  }
-
-  link = g_convert (data, len, "UTF-8", "ISO-8859-1", NULL, NULL, NULL);
-
-  if (link == NULL || !gst_uri_is_valid (link)) {
-    GST_DEBUG ("Invalid URI in %s frame: %s", work->frame_id,
-        GST_STR_NULL (link));
-    g_free (link);
-    return NULL;
-  }
-
-  /* we don't know if it's a link to a page that explains the copyright
-   * situation, or a link that points to/represents a license, the ID3 spec
-   * does not separate those two things; for now only put known license URIs
-   * into GST_TAG_LICENSE_URI and everything else into GST_TAG_COPYRIGHT_URI */
-  if (strcmp (work->frame_id, "WCOP") == 0) {
-    if (link_is_known_license (link))
-      *tag_name = GST_TAG_LICENSE_URI;
-    else
-      *tag_name = GST_TAG_COPYRIGHT_URI;
-  } else if (strcmp (work->frame_id, "WOAF") == 0) {
-    /* can't be bothered to create a CONTACT_URI tag for this, so let's just
-     * put into into GST_TAG_CONTACT, which is where it ends up when reading
-     * the info from vorbis comments as well */
-    *tag_name = GST_TAG_CONTACT;
-  }
-
-  return link;
-}
-
-
-static gchar *
-parse_user_text_identification_frame (ID3TagsWorking * work,
-    const gchar ** tag_name)
-{
-  gchar *ret;
-  guchar encoding;
-  GArray *fields = NULL;
-
-  *tag_name = NULL;
-
-  if (work->parse_size < 2)
-    return NULL;
-
-  encoding = work->parse_data[0];
-
-  parse_split_strings (encoding, (gchar *) work->parse_data + 1,
-      work->parse_size - 1, &fields);
-
-  if (fields == NULL)
-    return NULL;
-
-  if (fields->len != 2) {
-    GST_WARNING ("Expected 2 fields in TXXX frame, but got %d", fields->len);
-    free_tag_strings (fields);
-    return NULL;
-  }
-
-  *tag_name =
-      gst_tag_from_id3_user_tag ("TXXX", g_array_index (fields, gchar *, 0));
-
-  GST_LOG ("TXXX frame of size %d. Mapped descriptor '%s' to GStreamer tag %s",
-      work->parse_size - 1, g_array_index (fields, gchar *, 0),
-      GST_STR_NULL (*tag_name));
-
-  if (*tag_name) {
-    ret = g_strdup (g_array_index (fields, gchar *, 1));
-    /* GST_LOG ("%s = %s", *tag_name, GST_STR_NULL (ret)); */
-  } else {
-    ret = NULL;
-  }
-
-  free_tag_strings (fields);
-  return ret;
-}
-
-static gboolean
-parse_id_string (ID3TagsWorking * work, gchar ** p_str, gint * p_len,
-    gint * p_datalen)
-{
-  gint len, datalen;
-
-  if (work->parse_size < 2)
-    return FALSE;
-
-  for (len = 0; len < work->parse_size - 1; ++len) {
-    if (work->parse_data[len] == '\0')
-      break;
-  }
-
-  datalen = work->parse_size - (len + 1);
-  if (len == 0 || datalen <= 0)
-    return FALSE;
-
-  *p_str = g_strndup ((gchar *) work->parse_data, len);
-  *p_len = len;
-  *p_datalen = datalen;
-
-  return TRUE;
-}
-
-static gchar *
-parse_unique_file_identifier (ID3TagsWorking * work, const gchar ** tag_name)
-{
-  gint len, datalen;
-  gchar *owner_id, *data, *ret = NULL;
-
-  GST_LOG ("parsing UFID frame of size %d", work->parse_size);
-
-  if (!parse_id_string (work, &owner_id, &len, &datalen))
-    return NULL;
-
-  data = (gchar *) work->parse_data + len + 1;
-  GST_LOG ("UFID owner ID: %s (+ %d bytes of data)", owner_id, datalen);
-
-  if (strcmp (owner_id, "http://musicbrainz.org") == 0 &&
-      g_utf8_validate (data, datalen, NULL)) {
-    *tag_name = GST_TAG_MUSICBRAINZ_TRACKID;
-    ret = g_strndup (data, datalen);
-  } else {
-    GST_INFO ("Unknown UFID owner ID: %s", owner_id);
-  }
-  g_free (owner_id);
-
-  return ret;
-}
-
-/* parse data and return length of the next string in the given encoding,
- * including the NUL terminator */
-static gint
-scan_encoded_string (guint8 encoding, gchar * data, gint data_size)
-{
-  gint i;
-
-  switch (encoding) {
-    case ID3V2_ENCODING_ISO8859:
-    case ID3V2_ENCODING_UTF8:
-      for (i = 0; i < data_size; ++i) {
-        if (data[i] == '\0')
-          return i + 1;
-      }
-      break;
-    case ID3V2_ENCODING_UTF16:
-    case ID3V2_ENCODING_UTF16BE:
-      /* we don't care about BOMs here and treat them as part of the string */
-      /* Find '\0\0' terminator */
-      for (i = 0; i < data_size - 1; i += 2) {
-        if (data[i] == '\0' && data[i + 1] == '\0')
-          return i + 2;
-      }
-      break;
-    default:
-      break;
-  }
-
-  return 0;
-}
-
-static gboolean
-parse_picture_frame (ID3TagsWorking * work)
-{
-  guint8 txt_encoding, pic_type;
-  gchar *mime_str = NULL;
-  gint len, datalen;
-
-  GST_LOG ("APIC frame (ID3v2.%u)", ID3V2_VER_MAJOR (work->hdr.version));
-
-  if (work->parse_size < 1 + 1 + 1 + 1 + 1)
-    goto not_enough_data;
-
-  txt_encoding = work->parse_data[0];
-  ++work->parse_data;
-  --work->parse_size;
-
-  /* Read image format; in early ID3v2 versions this is a fixed-length
-   * 3-character string without terminator; in later versions (>= 2.3.0)
-   * this is a NUL-terminated string of variable length */
-  if (ID3V2_VER_MAJOR (work->hdr.version) < 3) {
-    if (work->parse_size < 3)
-      goto not_enough_data;
-
-    mime_str = g_strndup ((gchar *) work->parse_data, 3);
-    len = 3;
-  } else {
-    if (!parse_id_string (work, &mime_str, &len, &datalen))
-      return FALSE;
-    ++len;                      /* for string terminator */
-  }
-
-  if (work->parse_size < len + 1 + 1 + 1)
-    goto not_enough_data;
-
-  work->parse_data += len;
-  work->parse_size -= len;
-
-  /* Read image type */
-  pic_type = work->parse_data[0];
-  ++work->parse_data;
-  --work->parse_size;
-
-  GST_LOG ("APIC frame mime type    : %s", GST_STR_NULL (mime_str));
-  GST_LOG ("APIC frame picture type : 0x%02x", (guint) pic_type);
-
-  if (work->parse_size < 1 + 1)
-    goto not_enough_data;
-
-  len = scan_encoded_string (txt_encoding, (gchar *) work->parse_data,
-      work->parse_size);
-
-  if (len < 1)
-    goto error;
-
-  /* just skip the description string ... */
-  GST_LOG ("Skipping description string (%d bytes in original coding)", len);
-
-  if (work->parse_size < len + 1)
-    goto not_enough_data;
-
-  work->parse_data += len;
-  work->parse_size -= len;
-
-  GST_DEBUG ("image data is %u bytes", work->parse_size);
-
-  if (work->parse_size <= 0)
-    goto not_enough_data;
-
-  if (!gst_tag_list_add_id3_image (work->tags, (guint8 *) work->parse_data,
-          work->parse_size, pic_type)) {
-    goto error;
-  }
-
-  g_free (mime_str);
-  return TRUE;
-
-not_enough_data:
-  {
-    GST_DEBUG ("not enough data, skipping APIC frame");
-    /* fall through to error */
-  }
-error:
-  {
-    GST_DEBUG ("problem parsing APIC frame, skipping");
-    g_free (mime_str);
-    return FALSE;
-  }
-}
-
-#define ID3V2_RVA2_CHANNEL_MASTER  1
-
-static gboolean
-parse_relative_volume_adjustment_two (ID3TagsWorking * work)
-{
-  const gchar *gain_tag_name = NULL;
-  const gchar *peak_tag_name = NULL;
-  gdouble gain_dB, peak_val;
-  guint64 peak;
-  guint8 *data, chan, peak_bits;
-  gchar *id;
-  gint len, datalen, i;
-
-  if (!parse_id_string (work, &id, &len, &datalen))
-    return FALSE;
-
-  if (datalen < (1 + 2 + 1)) {
-    GST_WARNING ("broken RVA2 frame, data size only %d bytes", datalen);
-    g_free (id);
-    return FALSE;
-  }
-
-  data = work->parse_data + len + 1;
-  chan = GST_READ_UINT8 (data);
-  gain_dB = (gdouble) ((gint16) GST_READ_UINT16_BE (data + 1)) / 512.0;
-  /* The meaning of the peak value is not defined in the ID3v2 spec. However,
-   * the first/only implementation of this seems to have been in XMMS, and
-   * other libs (like mutagen) seem to follow that implementation as well:
-   * see http://bugs.xmms.org/attachment.cgi?id=113&action=view */
-  peak_bits = GST_READ_UINT8 (data + 1 + 2);
-  if (peak_bits > 64) {
-    GST_WARNING ("silly peak precision of %d bits, ignoring", (gint) peak_bits);
-    peak_bits = 0;
-  }
-  data += 1 + 2 + 1;
-  datalen -= 1 + 2 + 1;
-  if (peak_bits == 16) {
-    peak = GST_READ_UINT16_BE (data);
-  } else {
-    peak = 0;
-    for (i = 0; i < (GST_ROUND_UP_8 (peak_bits) / 8) && datalen > 0; ++i) {
-      peak = peak << 8;
-      peak |= GST_READ_UINT8 (data);
-      ++data;
-      --datalen;
-    }
-  }
-
-  peak = peak << (64 - GST_ROUND_UP_8 (peak_bits));
-  peak_val =
-      gst_guint64_to_gdouble (peak) / gst_util_guint64_to_gdouble (G_MAXINT64);
-  GST_LOG ("RVA2 frame: id=%s, chan=%u, adj=%.2fdB, peak_bits=%u, peak=%.2f",
-      id, chan, gain_dB, (guint) peak_bits, peak_val);
-
-  if (chan == ID3V2_RVA2_CHANNEL_MASTER && strcmp (id, "track") == 0) {
-    gain_tag_name = GST_TAG_TRACK_GAIN;
-    peak_tag_name = GST_TAG_TRACK_PEAK;
-  } else if (chan == ID3V2_RVA2_CHANNEL_MASTER && strcmp (id, "album") == 0) {
-    gain_tag_name = GST_TAG_ALBUM_GAIN;
-    peak_tag_name = GST_TAG_ALBUM_PEAK;
-  } else {
-    GST_INFO ("Unhandled RVA2 frame id '%s' for channel %d", id, chan);
-  }
-
-  if (gain_tag_name) {
-    gst_tag_list_add (work->tags, GST_TAG_MERGE_APPEND,
-        gain_tag_name, gain_dB, NULL);
-  }
-  if (peak_tag_name && peak_bits > 0) {
-    gst_tag_list_add (work->tags, GST_TAG_MERGE_APPEND,
-        peak_tag_name, peak_val, NULL);
-  }
-
-  g_free (id);
-
-  return (gain_tag_name != NULL || peak_tag_name != NULL);
-}
-
-static void
-parse_obsolete_tdat_frame (ID3TagsWorking * work)
-{
-  if (work->parse_size >= 5 &&
-      work->parse_data[0] == ID3V2_ENCODING_ISO8859 &&
-      g_ascii_isdigit (work->parse_data[1]) &&
-      g_ascii_isdigit (work->parse_data[2]) &&
-      g_ascii_isdigit (work->parse_data[3]) &&
-      g_ascii_isdigit (work->parse_data[4])) {
-    work->pending_day = (10 * g_ascii_digit_value (work->parse_data[1])) +
-        g_ascii_digit_value (work->parse_data[2]);
-    work->pending_month = (10 * g_ascii_digit_value (work->parse_data[3])) +
-        g_ascii_digit_value (work->parse_data[4]);
-    GST_LOG ("date (dd/mm) %02u/%02u", work->pending_day, work->pending_month);
-  }
-}
-
-static gboolean
-id3v2_tag_to_taglist (ID3TagsWorking * work, const gchar * tag_name,
-    const gchar * tag_str)
-{
-  GType tag_type = gst_tag_get_type (tag_name);
-  GstTagList *tag_list = work->tags;
-
-  if (tag_str == NULL)
-    return FALSE;
-
-  switch (tag_type) {
-    case G_TYPE_UINT:
-    {
-      gint current, total;
-
-      if (sscanf (tag_str, "%d/%d", &current, &total) == 2) {
-        if (total <= 0) {
-          GST_WARNING ("Ignoring invalid value for total %d in tag %s",
-              total, tag_name);
-        } else {
-          if (strcmp (tag_name, GST_TAG_TRACK_NUMBER) == 0) {
-            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) {
-            gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
-                GST_TAG_ALBUM_VOLUME_COUNT, total, NULL);
-          }
-        }
-      } else if (sscanf (tag_str, "%d", &current) != 1) {
-        /* Not an integer in the string */
-        GST_WARNING ("Tag string for tag %s does not contain an integer - "
-            "ignoring", tag_name);
-        break;
-      }
-
-      if (current <= 0) {
-        GST_WARNING ("Ignoring invalid value %d in tag %s", current, tag_name);
-      } else {
-        gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND, tag_name, current,
-            NULL);
-      }
-      break;
-    }
-    case G_TYPE_UINT64:
-    {
-      guint64 tmp;
-
-      g_assert (strcmp (tag_name, GST_TAG_DURATION) == 0);
-      tmp = strtoul (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:{
-      const GValue *val;
-      guint i, num;
-
-      /* make sure we add each unique string only once per tag, we don't want
-       * to have the same genre in the genre list multiple times, for example,
-       * or the same DiscID in there twice just because it's contained in the
-       * tag multiple times under different TXXX user tags */
-      num = gst_tag_list_get_tag_size (tag_list, tag_name);
-      for (i = 0; i < num; ++i) {
-        val = gst_tag_list_get_value_index (tag_list, tag_name, i);
-        if (val != NULL && strcmp (g_value_get_string (val), tag_str) == 0)
-          break;
-      }
-      if (i == num) {
-        gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
-            tag_name, tag_str, NULL);
-      }
-      break;
-    }
-
-    default:{
-      gchar *tmp = NULL;
-      GValue src = { 0, };
-      GValue dest = { 0, };
-
-      /* Ensure that any date string is complete */
-      if (tag_type == GST_TYPE_DATE) {
-        guint year = 1901, month = 1, day = 1;
-
-        /* Dates can be yyyy-MM-dd, yyyy-MM or yyyy, but we need
-         * the first type */
-        if (sscanf (tag_str, "%04u-%02u-%02u", &year, &month, &day) == 0)
-          break;
-
-        tmp = g_strdup_printf ("%04u-%02u-%02u", year, month, day);
-        tag_str = tmp;
-      }
-
-      /* handles anything else */
-      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 if (tag_type == G_TYPE_DOUBLE) {
-        /* replaygain tags in TXXX frames ... */
-        g_value_set_double (&dest, g_strtod (tag_str, NULL));
-        gst_tag_list_add_values (tag_list, GST_TAG_MERGE_KEEP,
-            tag_name, &dest, NULL);
-        GST_LOG ("Converted string '%s' to double %f", tag_str,
-            g_value_get_double (&dest));
-      } 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);
-      g_free (tmp);
-      break;
-    }
-  }
-
-  return TRUE;
-}
-
-/* Check that an array of characters contains only digits */
-static gboolean
-id3v2_are_digits (const gchar * chars, gint size)
-{
-  gint i;
-
-  for (i = 0; i < size; i++) {
-    if (!g_ascii_isdigit (chars[i]))
-      return FALSE;
-  }
-  return TRUE;
-}
-
-static gboolean
-id3v2_genre_string_to_taglist (ID3TagsWorking * work, const gchar * tag_name,
-    const gchar * tag_str, gint len)
-{
-  g_return_val_if_fail (tag_str != NULL, FALSE);
-
-  /* If it's a number, it might be a defined genre */
-  if (id3v2_are_digits (tag_str, len)) {
-    tag_str = gst_tag_id3_genre_get (strtol (tag_str, NULL, 10));
-    return id3v2_tag_to_taglist (work, tag_name, tag_str);
-  }
-  /* Otherwise it might be "RX" or "CR" */
-  if (len == 2) {
-    if (g_ascii_strncasecmp ("rx", tag_str, len) == 0)
-      return id3v2_tag_to_taglist (work, tag_name, "Remix");
-
-    if (g_ascii_strncasecmp ("cr", tag_str, len) == 0)
-      return id3v2_tag_to_taglist (work, tag_name, "Cover");
-  }
-
-  /* Otherwise it's a string */
-  return id3v2_tag_to_taglist (work, tag_name, tag_str);
-}
-
-static gboolean
-id3v2_genre_fields_to_taglist (ID3TagsWorking * work, const gchar * tag_name,
-    GArray * tag_fields)
-{
-  gchar *tag_str = NULL;
-  gboolean result = FALSE;
-  gint i;
-
-  for (i = 0; i < tag_fields->len; i++) {
-    gint len;
-
-    tag_str = g_array_index (tag_fields, gchar *, i);
-    if (tag_str == NULL)
-      continue;
-
-    len = strlen (tag_str);
-    /* Only supposed to see '(n)' type numeric genre strings in ID3 <= 2.3.0
-     * but apparently we see them in 2.4.0 sometimes too */
-    if (TRUE || work->hdr.version <= 0x300) {   /* <= 2.3.0 */
-      /* Check for genre numbers wrapped in parentheses, possibly
-       * followed by a string */
-      while (len >= 2) {
-        gint pos;
-        gboolean found = FALSE;
-
-        /* Double parenthesis ends the numeric genres, but we need
-         * to swallow the first one so we actually output '(' */
-        if (tag_str[0] == '(' && tag_str[1] == '(') {
-          tag_str++;
-          len--;
-          break;
-        }
-
-        /* If the first char is not a parenthesis, then stop
-         * looking for parenthesised genre strings */
-        if (tag_str[0] != '(')
-          break;
-
-        for (pos = 1; pos < len; pos++) {
-          if (tag_str[pos] == ')') {
-            gchar *tmp_str;
-
-            tmp_str = g_strndup (tag_str + 1, pos - 1);
-            result |=
-                id3v2_genre_string_to_taglist (work, tag_name, tmp_str,
-                pos - 1);
-            g_free (tmp_str);
-            tag_str += pos + 1;
-            len -= pos + 1;
-            found = TRUE;
-            break;
-          }
-
-          /* If we encounter a non-digit while searching for a closing 
-           * parenthesis, we should not try and interpret this as a 
-           * numeric genre string */
-          if (!g_ascii_isdigit (tag_str[pos]))
-            break;
-        }
-        if (!found)
-          break;                /* There was no closing parenthesis */
-      }
-    }
-
-    if (len > 0 && tag_str != NULL)
-      result |= id3v2_genre_string_to_taglist (work, tag_name, tag_str, len);
-  }
-  return result;
-}
-
-static const gchar utf16enc[] = "UTF-16";
-static const gchar utf16leenc[] = "UTF-16LE";
-static const gchar utf16beenc[] = "UTF-16BE";
-
-static gboolean
-find_utf16_bom (gchar * data, const gchar ** p_in_encoding)
-{
-  guint16 marker = (GST_READ_UINT8 (data) << 8) | GST_READ_UINT8 (data + 1);
-
-  switch (marker) {
-    case 0xFFFE:
-      *p_in_encoding = utf16leenc;
-      return TRUE;
-    case 0xFEFF:
-      *p_in_encoding = utf16beenc;
-      return TRUE;
-    default:
-      break;
-  }
-  return FALSE;
-}
-
-static void *
-string_utf8_dup (const gchar * start, const guint size)
-{
-  const gchar *env;
-  gsize bytes_read;
-  gchar *utf8;
-
-  /* Should we try the charsets specified
-   * via environment variables FIRST ? */
-  if (g_utf8_validate (start, size, NULL)) {
-    utf8 = g_strndup (start, size);
-    goto beach;
-  }
-
-  env = g_getenv ("GST_ID3V1_TAG_ENCODING");
-  if (!env || *env == '\0')
-    env = g_getenv ("GST_ID3_TAG_ENCODING");
-  if (!env || *env == '\0')
-    env = g_getenv ("GST_TAG_ENCODING");
-
-  /* Try charsets specified via the environment */
-  if (env && *env != '\0') {
-    gchar **c, **csets;
-
-    csets = g_strsplit (env, G_SEARCHPATH_SEPARATOR_S, -1);
-
-    for (c = csets; c && *c; ++c) {
-      if ((utf8 =
-              g_convert (start, size, "UTF-8", *c, &bytes_read, NULL, NULL))) {
-        if (bytes_read == size) {
-          GST_DEBUG ("Using charset %s to interperate id3 tags\n", *c);
-          g_strfreev (csets);
-          goto beach;
-        }
-        g_free (utf8);
-        utf8 = NULL;
-      }
-    }
-  }
-  /* Try current locale (if not UTF-8) */
-  if (!g_get_charset (&env)) {
-    if ((utf8 = g_locale_to_utf8 (start, size, &bytes_read, NULL, NULL))) {
-      if (bytes_read == size) {
-        goto beach;
-      }
-      g_free (utf8);
-      utf8 = NULL;
-    }
-  }
-
-  /* Try ISO-8859-1 */
-  utf8 =
-      g_convert (start, size, "UTF-8", "ISO-8859-1", &bytes_read, NULL, NULL);
-  if (utf8 != NULL && bytes_read == size) {
-    goto beach;
-  }
-
-  g_free (utf8);
-  return NULL;
-
-beach:
-
-  g_strchomp (utf8);
-
-  return (utf8);
-}
-
-static void
-parse_insert_string_field (guint8 encoding, gchar * data, gint data_size,
-    GArray * fields)
-{
-  gchar *field = NULL;
-
-  switch (encoding) {
-    case ID3V2_ENCODING_UTF16:
-    case ID3V2_ENCODING_UTF16BE:
-    {
-      const gchar *in_encode;
-
-      if (encoding == ID3V2_ENCODING_UTF16)
-        in_encode = utf16enc;
-      else
-        in_encode = utf16beenc;
-
-      /* Sometimes we see strings with multiple BOM markers at the start.
-       * In that case, we assume the innermost one is correct. If that fails
-       * to produce valid UTF-8, we try the other endianness anyway */
-      while (data_size > 2 && find_utf16_bom (data, &in_encode)) {
-        data += 2;              /* skip BOM */
-        data_size -= 2;
-      }
-
-      field = g_convert (data, data_size, "UTF-8", in_encode, NULL, NULL, NULL);
-
-      if (field == NULL || g_utf8_validate (field, -1, NULL) == FALSE) {
-        /* As a fallback, try interpreting UTF-16 in the other endianness */
-        if (in_encode == utf16beenc)
-          field = g_convert (data, data_size, "UTF-8", utf16leenc,
-              NULL, NULL, NULL);
-      }
-    }
-
-      break;
-    case ID3V2_ENCODING_ISO8859:
-      if (g_utf8_validate (data, data_size, NULL))
-        field = g_strndup (data, data_size);
-      else
-        /* field = g_convert (data, data_size, "UTF-8", "ISO-8859-1",
-           NULL, NULL, NULL); */
-        field = string_utf8_dup (data, data_size);
-      break;
-    default:
-      field = g_strndup (data, data_size);
-      break;
-  }
-
-  if (field) {
-    if (g_utf8_validate (field, -1, NULL)) {
-      g_array_append_val (fields, field);
-      return;
-    }
-
-    GST_DEBUG ("%s was bad UTF-8 after conversion from encoding %d. Ignoring",
-        field, encoding);
-    g_free (field);
-  }
-}
-
-static void
-parse_split_strings (guint8 encoding, gchar * data, gint data_size,
-    GArray ** out_fields)
-{
-  GArray *fields = g_array_new (FALSE, TRUE, sizeof (gchar *));
-  gint text_pos;
-  gint prev = 0;
-
-  g_return_if_fail (out_fields != NULL);
-
-  switch (encoding) {
-    case ID3V2_ENCODING_ISO8859:
-      for (text_pos = 0; text_pos < data_size; text_pos++) {
-        if (data[text_pos] == 0) {
-          parse_insert_string_field (encoding, data + prev,
-              text_pos - prev + 1, fields);
-          prev = text_pos + 1;
-        }
-      }
-      if (data_size - prev > 0 && data[prev] != 0x00) {
-        parse_insert_string_field (encoding, data + prev,
-            data_size - prev, fields);
-      }
-
-      break;
-    case ID3V2_ENCODING_UTF8:
-      for (prev = 0, text_pos = 0; text_pos < data_size; text_pos++) {
-        if (data[text_pos] == '\0') {
-          parse_insert_string_field (encoding, data + prev,
-              text_pos - prev + 1, fields);
-          prev = text_pos + 1;
-        }
-      }
-      if (data_size - prev > 0 && data[prev] != 0x00) {
-        parse_insert_string_field (encoding, data + prev,
-            data_size - prev, fields);
-      }
-      break;
-    case ID3V2_ENCODING_UTF16:
-    case ID3V2_ENCODING_UTF16BE:
-    {
-      /* Find '\0\0' terminator */
-      for (text_pos = 0; text_pos < data_size - 1; text_pos += 2) {
-        if (data[text_pos] == '\0' && data[text_pos + 1] == '\0') {
-          /* found a delimiter */
-          parse_insert_string_field (encoding, data + prev,
-              text_pos - prev + 2, fields);
-          text_pos++;           /* Advance to the 2nd NULL terminator */
-          prev = text_pos + 1;
-          break;
-        }
-      }
-      if (data_size - prev > 1 &&
-          (data[prev] != 0x00 || data[prev + 1] != 0x00)) {
-        /* There were 2 or more non-null chars left, convert those too */
-        parse_insert_string_field (encoding, data + prev,
-            data_size - prev, fields);
-      }
-      break;
-    }
-  }
-  if (fields->len > 0)
-    *out_fields = fields;
-  else
-    g_array_free (fields, TRUE);
-}
-
-static void
-free_tag_strings (GArray * fields)
-{
-  if (fields) {
-    gint i;
-    gchar *c;
-
-    for (i = 0; i < fields->len; i++) {
-      c = g_array_index (fields, gchar *, i);
-      g_free (c);
-    }
-    g_array_free (fields, TRUE);
-  }
-}