2 * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
4 * gstid3tag.c: plugin for reading / modifying id3 tags
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.
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.
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.
24 * @short_description: tag mappings and support functions for plugins
25 * dealing with ID3v1 and ID3v2 tags
26 * @see_also: #GstTagList
30 * Contains various utility functions for plugins to parse or create
31 * ID3 tags and map ID3v2 identifiers to and from GStreamer identifiers.
40 #include "gsttageditingprivate.h"
44 static const gchar *genres[] = {
181 "Christian Gangsta Rap",
185 "Contemporary Christian",
195 static 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_COPYRIGHT, "TCOP"},
201 {GST_TAG_GENRE, "TCON"},
202 {GST_TAG_DATE, "TDRC"},
203 {GST_TAG_COMMENT, "COMM"},
204 {GST_TAG_ALBUM_VOLUME_NUMBER, "TPOS"},
205 {GST_TAG_DURATION, "TLEN"},
210 * gst_tag_from_id3_tag:
211 * @id3_tag: ID3v2 tag to convert to GStreamer tag
213 * Looks up the GStreamer tag for a ID3v2 tag.
215 * Returns: The corresponding GStreamer tag or NULL if none exists.
217 G_CONST_RETURN gchar *
218 gst_tag_from_id3_tag (const gchar * id3_tag)
222 g_return_val_if_fail (id3_tag != NULL, NULL);
224 while (tag_matches[i].gstreamer_tag != NULL) {
225 if (strncmp (id3_tag, tag_matches[i].original_tag, 5) == 0) {
226 return tag_matches[i].gstreamer_tag;
231 GST_INFO ("Cannot map ID3v2 tag '%c%c%c%c' to GStreamer tag",
232 id3_tag[0], id3_tag[1], id3_tag[2], id3_tag[3]);
238 * gst_tag_to_id3_tag:
239 * @gst_tag: GStreamer tag to convert to vorbiscomment tag
241 * Looks up the ID3v2 tag for a GStreamer tag.
243 * Returns: The corresponding ID3v2 tag or NULL if none exists.
245 G_CONST_RETURN gchar *
246 gst_tag_to_id3_tag (const gchar * gst_tag)
250 g_return_val_if_fail (gst_tag != NULL, NULL);
252 while (tag_matches[i].gstreamer_tag != NULL) {
253 if (strcmp (gst_tag, tag_matches[i].gstreamer_tag) == 0) {
254 return tag_matches[i].original_tag;
262 gst_tag_extract_id3v1_string (GstTagList * list, const gchar * tag,
263 const gchar * start, const guint size)
269 /* Should we try the charsets specified
270 * via environment variables FIRST ? */
271 if (g_utf8_validate (start, size, NULL)) {
272 utf8 = g_strndup (start, size);
276 env = g_getenv ("GST_ID3V1_TAG_ENCODING");
277 if (!env || *env == '\0')
278 env = g_getenv ("GST_ID3_TAG_ENCODING");
279 if (!env || *env == '\0')
280 env = g_getenv ("GST_TAG_ENCODING");
282 /* Try charsets specified via the environment */
283 if (env && *env != '\0') {
286 csets = g_strsplit (env, G_SEARCHPATH_SEPARATOR_S, -1);
288 for (c = csets; c && *c; ++c) {
290 g_convert (start, size, "UTF-8", *c, &bytes_read, NULL, NULL))) {
291 if (bytes_read == size) {
300 /* Try current locale (if not UTF-8) */
301 if (!g_get_charset (&env)) {
302 if ((utf8 = g_locale_to_utf8 (start, size, &bytes_read, NULL, NULL))) {
303 if (bytes_read == size) {
313 g_convert (start, size, "UTF-8", "ISO-8859-1", &bytes_read, NULL, NULL);
314 if (utf8 != NULL && bytes_read == size) {
324 if (utf8 && utf8[0] != '\0') {
325 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, tag, utf8, NULL);
332 * gst_tag_list_new_from_id3v1:
333 * @data: 128 bytes of data containing the ID3v1 tag
335 * Parses the data containing an ID3v1 tag and returns a #GstTagList from the
338 * Returns: A new tag list or NULL if the data was not an ID3v1 tag.
341 gst_tag_list_new_from_id3v1 (const guint8 * data)
347 g_return_val_if_fail (data != NULL, NULL);
349 if (data[0] != 'T' || data[1] != 'A' || data[2] != 'G')
351 list = gst_tag_list_new ();
352 gst_tag_extract_id3v1_string (list, GST_TAG_TITLE, (gchar *) & data[3], 30);
353 gst_tag_extract_id3v1_string (list, GST_TAG_ARTIST, (gchar *) & data[33], 30);
354 gst_tag_extract_id3v1_string (list, GST_TAG_ALBUM, (gchar *) & data[63], 30);
355 ystr = g_strndup ((gchar *) & data[93], 4);
356 year = strtoul (ystr, NULL, 10);
359 GDate *date = g_date_new_dmy (1, 1, year);
361 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, date, NULL);
364 if (data[125] == 0) {
365 gst_tag_extract_id3v1_string (list, GST_TAG_COMMENT, (gchar *) & data[97],
367 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_NUMBER,
368 (guint) data[126], NULL);
370 gst_tag_extract_id3v1_string (list, GST_TAG_COMMENT, (gchar *) & data[97],
373 if (data[127] < gst_tag_id3_genre_count ()) {
374 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_GENRE,
375 gst_tag_id3_genre_get (data[127]), NULL);
382 * gst_tag_id3_genre_count:
384 * Gets the number of ID3v1 genres that can be identified. Winamp genres are
387 * Returns: the number of ID3v1 genres that can be identified
390 gst_tag_id3_genre_count (void)
392 return G_N_ELEMENTS (genres);
396 * gst_tag_id3_genre_get:
397 * @id: ID of genre to query
399 * Gets the ID3v1 genre name for a given ID.
401 * Returns: the genre or NULL if no genre is associated with that ID.
403 G_CONST_RETURN gchar *
404 gst_tag_id3_genre_get (const guint id)
406 if (id >= G_N_ELEMENTS (genres))