hythmbox-2.98/backends/gstreamer/rb-encoder-gst.c

No issues found

  1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
  2 /*
  3  * Copyright (C) 2010 Jonathan Matthew  <jonathan@d14n.org>
  4  *
  5  * This program is free software; you can redistribute it and/or
  6  * modify it under the terms of the GNU General Public License as
  7  * published by the Free Software Foundation; either version 2 of the
  8  * License, or (at your option) any later version.
  9  *
 10  * The Rhythmbox authors hereby grant permission for non-GPL compatible
 11  * GStreamer plugins to be used and distributed together with GStreamer
 12  * and Rhythmbox. This permission is above and beyond the permissions granted
 13  * by the GPL license by which Rhythmbox is covered. If you modify this code
 14  * you may extend this exception to your version of the code, but you are not
 15  * obligated to do so. If you do not wish to do so, delete this exception
 16  * statement from your version.
 17  *
 18  * This program is distributed in the hope that it will be useful,
 19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 21  * General Public License for more details.
 22  *
 23  * You should have received a copy of the GNU General Public
 24  * License along with this program; if not, write to the
 25  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 26  * Boston, MA 02110-1301  USA.
 27  */
 28 
 29 #include <config.h>
 30 
 31 #include <glib/gi18n.h>
 32 #include <gst/gst.h>
 33 #include <gst/tag/tag.h>
 34 #include <string.h>
 35 #include <gtk/gtk.h>
 36 #include <gio/gio.h>
 37 #include <gst/pbutils/missing-plugins.h>
 38 #include <gst/pbutils/encoding-profile.h>
 39 
 40 #include "rhythmdb.h"
 41 #include "rb-encoder.h"
 42 #include "rb-encoder-gst.h"
 43 #include "rb-debug.h"
 44 #include "rb-util.h"
 45 #include "rb-file-helpers.h"
 46 #include "rb-gst-media-types.h"
 47 
 48 static void rb_encoder_gst_init (RBEncoderGst *encoder);
 49 static void rb_encoder_init (RBEncoderIface *iface);
 50 
 51 struct _RBEncoderGstPrivate {
 52 	GstEncodingProfile *profile;
 53 
 54 	GstElement *encodebin;
 55 	GstElement *pipeline;
 56 
 57 	gboolean transcoding;
 58 	gint decoded_pads;
 59 
 60 	gboolean completion_emitted;
 61 	gboolean cancelled;
 62 
 63 	GstFormat position_format;
 64 	gint64 total_length;
 65 	guint progress_id;
 66 	char *dest_uri;
 67 	char *dest_media_type;
 68 
 69 	GOutputStream *outstream;
 70 
 71 	GError *error;
 72 };
 73 
 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 
 78 static void
 79 set_error (RBEncoderGst *encoder, GError *error)
 80 {
 81 	if (encoder->priv->error != NULL) {
 82 		g_warning ("got encoding error %s, but already have one: %s",
 83 			   error->message,
 84 			   encoder->priv->error->message);
 85 		return;
 86 	}
 87 
 88 	/* translate some GStreamer errors into generic ones */
 89 	if (g_error_matches (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NO_SPACE_LEFT)) {
 90 		encoder->priv->error = g_error_new (RB_ENCODER_ERROR, RB_ENCODER_ERROR_OUT_OF_SPACE, "%s", error->message);
 91 	} else if (g_error_matches (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_OPEN_WRITE)) {
 92 		encoder->priv->error = g_error_new (RB_ENCODER_ERROR, RB_ENCODER_ERROR_DEST_READ_ONLY, "%s", error->message);
 93 	} else {
 94 		encoder->priv->error = g_error_copy (error);
 95 	}
 96 }
 97 
 98 static void
 99 rb_encoder_gst_emit_completed (RBEncoderGst *encoder)
