gst_plugins_base/gst-libs/gst/tag/gstid3tag.c
changeset 0 0e761a78d257
child 8 4a7fac7dd34a
equal deleted inserted replaced
-1:000000000000 0:0e761a78d257
       
     1 /* GStreamer
       
     2  * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
       
     3  *
       
     4  * gstid3tag.c: plugin for reading / modifying id3 tags
       
     5  *
       
     6  * This library is free software; you can redistribute it and/or
       
     7  * modify it under the terms of the GNU Library General Public
       
     8  * License as published by the Free Software Foundation; either
       
     9  * version 2 of the License, or (at your option) any later version.
       
    10  *
       
    11  * This library is distributed in the hope that it will be useful,
       
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    14  * Library General Public License for more details.
       
    15  *
       
    16  * You should have received a copy of the GNU Library General Public
       
    17  * License along with this library; if not, write to the
       
    18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    19  * Boston, MA 02111-1307, USA.
       
    20  */
       
    21 
       
    22 /**
       
    23  * SECTION:gsttagid3
       
    24  * @short_description: tag mappings and support functions for plugins
       
    25  *                     dealing with ID3v1 and ID3v2 tags
       
    26  * @see_also: #GstTagList
       
    27  * 
       
    28  * <refsect2>
       
    29  * <para>
       
    30  * Contains various utility functions for plugins to parse or create
       
    31  * ID3 tags and map ID3v2 identifiers to and from GStreamer identifiers.
       
    32  * </para>
       
    33  * </refsect2>
       
    34  */
       
    35 
       
    36 #ifdef HAVE_CONFIG_H
       
    37 #include "config.h"
       
    38 #endif
       
    39 
       
    40 #include "gsttageditingprivate.h"
       
    41 #include <stdlib.h>
       
    42 #include <string.h>
       
    43 
       
    44 static const gchar *genres[] = {
       
    45   "Blues",
       
    46   "Classic Rock",
       
    47   "Country",
       
    48   "Dance",
       
    49   "Disco",
       
    50   "Funk",
       
    51   "Grunge",
       
    52   "Hip-Hop",
       
    53   "Jazz",
       
    54   "Metal",
       
    55   "New Age",
       
    56   "Oldies",
       
    57   "Other",
       
    58   "Pop",
       
    59   "R&B",
       
    60   "Rap",
       
    61   "Reggae",
       
    62   "Rock",
       
    63   "Techno",
       
    64   "Industrial",
       
    65   "Alternative",
       
    66   "Ska",
       
    67   "Death Metal",
       
    68   "Pranks",
       
    69   "Soundtrack",
       
    70   "Euro-Techno",
       
    71   "Ambient",
       
    72   "Trip-Hop",
       
    73   "Vocal",
       
    74   "Jazz+Funk",
       
    75   "Fusion",
       
    76   "Trance",
       
    77   "Classical",
       
    78   "Instrumental",
       
    79   "Acid",
       
    80   "House",
       
    81   "Game",
       
    82   "Sound Clip",
       
    83   "Gospel",
       
    84   "Noise",
       
    85   "Alternative Rock",
       
    86   "Bass",
       
    87   "Soul",
       
    88   "Punk",
       
    89   "Space",
       
    90   "Meditative",
       
    91   "Instrumental Pop",
       
    92   "Instrumental Rock",
       
    93   "Ethnic",
       
    94   "Gothic",
       
    95   "Darkwave",
       
    96   "Techno-Industrial",
       
    97   "Electronic",
       
    98   "Pop-Folk",
       
    99   "Eurodance",
       
   100   "Dream",
       
   101   "Southern Rock",
       
   102   "Comedy",
       
   103   "Cult",
       
   104   "Gangsta",
       
   105   "Top 40",
       
   106   "Christian Rap",
       
   107   "Pop/Funk",
       
   108   "Jungle",
       
   109   "Native American",
       
   110   "Cabaret",
       
   111   "New Wave",
       
   112   "Psychadelic",
       
   113   "Rave",
       
   114   "Showtunes",
       
   115   "Trailer",
       
   116   "Lo-Fi",
       
   117   "Tribal",
       
   118   "Acid Punk",
       
   119   "Acid Jazz",
       
   120   "Polka",
       
   121   "Retro",
       
   122   "Musical",
       
   123   "Rock & Roll",
       
   124   "Hard Rock",
       
   125   "Folk",
       
   126   "Folk/Rock",
       
   127   "National Folk",
       
   128   "Swing",
       
   129   "Fusion",
       
   130   "Bebob",
       
   131   "Latin",
       
   132   "Revival",
       
   133   "Celtic",
       
   134   "Bluegrass",
       
   135   "Avantgarde",
       
   136   "Gothic Rock",
       
   137   "Progressive Rock",
       
   138   "Psychadelic Rock",
       
   139   "Symphonic Rock",
       
   140   "Slow Rock",
       
   141   "Big Band",
       
   142   "Chorus",
       
   143   "Easy Listening",
       
   144   "Acoustic",
       
   145   "Humour",
       
   146   "Speech",
       
   147   "Chanson",
       
   148   "Opera",
       
   149   "Chamber Music",
       
   150   "Sonata",
       
   151   "Symphony",
       
   152   "Booty Bass",
       
   153   "Primus",
       
   154   "Porn Groove",
       
   155   "Satire",
       
   156   "Slow Jam",
       
   157   "Club",
       
   158   "Tango",
       
   159   "Samba",
       
   160   "Folklore",
       
   161   "Ballad",
       
   162   "Power Ballad",
       
   163   "Rhythmic Soul",
       
   164   "Freestyle",
       
   165   "Duet",
       
   166   "Punk Rock",
       
   167   "Drum Solo",
       
   168   "A Capella",
       
   169   "Euro-House",
       
   170   "Dance Hall",
       
   171   "Goa",
       
   172   "Drum & Bass",
       
   173   "Club-House",
       
   174   "Hardcore",
       
   175   "Terror",
       
   176   "Indie",
       
   177   "BritPop",
       
   178   "Negerpunk",
       
   179   "Polsk Punk",
       
   180   "Beat",
       
   181   "Christian Gangsta Rap",
       
   182   "Heavy Metal",
       
   183   "Black Metal",
       
   184   "Crossover",
       
   185   "Contemporary Christian",
       
   186   "Christian Rock",
       
   187   "Merengue",
       
   188   "Salsa",
       
   189   "Thrash Metal",
       
   190   "Anime",
       
   191   "Jpop",
       
   192   "Synthpop"
       
   193 };
       
   194 
       
   195 static const GstTagEntryMatch tag_matches[] = {
       
   196   {GST_TAG_TITLE, "TIT2"},
       
   197   {GST_TAG_ALBUM, "TALB"},
       
   198   {GST_TAG_TRACK_NUMBER, "TRCK"},
       
   199   {GST_TAG_ARTIST, "TPE1"},
       
   200   {GST_TAG_COMPOSER, "TCOM"},
       
   201   {GST_TAG_COPYRIGHT, "TCOP"},
       
   202   {GST_TAG_COPYRIGHT_URI, "WCOP"},
       
   203   {GST_TAG_GENRE, "TCON"},
       
   204   {GST_TAG_DATE, "TDRC"},
       
   205   {GST_TAG_COMMENT, "COMM"},
       
   206   {GST_TAG_ALBUM_VOLUME_NUMBER, "TPOS"},
       
   207   {GST_TAG_DURATION, "TLEN"},
       
   208   {GST_TAG_ISRC, "TSRC"},
       
   209   {GST_TAG_IMAGE, "APIC"},
       
   210   {GST_TAG_ENCODER, "TSSE"},
       
   211   {GST_TAG_BEATS_PER_MINUTE, "TBPM"},
       
   212   {GST_TAG_ARTIST_SORTNAME, "TSOP"},
       
   213   {GST_TAG_ALBUM_SORTNAME, "TSOA"},
       
   214   {GST_TAG_TITLE_SORTNAME, "TSOT"},
       
   215   {NULL, NULL}
       
   216 };
       
   217 
       
   218 /**
       
   219  * gst_tag_from_id3_tag:
       
   220  * @id3_tag: ID3v2 tag to convert to GStreamer tag
       
   221  *
       
   222  * Looks up the GStreamer tag for a ID3v2 tag.
       
   223  *
       
   224  * Returns: The corresponding GStreamer tag or NULL if none exists.
       
   225  */
       
   226 #ifdef __SYMBIAN32__
       
   227 EXPORT_C
       
   228 #endif
       
   229 
       
   230 G_CONST_RETURN gchar *
       
   231 gst_tag_from_id3_tag (const gchar * id3_tag)
       
   232 {
       
   233   int i = 0;
       
   234 
       
   235   g_return_val_if_fail (id3_tag != NULL, NULL);
       
   236 
       
   237   while (tag_matches[i].gstreamer_tag != NULL) {
       
   238     if (strncmp (id3_tag, tag_matches[i].original_tag, 5) == 0) {
       
   239       return tag_matches[i].gstreamer_tag;
       
   240     }
       
   241     i++;
       
   242   }
       
   243 
       
   244   GST_INFO ("Cannot map ID3v2 tag '%c%c%c%c' to GStreamer tag",
       
   245       id3_tag[0], id3_tag[1], id3_tag[2], id3_tag[3]);
       
   246 
       
   247   return NULL;
       
   248 }
       
   249 
       
   250 static const GstTagEntryMatch user_tag_matches[] = {
       
   251   /* musicbrainz identifiers being used in the real world (foobar2000) */
       
   252   {GST_TAG_MUSICBRAINZ_ARTISTID, "TXXX|musicbrainz_artistid"},
       
   253   {GST_TAG_MUSICBRAINZ_ALBUMID, "TXXX|musicbrainz_albumid"},
       
   254   {GST_TAG_MUSICBRAINZ_ALBUMARTISTID, "TXXX|musicbrainz_albumartistid"},
       
   255   {GST_TAG_MUSICBRAINZ_TRMID, "TXXX|musicbrainz_trmid"},
       
   256   {GST_TAG_CDDA_MUSICBRAINZ_DISCID, "TXXX|musicbrainz_discid"},
       
   257   /* musicbrainz identifiers according to spec no one pays
       
   258    * attention to (http://musicbrainz.org/docs/specs/metadata_tags.html) */
       
   259   {GST_TAG_MUSICBRAINZ_ARTISTID, "TXXX|MusicBrainz Artist Id"},
       
   260   {GST_TAG_MUSICBRAINZ_ALBUMID, "TXXX|MusicBrainz Album Id"},
       
   261   {GST_TAG_MUSICBRAINZ_ALBUMARTISTID, "TXXX|MusicBrainz Album Artist Id"},
       
   262   {GST_TAG_MUSICBRAINZ_TRMID, "TXXX|MusicBrainz TRM Id"},
       
   263   /* according to: http://wiki.musicbrainz.org/MusicBrainzTag (yes, no space
       
   264    * before 'ID' and not 'Id' either this time, yay for consistency) */
       
   265   {GST_TAG_CDDA_MUSICBRAINZ_DISCID, "TXXX|MusicBrainz DiscID"},
       
   266   /* foobar2000 uses these identifiers to store gain/peak information in
       
   267    * ID3v2 tags <= v2.3.0. In v2.4.0 there's the RVA2 frame for that */
       
   268   {GST_TAG_TRACK_GAIN, "TXXX|replaygain_track_gain"},
       
   269   {GST_TAG_TRACK_PEAK, "TXXX|replaygain_track_peak"},
       
   270   {GST_TAG_ALBUM_GAIN, "TXXX|replaygain_album_gain"},
       
   271   {GST_TAG_ALBUM_PEAK, "TXXX|replaygain_album_peak"},
       
   272   /* the following two are more or less made up, there seems to be little
       
   273    * evidence that any popular application is actually putting this info
       
   274    * into TXXX frames; the first one comes from a musicbrainz wiki 'proposed
       
   275    * tags' page, the second one is analogue to the vorbis/ape/flac tag. */
       
   276   {GST_TAG_CDDA_CDDB_DISCID, "TXXX|discid"},
       
   277   {GST_TAG_CDDA_CDDB_DISCID, "TXXX|CDDB DiscID"}
       
   278 };
       
   279 
       
   280 /**
       
   281  * gst_tag_from_id3_user_tag:
       
   282  * @type: the type of ID3v2 user tag (e.g. "TXXX" or "UDIF")
       
   283  * @id3_user_tag: ID3v2 user tag to convert to GStreamer tag
       
   284  *
       
   285  * Looks up the GStreamer tag for an ID3v2 user tag (e.g. description in
       
   286  * TXXX frame or owner in UFID frame).
       
   287  *
       
   288  * Returns: The corresponding GStreamer tag or NULL if none exists.
       
   289  */
       
   290 #ifdef __SYMBIAN32__
       
   291 EXPORT_C
       
   292 #endif
       
   293 
       
   294 G_CONST_RETURN gchar *
       
   295 gst_tag_from_id3_user_tag (const gchar * type, const gchar * id3_user_tag)
       
   296 {
       
   297   int i = 0;
       
   298 
       
   299   g_return_val_if_fail (type != NULL && strlen (type) == 4, NULL);
       
   300   g_return_val_if_fail (id3_user_tag != NULL, NULL);
       
   301 
       
   302   for (i = 0; i < G_N_ELEMENTS (user_tag_matches); ++i) {
       
   303     if (strncmp (type, user_tag_matches[i].original_tag, 4) == 0 &&
       
   304         g_ascii_strcasecmp (id3_user_tag,
       
   305             user_tag_matches[i].original_tag + 5) == 0) {
       
   306       GST_LOG ("Mapped ID3v2 user tag '%s' to GStreamer tag '%s'",
       
   307           user_tag_matches[i].original_tag, user_tag_matches[i].gstreamer_tag);
       
   308       return user_tag_matches[i].gstreamer_tag;
       
   309     }
       
   310   }
       
   311 
       
   312   GST_INFO ("Cannot map ID3v2 user tag '%s' of type '%s' to GStreamer tag",
       
   313       id3_user_tag, type);
       
   314 
       
   315   return NULL;
       
   316 }
       
   317 
       
   318 /**
       
   319  * gst_tag_to_id3_tag:
       
   320  * @gst_tag: GStreamer tag to convert to vorbiscomment tag
       
   321  *
       
   322  * Looks up the ID3v2 tag for a GStreamer tag.
       
   323  *
       
   324  * Returns: The corresponding ID3v2 tag or NULL if none exists.
       
   325  */
       
   326 #ifdef __SYMBIAN32__
       
   327 EXPORT_C
       
   328 #endif
       
   329 
       
   330 G_CONST_RETURN gchar *
       
   331 gst_tag_to_id3_tag (const gchar * gst_tag)
       
   332 {
       
   333   int i = 0;
       
   334 
       
   335   g_return_val_if_fail (gst_tag != NULL, NULL);
       
   336 
       
   337   while (tag_matches[i].gstreamer_tag != NULL) {
       
   338     if (strcmp (gst_tag, tag_matches[i].gstreamer_tag) == 0) {
       
   339       return tag_matches[i].original_tag;
       
   340     }
       
   341     i++;
       
   342   }
       
   343   return NULL;
       
   344 }
       
   345 
       
   346 static void
       
   347 gst_tag_extract_id3v1_string (GstTagList * list, const gchar * tag,
       
   348     const gchar * start, const guint size)
       
   349 {
       
   350   const gchar *env_vars[] = { "GST_ID3V1_TAG_ENCODING",
       
   351     "GST_ID3_TAG_ENCODING", "GST_TAG_ENCODING", NULL
       
   352   };
       
   353   gchar *utf8;
       
   354 
       
   355   utf8 = gst_tag_freeform_string_to_utf8 (start, size, env_vars);
       
   356 
       
   357   if (utf8 && *utf8 != '\0') {
       
   358     gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, tag, utf8, NULL);
       
   359   }
       
   360 
       
   361   g_free (utf8);
       
   362 }
       
   363 
       
   364 /**
       
   365  * gst_tag_list_new_from_id3v1:
       
   366  * @data: 128 bytes of data containing the ID3v1 tag
       
   367  *
       
   368  * Parses the data containing an ID3v1 tag and returns a #GstTagList from the
       
   369  * parsed data.
       
   370  *
       
   371  * Returns: A new tag list or NULL if the data was not an ID3v1 tag.
       
   372  */
       
   373 #ifdef __SYMBIAN32__
       
   374 EXPORT_C
       
   375 #endif
       
   376 
       
   377 GstTagList *
       
   378 gst_tag_list_new_from_id3v1 (const guint8 * data)
       
   379 {
       
   380   guint year;
       
   381   gchar *ystr;
       
   382   GstTagList *list;
       
   383 
       
   384   g_return_val_if_fail (data != NULL, NULL);
       
   385 
       
   386   if (data[0] != 'T' || data[1] != 'A' || data[2] != 'G')
       
   387     return NULL;
       
   388   list = gst_tag_list_new ();
       
   389   gst_tag_extract_id3v1_string (list, GST_TAG_TITLE, (gchar *) & data[3], 30);
       
   390   gst_tag_extract_id3v1_string (list, GST_TAG_ARTIST, (gchar *) & data[33], 30);
       
   391   gst_tag_extract_id3v1_string (list, GST_TAG_ALBUM, (gchar *) & data[63], 30);
       
   392   ystr = g_strndup ((gchar *) & data[93], 4);
       
   393   year = strtoul (ystr, NULL, 10);
       
   394   g_free (ystr);
       
   395   if (year > 0) {
       
   396     GDate *date = g_date_new_dmy (1, 1, year);
       
   397 
       
   398     gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, date, NULL);
       
   399     g_date_free (date);
       
   400   }
       
   401   if (data[125] == 0) {
       
   402     gst_tag_extract_id3v1_string (list, GST_TAG_COMMENT, (gchar *) & data[97],
       
   403         28);
       
   404     gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_NUMBER,
       
   405         (guint) data[126], NULL);
       
   406   } else {
       
   407     gst_tag_extract_id3v1_string (list, GST_TAG_COMMENT, (gchar *) & data[97],
       
   408         30);
       
   409   }
       
   410   if (data[127] < gst_tag_id3_genre_count ()) {
       
   411     gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_GENRE,
       
   412         gst_tag_id3_genre_get (data[127]), NULL);
       
   413   }
       
   414 
       
   415   return list;
       
   416 }
       
   417 
       
   418 /**
       
   419  * gst_tag_id3_genre_count:
       
   420  *
       
   421  * Gets the number of ID3v1 genres that can be identified. Winamp genres are 
       
   422  * included.
       
   423  *
       
   424  * Returns: the number of ID3v1 genres that can be identified
       
   425  */
       
   426 #ifdef __SYMBIAN32__
       
   427 EXPORT_C
       
   428 #endif
       
   429 
       
   430 guint
       
   431 gst_tag_id3_genre_count (void)
       
   432 {
       
   433   return G_N_ELEMENTS (genres);
       
   434 }
       
   435 
       
   436 /**
       
   437  * gst_tag_id3_genre_get:
       
   438  * @id: ID of genre to query
       
   439  *
       
   440  * Gets the ID3v1 genre name for a given ID.
       
   441  *
       
   442  * Returns: the genre or NULL if no genre is associated with that ID.
       
   443  */
       
   444 #ifdef __SYMBIAN32__
       
   445 EXPORT_C
       
   446 #endif
       
   447 
       
   448 G_CONST_RETURN gchar *
       
   449 gst_tag_id3_genre_get (const guint id)
       
   450 {
       
   451   if (id >= G_N_ELEMENTS (genres))
       
   452     return NULL;
       
   453   return genres[id];
       
   454 }