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
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   "Psychedelic",
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   "Psychedelic 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_ALBUM_ARTIST, "TPE2"},
201   {GST_TAG_COMPOSER, "TCOM"},
202   {GST_TAG_COPYRIGHT, "TCOP"},
203   {GST_TAG_COPYRIGHT_URI, "WCOP"},
204   {GST_TAG_ENCODED_BY, "TENC"},
205   {GST_TAG_GENRE, "TCON"},
206   {GST_TAG_DATE, "TDRC"},
207   {GST_TAG_COMMENT, "COMM"},
208   {GST_TAG_ALBUM_VOLUME_NUMBER, "TPOS"},
209   {GST_TAG_DURATION, "TLEN"},
210   {GST_TAG_ISRC, "TSRC"},
211   {GST_TAG_IMAGE, "APIC"},
212   {GST_TAG_ENCODER, "TSSE"},
213   {GST_TAG_BEATS_PER_MINUTE, "TBPM"},
214   {GST_TAG_ARTIST_SORTNAME, "TSOP"},
215   {GST_TAG_ALBUM_SORTNAME, "TSOA"},
216   {GST_TAG_TITLE_SORTNAME, "TSOT"},
217   {NULL, NULL}
218 };
219
220 /**
221  * gst_tag_from_id3_tag:
222  * @id3_tag: ID3v2 tag to convert to GStreamer tag
223  *
224  * Looks up the GStreamer tag for a ID3v2 tag.
225  *
226  * Returns: The corresponding GStreamer tag or NULL if none exists.
227  */
228 G_CONST_RETURN gchar *
229 gst_tag_from_id3_tag (const gchar * id3_tag)
230 {
231   int i = 0;
232
233   g_return_val_if_fail (id3_tag != NULL, NULL);
234
235   while (tag_matches[i].gstreamer_tag != NULL) {
236     if (strncmp (id3_tag, tag_matches[i].original_tag, 5) == 0) {
237       return tag_matches[i].gstreamer_tag;
238     }
239     i++;
240   }
241
242   GST_INFO ("Cannot map ID3v2 tag '%c%c%c%c' to GStreamer tag",
243       id3_tag[0], id3_tag[1], id3_tag[2], id3_tag[3]);
244
245   return NULL;
246 }
247
248 static const GstTagEntryMatch user_tag_matches[] = {
249   /* musicbrainz identifiers being used in the real world (foobar2000) */
250   {GST_TAG_MUSICBRAINZ_ARTISTID, "TXXX|musicbrainz_artistid"},
251   {GST_TAG_MUSICBRAINZ_ALBUMID, "TXXX|musicbrainz_albumid"},
252   {GST_TAG_MUSICBRAINZ_ALBUMARTISTID, "TXXX|musicbrainz_albumartistid"},
253   {GST_TAG_MUSICBRAINZ_TRMID, "TXXX|musicbrainz_trmid"},
254   {GST_TAG_CDDA_MUSICBRAINZ_DISCID, "TXXX|musicbrainz_discid"},
255   /* musicbrainz identifiers according to spec no one pays
256    * attention to (http://musicbrainz.org/docs/specs/metadata_tags.html) */
257   {GST_TAG_MUSICBRAINZ_ARTISTID, "TXXX|MusicBrainz Artist Id"},
258   {GST_TAG_MUSICBRAINZ_ALBUMID, "TXXX|MusicBrainz Album Id"},
259   {GST_TAG_MUSICBRAINZ_ALBUMARTISTID, "TXXX|MusicBrainz Album Artist Id"},
260   {GST_TAG_MUSICBRAINZ_TRMID, "TXXX|MusicBrainz TRM Id"},
261   /* according to: http://wiki.musicbrainz.org/MusicBrainzTag (yes, no space
262    * before 'ID' and not 'Id' either this time, yay for consistency) */
263   {GST_TAG_CDDA_MUSICBRAINZ_DISCID, "TXXX|MusicBrainz DiscID"},
264   /* foobar2000 uses these identifiers to store gain/peak information in
265    * ID3v2 tags <= v2.3.0. In v2.4.0 there's the RVA2 frame for that */
266   {GST_TAG_TRACK_GAIN, "TXXX|replaygain_track_gain"},
267   {GST_TAG_TRACK_PEAK, "TXXX|replaygain_track_peak"},
268   {GST_TAG_ALBUM_GAIN, "TXXX|replaygain_album_gain"},
269   {GST_TAG_ALBUM_PEAK, "TXXX|replaygain_album_peak"},
270   /* the following two are more or less made up, there seems to be little
271    * evidence that any popular application is actually putting this info
272    * into TXXX frames; the first one comes from a musicbrainz wiki 'proposed
273    * tags' page, the second one is analogue to the vorbis/ape/flac tag. */
274   {GST_TAG_CDDA_CDDB_DISCID, "TXXX|discid"},
275   {GST_TAG_CDDA_CDDB_DISCID, "TXXX|CDDB DiscID"}
276 };
277
278 /**
279  * gst_tag_from_id3_user_tag:
280  * @type: the type of ID3v2 user tag (e.g. "TXXX" or "UDIF")
281  * @id3_user_tag: ID3v2 user tag to convert to GStreamer tag
282  *
283  * Looks up the GStreamer tag for an ID3v2 user tag (e.g. description in
284  * TXXX frame or owner in UFID frame).
285  *
286  * Returns: The corresponding GStreamer tag or NULL if none exists.
287  */
288 G_CONST_RETURN gchar *
289 gst_tag_from_id3_user_tag (const gchar * type, const gchar * id3_user_tag)
290 {
291   int i = 0;
292
293   g_return_val_if_fail (type != NULL && strlen (type) == 4, NULL);
294   g_return_val_if_fail (id3_user_tag != NULL, NULL);
295
296   for (i = 0; i < G_N_ELEMENTS (user_tag_matches); ++i) {
297     if (strncmp (type, user_tag_matches[i].original_tag, 4) == 0 &&
298         g_ascii_strcasecmp (id3_user_tag,
299             user_tag_matches[i].original_tag + 5) == 0) {
300       GST_LOG ("Mapped ID3v2 user tag '%s' to GStreamer tag '%s'",
301           user_tag_matches[i].original_tag, user_tag_matches[i].gstreamer_tag);
302       return user_tag_matches[i].gstreamer_tag;
303     }
304   }
305
306   GST_INFO ("Cannot map ID3v2 user tag '%s' of type '%s' to GStreamer tag",
307       id3_user_tag, type);
308
309   return NULL;
310 }
311
312 /**
313  * gst_tag_to_id3_tag:
314  * @gst_tag: GStreamer tag to convert to vorbiscomment tag
315  *
316  * Looks up the ID3v2 tag for a GStreamer tag.
317  *
318  * Returns: The corresponding ID3v2 tag or NULL if none exists.
319  */
320 G_CONST_RETURN gchar *
321 gst_tag_to_id3_tag (const gchar * gst_tag)
322 {
323   int i = 0;
324
325   g_return_val_if_fail (gst_tag != NULL, NULL);
326
327   while (tag_matches[i].gstreamer_tag != NULL) {
328     if (strcmp (gst_tag, tag_matches[i].gstreamer_tag) == 0) {
329       return tag_matches[i].original_tag;
330     }
331     i++;
332   }
333   return NULL;
334 }
335
336 static void
337 gst_tag_extract_id3v1_string (GstTagList * list, const gchar * tag,
338     const gchar * start, const guint size)
339 {
340   const gchar *env_vars[] = { "GST_ID3V1_TAG_ENCODING",
341     "GST_ID3_TAG_ENCODING", "GST_TAG_ENCODING", NULL
342   };
343   gchar *utf8;
344
345   utf8 = gst_tag_freeform_string_to_utf8 (start, size, env_vars);
346
347   if (utf8 && *utf8 != '\0') {
348     gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, tag, utf8, NULL);
349   }
350
351   g_free (utf8);
352 }
353
354 /**
355  * gst_tag_list_new_from_id3v1:
356  * @data: 128 bytes of data containing the ID3v1 tag
357  *
358  * Parses the data containing an ID3v1 tag and returns a #GstTagList from the
359  * parsed data.
360  *
361  * Returns: A new tag list or NULL if the data was not an ID3v1 tag.
362  */
363 GstTagList *
364 gst_tag_list_new_from_id3v1 (const guint8 * data)
365 {
366   guint year;
367   gchar *ystr;
368   GstTagList *list;
369
370   g_return_val_if_fail (data != NULL, NULL);
371
372   if (data[0] != 'T' || data[1] != 'A' || data[2] != 'G')
373     return NULL;
374   list = gst_tag_list_new ();
375   gst_tag_extract_id3v1_string (list, GST_TAG_TITLE, (gchar *) & data[3], 30);
376   gst_tag_extract_id3v1_string (list, GST_TAG_ARTIST, (gchar *) & data[33], 30);
377   gst_tag_extract_id3v1_string (list, GST_TAG_ALBUM, (gchar *) & data[63], 30);
378   ystr = g_strndup ((gchar *) & data[93], 4);
379   year = strtoul (ystr, NULL, 10);
380   g_free (ystr);
381   if (year > 0) {
382     GDate *date = g_date_new_dmy (1, 1, year);
383
384     gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, date, NULL);
385     g_date_free (date);
386   }
387   if (data[125] == 0 && data[126] != 0) {
388     gst_tag_extract_id3v1_string (list, GST_TAG_COMMENT, (gchar *) & data[97],
389         28);
390     gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_NUMBER,
391         (guint) data[126], NULL);
392   } else {
393     gst_tag_extract_id3v1_string (list, GST_TAG_COMMENT, (gchar *) & data[97],
394         30);
395   }
396   if (data[127] < gst_tag_id3_genre_count () && !gst_tag_list_is_empty (list)) {
397     gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_GENRE,
398         gst_tag_id3_genre_get (data[127]), NULL);
399   }
400
401   return list;
402 }
403
404 /**
405  * gst_tag_id3_genre_count:
406  *
407  * Gets the number of ID3v1 genres that can be identified. Winamp genres are 
408  * included.
409  *
410  * Returns: the number of ID3v1 genres that can be identified
411  */
412 guint
413 gst_tag_id3_genre_count (void)
414 {
415   return G_N_ELEMENTS (genres);
416 }
417
418 /**
419  * gst_tag_id3_genre_get:
420  * @id: ID of genre to query
421  *
422  * Gets the ID3v1 genre name for a given ID.
423  *
424  * Returns: the genre or NULL if no genre is associated with that ID.
425  */
426 G_CONST_RETURN gchar *
427 gst_tag_id3_genre_get (const guint id)
428 {
429   if (id >= G_N_ELEMENTS (genres))
430     return NULL;
431   return genres[id];
432 }
433
434 /**
435  * gst_tag_list_add_id3_image:
436  * @tag_list: a tag list
437  * @image_data: the (encoded) image
438  * @image_data_len: the length of the encoded image data at @image_data
439  * @id3_picture_type: picture type as per the ID3 (v2.4.0) specification for
440  *    the APIC frame (0 = unknown/other)
441  *
442  * Adds an image from an ID3 APIC frame (or similar, such as used in FLAC)
443  * to the given tag list. Also see gst_tag_image_data_to_image_buffer() for
444  * more information on image tags in GStreamer.
445  *
446  * Returns: %TRUE if the image was processed, otherwise %FALSE
447  *
448  * Since: 0.10.20
449  */
450 gboolean
451 gst_tag_list_add_id3_image (GstTagList * tag_list, const guint8 * image_data,
452     guint image_data_len, guint id3_picture_type)
453 {
454   GstTagImageType tag_image_type;
455   const gchar *tag_name;
456   GstBuffer *image;
457
458   g_return_val_if_fail (GST_IS_TAG_LIST (tag_list), FALSE);
459   g_return_val_if_fail (image_data != NULL, FALSE);
460   g_return_val_if_fail (image_data_len > 0, FALSE);
461
462   if (id3_picture_type == 0x01 || id3_picture_type == 0x02) {
463     /* file icon for preview. Don't add image-type to caps, since there
464      * is only supposed to be one of these, and the type is already indicated
465      * via the special tag */
466     tag_name = GST_TAG_PREVIEW_IMAGE;
467     tag_image_type = GST_TAG_IMAGE_TYPE_NONE;
468   } else {
469     tag_name = GST_TAG_IMAGE;
470
471     /* Remap the ID3v2 APIC type our ImageType enum */
472     if (id3_picture_type >= 0x3 && id3_picture_type <= 0x14)
473       tag_image_type = (GstTagImageType) (id3_picture_type - 2);
474     else
475       tag_image_type = GST_TAG_IMAGE_TYPE_UNDEFINED;
476   }
477
478   image = gst_tag_image_data_to_image_buffer (image_data, image_data_len,
479       tag_image_type);
480
481   if (image == NULL)
482     return FALSE;
483
484   gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND, tag_name, image, NULL);
485   gst_buffer_unref (image);
486   return TRUE;
487 }