2006-09-24 James Livingston <doclivingston@gmail.com>
[rhythmbox.git] / backends / gstreamer / rb-encoder-gst.c
bloba366b5de187bddfbee5ed0141f7ac1a892c76a46
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * arch-tag: Implementation of GStreamer encoding backend
5 * Based on Sound-Juicer's ripping code
7 * Copyright (C) 2003 Ross Burton <ross@burtonini.com>
8 * Copyright (C) 2006 James Livingston <jrl@ids.org.au>
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of the
13 * License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * You should have received a copy of the GNU General Public
21 * License along with this program; if not, write to the
22 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
23 * Boston, MA 02110-1301 USA.
26 #include <config.h>
28 #include <glib/gi18n.h>
29 #include <libgnomevfs/gnome-vfs-utils.h>
30 #include <libgnomevfs/gnome-vfs-uri.h>
31 #include <libgnomevfs/gnome-vfs-ops.h>
32 #include <gst/gst.h>
33 #include <string.h>
34 #include <profiles/gnome-media-profiles.h>
35 #include <gtk/gtk.h>
37 #ifdef HAVE_GSTREAMER_0_10
38 #include <gst/tag/tag.h>
39 #endif
41 #include "rhythmdb.h"
42 #include "eel-gconf-extensions.h"
43 #include "rb-preferences.h"
44 #include "rb-encoder.h"
45 #include "rb-encoder-gst.h"
46 #include "rb-debug.h"
48 #ifdef HAVE_GSTREAMER_0_8
49 #define GstStateChangeReturn GstElementStateReturn
50 #define GstState GstElementState
51 #define GST_STATE_CHANGE_FAILURE GST_STATE_FAILURE
52 #define gst_caps_unref gst_caps_free
53 #endif
55 static void rb_encoder_gst_class_init (RBEncoderGstClass *klass);
56 static void rb_encoder_gst_init (RBEncoderGst *encoder);
57 static void rb_encoder_gst_finalize (GObject *object);
58 static void rb_encoder_init (RBEncoderIface *iface);
60 struct _RBEncoderGstPrivate {
61 GstElement *enc;
62 GstElement *pipeline;
64 gboolean transcoding;
65 gint decoded_pads;
67 gboolean error_emitted;
68 gboolean completion_emitted;
70 gulong total_length;
71 guint progress_id;
74 G_DEFINE_TYPE_WITH_CODE(RBEncoderGst, rb_encoder_gst, G_TYPE_OBJECT,
75 G_IMPLEMENT_INTERFACE(RB_TYPE_ENCODER,
76 rb_encoder_init))
77 #define RB_ENCODER_GST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_ENCODER_GST, RBEncoderGstPrivate))
79 static gboolean rb_encoder_gst_encode (RBEncoder *encoder,
80 RhythmDBEntry *entry,
81 const char *dest,
82 const char *mime_type);
83 static void rb_encoder_gst_cancel (RBEncoder *encoder);
84 static void rb_encoder_gst_emit_completed (RBEncoderGst *encoder);
86 static void
87 rb_encoder_gst_class_init (RBEncoderGstClass *klass)
89 GObjectClass *object_class = (GObjectClass *) klass;
91 object_class->finalize = rb_encoder_gst_finalize;
93 g_type_class_add_private (klass, sizeof (RBEncoderGstPrivate));
96 static void
97 rb_encoder_gst_init (RBEncoderGst *encoder)
99 encoder->priv = RB_ENCODER_GST_GET_PRIVATE (encoder);
102 static void
103 rb_encoder_init (RBEncoderIface *iface)
105 iface->encode = rb_encoder_gst_encode;
106 iface->cancel = rb_encoder_gst_cancel;
109 static void
110 rb_encoder_gst_finalize (GObject *object)
112 RBEncoderGst *encoder = RB_ENCODER_GST (object);
114 if (encoder->priv->progress_id != 0)
115 g_source_remove (encoder->priv->progress_id);
117 if (encoder->priv->pipeline) {
118 gst_element_set_state (encoder->priv->pipeline, GST_STATE_NULL);
119 g_object_unref (encoder->priv->pipeline);
120 encoder->priv->pipeline = NULL;
123 G_OBJECT_CLASS (rb_encoder_gst_parent_class)->finalize (object);
126 RBEncoder*
127 rb_encoder_gst_new (void)
129 return RB_ENCODER (g_object_new (RB_TYPE_ENCODER_GST, NULL));
132 static void
133 rb_encoder_gst_emit_error (RBEncoderGst *encoder, GError *error)
135 encoder->priv->error_emitted = TRUE;
136 _rb_encoder_emit_error (RB_ENCODER (encoder), error);
139 static void
140 rb_encoder_gst_emit_completed (RBEncoderGst *encoder)
142 GError *error = NULL;
144 g_return_if_fail (encoder->priv->completion_emitted == FALSE);
146 if (encoder->priv->progress_id != 0)
147 g_source_remove (encoder->priv->progress_id);
149 /* emit an error if no audio pad has been found and it wasn't due to an
150 * error */
151 if (encoder->priv->error_emitted == FALSE &&
152 encoder->priv->transcoding &&
153 encoder->priv->decoded_pads == 0) {
154 rb_debug ("received EOS and no decoded pad");
155 g_set_error (&error,
156 RB_ENCODER_ERROR,
157 RB_ENCODER_ERROR_FORMAT_UNSUPPORTED,
158 "no decodable audio pad found");
160 rb_encoder_gst_emit_error (encoder, error);
161 g_error_free (error);
164 encoder->priv->completion_emitted = TRUE;
165 _rb_encoder_emit_completed (RB_ENCODER (encoder));
168 #ifdef HAVE_GSTREAMER_0_10
169 static gboolean
170 bus_watch_cb (GstBus *bus, GstMessage *message, gpointer data)
172 RBEncoderGst *encoder = RB_ENCODER_GST (data);
173 char *string;
174 GError *error = NULL;
176 /* ref ourselves, in case one of the signal handler unrefs us */
177 g_object_ref (G_OBJECT (encoder));
179 switch (GST_MESSAGE_TYPE (message)) {
180 case GST_MESSAGE_ERROR:
181 gst_message_parse_error (message, &error, &string);
182 rb_encoder_gst_emit_error (encoder, error);
183 rb_debug ("received error %s", string);
184 g_error_free (error);
185 g_free (string);
187 rb_encoder_cancel (RB_ENCODER (encoder));
188 break;
190 case GST_MESSAGE_WARNING:
191 gst_message_parse_warning (message, &error, &string);
192 rb_debug ("received warning %s", string);
193 g_error_free (error);
194 g_free (string);
195 break;
197 case GST_MESSAGE_EOS:
198 rb_debug ("received EOS");
200 gst_element_set_state (encoder->priv->pipeline, GST_STATE_NULL);
202 rb_encoder_gst_emit_completed (encoder);
204 g_object_unref (encoder->priv->pipeline);
205 encoder->priv->pipeline = NULL;
206 break;
208 default:
209 rb_debug ("message of type %s", gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
210 break;
213 g_object_unref (G_OBJECT (encoder));
214 return TRUE;
216 #endif
218 #ifdef HAVE_GSTREAMER_0_8
219 static void
220 gst_eos_cb (GstElement *element, RBEncoderGst *encoder)
222 rb_debug ("received EOS");
224 gst_element_set_state (encoder->priv->pipeline, GST_STATE_NULL);
226 rb_encoder_gst_emit_completed (encoder);
228 g_object_unref (encoder->priv->pipeline);
229 encoder->priv->pipeline = NULL;
232 static void
233 gst_error_cb (GstElement *element,
234 GstElement *source,
235 GError *error,
236 gchar *debug,
237 RBEncoderGst *encoder)
239 rb_encoder_gst_emit_error (encoder, error);
240 rb_debug ("received error %s", debug);
242 rb_encoder_cancel (RB_ENCODER (encoder));
245 #endif
247 static gboolean
248 progress_timeout_cb (RBEncoderGst *encoder)
250 gint64 nanos;
251 gint secs;
252 static GstFormat format = GST_FORMAT_TIME;
253 GstState state;
255 if (encoder->priv->pipeline == NULL)
256 return FALSE;
258 #ifdef HAVE_GSTREAMER_0_10
259 gst_element_get_state (encoder->priv->pipeline, &state, NULL, GST_CLOCK_TIME_NONE);
260 if (state != GST_STATE_PLAYING)
261 return FALSE;
263 if (!gst_element_query_position (encoder->priv->pipeline, &format, &nanos)) {
264 g_warning ("Could not get current track position");
265 return TRUE;
267 #elif HAVE_GSTREAMER_0_8
268 state = gst_element_get_state (encoder->priv->pipeline);
269 if (state != GST_STATE_PLAYING)
270 return FALSE;
272 if (!gst_element_query (encoder->priv->pipeline, GST_QUERY_POSITION, &format, &nanos)) {
273 g_warning ("Could not get current track position");
274 return TRUE;
276 #endif
278 secs = nanos / GST_SECOND;
279 rb_debug ("encoding progress at %d out of %lu", secs, encoder->priv->total_length);
280 _rb_encoder_emit_progress (RB_ENCODER (encoder), ((double)secs) / encoder->priv->total_length);
282 return TRUE;
285 static gboolean
286 start_pipeline (RBEncoderGst *encoder, GError **error)
288 GstStateChangeReturn result;
289 #ifdef HAVE_GSTREAMER_0_10
290 GstBus *bus;
292 g_return_val_if_fail (encoder->priv->pipeline != NULL, FALSE);
294 bus = gst_pipeline_get_bus (GST_PIPELINE (encoder->priv->pipeline));
295 gst_bus_add_watch (bus, bus_watch_cb, encoder);
297 result = gst_element_set_state (encoder->priv->pipeline, GST_STATE_PLAYING);
298 #elif HAVE_GSTREAMER_0_8
300 g_return_val_if_fail (encoder->priv->pipeline != NULL, FALSE);
302 g_signal_connect_object (G_OBJECT (encoder->priv->pipeline),
303 "error", G_CALLBACK (gst_error_cb),
304 encoder, 0);
305 g_signal_connect_object (G_OBJECT (encoder->priv->pipeline),
306 "eos", G_CALLBACK (gst_eos_cb),
307 encoder, 0);
308 result = gst_element_set_state (encoder->priv->pipeline, GST_STATE_PLAYING);
309 #endif
311 if (result != GST_STATE_CHANGE_FAILURE) {
312 /* start reporting progress */
313 if (encoder->priv->total_length > 0) {
314 _rb_encoder_emit_progress (RB_ENCODER (encoder), 0.0);
315 encoder->priv->progress_id = g_timeout_add (250, (GSourceFunc)progress_timeout_cb, encoder);
316 } else {
317 _rb_encoder_emit_progress (RB_ENCODER (encoder), -1);
321 return (result != GST_STATE_CHANGE_FAILURE);
324 #ifdef HAVE_GSTREAMER_0_8
325 /* this is basically what the function in 0.10 does */
326 static GstPad*
327 rb_gst_bin_find_unconnected_pad (GstBin *bin, GstPadDirection dir)
329 const GList *elements, *el;
331 elements = gst_bin_get_list (GST_BIN (bin));
332 for (el = elements; el != NULL; el = el->next) {
333 GstElement *element = GST_ELEMENT (el->data);
334 const GList *pads, *pl;
336 pads = gst_element_get_pad_list (element);
338 for (pl = pads; pl != NULL; pl = pl->next) {
339 GstPad *pad = GST_PAD (pl->data);
341 if (!GST_PAD_IS_LINKED (pad) && GST_PAD_DIRECTION (pad) == dir)
342 return pad;
346 return NULL;
349 #define gst_bin_find_unconnected_pad rb_gst_bin_find_unconnected_pad
351 const char *GST_ENCODING_PROFILE = "audioscale ! audioconvert ! %s";
352 #elif HAVE_GSTREAMER_0_10
353 const char *GST_ENCODING_PROFILE = "audioresample ! audioconvert ! %s";
354 #endif
356 static GstElement*
357 add_encoding_pipeline (RBEncoderGst *encoder,
358 GMAudioProfile *profile,
359 GError **error)
361 GstElement *queue, *encoding_bin, *queue2;
362 GstPad *pad;
363 char *tmp;
365 queue = gst_element_factory_make ("queue", NULL);
366 if (queue == NULL)
367 return NULL;
368 gst_bin_add (GST_BIN (encoder->priv->pipeline), queue);
370 queue2 = gst_element_factory_make ("queue", NULL);
371 if (queue2 == NULL)
372 return NULL;
373 gst_bin_add (GST_BIN (encoder->priv->pipeline), queue2);
375 /* Nice big buffers... */
376 g_object_set (queue, "max-size-time", 120 * GST_SECOND, NULL);
378 tmp = g_strdup_printf (GST_ENCODING_PROFILE, gm_audio_profile_get_pipeline (profile));
379 encoding_bin = GST_ELEMENT (gst_parse_launch (tmp, error));
380 g_free (tmp);
382 if (encoding_bin == NULL)
383 return NULL;
385 /* find pads and ghost them if necessary */
386 if ((pad = gst_bin_find_unconnected_pad (GST_BIN (encoding_bin), GST_PAD_SRC)))
387 gst_element_add_pad (encoding_bin, gst_ghost_pad_new ("src", pad));
388 if ((pad = gst_bin_find_unconnected_pad (GST_BIN (encoding_bin), GST_PAD_SINK)))
389 gst_element_add_pad (encoding_bin, gst_ghost_pad_new ("sink", pad));
391 gst_bin_add (GST_BIN (encoder->priv->pipeline), encoding_bin);
393 if (gst_element_link_many (queue, encoding_bin, queue2, NULL) == FALSE)
394 return NULL;
396 /* store the first element of the encoding graph. new_decoded_pad_cb
397 * will link to this once a decoded pad is found */
398 encoder->priv->enc = queue;
400 return queue2;
403 static gboolean
404 add_tags_from_entry (RBEncoderGst *encoder,
405 RhythmDBEntry *entry,
406 GError **error)
408 GstTagList *tags;
409 gboolean result = TRUE;
410 gulong day;
412 tags = gst_tag_list_new ();
414 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE_ALL,
415 /* TODO: compute replay-gain */
416 GST_TAG_TITLE, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE),
417 GST_TAG_ARTIST, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST),
418 GST_TAG_TRACK_NUMBER, rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER),
419 GST_TAG_ALBUM_VOLUME_NUMBER, rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DISC_NUMBER),
420 GST_TAG_ALBUM, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM),
421 GST_TAG_GENRE, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_GENRE),
422 GST_TAG_ENCODER, "Rhythmbox",
423 GST_TAG_ENCODER_VERSION, VERSION,
424 NULL);
426 day = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DATE);
428 if (day > 0) {
429 GDate *date;
431 date = g_date_new_julian (day);
432 gst_tag_list_add (tags, GST_TAG_MERGE_APPEND,
433 GST_TAG_DATE, date,
434 NULL);
435 g_date_free (date);
437 #ifdef GST_TAG_MUSICBRAINZ_TRACKID
438 if (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MUSICBRAINZ_TRACKID)) {
439 gst_tag_list_add (tags, GST_TAG_MERGE_APPEND,
440 GST_TAG_MUSICBRAINZ_TRACKID, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MUSICBRAINZ_TRACKID),
441 NULL);
443 #endif
445 #ifdef HAVE_GSTREAMER_0_10
447 GstIterator *iter;
448 gboolean done;
450 iter = gst_bin_iterate_all_by_interface (GST_BIN (encoder->priv->pipeline), GST_TYPE_TAG_SETTER);
451 done = FALSE;
452 while (!done) {
453 GstTagSetter *tagger = NULL;
454 GstTagSetter **tagger_ptr = &tagger;
456 switch (gst_iterator_next (iter, (gpointer*)tagger_ptr)) {
457 case GST_ITERATOR_OK:
458 gst_tag_setter_merge_tags (tagger, tags, GST_TAG_MERGE_REPLACE_ALL);
459 break;
460 case GST_ITERATOR_RESYNC:
461 gst_iterator_resync (iter);
462 break;
463 case GST_ITERATOR_ERROR:
464 g_set_error (error,
465 RB_ENCODER_ERROR, RB_ENCODER_ERROR_INTERNAL,
466 "Could not add tags to tag-setter");
467 result = FALSE;
468 done = TRUE;
469 break;
470 case GST_ITERATOR_DONE:
471 done = TRUE;
472 break;
475 if (tagger)
476 gst_object_unref (tagger);
478 gst_iterator_free (iter);
480 #elif HAVE_GSTREAMER_0_8
482 GstElement *tagger;
484 tagger = gst_bin_get_by_interface (GST_BIN (encoder->priv->pipeline), GST_TYPE_TAG_SETTER);
485 if (tagger)
486 gst_tag_setter_merge (GST_TAG_SETTER (tagger), tags, GST_TAG_MERGE_REPLACE_ALL);
488 #endif
490 gst_tag_list_free (tags);
491 return result;
494 static gboolean
495 gnomevfs_allow_overwrite_cb (GstElement *element, GnomeVFSURI *uri, RBEncoderGst *encoder)
497 GtkWidget *dialog;
498 gint response;
499 char *name;
500 char *display_name;
502 name = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_USER_NAME | GNOME_VFS_URI_HIDE_PASSWORD);
503 display_name = gnome_vfs_format_uri_for_display (name);
505 dialog = gtk_message_dialog_new (NULL, 0,
506 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
507 _("Do you want to overwrite the file \"%s\"?"),
508 display_name);
509 response = gtk_dialog_run (GTK_DIALOG (dialog));
510 gtk_widget_destroy (dialog);
512 g_free (display_name);
513 g_free (name);
515 return (response == GTK_RESPONSE_YES);
518 static void
519 new_decoded_pad_cb (GstElement *decodebin,
520 GstPad *new_pad, gboolean arg1,
521 RBEncoderGst *encoder)
523 GstPad *enc_sinkpad;
524 GstCaps *caps;
525 gchar *caps_string;
527 rb_debug ("new decoded pad");
529 /* transcode only the first audio track. multitrack audio files are not
530 * so common anyway */
531 if (encoder->priv->decoded_pads > 0)
532 return;
534 caps = gst_pad_get_caps (new_pad);
535 caps_string = gst_caps_to_string (caps);
536 gst_caps_unref (caps);
538 /* only process audio data */
539 if (strncmp (caps_string, "audio/", 6) == 0) {
540 encoder->priv->decoded_pads++;
541 enc_sinkpad = gst_element_get_pad (encoder->priv->enc,
542 "sink");
543 if (gst_pad_link (new_pad, enc_sinkpad) != GST_PAD_LINK_OK)
544 rb_debug ("error linking pads");
547 g_free (caps_string);
550 static GstElement *
551 add_decoding_pipeline (RBEncoderGst *encoder,
552 GError **error)
554 GstElement *decodebin;
556 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
558 encoder->priv->transcoding = TRUE;
559 decodebin = gst_element_factory_make ("decodebin", NULL);
560 if (decodebin == NULL) {
561 g_set_error (error,
562 RB_ENCODER_ERROR,
563 RB_ENCODER_ERROR_INTERNAL,
564 "Could not create decodebin");
566 return NULL;
569 gst_bin_add (GST_BIN (encoder->priv->pipeline), decodebin);
571 g_signal_connect_object (decodebin,
572 "new-decoded-pad",
573 G_CALLBACK (new_decoded_pad_cb),
574 encoder, 0);
576 return decodebin;
579 static gboolean
580 attach_output_pipeline (RBEncoderGst *encoder,
581 GstElement *end,
582 const char *dest,
583 GError **error)
585 GstElement *sink;
587 sink = gst_element_make_from_uri (GST_URI_SINK, dest, "sink");
589 /* handle overwriting if we are using gnomevfssink
590 * it would be nice if GST had an interface for sinks with thi, but it doesn't
592 if (g_type_is_a (G_OBJECT_TYPE (sink), g_type_from_name ("GstGnomeVFSSink"))) {
593 g_signal_connect_object (G_OBJECT (sink),
594 "allow-overwrite", G_CALLBACK (gnomevfs_allow_overwrite_cb),
595 encoder, 0);
598 gst_bin_add (GST_BIN (encoder->priv->pipeline), sink);
599 gst_element_link (end, sink);
601 return TRUE;
604 static gboolean
605 encoder_match_mime (GstElement *encoder, const gchar *mime_type)
607 GstPad *srcpad;
608 GstCaps *caps;
609 GstStructure *structure;
610 const gchar *pad_mime;
611 gboolean match;
613 srcpad = gst_element_get_pad (encoder, "src");
614 caps = gst_pad_get_caps (srcpad);
615 structure = gst_caps_get_structure (caps, 0);
616 pad_mime = gst_structure_get_name (structure);
617 match = strcmp (mime_type, pad_mime) == 0;
618 gst_caps_unref (caps);
619 gst_object_unref (GST_OBJECT (srcpad));
621 return match;
624 static GstElement *
625 profile_bin_find_encoder (GstBin *profile_bin)
627 #ifdef HAVE_GSTREAMER_0_10
628 GstElementFactory *factory;
629 GstElement *encoder = NULL;
630 GstIterator *iter;
631 gboolean done = FALSE;
633 iter = gst_bin_iterate_elements (profile_bin);
634 while (!done) {
635 gpointer data;
637 switch (gst_iterator_next (iter, &data)) {
638 case GST_ITERATOR_OK:
639 factory = gst_element_get_factory (GST_ELEMENT (data));
640 if (strcmp (factory->details.klass,
641 "Codec/Encoder/Audio") == 0) {
642 encoder = GST_ELEMENT (data);
643 done = TRUE;
645 break;
646 case GST_ITERATOR_RESYNC:
647 gst_iterator_resync (iter);
648 break;
649 case GST_ITERATOR_ERROR:
650 /* !?? */
651 rb_debug ("iterator error");
652 done = TRUE;
653 break;
654 case GST_ITERATOR_DONE:
655 done = TRUE;
656 break;
659 gst_iterator_free (iter);
661 return encoder;
662 #else
663 return NULL;
664 #endif
667 static GMAudioProfile*
668 get_profile_from_mime_type (const char *mime_type)
670 GList *profiles, *walk;
671 gchar *pipeline_description;
672 GstElement *pipeline;
673 GstElement *encoder;
674 GMAudioProfile *profile;
675 GMAudioProfile *matching_profile = NULL;
676 GError *error = NULL;
678 profiles = gm_audio_profile_get_active_list ();
679 for (walk = profiles; walk; walk = g_list_next (walk)) {
680 profile = (GMAudioProfile *) walk->data;
681 pipeline_description =
682 g_strdup_printf ("fakesrc ! %s ! fakesink",
683 gm_audio_profile_get_pipeline (profile));
684 pipeline = gst_parse_launch (pipeline_description, &error);
685 g_free (pipeline_description);
686 if (error) {
687 g_error_free (error);
688 error = NULL;
689 continue;
692 encoder = profile_bin_find_encoder (GST_BIN (pipeline));
693 if (encoder == NULL) {
694 g_object_unref (pipeline);
695 continue;
698 if (encoder_match_mime (encoder, mime_type)) {
699 matching_profile = profile;
700 gst_object_unref (GST_OBJECT (encoder));
701 gst_object_unref (GST_OBJECT (pipeline));
702 break;
705 gst_object_unref (GST_OBJECT (encoder));
706 gst_object_unref (GST_OBJECT (pipeline));
709 return matching_profile;
712 static GstElement *
713 create_pipeline_and_source (RBEncoderGst *encoder,
714 RhythmDBEntry *entry,
715 GError **error)
717 char *uri;
718 GstElement *src;
720 uri = rhythmdb_entry_get_playback_uri (entry);
721 if (uri == NULL)
722 return NULL;
724 src = gst_element_make_from_uri (GST_URI_SRC, uri, "source");
725 if (src == NULL) {
726 g_set_error (error,
727 RB_ENCODER_ERROR, RB_ENCODER_ERROR_INTERNAL,
728 "could not create source element for '%s'", uri);
729 g_free (uri);
730 return NULL;
733 encoder->priv->pipeline = gst_pipeline_new ("pipeline");
734 gst_bin_add (GST_BIN (encoder->priv->pipeline), src);
736 /* TODO: add progress reporting */
738 g_free (uri);
740 return src;
743 static gboolean
744 copy_track (RBEncoderGst *encoder,
745 RhythmDBEntry *entry,
746 const char *dest,
747 GError **error)
749 /* source ! sink */
750 GstElement *src;
752 g_assert (encoder->priv->pipeline == NULL);
754 src = create_pipeline_and_source (encoder, entry, error);
755 if (src == NULL)
756 return FALSE;
758 if (!attach_output_pipeline (encoder, src, dest, error))
759 return FALSE;
761 if (!start_pipeline (encoder, error))
762 return FALSE;
764 return TRUE;
767 static gboolean
768 extract_track (RBEncoderGst *encoder,
769 RhythmDBEntry *entry,
770 const char *dest,
771 GError **error)
773 /* cdsrc ! encoder ! sink */
774 char *uri;
775 const char *device;
776 const char *profile_name;
777 GMAudioProfile *profile;
778 GstElement *src, *end;
780 g_assert (encoder->priv->pipeline == NULL);
782 profile_name = eel_gconf_get_string (CONF_LIBRARY_PREFERRED_FORMAT);
783 profile = gm_audio_profile_lookup (profile_name);
784 if (profile == NULL) {
785 g_set_error (error,
786 RB_ENCODER_ERROR, RB_ENCODER_ERROR_FORMAT_UNSUPPORTED,
787 "Could not find encoding profile '%s'", profile_name);
788 return FALSE;
791 src = create_pipeline_and_source (encoder, entry, error);
792 if (src == NULL)
793 return FALSE;
795 /* setup cd extraction properties */
796 uri = rhythmdb_entry_get_playback_uri (entry);
797 if (uri == NULL)
798 return FALSE;
800 device = g_utf8_strrchr (uri, -1, '#');
801 g_object_set (G_OBJECT (src),
802 "device", device + 1, /* skip the '#' */
803 "track", rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER),
804 NULL);
805 if (g_object_class_find_property (G_OBJECT_GET_CLASS (src), "paranoia-mode")) {
806 int paranoia_mode;
808 paranoia_mode = 255; /* TODO: make configurable */
809 g_object_set (G_OBJECT (src), "paranoia-mode", paranoia_mode, NULL);
811 g_free (uri);
813 end = add_encoding_pipeline (encoder, profile, error);
814 if (end == NULL)
815 return FALSE;
816 if (gst_element_link (src, encoder->priv->enc) == FALSE)
817 return FALSE;
819 if (!attach_output_pipeline (encoder, end, dest, error))
820 return FALSE;
821 if (!add_tags_from_entry (encoder, entry, error))
822 return FALSE;
823 if (!start_pipeline (encoder, error))
824 return FALSE;
826 return TRUE;
829 static gboolean
830 transcode_track (RBEncoderGst *encoder,
831 RhythmDBEntry *entry,
832 const char *dest,
833 const char *mime_type,
834 GError **error)
836 /* src ! decodebin ! queue ! encoding_profile ! queue ! sink */
837 GMAudioProfile *profile;
838 GstElement *src, *decoder, *end;
840 g_assert (encoder->priv->pipeline == NULL);
842 profile = get_profile_from_mime_type (mime_type);
843 if (profile == NULL) {
844 g_set_error (error,
845 RB_ENCODER_ERROR,
846 RB_ENCODER_ERROR_FORMAT_UNSUPPORTED,
847 "Unable to locate encoding profile for mime-type "
848 "'%s'", mime_type);
849 return FALSE;
850 } else {
851 rb_debug ("selected profile %s",
852 gm_audio_profile_get_name (profile));
855 src = create_pipeline_and_source (encoder, entry, error);
856 if (src == NULL)
857 return FALSE;
859 decoder = add_decoding_pipeline (encoder, error);
860 if (decoder == NULL)
861 return FALSE;
863 if (gst_element_link (src, decoder) == FALSE)
864 return FALSE;
866 end = add_encoding_pipeline (encoder, profile, error);
867 if (end == NULL)
868 return FALSE;
870 if (!attach_output_pipeline (encoder, end, dest, error))
871 return FALSE;
872 if (!add_tags_from_entry (encoder, entry, error))
873 return FALSE;
874 if (!start_pipeline (encoder, error))
875 return FALSE;
877 return TRUE;
880 static GnomeVFSResult
881 create_parent_dirs_uri (GnomeVFSURI *uri)
883 GnomeVFSURI *parent_uri;
884 GnomeVFSResult result;
886 if (gnome_vfs_uri_exists (uri))
887 return GNOME_VFS_OK;
889 parent_uri = gnome_vfs_uri_get_parent (uri);
890 result = create_parent_dirs_uri (parent_uri);
891 gnome_vfs_uri_unref (parent_uri);
892 if (result != GNOME_VFS_OK)
893 return result;
895 return gnome_vfs_make_directory_for_uri (uri, 0750);
898 static GnomeVFSResult
899 create_parent_dirs (const char *uri)
901 GnomeVFSURI *vfs_uri;
902 GnomeVFSURI *parent_uri;
903 GnomeVFSResult result;
905 vfs_uri = gnome_vfs_uri_new (uri);
906 parent_uri = gnome_vfs_uri_get_parent (vfs_uri);
908 result = create_parent_dirs_uri (parent_uri);
910 gnome_vfs_uri_unref (parent_uri);
911 gnome_vfs_uri_unref (vfs_uri);
912 return result;
915 static void
916 rb_encoder_gst_cancel (RBEncoder *encoder)
918 RBEncoderGstPrivate *priv = RB_ENCODER_GST (encoder)->priv;
920 if (priv->pipeline == NULL)
921 return;
923 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
924 g_object_unref (priv->pipeline);
925 priv->pipeline = NULL;
927 rb_encoder_gst_emit_completed (RB_ENCODER_GST (encoder));
930 static gboolean
931 rb_encoder_gst_encode (RBEncoder *encoder,
932 RhythmDBEntry *entry,
933 const char *dest,
934 const char *mime_type)
936 RBEncoderGstPrivate *priv = RB_ENCODER_GST (encoder)->priv;
937 const char *entry_mime_type;
938 gboolean was_raw;
939 gboolean result;
940 GError *error = NULL;
941 GnomeVFSResult vfsresult;
943 g_return_val_if_fail (priv->pipeline == NULL, FALSE);
945 entry_mime_type = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MIMETYPE);
946 was_raw = g_str_has_prefix (entry_mime_type, "audio/x-raw");
948 priv->total_length = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
950 vfsresult = create_parent_dirs (dest);
951 if (vfsresult != GNOME_VFS_OK) {
952 error = g_error_new_literal (RB_ENCODER_ERROR,
953 RB_ENCODER_ERROR_FILE_ACCESS,
954 gnome_vfs_result_to_string (vfsresult));
956 _rb_encoder_emit_error (encoder, error);
957 _rb_encoder_emit_completed (encoder);
958 g_error_free (error);
959 return FALSE;
962 if ((mime_type == NULL && !was_raw) || (mime_type && (strcmp (mime_type, entry_mime_type) == 0))) {
963 result = copy_track (RB_ENCODER_GST (encoder), entry, dest, &error);
964 } else {
965 if (mime_type == NULL) {
966 result = extract_track (RB_ENCODER_GST (encoder), entry, dest, &error);
967 } else {
968 result = transcode_track (RB_ENCODER_GST (encoder), entry, dest, mime_type, &error);
972 if (error) {
973 RBEncoderGst *enc = RB_ENCODER_GST (encoder);
975 rb_encoder_gst_emit_error (enc, error);
976 g_error_free (error);
977 if (enc->priv->pipeline == NULL)
978 rb_encoder_gst_emit_completed (enc);
979 else
980 /* this will unref the pipeline and call emit_completed
982 rb_encoder_gst_cancel (encoder);
985 return result;