udapted vi.po
[rhythmbox.git] / metadata / rb-metadata-gst.c
blob261a9da26d30d3f3d47de40e529434cf6d6821cb
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * arch-tag: Implementation of metadata reading using GStreamer
5 * Copyright (C) 2003,2004 Colin Walters <walters@verbum.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include <config.h>
25 #include <string.h>
27 #include <glib/gi18n.h>
28 #include <gst/tag/tag.h>
29 #include <gst/gsturi.h>
31 #ifdef HAVE_GSTREAMER_0_8
32 #include <gst/gsttag.h>
33 #define gst_caps_unref gst_caps_free
34 #define gst_tag_setter_add_tag_values gst_tag_setter_add_values
35 #define gst_tag_setter_set_tag_merge_mode gst_tag_setter_set_merge_mode
36 #define gst_tag_setter_merge_tags gst_tag_setter_merge
37 #endif
38 #ifdef HAVE_GSTREAMER_0_10
39 #include <gst/gsttagsetter.h>
40 #endif
42 #include "rb-metadata.h"
43 #include "rb-debug.h"
44 #include "rb-util.h"
45 #include "rb-file-helpers.h"
47 G_DEFINE_TYPE(RBMetaData, rb_metadata, G_TYPE_OBJECT)
49 static void rb_metadata_finalize (GObject *object);
51 typedef GstElement *(*RBAddTaggerElem) (RBMetaData *, GstElement *);
54 * The list of mine type prefixes for files that shouldn't display errors about being non-audio.
55 * Useful for people who have cover art, et cetera, in their music directories
57 const char * ignore_mime_types[] = {
58 "image/",
59 "text/",
60 "application/xml",
61 "application/zip"
65 * File size below which we will simply ignore files that can't be identified.
66 * This is mostly here so we ignore the various text files that are packaged
67 * with many netlabel releases and other downloads.
69 #define REALLY_SMALL_FILE_SIZE (4096)
71 struct RBMetadataGstType
73 char *mimetype;
74 RBAddTaggerElem tag_func;
75 char *human_name;
78 struct RBMetaDataPrivate
80 char *uri;
82 GHashTable *metadata;
84 GstElement *pipeline;
85 GstElement *sink;
86 gulong typefind_cb_id;
87 GstTagList *tags;
89 /* Array of RBMetadataGstType */
90 GPtrArray *supported_types;
92 char *type;
93 gboolean handoff;
94 gboolean eos;
95 gboolean non_audio;
96 gboolean has_video;
97 GError *error;
100 #define RB_METADATA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_METADATA, RBMetaDataPrivate))
102 #ifdef HAVE_GSTREAMER_0_10
103 static void
104 gst_date_gulong_transform (const GValue *src, GValue *dest)
106 const GDate *date = gst_value_get_date (src);
108 g_value_set_ulong (dest, (date) ? g_date_get_julian (date) : 0);
111 static void
112 gulong_gst_date_transform (const GValue *src, GValue *dest)
114 gulong day = g_value_get_ulong (src);
115 GDate *date = g_date_new_julian (day);
117 gst_value_set_date (dest, date);
118 g_date_free (date);
121 #endif
123 static void
124 rb_metadata_class_init (RBMetaDataClass *klass)
126 GObjectClass *object_class = G_OBJECT_CLASS (klass);
128 object_class->finalize = rb_metadata_finalize;
130 g_type_class_add_private (klass, sizeof (RBMetaDataPrivate));
131 #ifdef HAVE_GSTREAMER_0_10
132 g_value_register_transform_func (GST_TYPE_DATE, G_TYPE_ULONG, gst_date_gulong_transform);
133 g_value_register_transform_func (G_TYPE_ULONG, GST_TYPE_DATE, gulong_gst_date_transform);
134 #endif
137 static GstElement *
138 rb_add_flac_tagger (RBMetaData *md, GstElement *element)
140 GstElement *tagger = NULL;
142 if (!(tagger = gst_element_factory_make ("flactag", "flactag")))
143 return NULL;
145 gst_bin_add (GST_BIN (md->priv->pipeline), tagger);
146 gst_element_link_many (element, tagger, NULL);
148 gst_tag_setter_merge_tags (GST_TAG_SETTER (tagger), md->priv->tags, GST_TAG_MERGE_REPLACE_ALL);
150 return tagger;
153 #ifdef HAVE_GSTREAMER_0_10
154 static void
155 id3_pad_added_cb (GstElement *demux, GstPad *pad, GstElement *mux)
157 GstPad *mux_pad;
159 mux_pad = gst_element_get_compatible_pad (mux, pad, NULL);
160 if (gst_pad_link (pad, mux_pad) != GST_PAD_LINK_OK)
161 rb_debug ("unable to link pad from id3demux to id3mux");
162 else
163 rb_debug ("linked pad from id3de to id3mux");
166 static gboolean
167 rb_gst_plugin_greater (const char *plugin, const char *element, gint major, gint minor, gint micro)
169 const char *version;
170 GstPlugin *p;
171 guint i;
172 guint count;
174 if (gst_default_registry_check_feature_version (element, major, minor, micro + 1))
175 return TRUE;
177 if (!gst_default_registry_check_feature_version (element, major, minor, micro))
178 return FALSE;
180 p = gst_default_registry_find_plugin (plugin);
181 if (p == NULL)
182 return FALSE;
184 version = gst_plugin_get_version (p);
186 /* check if it's not a release */
187 count = sscanf (version, "%u.%u.%u.%u", &i, &i, &i, &i);
188 return (count > 3);
191 static GstElement *
192 rb_add_id3_tagger (RBMetaData *md, GstElement *element)
194 GstElement *demux = NULL;
195 GstElement *mux = NULL;
197 demux = gst_element_factory_make ("id3demux", NULL);
199 mux = gst_element_factory_make ("id3v2mux", NULL);
200 if (mux != NULL) {
201 /* check for backwards id3v2mux merge-mode */
202 if (!rb_gst_plugin_greater ("taglib", "id3v2mux", 0, 10, 3)) {
203 rb_debug ("using id3v2mux with backwards merge mode");
204 gst_tag_setter_set_tag_merge_mode (GST_TAG_SETTER (mux), GST_TAG_MERGE_REPLACE);
205 } else {
206 rb_debug ("using id3v2mux");
208 } else {
209 mux = gst_element_factory_make ("id3mux", NULL);
211 /* check for backwards id3mux merge-mode */
212 if (mux && !rb_gst_plugin_greater ("mad", "id3mux", 0, 10, 3)) {
213 rb_debug ("using id3mux with backwards merge mode");
214 gst_tag_setter_set_tag_merge_mode (GST_TAG_SETTER (mux), GST_TAG_MERGE_REPLACE);
215 } else {
216 rb_debug ("using id3mux");
220 if (demux == NULL || mux == NULL)
221 goto error;
223 gst_bin_add_many (GST_BIN (md->priv->pipeline), demux, mux, NULL);
224 if (!gst_element_link (element, demux))
225 goto error;
227 g_signal_connect (demux, "pad-added", (GCallback)id3_pad_added_cb, mux);
229 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), md->priv->tags, GST_TAG_MERGE_REPLACE_ALL);
231 return mux;
233 error:
234 g_object_unref (demux);
235 g_object_unref (mux);
236 return NULL;
239 static void
240 ogg_pad_added_cb (GstElement *demux, GstPad *pad, RBMetaData *md)
242 GstCaps *caps;
243 GstStructure *structure;
244 const gchar *mimetype;
245 GstPad *conn_pad = NULL;
246 GstElement *mux;
248 caps = gst_pad_get_caps (pad);
249 structure = gst_caps_get_structure (caps, 0);
250 mimetype = gst_structure_get_name (structure);
252 mux = g_object_get_data (G_OBJECT (demux), "mux");
254 if (strcmp (mimetype, "audio/x-vorbis") == 0) {
255 GstElement *tagger, *parser;
256 GstBin *bin;
257 GstState state;
259 rb_debug ("found vorbis stream in ogg container, using vorbistag");
261 parser = gst_element_factory_make ("vorbisparse", NULL);
262 if (parser == NULL) {
263 rb_debug ("could not create vorbisparse element");
264 goto end;
267 tagger = gst_element_factory_make ("vorbistag", NULL);
268 if (tagger == NULL) {
269 rb_debug ("could not create vorbistag element");
270 gst_object_unref (parser);
271 goto end;
274 bin = GST_BIN (gst_element_get_parent (mux));
275 gst_bin_add_many (bin, tagger, parser, NULL);
276 gst_object_unref (GST_OBJECT (bin));
278 /* connect and bring them up to the same state */
279 gst_element_link_many (tagger, parser, mux, NULL);
280 gst_element_get_state (mux, &state, NULL, 0);
281 gst_element_set_state (parser, state);
282 gst_element_set_state (tagger, state);
284 conn_pad = gst_element_get_compatible_pad (tagger, pad, NULL);
285 gst_pad_link (pad, conn_pad);
287 gst_tag_setter_merge_tags (GST_TAG_SETTER (tagger), md->priv->tags, GST_TAG_MERGE_REPLACE_ALL);
288 } else {
289 conn_pad = gst_element_get_compatible_pad (mux, pad, NULL);
290 gst_pad_link (pad, conn_pad);
291 rb_debug ("found stream in ogg container with no known tagging element");
294 end:
295 gst_caps_unref (caps);
298 static GstElement *
299 rb_add_ogg_tagger (RBMetaData *md, GstElement *element)
301 GstElement *demux = NULL;
302 GstElement *mux = NULL;
304 demux = gst_element_factory_make ("oggdemux", NULL);
305 mux = gst_element_factory_make ("oggmux", NULL);
307 if (demux == NULL || mux == NULL)
308 goto error;
310 gst_bin_add_many (GST_BIN (md->priv->pipeline), demux, mux, NULL);
311 if (!gst_element_link (element, demux))
312 goto error;
314 g_object_set_data (G_OBJECT (demux), "mux", mux);
315 g_signal_connect (demux, "pad-added", (GCallback)ogg_pad_added_cb, md);
317 return mux;
319 error:
320 g_object_unref (demux);
321 g_object_unref (mux);
322 return NULL;
325 #elif HAVE_GSTREAMER_0_8
326 static GstElement *
327 rb_add_id3_tagger (RBMetaData *md, GstElement *element)
329 GstElement *tagger, *identity;
330 GstCaps *filtercaps = NULL;
332 if (!(tagger = gst_element_factory_make ("id3tag", "tagger")))
333 return NULL;
335 if (!(identity = gst_element_factory_make ("identity", "identity"))) {
336 gst_object_unref (GST_OBJECT (tagger));
337 return NULL;
340 gst_bin_add_many (GST_BIN (md->priv->pipeline), tagger, identity, NULL);
342 filtercaps = gst_caps_new_simple ("application/x-id3", NULL);
343 if (!gst_element_link_filtered (tagger, identity, filtercaps)) {
344 g_warning ("Linking id3tag and identity failed");
345 gst_caps_free (filtercaps);
346 gst_object_unref (GST_OBJECT(tagger));
347 gst_object_unref (GST_OBJECT(identity));
348 return NULL;
350 gst_caps_free (filtercaps);
352 gst_element_link_many(element, tagger, NULL);
354 gst_tag_setter_merge_tags (GST_TAG_SETTER (tagger), md->priv->tags, GST_TAG_MERGE_REPLACE_ALL);
356 return identity;
358 #endif
360 static void
361 add_supported_type (RBMetaData *md,
362 const char *mime,
363 RBAddTaggerElem add_tagger_func,
364 const char *human_name)
366 struct RBMetadataGstType *type = g_new0 (struct RBMetadataGstType, 1);
367 type->mimetype = g_strdup (mime);
368 type->tag_func = add_tagger_func;
369 type->human_name = g_strdup (human_name);
370 g_ptr_array_add (md->priv->supported_types, type);
373 static void
374 rb_metadata_init (RBMetaData *md)
376 RBAddTaggerElem tagger;
377 gboolean has_gnomevfssink = FALSE;
378 gboolean has_id3 = FALSE;
380 md->priv = RB_METADATA_GET_PRIVATE (md);
382 md->priv->supported_types = g_ptr_array_new ();
384 /* the list of supported types serves two purposes:
385 * - it knows how to construct elements for tag writing
386 * - it knows human-readable names for MIME types so we can say
387 * "There is no plugin available to play WMV files"
388 * rather than " .. play video/x-ms-asf files".
390 * only registering types we have plugins for defeats the second
391 * purpose.
393 has_gnomevfssink = (gst_element_factory_find ("gnomevfssrc") != NULL &&
394 gst_element_factory_find ("gnomevfssink") != NULL);
395 #ifdef HAVE_GSTREAMER_0_10
396 has_id3 = (gst_element_factory_find ("id3demux") != NULL) &&
397 (gst_default_registry_check_feature_version ("id3mux", 0, 10, 2) ||
398 (gst_element_factory_find ("id3v2mux") != NULL));
399 #elif HAVE_GSTREAMER_0_8
400 has_id3 = (gst_element_factory_find ("id3tag") != NULL);
401 #endif
403 tagger = (has_gnomevfssink && has_id3) ? rb_add_id3_tagger : NULL;
404 add_supported_type (md, "application/x-id3", tagger, "MP3");
405 add_supported_type (md, "audio/mpeg", tagger, "MP3");
407 #ifdef HAVE_GSTREAMER_0_10
409 gboolean has_vorbis;
411 has_vorbis = ((gst_element_factory_find ("vorbistag") != NULL) &&
412 gst_default_registry_check_feature_version ("vorbisparse", 0, 10, 6) &&
413 gst_default_registry_check_feature_version ("oggmux", 0, 10, 6) &&
414 gst_default_registry_check_feature_version ("oggdemux", 0, 10, 6));
415 tagger = (has_gnomevfssink && has_vorbis) ? rb_add_ogg_tagger : NULL;
417 #else
418 tagger = NULL;
419 #endif
420 add_supported_type (md, "application/ogg", tagger, "Ogg Vorbis");
421 add_supported_type (md, "audio/x-vorbis", tagger, "Ogg Vorbis");
423 add_supported_type (md, "audio/x-mod", NULL, "MOD");
424 add_supported_type (md, "audio/x-wav", NULL, "WAV");
425 add_supported_type (md, "video/x-ms-asf", NULL, "ASF");
427 tagger = (has_gnomevfssink && gst_element_factory_find ("flactag")) ? rb_add_flac_tagger : NULL;
428 add_supported_type (md, "audio/x-flac", tagger, "FLAC");
432 static void
433 rb_metadata_finalize (GObject *object)
435 int i;
436 RBMetaData *md;
438 md = RB_METADATA (object);
440 for (i = 0; i < md->priv->supported_types->len; i++) {
441 struct RBMetadataGstType *type = g_ptr_array_index (md->priv->supported_types, i);
442 g_free (type->mimetype);
443 g_free (type->human_name);
444 g_free (type);
446 g_ptr_array_free (md->priv->supported_types, TRUE);
448 if (md->priv->metadata)
449 g_hash_table_destroy (md->priv->metadata);
451 if (md->priv->pipeline)
452 gst_object_unref (GST_OBJECT (md->priv->pipeline));
454 g_free (md->priv->type);
455 g_free (md->priv->uri);
456 g_clear_error (&md->priv->error);
458 G_OBJECT_CLASS (rb_metadata_parent_class)->finalize (object);
461 RBMetaData *
462 rb_metadata_new (void)
464 return RB_METADATA (g_object_new (RB_TYPE_METADATA, NULL, NULL));
467 static void
468 free_gvalue (GValue *val)
470 g_value_unset (val);
471 g_free (val);
474 static int
475 rb_metadata_gst_tag_to_field (const char *tag)
477 if (!strcmp (tag, GST_TAG_TITLE))
478 return RB_METADATA_FIELD_TITLE;
479 else if (!strcmp (tag, GST_TAG_ARTIST))
480 return RB_METADATA_FIELD_ARTIST;
481 else if (!strcmp (tag, GST_TAG_ALBUM))
482 return RB_METADATA_FIELD_ALBUM;
483 else if (!strcmp (tag, GST_TAG_DATE))
484 return RB_METADATA_FIELD_DATE;
485 else if (!strcmp (tag, GST_TAG_GENRE))
486 return RB_METADATA_FIELD_GENRE;
487 else if (!strcmp (tag, GST_TAG_COMMENT))
488 return RB_METADATA_FIELD_COMMENT;
489 else if (!strcmp (tag, GST_TAG_TRACK_NUMBER))
490 return RB_METADATA_FIELD_TRACK_NUMBER;
491 else if (!strcmp (tag, GST_TAG_TRACK_COUNT))
492 return RB_METADATA_FIELD_MAX_TRACK_NUMBER;
493 else if (!strcmp (tag, GST_TAG_ALBUM_VOLUME_NUMBER))
494 return RB_METADATA_FIELD_DISC_NUMBER;
495 else if (!strcmp (tag, GST_TAG_ALBUM_VOLUME_COUNT))
496 return RB_METADATA_FIELD_MAX_TRACK_NUMBER;
497 else if (!strcmp (tag, GST_TAG_DESCRIPTION))
498 return RB_METADATA_FIELD_DESCRIPTION;
499 else if (!strcmp (tag, GST_TAG_VERSION))
500 return RB_METADATA_FIELD_VERSION;
501 else if (!strcmp (tag, GST_TAG_ISRC))
502 return RB_METADATA_FIELD_ISRC;
503 else if (!strcmp (tag, GST_TAG_ORGANIZATION))
504 return RB_METADATA_FIELD_ORGANIZATION;
505 else if (!strcmp (tag, GST_TAG_COPYRIGHT))
506 return RB_METADATA_FIELD_COPYRIGHT;
507 else if (!strcmp (tag, GST_TAG_CONTACT))
508 return RB_METADATA_FIELD_CONTACT;
509 else if (!strcmp (tag, GST_TAG_LICENSE))
510 return RB_METADATA_FIELD_LICENSE;
511 else if (!strcmp (tag, GST_TAG_PERFORMER))
512 return RB_METADATA_FIELD_PERFORMER;
513 else if (!strcmp (tag, GST_TAG_DURATION))
514 return RB_METADATA_FIELD_DURATION;
515 else if (!strcmp (tag, GST_TAG_CODEC))
516 return RB_METADATA_FIELD_CODEC;
517 else if (!strcmp (tag, GST_TAG_BITRATE))
518 return RB_METADATA_FIELD_BITRATE;
519 else if (!strcmp (tag, GST_TAG_TRACK_GAIN))
520 return RB_METADATA_FIELD_TRACK_GAIN;
521 else if (!strcmp (tag, GST_TAG_TRACK_PEAK))
522 return RB_METADATA_FIELD_TRACK_PEAK;
523 else if (!strcmp (tag, GST_TAG_ALBUM_GAIN))
524 return RB_METADATA_FIELD_ALBUM_GAIN;
525 else if (!strcmp (tag, GST_TAG_ALBUM_PEAK))
526 return RB_METADATA_FIELD_ALBUM_PEAK;
527 #ifdef GST_TAG_MUSICBRAINZ_TRACKID
528 else if (!strcmp (tag, GST_TAG_MUSICBRAINZ_TRACKID))
529 return RB_METADATA_FIELD_MUSICBRAINZ_TRACKID;
530 #endif
531 else
532 return -1;
535 static const char *
536 rb_metadata_gst_field_to_gst_tag (RBMetaDataField field)
538 switch (field)
540 case RB_METADATA_FIELD_TITLE:
541 return GST_TAG_TITLE;
542 case RB_METADATA_FIELD_ARTIST:
543 return GST_TAG_ARTIST;
544 case RB_METADATA_FIELD_ALBUM:
545 return GST_TAG_ALBUM;
546 case RB_METADATA_FIELD_DATE:
547 return GST_TAG_DATE;
548 case RB_METADATA_FIELD_GENRE:
549 return GST_TAG_GENRE;
550 case RB_METADATA_FIELD_COMMENT:
551 return GST_TAG_COMMENT;
552 case RB_METADATA_FIELD_TRACK_NUMBER:
553 return GST_TAG_TRACK_NUMBER;
554 case RB_METADATA_FIELD_MAX_TRACK_NUMBER:
555 return GST_TAG_TRACK_COUNT;
556 case RB_METADATA_FIELD_DISC_NUMBER:
557 return GST_TAG_ALBUM_VOLUME_NUMBER;
558 case RB_METADATA_FIELD_MAX_DISC_NUMBER:
559 return GST_TAG_ALBUM_VOLUME_COUNT;
560 case RB_METADATA_FIELD_DESCRIPTION:
561 return GST_TAG_DESCRIPTION;
562 case RB_METADATA_FIELD_VERSION:
563 return GST_TAG_VERSION;
564 case RB_METADATA_FIELD_ISRC:
565 return GST_TAG_ISRC;
566 case RB_METADATA_FIELD_ORGANIZATION:
567 return GST_TAG_ORGANIZATION;
568 case RB_METADATA_FIELD_COPYRIGHT:
569 return GST_TAG_COPYRIGHT;
570 case RB_METADATA_FIELD_CONTACT:
571 return GST_TAG_CONTACT;
572 case RB_METADATA_FIELD_LICENSE:
573 return GST_TAG_LICENSE;
574 case RB_METADATA_FIELD_PERFORMER:
575 return GST_TAG_PERFORMER;
576 case RB_METADATA_FIELD_DURATION:
577 return GST_TAG_DURATION;
578 case RB_METADATA_FIELD_CODEC:
579 return GST_TAG_CODEC;
580 case RB_METADATA_FIELD_BITRATE:
581 return GST_TAG_BITRATE;
582 case RB_METADATA_FIELD_TRACK_GAIN:
583 return GST_TAG_TRACK_GAIN;
584 case RB_METADATA_FIELD_TRACK_PEAK:
585 return GST_TAG_TRACK_PEAK;
586 case RB_METADATA_FIELD_ALBUM_GAIN:
587 return GST_TAG_ALBUM_GAIN;
588 case RB_METADATA_FIELD_ALBUM_PEAK:
589 return GST_TAG_ALBUM_PEAK;
590 #ifdef GST_TAG_MUSICBRAINZ_TRACKID
591 case RB_METADATA_FIELD_MUSICBRAINZ_TRACKID:
592 return GST_TAG_MUSICBRAINZ_TRACKID;
593 #endif
594 default:
595 return NULL;
599 static const char *
600 rb_metadata_gst_type_to_name (RBMetaData *md, const char *mimetype)
602 int i;
603 for (i = 0; i < md->priv->supported_types->len; i++) {
604 struct RBMetadataGstType *type = g_ptr_array_index (md->priv->supported_types, i);
605 if (!strcmp (type->mimetype, mimetype))
606 return type->human_name;
608 return NULL;
611 static RBAddTaggerElem
612 rb_metadata_gst_type_to_tag_function (RBMetaData *md, const char *mimetype)
614 int i;
615 for (i = 0; i < md->priv->supported_types->len; i++) {
616 struct RBMetadataGstType *type = g_ptr_array_index (md->priv->supported_types, i);
617 if (!strcmp (type->mimetype, mimetype))
618 return type->tag_func;
620 return NULL;
623 static char *
624 make_undecodable_error (RBMetaData *md)
626 const char *human_name;
628 human_name= rb_metadata_gst_type_to_name (md, md->priv->type);
629 if (human_name == NULL)
630 human_name = rb_mime_get_friendly_name (md->priv->type);
632 if (human_name) {
633 return g_strdup_printf (_("The GStreamer plugins to decode \"%s\" files cannot be found"),
634 human_name);
635 } else {
636 return g_strdup_printf (_("The file contains a stream of type %s, which is not decodable"),
637 md->priv->type);
641 static void
642 rb_metadata_gst_load_tag (const GstTagList *list, const gchar *tag, RBMetaData *md)
644 int count, tem, type;
645 RBMetaDataField field;
646 GValue *newval;
647 const GValue *val;
649 rb_debug ("uri: %s tag: %s ", md->priv->uri, tag);
651 count = gst_tag_list_get_tag_size (list, tag);
652 if (count < 1)
653 return;
655 tem = rb_metadata_gst_tag_to_field (tag);
656 if (tem < 0)
657 return;
658 field = (RBMetaDataField) tem;
660 type = rb_metadata_get_field_type (field);
661 val = gst_tag_list_get_value_index (list, tag, 0);
662 newval = g_new0 (GValue, 1);
663 g_value_init (newval, type);
664 if (!g_value_transform (val, newval)) {
666 rb_debug ("Could not transform tag value type %s into %s",
667 g_type_name (G_VALUE_TYPE (val)),
668 g_type_name (G_VALUE_TYPE (newval)));
669 g_value_unset (newval);
670 g_free (newval);
671 return;
674 switch (type) {
675 case G_TYPE_STRING: {
676 /* Reject invalid utf-8 strings, and then
677 * remove leading and trailing whitespace.
679 char *str;
680 str = g_value_dup_string (newval);
682 if (!g_utf8_validate (str, -1, NULL)) {
683 rb_debug ("Got invalid UTF-8 tag data");
684 g_free (str);
685 g_value_unset (newval);
686 g_free (newval);
687 return;
689 str = g_strstrip (str);
690 g_value_take_string (newval, str);
691 break;
693 default:
694 break;
697 switch (field) {
698 case RB_METADATA_FIELD_BITRATE: {
699 /* GStreamer sends us bitrate in bps, but we need it in kbps*/
700 gulong bitrate;
701 bitrate = g_value_get_ulong (newval);
702 g_value_set_ulong (newval, bitrate/1000);
703 break;
706 case RB_METADATA_FIELD_DURATION: {
707 /* GStreamer sends us duration in ns,
708 * but we need it in seconds
710 guint64 duration;
711 duration = g_value_get_uint64 (val);
712 g_value_set_ulong (newval, duration/(1000*1000*1000));
713 break;
716 default:
717 break;
720 g_hash_table_insert (md->priv->metadata,
721 GINT_TO_POINTER (field),
722 newval);
725 #ifdef HAVE_GSTREAMER_0_8
726 static void
727 rb_metadata_gst_found_tag (GObject *pipeline, GstElement *source, GstTagList *tags, RBMetaData *md)
729 gst_tag_list_foreach (tags, (GstTagForeachFunc) rb_metadata_gst_load_tag, md);
732 static void
733 rb_metadata_gst_fakesink_handoff_cb (GstElement *fakesink, GstBuffer *buf, GstPad *pad, RBMetaData *md)
735 if (md->priv->handoff) {
736 /* we get lots of these with decodebin. why? */
737 /* rb_debug ("caught recursive handoff!"); */
738 return;
739 } else if (md->priv->eos) {
740 rb_debug ("caught handoff after eos!");
741 return;
744 rb_debug ("in fakesink handoff");
745 md->priv->handoff = TRUE;
748 static void
749 rb_metadata_gst_error_cb (GstElement *element,
750 GstElement *source,
751 GError *error,
752 gchar *debug,
753 RBMetaData *md)
755 if (error->domain == GST_STREAM_ERROR &&
756 error->code == GST_STREAM_ERROR_TYPE_NOT_FOUND) {
757 rb_debug ("caught type not found error");
759 } else {
760 rb_debug ("caught error: %s ", error->message);
761 md->priv->error = g_error_new_literal (RB_METADATA_ERROR,
762 RB_METADATA_ERROR_GENERAL,
763 error->message);
767 static void
768 rb_metadata_gst_eos_cb (GstElement *element, RBMetaData *md)
770 rb_debug ("caught eos");
771 if (md->priv->eos) {
772 rb_debug ("RENTERED EOS!");
773 return;
775 gst_element_set_state (md->priv->sink, GST_STATE_NULL);
776 md->priv->eos = TRUE;
779 #endif
781 static void
782 rb_metadata_gst_typefind_cb (GstElement *typefind, guint probability, GstCaps *caps, RBMetaData *md)
784 if (!(gst_caps_is_empty (caps) || gst_caps_is_any (caps))) {
785 g_free (md->priv->type);
786 md->priv->type = g_strdup (gst_structure_get_name (gst_caps_get_structure (caps, 0)));
787 rb_debug ("found type %s", md->priv->type);
790 g_signal_handler_disconnect (typefind, md->priv->typefind_cb_id);
793 static void
794 rb_metadata_gst_new_decoded_pad_cb (GstElement *decodebin, GstPad *pad, gboolean last, RBMetaData *md)
796 GstCaps *caps;
797 GstStructure *structure;
798 const gchar *mimetype;
799 gboolean cancel = FALSE;
801 caps = gst_pad_get_caps (pad);
803 /* we get "ANY" caps for text/plain files etc. */
804 if (gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
805 rb_debug ("decoded pad with no caps or any caps. this file is boring.");
806 md->priv->non_audio = TRUE;
807 cancel = TRUE;
808 } else {
809 GstPad *sink_pad;
811 sink_pad = gst_element_get_pad (md->priv->sink, "sink");
812 gst_pad_link (pad, sink_pad);
814 /* is this pad audio? */
815 structure = gst_caps_get_structure (caps, 0);
816 mimetype = gst_structure_get_name (structure);
818 if (g_str_has_prefix (mimetype, "audio/x-raw")) {
819 rb_debug ("got decoded audio pad of type %s", mimetype);
820 } else if (g_str_has_prefix (mimetype, "video/")) {
821 rb_debug ("got decoded video pad of type %s", mimetype);
822 md->priv->non_audio = TRUE;
823 md->priv->has_video = TRUE;
824 } else {
825 /* assume anything we can get a video or text stream out of is
826 * something that should be fed to totem rather than rhythmbox.
828 rb_debug ("got decoded pad of non-audio type %s", mimetype);
829 md->priv->non_audio = TRUE;
833 gst_caps_unref (caps);
835 #ifdef HAVE_GSTREAMER_0_10
836 /* if this is non-audio, cancel the operation
837 * under 0.8 this causes assertion faliures when a pad with no caps in found
838 * it isn't /needed/ under 0.8, so we don't do it.
840 * This seems to cause some deadlocks with video files, so only do it
841 * when we get no/any caps.
843 if (cancel)
844 gst_element_set_state (md->priv->pipeline, GST_STATE_NULL);
845 #endif
848 static void
849 rb_metadata_gst_unknown_type_cb (GstElement *decodebin, GstPad *pad, GstCaps *caps, RBMetaData *md)
851 /* try to shortcut it a bit */
852 md->priv->non_audio = TRUE;
854 if (!gst_caps_is_empty (caps) && !gst_caps_is_any (caps)) {
855 GstStructure *structure;
856 const gchar *mimetype;
858 structure = gst_caps_get_structure (caps, 0);
859 mimetype = gst_structure_get_name (structure);
861 g_free (md->priv->type);
862 md->priv->type = g_strdup (mimetype);
864 rb_debug ("decodebin emitted unknown type signal for %s", mimetype);
865 } else {
866 rb_debug ("decodebin emitted unknown type signal");
869 #ifdef HAVE_GSTREAMER_0_10
871 char *msg;
873 msg = make_undecodable_error (md);
874 GST_ELEMENT_ERROR (md->priv->pipeline, STREAM, CODEC_NOT_FOUND, ("%s", msg), (NULL));
875 g_free (msg);
877 #endif
880 static GstElement *make_pipeline_element (GstElement *pipeline, const char *element, GError **error)
882 GstElement *elem = gst_element_factory_make (element, element);
883 if (elem == NULL) {
884 g_set_error (error,
885 RB_METADATA_ERROR,
886 RB_METADATA_ERROR_MISSING_PLUGIN,
887 _("Failed to create %s element; check your installation"),
888 element);
889 return NULL;
892 gst_bin_add (GST_BIN (pipeline), elem);
893 return elem;
896 #ifdef HAVE_GSTREAMER_0_10
897 static gboolean
898 rb_metadata_bus_handler (GstBus *bus, GstMessage *message, RBMetaData *md)
900 switch (GST_MESSAGE_TYPE (message)) {
901 case GST_MESSAGE_EOS:
902 rb_debug ("EOS reached");
903 md->priv->eos = TRUE;
904 break;
905 case GST_MESSAGE_ERROR:
907 GError *gerror;
908 gchar *debug;
910 gst_message_parse_error (message, &gerror, &debug);
911 if (gerror->domain == GST_STREAM_ERROR &&
912 gerror->code == GST_STREAM_ERROR_TYPE_NOT_FOUND) {
913 rb_debug ("caught type not found error");
914 } else if (md->priv->error) {
915 rb_debug ("caught error: %s, but we've already got one", gerror->message);
916 } else {
917 rb_debug ("caught error: %s ", gerror->message);
919 g_clear_error (&md->priv->error);
920 md->priv->error = g_error_new_literal (RB_METADATA_ERROR,
921 RB_METADATA_ERROR_GENERAL,
922 gerror->message);
925 g_error_free (gerror);
926 g_free (debug);
927 break;
929 case GST_MESSAGE_TAG:
931 GstTagList *tags;
933 gst_message_parse_tag (message, &tags);
934 if (tags) {
935 gst_tag_list_foreach (tags, (GstTagForeachFunc) rb_metadata_gst_load_tag, md);
936 gst_tag_list_free (tags);
937 } else {
938 const gchar *errmsg = "Could not retrieve tag list";
940 rb_debug ("caught error: %s ", errmsg);
941 md->priv->error = g_error_new_literal (RB_METADATA_ERROR,
942 RB_METADATA_ERROR_GENERAL,
943 errmsg);
945 break;
947 default:
948 rb_debug ("message of type %d", GST_MESSAGE_TYPE (message));
949 break;
952 return FALSE;
955 static void
956 rb_metadata_event_loop (RBMetaData *md, GstElement *element, gboolean block)
958 GstBus *bus;
959 gboolean done = FALSE;
961 bus = gst_element_get_bus (element);
962 g_return_if_fail (bus != NULL);
964 while (!done && !md->priv->eos) {
965 GstMessage *message;
967 if (block)
968 message = gst_bus_poll (bus, GST_MESSAGE_ANY, -1);
969 else
970 message = gst_bus_pop (bus);
972 if (message == NULL) {
973 gst_object_unref (bus);
974 return;
977 done = rb_metadata_bus_handler (bus, message, md);
978 gst_message_unref (message);
980 gst_object_unref (bus);
982 #endif
984 void
985 rb_metadata_load (RBMetaData *md,
986 const char *uri,
987 GError **error)
989 GstElement *pipeline = NULL;
990 GstElement *urisrc = NULL;
991 GstElement *decodebin = NULL;
992 GstElement *typefind = NULL;
993 gint64 file_size = -1;
994 GstFormat file_size_format = GST_FORMAT_BYTES;
995 #ifdef HAVE_GSTREAMER_0_10
996 GstStateChangeReturn state_ret;
997 int change_timeout;
998 #endif
1000 g_free (md->priv->uri);
1001 md->priv->uri = NULL;
1002 g_free (md->priv->type);
1003 md->priv->type = NULL;
1004 md->priv->error = NULL;
1005 md->priv->eos = FALSE;
1006 md->priv->handoff = FALSE;
1007 md->priv->non_audio = FALSE;
1008 md->priv->has_video = FALSE;
1010 if (md->priv->pipeline) {
1011 gst_object_unref (GST_OBJECT (md->priv->pipeline));
1012 md->priv->pipeline = NULL;
1015 if (uri == NULL)
1016 return;
1018 rb_debug ("loading metadata for uri: %s", uri);
1019 md->priv->uri = g_strdup (uri);
1021 if (md->priv->metadata)
1022 g_hash_table_destroy (md->priv->metadata);
1023 md->priv->metadata = g_hash_table_new_full (g_direct_hash, g_direct_equal,
1024 NULL, (GDestroyNotify) free_gvalue);
1026 /* The main tagfinding pipeline looks like this:
1027 * gnomevfssrc ! decodebin ! fakesink
1028 * or
1029 * filesrc ! decodebin ! fakesink
1031 * but we can only link the fakesink in when the decodebin
1032 * creates an audio source pad. we do this in the 'new-decoded-pad'
1033 * signal handler.
1035 pipeline = gst_pipeline_new ("pipeline");
1037 urisrc = gst_element_make_from_uri (GST_URI_SRC, uri, "urisrc");
1038 if (urisrc == NULL) {
1039 g_set_error (error,
1040 RB_METADATA_ERROR,
1041 RB_METADATA_ERROR_MISSING_PLUGIN,
1042 _("Failed to create a source element; check your installation"));
1043 rb_debug ("missing an element to load the uri, sadly");
1044 goto out;
1046 gst_bin_add (GST_BIN (pipeline), urisrc);
1048 decodebin = make_pipeline_element (pipeline, "decodebin", error);
1049 md->priv->sink = make_pipeline_element (pipeline, "fakesink", error);
1050 if (!(urisrc && decodebin && md->priv->sink)) {
1051 rb_debug ("missing an element, sadly");
1052 goto out;
1055 #ifdef HAVE_GSTREAMER_0_8
1056 g_object_set (G_OBJECT (md->priv->sink), "signal-handoffs", TRUE, NULL);
1058 g_signal_connect_object (pipeline, "error", G_CALLBACK (rb_metadata_gst_error_cb), md, 0);
1059 g_signal_connect_object (pipeline, "found-tag", G_CALLBACK (rb_metadata_gst_found_tag), md, 0);
1061 g_signal_connect_object (md->priv->sink, "handoff", G_CALLBACK (rb_metadata_gst_fakesink_handoff_cb), md, 0);
1062 g_signal_connect_object (md->priv->sink, "eos", G_CALLBACK (rb_metadata_gst_eos_cb), md, 0);
1063 #endif
1064 g_signal_connect_object (decodebin, "new-decoded-pad", G_CALLBACK (rb_metadata_gst_new_decoded_pad_cb), md, 0);
1065 g_signal_connect_object (decodebin, "unknown-type", G_CALLBACK (rb_metadata_gst_unknown_type_cb), md, 0);
1067 /* locate the decodebin's typefind, so we can get the have_type signal too.
1068 * this is kind of nasty, since it relies on an essentially arbitrary string
1069 * in the decodebin code not changing. the alternative is to have our own
1070 * typefind instance before the decodebin. it might not like that.
1072 typefind = gst_bin_get_by_name (GST_BIN (decodebin), "typefind");
1073 g_assert (typefind != NULL);
1074 md->priv->typefind_cb_id = g_signal_connect_object (typefind,
1075 "have_type",
1076 G_CALLBACK (rb_metadata_gst_typefind_cb),
1079 gst_object_unref (GST_OBJECT (typefind));
1081 gst_element_link (urisrc, decodebin);
1083 md->priv->pipeline = pipeline;
1084 #ifdef HAVE_GSTREAMER_0_8
1085 gst_element_set_state (pipeline, GST_STATE_PLAYING);
1086 while (gst_bin_iterate (GST_BIN (pipeline))
1087 && md->priv->error == NULL
1088 && !md->priv->handoff
1089 && !md->priv->eos)
1092 if (md->priv->handoff) {
1093 #endif
1094 #ifdef HAVE_GSTREAMER_0_10
1095 rb_debug ("going to PAUSED for metadata, uri: %s", uri);
1096 state_ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
1097 change_timeout = 5;
1098 while (state_ret == GST_STATE_CHANGE_ASYNC &&
1099 !md->priv->eos &&
1100 !md->priv->non_audio &&
1101 change_timeout > 0) {
1102 GstState state;
1103 rb_debug ("element state changing asynchronously: %d, %d", state_ret, state);
1104 state_ret = gst_element_get_state (GST_ELEMENT (pipeline),
1105 &state, NULL, 1 * GST_SECOND);
1106 change_timeout--;
1108 if (state_ret != GST_STATE_CHANGE_SUCCESS) {
1109 rb_debug ("failed to go to PAUSED for %s", uri);
1110 rb_metadata_event_loop (md, GST_ELEMENT (pipeline), FALSE);
1111 if (!md->priv->non_audio && md->priv->error == NULL)
1112 g_set_error (error,
1113 RB_METADATA_ERROR,
1114 RB_METADATA_ERROR_INTERNAL,
1115 _("GStreamer error: failed to change state"));
1116 } else
1117 rb_debug ("gone to PAUSED for %s", uri);
1119 if (state_ret == GST_STATE_CHANGE_SUCCESS) {
1120 /* Post application specific message so we'll know when to stop
1121 * the message loop */
1122 GstBus *bus;
1123 bus = gst_element_get_bus (GST_ELEMENT (pipeline));
1124 if (bus) {
1125 gst_bus_post (bus,
1126 gst_message_new_application (GST_OBJECT (pipeline), NULL));
1127 gst_object_unref (bus);
1130 /* Poll the bus for messages */
1131 rb_metadata_event_loop (md, GST_ELEMENT (pipeline), FALSE);
1132 #endif
1133 /* We caught the first buffer(0.8) or went to PAUSED (0.10),
1134 * which means the decoder should have read all
1135 * of the metadata, and should know the length now.
1137 if (g_hash_table_lookup (md->priv->metadata, GINT_TO_POINTER (RB_METADATA_FIELD_DURATION)) == NULL) {
1138 GstFormat format = GST_FORMAT_TIME;
1139 gint64 length;
1140 GValue *newval;
1142 #ifdef HAVE_GSTREAMER_0_8
1143 if (gst_element_query (md->priv->sink, GST_QUERY_TOTAL, &format, &length)) {
1144 #endif
1145 #ifdef HAVE_GSTREAMER_0_10
1146 if (gst_element_query_duration (md->priv->sink, &format, &length)) {
1147 g_assert (format == GST_FORMAT_TIME);
1148 #endif
1149 newval = g_new0 (GValue, 1);
1151 rb_debug ("duration query succeeded");
1153 g_value_init (newval, G_TYPE_ULONG);
1154 /* FIXME - use guint64 for duration? */
1155 g_value_set_ulong (newval, (long) (length / GST_SECOND));
1156 g_hash_table_insert (md->priv->metadata, GINT_TO_POINTER (RB_METADATA_FIELD_DURATION),
1157 newval);
1158 } else {
1159 rb_debug ("duration query failed!");
1163 #ifdef HAVE_GSTREAMER_0_8
1164 else if (md->priv->eos) {
1165 g_warning ("caught eos without handoff!");
1167 #endif
1169 /* Get the file size, since it might be interesting, and return
1170 * the pipeline to NULL state.
1172 #ifdef HAVE_GSTREAMER_0_8
1173 gst_element_query (urisrc, GST_QUERY_TOTAL, &file_size_format, &file_size);
1174 gst_element_set_state (pipeline, GST_STATE_NULL);
1175 #endif
1176 #ifdef HAVE_GSTREAMER_0_10
1177 if (gst_element_query_duration (urisrc, &file_size_format, &file_size))
1178 g_assert (file_size_format == GST_FORMAT_BYTES);
1180 state_ret = gst_element_set_state (pipeline, GST_STATE_NULL);
1181 if (state_ret == GST_STATE_CHANGE_ASYNC) {
1182 g_warning ("Failed to return metadata reader to NULL state");
1184 md->priv->handoff = (state_ret == GST_STATE_CHANGE_SUCCESS);
1185 #endif
1187 /* report errors for various failure cases.
1188 * these don't include the URI as the import errors source
1189 * already displays it.
1191 if (md->priv->non_audio || !md->priv->handoff) {
1192 gboolean ignore = FALSE;
1193 int i;
1195 if (md->priv->has_video) {
1196 ignore = TRUE;
1197 } else {
1198 for (i = 0; i < G_N_ELEMENTS (ignore_mime_types); i++) {
1199 if (g_str_has_prefix (md->priv->type, ignore_mime_types[i])) {
1200 ignore = TRUE;
1201 break;
1206 if (!ignore) {
1207 char *msg = make_undecodable_error (md);
1208 g_set_error (error,
1209 RB_METADATA_ERROR,
1210 RB_METADATA_ERROR_NOT_AUDIO,
1211 "%s", msg);
1212 g_free (msg);
1213 } else {
1214 /* we don't need an error message here (it'll never be
1215 * displayed). using NULL causes crashes with some C
1216 * libraries, and gcc doesn't like zero-length format
1217 * strings, so we use a single space instead.
1219 g_set_error (error,
1220 RB_METADATA_ERROR,
1221 RB_METADATA_ERROR_NOT_AUDIO_IGNORE,
1222 " ");
1224 } else if (md->priv->error) {
1225 g_propagate_error (error, md->priv->error);
1226 } else if (!md->priv->type) {
1227 /* ignore really small files that can't be identified */
1228 gint error_code = RB_METADATA_ERROR_UNRECOGNIZED;
1229 if (file_size > 0 && file_size < REALLY_SMALL_FILE_SIZE) {
1230 rb_debug ("ignoring %s because it's too small to care about", md->priv->uri);
1231 error_code = RB_METADATA_ERROR_NOT_AUDIO_IGNORE;
1234 g_clear_error (error);
1235 g_set_error (error,
1236 RB_METADATA_ERROR,
1237 error_code,
1238 _("The MIME type of the file could not be identified"));
1239 } else {
1240 /* yay, it worked */
1241 rb_debug ("successfully read metadata for %s", uri);
1243 /* it doesn't matter if we don't recognise the format,
1244 * as long as gstreamer can play it, we can put it in
1245 * the library.
1247 if (!rb_metadata_gst_type_to_name (md, md->priv->type)) {
1248 rb_debug ("we don't know what type %s (from file %s) is, but we'll play it anyway",
1249 md->priv->type, uri);
1253 out:
1254 if (pipeline != NULL)
1255 gst_object_unref (GST_OBJECT (pipeline));
1256 md->priv->pipeline = NULL;
1259 gboolean
1260 rb_metadata_can_save (RBMetaData *md, const char *mimetype)
1262 #ifdef ENABLE_TAG_WRITING
1263 return rb_metadata_gst_type_to_tag_function (md, mimetype) != NULL;
1264 #else
1265 return FALSE;
1266 #endif
1269 static void
1270 rb_metadata_gst_add_tag_data (gpointer key, const GValue *val, RBMetaData *md)
1272 RBMetaDataField field = GPOINTER_TO_INT (key);
1273 const char *tag = rb_metadata_gst_field_to_gst_tag (field);
1275 /* don't write this out */
1276 if (field == RB_METADATA_FIELD_DURATION)
1277 return;
1279 if (tag) {
1280 if (field == RB_METADATA_FIELD_DATE && g_value_get_ulong (val) == 0) {
1281 /* we should ask gstreamer to remove the tag,
1282 * but there is no easy way of doing so
1284 } else {
1285 GValue newval = {0,};
1287 g_value_init (&newval, gst_tag_get_type (tag));
1288 if (g_value_transform (val, &newval)) {
1289 rb_debug("Setting %s",tag);
1291 gst_tag_list_add_values (md->priv->tags,
1292 GST_TAG_MERGE_APPEND,
1293 tag, &newval,
1294 NULL);
1296 g_value_unset (&newval);
1301 static gboolean
1302 rb_metadata_file_valid (char *original, char *newfile)
1304 RBMetaData *md = rb_metadata_new ();
1305 GError *error = NULL;
1306 gboolean ret;
1308 rb_metadata_load (md, newfile, &error);
1309 ret = (error == NULL);
1311 /* TODO: check that the tags are correct? */
1313 if (error != NULL)
1314 g_error_free (error);
1315 g_object_unref (G_OBJECT (md));
1316 return ret;
1319 void
1320 rb_metadata_save (RBMetaData *md, GError **error)
1322 GstElement *pipeline = NULL;
1323 GstElement *gnomevfssrc = NULL;
1324 GstElement *retag_end = NULL; /* the last elemet after retagging subpipeline */
1325 const char *plugin_name = NULL;
1326 char *tmpname = NULL;
1327 GnomeVFSHandle *handle = NULL;
1328 GnomeVFSResult result;
1329 RBAddTaggerElem add_tagger_func;
1331 g_return_if_fail (md->priv->uri != NULL);
1332 g_return_if_fail (md->priv->type != NULL);
1334 rb_debug ("saving metadata for uri: %s", md->priv->uri);
1336 if ((result = rb_uri_mkstemp (md->priv->uri, &tmpname, &handle)) != GNOME_VFS_OK)
1337 goto vfs_error;
1339 pipeline = gst_pipeline_new ("pipeline");
1340 md->priv->pipeline = pipeline;
1342 #ifdef HAVE_GSTREAMER_0_8
1343 g_signal_connect_object (pipeline, "error", G_CALLBACK (rb_metadata_gst_error_cb), md, 0);
1344 #endif
1346 /* Source */
1347 plugin_name = "gnomevfssrc";
1348 if (!(gnomevfssrc = gst_element_factory_make (plugin_name, plugin_name)))
1349 goto missing_plugin;
1350 gst_bin_add (GST_BIN (pipeline), gnomevfssrc);
1351 g_object_set (G_OBJECT (gnomevfssrc), "location", md->priv->uri, NULL);
1353 /* Sink */
1354 plugin_name = "gnomevfssink";
1355 if (!(md->priv->sink = gst_element_factory_make (plugin_name, plugin_name)))
1356 goto missing_plugin;
1358 g_object_set (G_OBJECT (md->priv->sink), "handle", handle, NULL);
1359 #if HAVE_GSTREAMER_0_8
1360 g_signal_connect_object (md->priv->sink, "eos", G_CALLBACK (rb_metadata_gst_eos_cb), md, 0);
1361 #endif
1363 md->priv->tags = gst_tag_list_new ();
1364 g_hash_table_foreach (md->priv->metadata,
1365 (GHFunc) rb_metadata_gst_add_tag_data,
1366 md);
1368 /* Tagger element(s) */
1369 add_tagger_func = rb_metadata_gst_type_to_tag_function (md, md->priv->type);
1371 if (!add_tagger_func) {
1372 g_set_error (error,
1373 RB_METADATA_ERROR,
1374 RB_METADATA_ERROR_UNSUPPORTED,
1375 _("Unsupported file type: %s"), md->priv->type);
1376 goto out_error;
1379 retag_end = add_tagger_func (md, gnomevfssrc);
1380 if (!retag_end) {
1381 g_set_error (error,
1382 RB_METADATA_ERROR,
1383 RB_METADATA_ERROR_UNSUPPORTED,
1384 _("Unable to create tag-writing elements"));
1385 goto out_error;
1388 gst_bin_add (GST_BIN (pipeline), md->priv->sink);
1389 gst_element_link_many (retag_end, md->priv->sink, NULL);
1391 gst_element_set_state (pipeline, GST_STATE_PLAYING);
1393 #ifdef HAVE_GSTREAMER_0_8
1394 while (gst_bin_iterate (GST_BIN (pipeline))
1395 && md->priv->error == NULL
1396 && !md->priv->eos)
1398 gst_element_set_state (pipeline, GST_STATE_NULL);
1399 #endif
1400 #ifdef HAVE_GSTREAMER_0_10
1401 rb_metadata_event_loop (md, GST_ELEMENT (pipeline), TRUE);
1402 if (gst_element_set_state (pipeline, GST_STATE_NULL) == GST_STATE_CHANGE_ASYNC) {
1403 if (gst_element_get_state (pipeline, NULL, NULL, 3 * GST_SECOND) != GST_STATE_CHANGE_SUCCESS) {
1404 g_set_error (error,
1405 RB_METADATA_ERROR,
1406 RB_METADATA_ERROR_INTERNAL,
1407 _("Timeout while setting pipeline to NULL"));
1408 goto out_error;
1411 #endif
1413 if (md->priv->error) {
1414 g_propagate_error (error, md->priv->error);
1415 goto out_error;
1417 if (handle != NULL) {
1418 if ((result = gnome_vfs_close (handle)) != GNOME_VFS_OK)
1419 goto vfs_error;
1420 handle = NULL;
1422 /* check to ensure the file isn't corrupt */
1423 if (!rb_metadata_file_valid (md->priv->uri, tmpname)) {
1424 g_set_error (error,
1425 RB_METADATA_ERROR,
1426 RB_METADATA_ERROR_INTERNAL,
1427 _("File corrupted during write"));
1428 goto out_error;
1431 if ((result = gnome_vfs_move (tmpname, md->priv->uri, TRUE)) != GNOME_VFS_OK)
1432 goto vfs_error;
1435 goto out;
1436 vfs_error:
1437 g_set_error (error,
1438 RB_METADATA_ERROR,
1439 RB_METADATA_ERROR_GNOMEVFS,
1440 "%s",
1441 gnome_vfs_result_to_string (result));
1442 goto out_error;
1443 missing_plugin:
1444 g_set_error (error,
1445 RB_METADATA_ERROR,
1446 RB_METADATA_ERROR_MISSING_PLUGIN,
1447 _("Failed to create %s element; check your installation"),
1448 plugin_name);
1449 out_error:
1450 if (handle != NULL)
1451 gnome_vfs_close (handle);
1452 if (tmpname != NULL)
1453 gnome_vfs_unlink (tmpname);
1455 out:
1456 if (md->priv->tags)
1457 gst_tag_list_free (md->priv->tags);
1458 md->priv->tags = NULL;
1460 if (pipeline != NULL)
1461 gst_object_unref (GST_OBJECT (pipeline));
1462 md->priv->pipeline = NULL;
1465 gboolean
1466 rb_metadata_get (RBMetaData *md, RBMetaDataField field,
1467 GValue *ret)
1469 GValue *val;
1470 if ((val = g_hash_table_lookup (md->priv->metadata,
1471 GINT_TO_POINTER (field)))) {
1472 g_value_init (ret, G_VALUE_TYPE (val));
1473 g_value_copy (val, ret);
1474 return TRUE;
1476 return FALSE;
1479 const char *
1480 rb_metadata_get_mime (RBMetaData *md)
1482 return md->priv->type;
1485 gboolean
1486 rb_metadata_set (RBMetaData *md, RBMetaDataField field,
1487 const GValue *val)
1489 GValue *newval;
1490 GType type;
1492 if (rb_metadata_gst_field_to_gst_tag (field) == NULL) {
1493 return FALSE;
1496 type = rb_metadata_get_field_type (field);
1497 g_return_val_if_fail (type == G_VALUE_TYPE (val), FALSE);
1499 newval = g_new0 (GValue, 1);
1500 g_value_init (newval, type);
1501 g_value_copy (val, newval);
1503 g_hash_table_insert (md->priv->metadata, GINT_TO_POINTER (field),
1504 newval);
1505 return TRUE;