Added gst-plugins-base-subtitles0.10-0.10.34 for Meego Harmattan 1.2
[mafwsubrenderer] / gst-plugins-base-subtitles0.10 / gst-libs / gst / tag / gstid3tag.c
diff --git a/gst-plugins-base-subtitles0.10/gst-libs/gst/tag/gstid3tag.c b/gst-plugins-base-subtitles0.10/gst-libs/gst/tag/gstid3tag.c
new file mode 100644 (file)
index 0000000..2c3c661
--- /dev/null
@@ -0,0 +1,487 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gstid3tag.c: plugin for reading / modifying id3 tags
+ *
+ * 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.
+ */
+
+/**
+ * SECTION:gsttagid3
+ * @short_description: tag mappings and support functions for plugins
+ *                     dealing with ID3v1 and ID3v2 tags
+ * @see_also: #GstTagList
+ * 
+ * <refsect2>
+ * <para>
+ * Contains various utility functions for plugins to parse or create
+ * ID3 tags and map ID3v2 identifiers to and from GStreamer identifiers.
+ * </para>
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gsttageditingprivate.h"
+#include <stdlib.h>
+#include <string.h>
+
+static const gchar *genres[] = {
+  "Blues",
+  "Classic Rock",
+  "Country",
+  "Dance",
+  "Disco",
+  "Funk",
+  "Grunge",
+  "Hip-Hop",
+  "Jazz",
+  "Metal",
+  "New Age",
+  "Oldies",
+  "Other",
+  "Pop",
+  "R&B",
+  "Rap",
+  "Reggae",
+  "Rock",
+  "Techno",
+  "Industrial",
+  "Alternative",
+  "Ska",
+  "Death Metal",
+  "Pranks",
+  "Soundtrack",
+  "Euro-Techno",
+  "Ambient",
+  "Trip-Hop",
+  "Vocal",
+  "Jazz+Funk",
+  "Fusion",
+  "Trance",
+  "Classical",
+  "Instrumental",
+  "Acid",
+  "House",
+  "Game",
+  "Sound Clip",
+  "Gospel",
+  "Noise",
+  "Alternative Rock",
+  "Bass",
+  "Soul",
+  "Punk",
+  "Space",
+  "Meditative",
+  "Instrumental Pop",
+  "Instrumental Rock",
+  "Ethnic",
+  "Gothic",
+  "Darkwave",
+  "Techno-Industrial",
+  "Electronic",
+  "Pop-Folk",
+  "Eurodance",
+  "Dream",
+  "Southern Rock",
+  "Comedy",
+  "Cult",
+  "Gangsta",
+  "Top 40",
+  "Christian Rap",
+  "Pop/Funk",
+  "Jungle",
+  "Native American",
+  "Cabaret",
+  "New Wave",
+  "Psychedelic",
+  "Rave",
+  "Showtunes",
+  "Trailer",
+  "Lo-Fi",
+  "Tribal",
+  "Acid Punk",
+  "Acid Jazz",
+  "Polka",
+  "Retro",
+  "Musical",
+  "Rock & Roll",
+  "Hard Rock",
+  "Folk",
+  "Folk/Rock",
+  "National Folk",
+  "Swing",
+  "Fusion",
+  "Bebob",
+  "Latin",
+  "Revival",
+  "Celtic",
+  "Bluegrass",
+  "Avantgarde",
+  "Gothic Rock",
+  "Progressive Rock",
+  "Psychedelic Rock",
+  "Symphonic Rock",
+  "Slow Rock",
+  "Big Band",
+  "Chorus",
+  "Easy Listening",
+  "Acoustic",
+  "Humour",
+  "Speech",
+  "Chanson",
+  "Opera",
+  "Chamber Music",
+  "Sonata",
+  "Symphony",
+  "Booty Bass",
+  "Primus",
+  "Porn Groove",
+  "Satire",
+  "Slow Jam",
+  "Club",
+  "Tango",
+  "Samba",
+  "Folklore",
+  "Ballad",
+  "Power Ballad",
+  "Rhythmic Soul",
+  "Freestyle",
+  "Duet",
+  "Punk Rock",
+  "Drum Solo",
+  "A Capella",
+  "Euro-House",
+  "Dance Hall",
+  "Goa",
+  "Drum & Bass",
+  "Club-House",
+  "Hardcore",
+  "Terror",
+  "Indie",
+  "BritPop",
+  "Negerpunk",
+  "Polsk Punk",
+  "Beat",
+  "Christian Gangsta Rap",
+  "Heavy Metal",
+  "Black Metal",
+  "Crossover",
+  "Contemporary Christian",
+  "Christian Rock",
+  "Merengue",
+  "Salsa",
+  "Thrash Metal",
+  "Anime",
+  "Jpop",
+  "Synthpop"
+};
+
+static const GstTagEntryMatch tag_matches[] = {
+  {GST_TAG_TITLE, "TIT2"},
+  {GST_TAG_ALBUM, "TALB"},
+  {GST_TAG_TRACK_NUMBER, "TRCK"},
+  {GST_TAG_ARTIST, "TPE1"},
+  {GST_TAG_ALBUM_ARTIST, "TPE2"},
+  {GST_TAG_COMPOSER, "TCOM"},
+  {GST_TAG_COPYRIGHT, "TCOP"},
+  {GST_TAG_COPYRIGHT_URI, "WCOP"},
+  {GST_TAG_ENCODED_BY, "TENC"},
+  {GST_TAG_GENRE, "TCON"},
+  {GST_TAG_DATE, "TDRC"},
+  {GST_TAG_COMMENT, "COMM"},
+  {GST_TAG_ALBUM_VOLUME_NUMBER, "TPOS"},
+  {GST_TAG_DURATION, "TLEN"},
+  {GST_TAG_ISRC, "TSRC"},
+  {GST_TAG_IMAGE, "APIC"},
+  {GST_TAG_ENCODER, "TSSE"},
+  {GST_TAG_BEATS_PER_MINUTE, "TBPM"},
+  {GST_TAG_ARTIST_SORTNAME, "TSOP"},
+  {GST_TAG_ALBUM_SORTNAME, "TSOA"},
+  {GST_TAG_TITLE_SORTNAME, "TSOT"},
+  {NULL, NULL}
+};
+
+/**
+ * gst_tag_from_id3_tag:
+ * @id3_tag: ID3v2 tag to convert to GStreamer tag
+ *
+ * Looks up the GStreamer tag for a ID3v2 tag.
+ *
+ * Returns: The corresponding GStreamer tag or NULL if none exists.
+ */
+G_CONST_RETURN gchar *
+gst_tag_from_id3_tag (const gchar * id3_tag)
+{
+  int i = 0;
+
+  g_return_val_if_fail (id3_tag != NULL, NULL);
+
+  while (tag_matches[i].gstreamer_tag != NULL) {
+    if (strncmp (id3_tag, tag_matches[i].original_tag, 5) == 0) {
+      return tag_matches[i].gstreamer_tag;
+    }
+    i++;
+  }
+
+  GST_INFO ("Cannot map ID3v2 tag '%c%c%c%c' to GStreamer tag",
+      id3_tag[0], id3_tag[1], id3_tag[2], id3_tag[3]);
+
+  return NULL;
+}
+
+static const GstTagEntryMatch user_tag_matches[] = {
+  /* musicbrainz identifiers being used in the real world (foobar2000) */
+  {GST_TAG_MUSICBRAINZ_ARTISTID, "TXXX|musicbrainz_artistid"},
+  {GST_TAG_MUSICBRAINZ_ALBUMID, "TXXX|musicbrainz_albumid"},
+  {GST_TAG_MUSICBRAINZ_ALBUMARTISTID, "TXXX|musicbrainz_albumartistid"},
+  {GST_TAG_MUSICBRAINZ_TRMID, "TXXX|musicbrainz_trmid"},
+  {GST_TAG_CDDA_MUSICBRAINZ_DISCID, "TXXX|musicbrainz_discid"},
+  /* musicbrainz identifiers according to spec no one pays
+   * attention to (http://musicbrainz.org/docs/specs/metadata_tags.html) */
+  {GST_TAG_MUSICBRAINZ_ARTISTID, "TXXX|MusicBrainz Artist Id"},
+  {GST_TAG_MUSICBRAINZ_ALBUMID, "TXXX|MusicBrainz Album Id"},
+  {GST_TAG_MUSICBRAINZ_ALBUMARTISTID, "TXXX|MusicBrainz Album Artist Id"},
+  {GST_TAG_MUSICBRAINZ_TRMID, "TXXX|MusicBrainz TRM Id"},
+  /* according to: http://wiki.musicbrainz.org/MusicBrainzTag (yes, no space
+   * before 'ID' and not 'Id' either this time, yay for consistency) */
+  {GST_TAG_CDDA_MUSICBRAINZ_DISCID, "TXXX|MusicBrainz DiscID"},
+  /* foobar2000 uses these identifiers to store gain/peak information in
+   * ID3v2 tags <= v2.3.0. In v2.4.0 there's the RVA2 frame for that */
+  {GST_TAG_TRACK_GAIN, "TXXX|replaygain_track_gain"},
+  {GST_TAG_TRACK_PEAK, "TXXX|replaygain_track_peak"},
+  {GST_TAG_ALBUM_GAIN, "TXXX|replaygain_album_gain"},
+  {GST_TAG_ALBUM_PEAK, "TXXX|replaygain_album_peak"},
+  /* the following two are more or less made up, there seems to be little
+   * evidence that any popular application is actually putting this info
+   * into TXXX frames; the first one comes from a musicbrainz wiki 'proposed
+   * tags' page, the second one is analogue to the vorbis/ape/flac tag. */
+  {GST_TAG_CDDA_CDDB_DISCID, "TXXX|discid"},
+  {GST_TAG_CDDA_CDDB_DISCID, "TXXX|CDDB DiscID"}
+};
+
+/**
+ * gst_tag_from_id3_user_tag:
+ * @type: the type of ID3v2 user tag (e.g. "TXXX" or "UDIF")
+ * @id3_user_tag: ID3v2 user tag to convert to GStreamer tag
+ *
+ * Looks up the GStreamer tag for an ID3v2 user tag (e.g. description in
+ * TXXX frame or owner in UFID frame).
+ *
+ * Returns: The corresponding GStreamer tag or NULL if none exists.
+ */
+G_CONST_RETURN gchar *
+gst_tag_from_id3_user_tag (const gchar * type, const gchar * id3_user_tag)
+{
+  int i = 0;
+
+  g_return_val_if_fail (type != NULL && strlen (type) == 4, NULL);
+  g_return_val_if_fail (id3_user_tag != NULL, NULL);
+
+  for (i = 0; i < G_N_ELEMENTS (user_tag_matches); ++i) {
+    if (strncmp (type, user_tag_matches[i].original_tag, 4) == 0 &&
+        g_ascii_strcasecmp (id3_user_tag,
+            user_tag_matches[i].original_tag + 5) == 0) {
+      GST_LOG ("Mapped ID3v2 user tag '%s' to GStreamer tag '%s'",
+          user_tag_matches[i].original_tag, user_tag_matches[i].gstreamer_tag);
+      return user_tag_matches[i].gstreamer_tag;
+    }
+  }
+
+  GST_INFO ("Cannot map ID3v2 user tag '%s' of type '%s' to GStreamer tag",
+      id3_user_tag, type);
+
+  return NULL;
+}
+
+/**
+ * gst_tag_to_id3_tag:
+ * @gst_tag: GStreamer tag to convert to vorbiscomment tag
+ *
+ * Looks up the ID3v2 tag for a GStreamer tag.
+ *
+ * Returns: The corresponding ID3v2 tag or NULL if none exists.
+ */
+G_CONST_RETURN gchar *
+gst_tag_to_id3_tag (const gchar * gst_tag)
+{
+  int i = 0;
+
+  g_return_val_if_fail (gst_tag != NULL, NULL);
+
+  while (tag_matches[i].gstreamer_tag != NULL) {
+    if (strcmp (gst_tag, tag_matches[i].gstreamer_tag) == 0) {
+      return tag_matches[i].original_tag;
+    }
+    i++;
+  }
+  return NULL;
+}
+
+static void
+gst_tag_extract_id3v1_string (GstTagList * list, const gchar * tag,
+    const gchar * start, const guint size)
+{
+  const gchar *env_vars[] = { "GST_ID3V1_TAG_ENCODING",
+    "GST_ID3_TAG_ENCODING", "GST_TAG_ENCODING", NULL
+  };
+  gchar *utf8;
+
+  utf8 = gst_tag_freeform_string_to_utf8 (start, size, env_vars);
+
+  if (utf8 && *utf8 != '\0') {
+    gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, tag, utf8, NULL);
+  }
+
+  g_free (utf8);
+}
+
+/**
+ * gst_tag_list_new_from_id3v1:
+ * @data: 128 bytes of data containing the ID3v1 tag
+ *
+ * Parses the data containing an ID3v1 tag and returns a #GstTagList from the
+ * parsed data.
+ *
+ * Returns: A new tag list or NULL if the data was not an ID3v1 tag.
+ */
+GstTagList *
+gst_tag_list_new_from_id3v1 (const guint8 * data)
+{
+  guint year;
+  gchar *ystr;
+  GstTagList *list;
+
+  g_return_val_if_fail (data != NULL, NULL);
+
+  if (data[0] != 'T' || data[1] != 'A' || data[2] != 'G')
+    return NULL;
+  list = gst_tag_list_new ();
+  gst_tag_extract_id3v1_string (list, GST_TAG_TITLE, (gchar *) & data[3], 30);
+  gst_tag_extract_id3v1_string (list, GST_TAG_ARTIST, (gchar *) & data[33], 30);
+  gst_tag_extract_id3v1_string (list, GST_TAG_ALBUM, (gchar *) & data[63], 30);
+  ystr = g_strndup ((gchar *) & data[93], 4);
+  year = strtoul (ystr, NULL, 10);
+  g_free (ystr);
+  if (year > 0) {
+    GDate *date = g_date_new_dmy (1, 1, year);
+
+    gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, date, NULL);
+    g_date_free (date);
+  }
+  if (data[125] == 0 && data[126] != 0) {
+    gst_tag_extract_id3v1_string (list, GST_TAG_COMMENT, (gchar *) & data[97],
+        28);
+    gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_NUMBER,
+        (guint) data[126], NULL);
+  } else {
+    gst_tag_extract_id3v1_string (list, GST_TAG_COMMENT, (gchar *) & data[97],
+        30);
+  }
+  if (data[127] < gst_tag_id3_genre_count () && !gst_tag_list_is_empty (list)) {
+    gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_GENRE,
+        gst_tag_id3_genre_get (data[127]), NULL);
+  }
+
+  return list;
+}
+
+/**
+ * gst_tag_id3_genre_count:
+ *
+ * Gets the number of ID3v1 genres that can be identified. Winamp genres are 
+ * included.
+ *
+ * Returns: the number of ID3v1 genres that can be identified
+ */
+guint
+gst_tag_id3_genre_count (void)
+{
+  return G_N_ELEMENTS (genres);
+}
+
+/**
+ * gst_tag_id3_genre_get:
+ * @id: ID of genre to query
+ *
+ * Gets the ID3v1 genre name for a given ID.
+ *
+ * Returns: the genre or NULL if no genre is associated with that ID.
+ */
+G_CONST_RETURN gchar *
+gst_tag_id3_genre_get (const guint id)
+{
+  if (id >= G_N_ELEMENTS (genres))
+    return NULL;
+  return genres[id];
+}
+
+/**
+ * gst_tag_list_add_id3_image:
+ * @tag_list: a tag list
+ * @image_data: the (encoded) image
+ * @image_data_len: the length of the encoded image data at @image_data
+ * @id3_picture_type: picture type as per the ID3 (v2.4.0) specification for
+ *    the APIC frame (0 = unknown/other)
+ *
+ * Adds an image from an ID3 APIC frame (or similar, such as used in FLAC)
+ * to the given tag list. Also see gst_tag_image_data_to_image_buffer() for
+ * more information on image tags in GStreamer.
+ *
+ * Returns: %TRUE if the image was processed, otherwise %FALSE
+ *
+ * Since: 0.10.20
+ */
+gboolean
+gst_tag_list_add_id3_image (GstTagList * tag_list, const guint8 * image_data,
+    guint image_data_len, guint id3_picture_type)
+{
+  GstTagImageType tag_image_type;
+  const gchar *tag_name;
+  GstBuffer *image;
+
+  g_return_val_if_fail (GST_IS_TAG_LIST (tag_list), FALSE);
+  g_return_val_if_fail (image_data != NULL, FALSE);
+  g_return_val_if_fail (image_data_len > 0, FALSE);
+
+  if (id3_picture_type == 0x01 || id3_picture_type == 0x02) {
+    /* file icon for preview. Don't add image-type to caps, since there
+     * is only supposed to be one of these, and the type is already indicated
+     * via the special tag */
+    tag_name = GST_TAG_PREVIEW_IMAGE;
+    tag_image_type = GST_TAG_IMAGE_TYPE_NONE;
+  } else {
+    tag_name = GST_TAG_IMAGE;
+
+    /* Remap the ID3v2 APIC type our ImageType enum */
+    if (id3_picture_type >= 0x3 && id3_picture_type <= 0x14)
+      tag_image_type = (GstTagImageType) (id3_picture_type - 2);
+    else
+      tag_image_type = GST_TAG_IMAGE_TYPE_UNDEFINED;
+  }
+
+  image = gst_tag_image_data_to_image_buffer (image_data, image_data_len,
+      tag_image_type);
+
+  if (image == NULL)
+    return FALSE;
+
+  gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND, tag_name, image, NULL);
+  gst_buffer_unref (image);
+  return TRUE;
+}