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 }