100 {
101 	GError *error = NULL;
102 	guint64 dest_size;
103 	GFile *file;
104 	GFileInfo *file_info;
105 
106 	g_return_if_fail (encoder->priv->completion_emitted == FALSE);
107 
108 	if (encoder->priv->progress_id != 0) {
109 		g_source_remove (encoder->priv->progress_id);
110 		encoder->priv->progress_id = 0;
111 	}
112 
113 	/* emit an error if no audio pad has been found
114 	 * and it wasn't due to an error
115 	 */
116 	if (encoder->priv->error == NULL &&
117 	    encoder->priv->transcoding &&
118 	    encoder->priv->decoded_pads == 0) {
119 		rb_debug ("received EOS and no decoded pad");
120 		g_set_error (&error,
121 				RB_ENCODER_ERROR,
122 				RB_ENCODER_ERROR_FORMAT_UNSUPPORTED,
123 				"no decodable audio pad found");
124 
125 		set_error (encoder, error);
126 		g_error_free (error);
127 		error = NULL;
128 	}
129 
130 	/* find the size of the output file, assuming we can get at it with gio */
131 	dest_size = 0;
132 	file = g_file_new_for_uri (encoder->priv->dest_uri);
133 	file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, NULL, &error);
134 	if (error != NULL) {
135 		rb_debug ("couldn't get size of destination %s: %s",
136 			  encoder->priv->dest_uri,
137 			  error->message);
138 		g_clear_error (&error);
139 	} else {
140 		dest_size = g_file_info_get_attribute_uint64 (file_info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
141 		rb_debug ("destination file size: %" G_GUINT64_FORMAT, dest_size);
142 		g_object_unref (file_info);
143 	}
144 	g_object_unref (file);
145 
146 	encoder->priv->completion_emitted = TRUE;
147 	_rb_encoder_emit_completed (RB_ENCODER (encoder), dest_size, encoder->priv->dest_media_type, encoder->priv->error);
148 }
149 
150 static void
151 output_close_cb (GOutputStream *stream, GAsyncResult *result, RBEncoderGst *encoder)
152 {
153 	GError *error = NULL;
154 	rb_debug ("finished closing output stream");
155 	g_output_stream_close_finish (encoder->priv->outstream, result, &error);
156 	if (error != NULL) {
157 		rb_debug ("error closing output stream: %s", error->message);
158 		g_error_free (error);
159 	}
160 
161 	rb_encoder_gst_emit_completed (encoder);
162 
163 	g_object_unref (encoder->priv->outstream);
164 	encoder->priv->outstream = NULL;
165 
166 	g_object_unref (encoder);
167 }
168 
169 static gboolean
170 bus_watch_cb (GstBus *bus, GstMessage *message, gpointer data)
171 {
172 	RBEncoderGst *encoder = RB_ENCODER_GST (data);
173 	char *string;
174 	GError *error = NULL;
175 
176 	/* ref ourselves, in case one of the signal handler unrefs us */
177 	g_object_ref (encoder);
178 
179 	switch (GST_MESSAGE_TYPE (message)) {
180 	case GST_MESSAGE_ERROR:
181 		gst_message_parse_error (message, &error, &string);
182 		set_error (encoder, error);
183 		rb_debug ("received error %s", string);
184 		g_error_free (error);
185 		g_free (string);
186 
187 		rb_encoder_cancel (RB_ENCODER (encoder));
188 		break;
189 
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;
196 
197 	case GST_MESSAGE_EOS:
198 
199 		gst_element_set_state (encoder->priv->pipeline, GST_STATE_NULL);
200 		if (encoder->priv->outstream != NULL) {
201 			rb_debug ("received EOS, closing output stream");
202 			g_output_stream_close_async (encoder->priv->outstream,
203 						     G_PRIORITY_DEFAULT,
204 						     NULL,
205 						     (GAsyncReadyCallback) output_close_cb,
206 						     g_object_ref (encoder));
207 		} else {
208 			rb_debug ("received EOS, but there's no output stream");
209 			rb_encoder_gst_emit_completed (encoder);
210 
211 			g_object_unref (encoder->priv->pipeline);
212 			encoder->priv->pipeline = NULL;
213 		}
214 
215 		break;
216 
217 	default:
218 		rb_debug ("message of type %s", gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
219 		break;
220 	}
221 
222 	g_object_unref (encoder);
223 	return TRUE;
224 }
225 
226 static gboolean
227 progress_timeout_cb (RBEncoderGst *encoder)
228 {
229 	gint64 position;
230 	static GstFormat format;
231 	GstState state;
232 
233 	if (encoder->priv->pipeline == NULL)
234 		return FALSE;
235 
236 	format = encoder->priv->position_format;
237 
238 	gst_element_get_state (encoder->priv->pipeline, &state, NULL, GST_CLOCK_TIME_NONE);
239 	if (state != GST_STATE_PLAYING)
240 		return FALSE;
241 
242 	if (!gst_element_query_position (encoder->priv->pipeline, &format, &position)) {
243 		g_warning ("Could not get current track position");
244 		return TRUE;
245 	}
246 
247 	if (format == GST_FORMAT_TIME) {
248 		gint secs;
249 
250 		secs = position / GST_SECOND;
251 		rb_debug ("encoding progress at %d out of %" G_GINT64_FORMAT,
252 			  secs,
253 			  encoder->priv->total_length);
254 		_rb_encoder_emit_progress (RB_ENCODER (encoder),
255 					   ((double)secs) / encoder->priv->total_length);
256 	} else {
257 		rb_debug ("encoding progress at %" G_GINT64_FORMAT " out of %" G_GINT64_FORMAT,
258 			  position,
259 			  encoder->priv->total_length);
260 		_rb_encoder_emit_progress (RB_ENCODER (encoder),
261 					   ((double) position) / encoder->priv->total_length);
262 	}
263 
264 	return TRUE;
265 }
266 
267 static void
268 start_pipeline (RBEncoderGst *encoder)
269 {
270 	GstStateChangeReturn result;
271 	GstBus *bus;
272 
273 	g_assert (encoder->priv->pipeline != NULL);
274 
275 	bus = gst_pipeline_get_bus (GST_PIPELINE (encoder->priv->pipeline));
276 	gst_bus_add_watch (bus, bus_watch_cb, encoder);
277 
278 	result = gst_element_set_state (encoder->priv->pipeline, GST_STATE_PLAYING);
279 	if (result != GST_STATE_CHANGE_FAILURE) {
280 		/* start reporting progress */
281 		if (encoder->priv->total_length > 0) {
282 			_rb_encoder_emit_progress (RB_ENCODER (encoder), 0.0);
283 			encoder->priv->progress_id = g_timeout_add (250, (GSourceFunc)progress_timeout_cb, encoder);
284 		} else {
285 			_rb_encoder_emit_progress (RB_ENCODER (encoder), -1);
286 		}
287 	}
288 }
289 
290 static void
291 add_string_tag (GstTagList *tags, GstTagMergeMode mode, const char *tag, RhythmDBEntry *entry, RhythmDBPropType property)
292 {
293 	const char *v;
294 	v = rhythmdb_entry_get_string (entry, property);
295 	if (v != NULL && v[0] != '\0') {
296 		gst_tag_list_add (tags, mode, tag, v, NULL);
297 	}
298 }
299 
300 static gboolean
301 add_tags_from_entry (RBEncoderGst *encoder,
302 		     RhythmDBEntry *entry,
303 		     GError **error)
304 {
305 	GstTagList *tags;
306 	GstTagSetter *tag_setter;
307 	GstIterator *iter;
308 	gulong day;
309 	gdouble bpm;
310 	gboolean done;
311 
312 	tags = gst_tag_list_new ();
313 
314 	gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE_ALL,
315 			  GST_TAG_TRACK_NUMBER, rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER),
316 			  GST_TAG_ALBUM_VOLUME_NUMBER, rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DISC_NUMBER),
317 			  GST_TAG_ENCODER, "Rhythmbox",
318 			  GST_TAG_ENCODER_VERSION, VERSION,
319 			  NULL);
320 
321 	add_string_tag (tags, GST_TAG_MERGE_APPEND, GST_TAG_TITLE, entry, RHYTHMDB_PROP_TITLE);
322 	add_string_tag (tags, GST_TAG_MERGE_APPEND, GST_TAG_ARTIST, entry, RHYTHMDB_PROP_ARTIST);
323 	add_string_tag (tags, GST_TAG_MERGE_APPEND, GST_TAG_ALBUM, entry, RHYTHMDB_PROP_ALBUM);
324 	add_string_tag (tags, GST_TAG_MERGE_APPEND, GST_TAG_GENRE, entry, RHYTHMDB_PROP_GENRE);
325 	add_string_tag (tags, GST_TAG_MERGE_APPEND, GST_TAG_COMMENT, entry, RHYTHMDB_PROP_COMMENT);
326 
327 	day = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DATE);
328 
329 	if (day > 0) {
330 		GDate *date;
331 
332 		date = g_date_new_julian (day);
333 		gst_tag_list_add (tags, GST_TAG_MERGE_APPEND,
334 				  GST_TAG_DATE, date,
335 				  NULL);
336 		g_date_free (date);
337 	}
338 	add_string_tag (tags, GST_TAG_MERGE_APPEND, GST_TAG_MUSICBRAINZ_TRACKID, entry, RHYTHMDB_PROP_MUSICBRAINZ_TRACKID);
339 	add_string_tag (tags, GST_TAG_MERGE_APPEND, GST_TAG_MUSICBRAINZ_ARTISTID, entry, RHYTHMDB_PROP_MUSICBRAINZ_ARTISTID);
340 	add_string_tag (tags, GST_TAG_MERGE_APPEND, GST_TAG_MUSICBRAINZ_ALBUMID, entry, RHYTHMDB_PROP_MUSICBRAINZ_ALBUMID);
341 	add_string_tag (tags, GST_TAG_MERGE_APPEND, GST_TAG_MUSICBRAINZ_ALBUMARTISTID, entry, RHYTHMDB_PROP_MUSICBRAINZ_ALBUMARTISTID);
342 	add_string_tag (tags, GST_TAG_MERGE_APPEND, GST_TAG_ARTIST_SORTNAME, entry, RHYTHMDB_PROP_ARTIST_SORTNAME);
343 	add_string_tag (tags, GST_TAG_MERGE_APPEND, GST_TAG_ALBUM_SORTNAME, entry, RHYTHMDB_PROP_ALBUM_SORTNAME);
344 
345 	/* is zero a valid BPM? */
346 	bpm = rhythmdb_entry_get_double (entry, RHYTHMDB_PROP_BPM);
347 	if (bpm > 0.001) {
348 		gst_tag_list_add (tags, GST_TAG_MERGE_APPEND, GST_TAG_BEATS_PER_MINUTE, bpm, NULL);
349 	}
350 
351 	/* XXX encodebin isn't a tag setter yet */
352 	/*
353 	gst_tag_setter_merge_tags (GST_TAG_SETTER (encoder->priv->encodebin), tags, GST_TAG_MERGE_REPLACE_ALL);
354 	*/
355 	iter = gst_bin_iterate_all_by_interface (GST_BIN (encoder->priv->encodebin), GST_TYPE_TAG_SETTER);
356 	done = FALSE;
357 	while (!done) {
358 		switch (gst_iterator_next (iter, (gpointer) & tag_setter)) {
359 		case GST_ITERATOR_OK:
360 			gst_tag_setter_merge_tags (tag_setter, tags, GST_TAG_MERGE_REPLACE_ALL);
361 			gst_object_unref (tag_setter);
362 			break;
363 		case GST_ITERATOR_RESYNC:
364 			gst_iterator_resync (iter);
365 			break;
366 		case GST_ITERATOR_ERROR:
367 			done = TRUE;
368 			break;
369 		case GST_ITERATOR_DONE:
370 			done = TRUE;
371 			break;
372 		}
373 	}
374 
375 	gst_tag_list_free (tags);
376 	return TRUE;
377 }
378 
379 static void
380 new_decoded_pad_cb (GstElement *decodebin, GstPad *new_pad, gboolean arg1, RBEncoderGst *encoder)
381 {
382 	GstPad *enc_sinkpad;
383 	GstCaps *caps;
384 	gchar *caps_string;
385 
386 	rb_debug ("new decoded pad");
387 
388 	/* transcode only the first audio track. multitrack audio files are not
389 	 * so common anyway */
390 	if (encoder->priv->decoded_pads > 0)
391 		return;
392 
393 	caps = gst_pad_get_caps (new_pad);
394 	caps_string = gst_caps_to_string (caps);
395 	gst_caps_unref (caps);
396 
397 	/* only process audio data */
398 	if (strncmp (caps_string, "audio/", 6) == 0) {
399 		encoder->priv->decoded_pads++;
400 		enc_sinkpad = gst_element_get_static_pad (encoder->priv->encodebin, "audio_0");
401 		if (gst_pad_link (new_pad, enc_sinkpad) != GST_PAD_LINK_OK)
402 			rb_debug ("error linking pads");
403 	}
404 
405 	g_free (caps_string);
406 }
407 
408 static GstElement *
409 add_decoding_pipeline (RBEncoderGst *encoder,
410 		       GError **error)
411 {
412 	GstElement *decodebin;
413 
414 	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
415 
416 	encoder->priv->transcoding = TRUE;
417 	decodebin = gst_element_factory_make ("decodebin2", NULL);
418 	if (decodebin == NULL) {
419 		rb_debug ("couldn't create decodebin2");
420 		g_set_error (error,
421 				RB_ENCODER_ERROR,
422 				RB_ENCODER_ERROR_INTERNAL,
423 				"Could not create decodebin2");
424 		return NULL;
425 	}
426 
427 	gst_bin_add (GST_BIN (encoder->priv->pipeline), decodebin);
428 
429 	g_signal_connect_object (decodebin,
430 			"new-decoded-pad",
431 			G_CALLBACK (new_decoded_pad_cb),
432 			encoder, 0);
433 
434 	return decodebin;
435 }
436 
437 static gboolean
438 attach_output_pipeline (RBEncoderGst *encoder,
439 			GstElement *end,
440 			const char *dest,
441 			gboolean overwrite,
442 			GError **error)
443 {
444 	GFile *file;
445 	GFileOutputStream *stream;
446 	GstElement *sink;
447 	GError *local_error = NULL;
448 
449 	/* if we can get to the location with gio, open the file here
450 	 * (prompting for overwrite if it already exists) and use giostreamsink.
451 	 * otherwise, create whatever sink element we can.
452 	 */
453 	rb_debug ("attempting to open output file %s", dest);
454 	file = g_file_new_for_uri (dest);
455 
456 	sink = gst_element_factory_make ("giostreamsink", NULL);
457 	if (sink != NULL) {
458 		stream = g_file_create (file, G_FILE_CREATE_NONE, NULL, &local_error);
459 		if (local_error != NULL) {
460 			if (g_error_matches (local_error,
461 					     G_IO_ERROR,
462 					     G_IO_ERROR_NOT_SUPPORTED)) {
463 				rb_debug ("gio can't write to %s, so using whatever sink will work", dest);
464 				g_object_unref (sink);
465 				sink = NULL;
466 				g_error_free (local_error);
467 			} else if (g_error_matches (local_error,
468 						    G_IO_ERROR,
469 						    G_IO_ERROR_EXISTS)) {
470 				if (overwrite) {
471 					g_error_free (local_error);
472 					stream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error);
473 					if (stream == NULL) {
474 						return FALSE;
475 					}
476 				} else {
477 					g_set_error_literal (error,
478 							     RB_ENCODER_ERROR,
479 							     RB_ENCODER_ERROR_DEST_EXISTS,
480 							     local_error->message);
481 					g_error_free (local_error);
482 					return FALSE;
483 				}
484 			} else {
485 				g_propagate_error (error, local_error);
486 				return FALSE;
487 			}
488 		}
489 
490 		if (stream != NULL) {
491 			g_object_set (sink, "stream", stream, NULL);
492 			encoder->priv->outstream = G_OUTPUT_STREAM (stream);
493 		}
494 	} else {
495 		rb_debug ("unable to create giostreamsink, falling back to default sink for %s", dest);
496 	}
497 
498 	if (sink == NULL) {
499 		sink = gst_element_make_from_uri (GST_URI_SINK, dest, "sink");
500 		if (sink == NULL) {
501 			g_set_error (error, RB_ENCODER_ERROR, RB_ENCODER_ERROR_FILE_ACCESS,
502 				     _("Could not create a GStreamer sink element to write to %s"),
503 				     dest);
504 			return FALSE;
505 		}
506 	}
507 
508 	/* provide a hook for setting sink properties */
509 	_rb_encoder_emit_prepare_sink (RB_ENCODER (encoder), dest, G_OBJECT (sink));
510 
511 	gst_bin_add (GST_BIN (encoder->priv->pipeline), sink);
512 	gst_element_link (end, sink);
513 
514 	return TRUE;
515 }
516 
517 static GstElement *
518 create_pipeline_and_source (RBEncoderGst *encoder,
519 			    RhythmDBEntry *entry,
520 			    GError **error)
521 {
522 	char *uri;
523 	GstElement *src;
524 
525 	uri = rhythmdb_entry_get_playback_uri (entry);
526 	if (uri == NULL) {
527 		g_set_error (error,
528 			     RB_ENCODER_ERROR, RB_ENCODER_ERROR_INTERNAL,
529 			     "Didn't get a playback URI for entry %s",
530 			     rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
531 		return NULL;
532 	}
533 
534 	src = gst_element_make_from_uri (GST_URI_SRC, uri, "source");
535 	if (src == NULL) {
536 		g_set_error (error,
537 			     RB_ENCODER_ERROR, RB_ENCODER_ERROR_INTERNAL,
538 			     "Could not create source element for '%s'", uri);
539 		g_free (uri);
540 		return NULL;
541 	}
542 
543 	encoder->priv->pipeline = gst_pipeline_new ("pipeline");
544 	gst_bin_add (GST_BIN (encoder->priv->pipeline), src);
545 
546 	/* provide a hook for setting source properties */
547 	_rb_encoder_emit_prepare_source (RB_ENCODER (encoder), uri, G_OBJECT (src));
548 
549 	g_free (uri);
550 	return src;
551 }
552 
553 static gboolean
554 copy_track (RBEncoderGst *encoder,
555 	    RhythmDBEntry *entry,
556 	    const char *dest,
557 	    gboolean overwrite,
558 	    GError **error)
559 {
560 	/* source ! sink */
561 	GstElement *src;
562 
563 	g_assert (encoder->priv->pipeline == NULL);
564 
565 	src = create_pipeline_and_source (encoder, entry, error);
566 	if (src == NULL)
567 		return FALSE;
568 
569 	if (!attach_output_pipeline (encoder, src, dest, overwrite, error))
570 		return FALSE;
571 
572 	start_pipeline (encoder);
573 	return TRUE;
574 }
575 
576 static gboolean
577 transcode_track (RBEncoderGst *encoder,
578 	 	 RhythmDBEntry *entry,
579 		 const char *dest,
580 		 gboolean overwrite,
581 		 GError **error)
582 {
583 	/* src ! decodebin2 ! encodebin ! sink */
584 	GstElement *src;
585 	GstElement *decoder;
586 
587 	g_assert (encoder->priv->pipeline == NULL);
588 	g_assert (encoder->priv->profile != NULL);
589 
590 	rb_debug ("transcoding to %s, profile %s", dest, gst_encoding_profile_get_name (encoder->priv->profile));
591 
592 	src = create_pipeline_and_source (encoder, entry, error);
593 	if (src == NULL)
594 		goto error;
595 
596 	decoder = add_decoding_pipeline (encoder, error);
597 	if (decoder == NULL)
598 		goto error;
599 
600 	if (gst_element_link (src, decoder) == FALSE) {
601 		rb_debug ("unable to link source element to decodebin");
602 		g_set_error (error,
603 			     RB_ENCODER_ERROR,
604 			     RB_ENCODER_ERROR_INTERNAL,
605 			     "Unable to link source element to decodebin");
606 		goto error;
607 	}
608 
609 	encoder->priv->encodebin = gst_element_factory_make ("encodebin", NULL);
610 	if (encoder->priv->encodebin == NULL) {
611 		rb_debug ("unable to create encodebin");
612 		g_set_error (error,
613 				RB_ENCODER_ERROR,
614 				RB_ENCODER_ERROR_INTERNAL,
615 				"Could not create encodebin");
616 		goto error;
617 	}
618 	g_object_set (encoder->priv->encodebin,
619 		      "profile", encoder->priv->profile,
620 		      "queue-bytes-max", 0,
621 		      "queue-buffers-max", 0,
622 		      "queue-time-max", 30 * GST_SECOND,
623 		      NULL);
624 	gst_bin_add (GST_BIN (encoder->priv->pipeline), encoder->priv->encodebin);
625 
626 	if (!attach_output_pipeline (encoder, encoder->priv->encodebin, dest, overwrite, error))
627 		goto error;
628 	if (!add_tags_from_entry (encoder, entry, error))
629 		goto error;
630 
631 	start_pipeline (encoder);
632 	return TRUE;
633 error:
634 	return FALSE;
635 }
636 
637 static void
638 impl_cancel (RBEncoder *bencoder)
639 {
640 	RBEncoderGst *encoder = RB_ENCODER_GST (bencoder);
641 
642 	if (encoder->priv->pipeline != NULL) {
643 		gst_element_set_state (encoder->priv->pipeline, GST_STATE_NULL);
644 		g_object_unref (encoder->priv->pipeline);
645 		encoder->priv->pipeline = NULL;
646 	}
647 
648 	if (encoder->priv->outstream != NULL) {
649 		GError *error = NULL;
650 		GFile *f;
651 		g_output_stream_close (encoder->priv->outstream, NULL, &error);
652 		if (error != NULL) {
653 			rb_debug ("error closing output stream: %s", error->message);
654 			g_error_free (error);
655 		}
656 		g_object_unref (encoder->priv->outstream);
657 		encoder->priv->outstream = NULL;
658 
659 		/* try to delete the output file, since it's incomplete */
660 		error = NULL;
661 		f = g_file_new_for_uri (encoder->priv->dest_uri);
662 		if (g_file_delete (f, NULL, &error) == FALSE) {
663 			rb_debug ("error deleting incomplete output file: %s", error->message);
664 			g_error_free (error);
665 		}
666 		g_object_unref (f);
667 	}
668 
669 	if (encoder->priv->error == NULL) {
670 		/* should never be displayed to the user anyway */
671 		encoder->priv->error = g_error_new (G_IO_ERROR, G_IO_ERROR_CANCELLED, " ");
672 	}
673 
674 	encoder->priv->cancelled = TRUE;
675 	rb_encoder_gst_emit_completed (encoder);
676 }
677 
678 static gboolean
679 cancel_idle (RBEncoder *encoder)
680 {
681 	impl_cancel (encoder);
682 	g_object_unref (encoder);
683 	return FALSE;
684 }
685 
686 static void
687 impl_encode (RBEncoder *bencoder,
688 	     RhythmDBEntry *entry,
689 	     const char *dest,
690 	     gboolean overwrite,
691 	     GstEncodingProfile *profile)
692 {
693 	RBEncoderGst *encoder = RB_ENCODER_GST (bencoder);
694 	gboolean result;
695 	GError *error = NULL;
696 
697 	g_return_if_fail (encoder->priv->pipeline == NULL);
698 
699 	if (rb_uri_create_parent_dirs (dest, &error) == FALSE) {
700 		error = g_error_new_literal (RB_ENCODER_ERROR,
701 					     RB_ENCODER_ERROR_FILE_ACCESS,
702 					     error->message);		/* I guess */
703 
704 		set_error (encoder, error);
705 		g_error_free (error);
706 		g_idle_add ((GSourceFunc) cancel_idle, g_object_ref (encoder));
707 		return;
708 	}
709 
710 	g_free (encoder->priv->dest_media_type);
711 	g_free (encoder->priv->dest_uri);
712 	encoder->priv->dest_uri = g_strdup (dest);
713 
714 	/* keep ourselves alive in case we get cancelled by a signal handler */
715 	g_object_ref (encoder);
716 
717 	/* if destination and source media types are the same, copy it */
718 	if (profile == NULL) {
719 		encoder->priv->total_length = rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE);
720 		encoder->priv->position_format = GST_FORMAT_BYTES;
721 		encoder->priv->dest_media_type = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_MEDIA_TYPE);
722 
723 		result = copy_track (encoder, entry, dest, overwrite, &error);
724 	} else {
725 		gst_encoding_profile_ref (profile);
726 		encoder->priv->profile = profile;
727 		encoder->priv->total_length = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
728 		encoder->priv->position_format = GST_FORMAT_TIME;
729 		encoder->priv->dest_media_type = rb_gst_encoding_profile_get_media_type (profile);
730 
731 		result = transcode_track (encoder, entry, dest, overwrite, &error);
732 	}
733 
734 	if (result == FALSE && encoder->priv->cancelled == FALSE) {
735 		set_error (encoder, error);
736 		g_idle_add ((GSourceFunc) cancel_idle, g_object_ref (encoder));
737 	}
738 
739 	if (error != NULL) {
740 		g_error_free (error);
741 	}
742 
743 	g_object_unref (encoder);
744 }
745 
746 static gboolean
747 impl_get_missing_plugins (RBEncoder *encoder,
748 			  GstEncodingProfile *profile,
749 			  char ***details,
750 			  char ***descriptions)
751 {
752 	GstElement *encodebin;
753 	GstBus *bus;
754 	GstPad *pad;
755 	gboolean ret;
756 
757 	ret = FALSE;
758 	rb_debug ("trying to check profile %s for missing plugins", gst_encoding_profile_get_name (profile));
759 
760 	encodebin = gst_element_factory_make ("encodebin", NULL);
761 	if (encodebin == NULL) {
762 		g_warning ("Unable to create encodebin");
763 		return FALSE;
764 	}
765 
766 	bus = gst_bus_new ();
767 	gst_element_set_bus (encodebin, bus);
768 	gst_bus_set_flushing (bus, FALSE);		/* necessary? */
769 
770 	g_object_set (encodebin, "profile", profile, NULL);
771 	pad = gst_element_get_static_pad (encodebin, "audio_0");
772 	if (pad == NULL) {
773 		GstMessage *message;
774 		GList *messages = NULL;
775 		GList *m;
776 		int i;
777 
778 		rb_debug ("didn't get request pad, profile %s doesn't work", gst_encoding_profile_get_name (profile));
779 		message = gst_bus_pop (bus);
780 		while (message != NULL) {
781 			if (gst_is_missing_plugin_message (message)) {
782 				messages = g_list_append (messages, message);
783 			} else {
784 				gst_message_unref (message);
785 			}
786 			message = gst_bus_pop (bus);
787 		}
788 
789 		if (messages != NULL) {
790 			if (details != NULL) {
791 				*details = g_new0(char *, g_list_length (messages)+1);
792 			}
793 			if (descriptions != NULL) {
794 				*descriptions = g_new0(char *, g_list_length (messages)+1);
795 			}
796 			i = 0;
797 			for (m = messages; m != NULL; m = m->next) {
798 				char *str;
799 				if (details != NULL) {
800 					str = gst_missing_plugin_message_get_installer_detail (m->data);
801 					rb_debug ("missing plugin for profile %s: %s",
802 						  gst_encoding_profile_get_name (profile),
803 						  str);
804 					(*details)[i] = str;
805 				}
806 				if (descriptions != NULL) {
807 					str = gst_missing_plugin_message_get_description (m->data);
808 					(*descriptions)[i] = str;
809 				}
810 				i++;
811 			}
812 
813 			ret = TRUE;
814 			rb_list_destroy_free (messages, (GDestroyNotify)gst_message_unref);
815 		}
816 
817 	} else {
818 		rb_debug ("got request pad, profile %s works", gst_encoding_profile_get_name (profile));
819 		gst_element_release_request_pad (encodebin, pad);
820 		gst_object_unref (pad);
821 	}
822 
823 	gst_object_unref (encodebin);
824 	gst_object_unref (bus);
825 	return ret;
826 }
827 
828 static void
829 impl_finalize (GObject *object)
830 {
831 	RBEncoderGst *encoder = RB_ENCODER_GST (object);
832 
833 	if (encoder->priv->progress_id != 0)
834 		g_source_remove (encoder->priv->progress_id);
835 
836 	if (encoder->priv->pipeline) {
837 		gst_element_set_state (encoder->priv->pipeline, GST_STATE_NULL);
838 		g_object_unref (encoder->priv->pipeline);
839 		encoder->priv->pipeline = NULL;
840 	}
841 
842 	if (encoder->priv->outstream) {
843 		g_output_stream_close (encoder->priv->outstream, NULL, NULL);
844 		g_object_unref (encoder->priv->outstream);
845 		encoder->priv->outstream = NULL;
846 	}
847 
848 	if (encoder->priv->profile) {
849 		gst_encoding_profile_unref (encoder->priv->profile);
850 		encoder->priv->profile = NULL;
851 	}
852 
853 	g_free (encoder->priv->dest_uri);
854 	g_free (encoder->priv->dest_media_type);
855 
856         G_OBJECT_CLASS (rb_encoder_gst_parent_class)->finalize (object);
857 }
858 
859 static void
860 rb_encoder_gst_init (RBEncoderGst *encoder)
861 {
862         encoder->priv = (G_TYPE_INSTANCE_GET_PRIVATE ((encoder), RB_TYPE_ENCODER_GST, RBEncoderGstPrivate));
863 }
864 
865 static void
866 rb_encoder_init (RBEncoderIface *iface)
867 {
868 	iface->encode = impl_encode;
869 	iface->cancel = impl_cancel;
870 	iface->get_missing_plugins = impl_get_missing_plugins;
871 }
872 
873 static void
874 rb_encoder_gst_class_init (RBEncoderGstClass *klass)
875 {
876         GObjectClass *object_class = (GObjectClass *) klass;
877 
878         object_class->finalize = impl_finalize;
879 
880         g_type_class_add_private (klass, sizeof (RBEncoderGstPrivate));
881 }
882 
883 RBEncoder*
884 rb_encoder_gst_new (void)
885 {
886 	return RB_ENCODER (g_object_new (RB_TYPE_ENCODER_GST, NULL));
887 }