transcoder: Handle the case where several errors are posted
[platform/upstream/gstreamer.git] / gst-libs / gst / transcoder / gsttranscoder.c
1 /* GStreamer
2  *
3  * Copyright (C) 2014-2015 Sebastian Dröge <sebastian@centricular.com>
4  * Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 /**
23  * SECTION:gsttranscoder
24  * @short_description: High level API to transcode media files
25  * from one format to any other format using the GStreamer framework.
26  * @symbols:
27  *   - gst_transcoder_error_quark
28  */
29
30 #include "gsttranscoder.h"
31
32 GST_DEBUG_CATEGORY_STATIC (gst_transcoder_debug);
33 #define GST_CAT_DEFAULT gst_transcoder_debug
34
35 #define DEFAULT_URI NULL
36 #define DEFAULT_POSITION GST_CLOCK_TIME_NONE
37 #define DEFAULT_DURATION GST_CLOCK_TIME_NONE
38 #define DEFAULT_POSITION_UPDATE_INTERVAL_MS 100
39 #define DEFAULT_AVOID_REENCODING   FALSE
40
41 GQuark
42 gst_transcoder_error_quark (void)
43 {
44   static GQuark quark;
45
46   if (!quark)
47     quark = g_quark_from_static_string ("gst-transcoder-error-quark");
48
49   return quark;
50 }
51
52 enum
53 {
54   PROP_0,
55   PROP_SIGNAL_DISPATCHER,
56   PROP_SRC_URI,
57   PROP_DEST_URI,
58   PROP_PROFILE,
59   PROP_POSITION,
60   PROP_DURATION,
61   PROP_PIPELINE,
62   PROP_POSITION_UPDATE_INTERVAL,
63   PROP_AVOID_REENCODING,
64   PROP_LAST
65 };
66
67 enum
68 {
69   SIGNAL_POSITION_UPDATED,
70   SIGNAL_DURATION_CHANGED,
71   SIGNAL_DONE,
72   SIGNAL_ERROR,
73   SIGNAL_WARNING,
74   SIGNAL_LAST
75 };
76
77 struct _GstTranscoder
78 {
79   GstObject parent;
80
81   GstTranscoderSignalDispatcher *signal_dispatcher;
82
83   GstEncodingProfile *profile;
84   gchar *source_uri;
85   gchar *dest_uri;
86
87   GThread *thread;
88   GCond cond;
89   GMainContext *context;
90   GMainLoop *loop;
91
92   GstElement *transcodebin;
93   GstBus *bus;
94   GstState target_state, current_state;
95   gboolean is_live, is_eos;
96   GSource *tick_source, *ready_timeout_source;
97
98   guint position_update_interval_ms;
99   gint wanted_cpu_usage;
100
101   GstClockTime last_duration;
102 };
103
104 struct _GstTranscoderClass
105 {
106   GstObjectClass parent_class;
107 };
108
109 static void
110 gst_transcoder_signal_dispatcher_dispatch (GstTranscoderSignalDispatcher * self,
111     GstTranscoder * transcoder, void (*emitter) (gpointer data), gpointer data,
112     GDestroyNotify destroy);
113
114 #define parent_class gst_transcoder_parent_class
115 G_DEFINE_TYPE (GstTranscoder, gst_transcoder, GST_TYPE_OBJECT);
116
117 static guint signals[SIGNAL_LAST] = { 0, };
118 static GParamSpec *param_specs[PROP_LAST] = { NULL, };
119
120 static void gst_transcoder_dispose (GObject * object);
121 static void gst_transcoder_finalize (GObject * object);
122 static void gst_transcoder_set_property (GObject * object, guint prop_id,
123     const GValue * value, GParamSpec * pspec);
124 static void gst_transcoder_get_property (GObject * object, guint prop_id,
125     GValue * value, GParamSpec * pspec);
126 static void gst_transcoder_constructed (GObject * object);
127
128 static gpointer gst_transcoder_main (gpointer data);
129
130 static gboolean gst_transcoder_set_position_update_interval_internal (gpointer
131     user_data);
132
133
134 /**
135  * gst_transcoder_set_cpu_usage:
136  * @self: The GstTranscoder to limit CPU usage on.
137  * @cpu_usage: The percentage of the CPU the process running the transcoder
138  * should try to use. It takes into account the number of cores available.
139  *
140  * Sets @cpu_usage as target percentage CPU usage of the process running the
141  * transcoding task. It will modulate the transcoding speed to reach that target
142  * usage.
143  */
144 void
145 gst_transcoder_set_cpu_usage (GstTranscoder * self, gint cpu_usage)
146 {
147   GST_OBJECT_LOCK (self);
148   self->wanted_cpu_usage = cpu_usage;
149   if (self->transcodebin)
150     g_object_set (self->transcodebin, "cpu-usage", cpu_usage, NULL);
151   GST_OBJECT_UNLOCK (self);
152 }
153
154 static void
155 gst_transcoder_init (GstTranscoder * self)
156 {
157   GST_TRACE_OBJECT (self, "Initializing");
158
159   self = gst_transcoder_get_instance_private (self);
160
161   g_cond_init (&self->cond);
162
163   self->context = g_main_context_new ();
164   self->loop = g_main_loop_new (self->context, FALSE);
165   self->wanted_cpu_usage = 100;
166
167   self->position_update_interval_ms = DEFAULT_POSITION_UPDATE_INTERVAL_MS;
168
169   GST_TRACE_OBJECT (self, "Initialized");
170 }
171
172 static void
173 gst_transcoder_class_init (GstTranscoderClass * klass)
174 {
175   GObjectClass *gobject_class = (GObjectClass *) klass;
176
177   gobject_class->set_property = gst_transcoder_set_property;
178   gobject_class->get_property = gst_transcoder_get_property;
179   gobject_class->dispose = gst_transcoder_dispose;
180   gobject_class->finalize = gst_transcoder_finalize;
181   gobject_class->constructed = gst_transcoder_constructed;
182
183   param_specs[PROP_SIGNAL_DISPATCHER] =
184       g_param_spec_object ("signal-dispatcher",
185       "Signal Dispatcher", "Dispatcher for the signals to e.g. event loops",
186       GST_TYPE_TRANSCODER_SIGNAL_DISPATCHER,
187       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
188
189   param_specs[PROP_SRC_URI] =
190       g_param_spec_string ("src-uri", "URI", "Source URI", DEFAULT_URI,
191       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
192
193   param_specs[PROP_DEST_URI] =
194       g_param_spec_string ("dest-uri", "URI", "Source URI", DEFAULT_URI,
195       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
196
197   param_specs[PROP_PROFILE] =
198       g_param_spec_object ("profile", "Profile",
199       "The GstEncodingProfile to use", GST_TYPE_ENCODING_PROFILE,
200       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
201
202   param_specs[PROP_POSITION] =
203       g_param_spec_uint64 ("position", "Position", "Current Position",
204       0, G_MAXUINT64, DEFAULT_POSITION,
205       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
206
207   param_specs[PROP_DURATION] =
208       g_param_spec_uint64 ("duration", "Duration", "Duration",
209       0, G_MAXUINT64, DEFAULT_DURATION,
210       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
211
212   param_specs[PROP_PIPELINE] =
213       g_param_spec_object ("pipeline", "Pipeline",
214       "GStreamer pipeline that is used",
215       GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
216
217   param_specs[PROP_POSITION_UPDATE_INTERVAL] =
218       g_param_spec_uint ("position-update-interval", "Position update interval",
219       "Interval in milliseconds between two position-updated signals."
220       "Pass 0 to stop updating the position.",
221       0, 10000, DEFAULT_POSITION_UPDATE_INTERVAL_MS,
222       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
223
224   /**
225    * GstTranscoder:avoid-reencoding:
226    *
227    * See #encodebin:avoid-reencoding
228    */
229   param_specs[PROP_AVOID_REENCODING] =
230       g_param_spec_boolean ("avoid-reencoding", "Avoid re-encoding",
231       "Whether to re-encode portions of compatible video streams that lay on segment boundaries",
232       DEFAULT_AVOID_REENCODING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
233
234   g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
235
236   signals[SIGNAL_POSITION_UPDATED] =
237       g_signal_new ("position-updated", G_TYPE_FROM_CLASS (klass),
238       G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
239       NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_CLOCK_TIME);
240
241   signals[SIGNAL_DURATION_CHANGED] =
242       g_signal_new ("duration-changed", G_TYPE_FROM_CLASS (klass),
243       G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
244       NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_CLOCK_TIME);
245
246   signals[SIGNAL_DONE] =
247       g_signal_new ("done", G_TYPE_FROM_CLASS (klass),
248       G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
249       NULL, NULL, G_TYPE_NONE, 0, G_TYPE_INVALID);
250
251   signals[SIGNAL_ERROR] =
252       g_signal_new ("error", G_TYPE_FROM_CLASS (klass),
253       G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
254       NULL, NULL, G_TYPE_NONE, 2, G_TYPE_ERROR, GST_TYPE_STRUCTURE);
255
256   signals[SIGNAL_WARNING] =
257       g_signal_new ("warning", G_TYPE_FROM_CLASS (klass),
258       G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
259       NULL, NULL, G_TYPE_NONE, 2, G_TYPE_ERROR, GST_TYPE_STRUCTURE);
260 }
261
262 static void
263 gst_transcoder_dispose (GObject * object)
264 {
265   GstTranscoder *self = GST_TRANSCODER (object);
266
267   GST_TRACE_OBJECT (self, "Stopping main thread");
268
269   if (self->loop) {
270     g_main_loop_quit (self->loop);
271
272     g_thread_join (self->thread);
273     self->thread = NULL;
274
275     g_main_loop_unref (self->loop);
276     self->loop = NULL;
277
278     g_main_context_unref (self->context);
279     self->context = NULL;
280
281   }
282
283   G_OBJECT_CLASS (parent_class)->dispose (object);
284 }
285
286 static void
287 gst_transcoder_finalize (GObject * object)
288 {
289   GstTranscoder *self = GST_TRANSCODER (object);
290
291   GST_TRACE_OBJECT (self, "Finalizing");
292
293   g_free (self->source_uri);
294   g_free (self->dest_uri);
295   if (self->signal_dispatcher)
296     g_object_unref (self->signal_dispatcher);
297   g_cond_clear (&self->cond);
298
299   G_OBJECT_CLASS (parent_class)->finalize (object);
300 }
301
302 static void
303 gst_transcoder_constructed (GObject * object)
304 {
305   GstTranscoder *self = GST_TRANSCODER (object);
306
307   GST_TRACE_OBJECT (self, "Constructed");
308
309   self->transcodebin =
310       gst_element_factory_make ("uritranscodebin", "uritranscodebin");
311
312   g_object_set (self->transcodebin, "source-uri", self->source_uri,
313       "dest-uri", self->dest_uri, "profile", self->profile,
314       "cpu-usage", self->wanted_cpu_usage, NULL);
315
316   GST_OBJECT_LOCK (self);
317   self->thread = g_thread_new ("GstTranscoder", gst_transcoder_main, self);
318   while (!self->loop || !g_main_loop_is_running (self->loop))
319     g_cond_wait (&self->cond, GST_OBJECT_GET_LOCK (self));
320   GST_OBJECT_UNLOCK (self);
321
322   G_OBJECT_CLASS (parent_class)->constructed (object);
323 }
324
325 static void
326 gst_transcoder_set_property (GObject * object, guint prop_id,
327     const GValue * value, GParamSpec * pspec)
328 {
329   GstTranscoder *self = GST_TRANSCODER (object);
330
331   switch (prop_id) {
332     case PROP_SIGNAL_DISPATCHER:
333       self->signal_dispatcher = g_value_dup_object (value);
334       break;
335     case PROP_SRC_URI:{
336       GST_OBJECT_LOCK (self);
337       g_free (self->source_uri);
338       self->source_uri = g_value_dup_string (value);
339       GST_DEBUG_OBJECT (self, "Set source_uri=%s", self->source_uri);
340       GST_OBJECT_UNLOCK (self);
341       break;
342     }
343     case PROP_DEST_URI:{
344       GST_OBJECT_LOCK (self);
345       g_free (self->dest_uri);
346       self->dest_uri = g_value_dup_string (value);
347       GST_DEBUG_OBJECT (self, "Set dest_uri=%s", self->dest_uri);
348       GST_OBJECT_UNLOCK (self);
349       break;
350     }
351     case PROP_POSITION_UPDATE_INTERVAL:
352       GST_OBJECT_LOCK (self);
353       self->position_update_interval_ms = g_value_get_uint (value);
354       GST_DEBUG_OBJECT (self, "Set position update interval=%u ms",
355           g_value_get_uint (value));
356       GST_OBJECT_UNLOCK (self);
357
358       gst_transcoder_set_position_update_interval_internal (self);
359       break;
360     case PROP_PROFILE:
361       GST_OBJECT_LOCK (self);
362       self->profile = g_value_dup_object (value);
363       GST_OBJECT_UNLOCK (self);
364       break;
365     case PROP_AVOID_REENCODING:
366       g_object_set (self->transcodebin, "avoid-reencoding",
367           g_value_get_boolean (value), NULL);
368       break;
369     default:
370       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
371       break;
372   }
373 }
374
375 static void
376 gst_transcoder_get_property (GObject * object, guint prop_id,
377     GValue * value, GParamSpec * pspec)
378 {
379   GstTranscoder *self = GST_TRANSCODER (object);
380
381   switch (prop_id) {
382     case PROP_SRC_URI:
383       GST_OBJECT_LOCK (self);
384       g_value_set_string (value, self->source_uri);
385       GST_OBJECT_UNLOCK (self);
386       break;
387     case PROP_DEST_URI:
388       GST_OBJECT_LOCK (self);
389       g_value_set_string (value, self->dest_uri);
390       GST_OBJECT_UNLOCK (self);
391       break;
392     case PROP_POSITION:{
393       gint64 position = 0;
394
395       if (self->is_eos)
396         position = self->last_duration;
397       else
398         gst_element_query_position (self->transcodebin, GST_FORMAT_TIME,
399             &position);
400       g_value_set_uint64 (value, position);
401       GST_TRACE_OBJECT (self, "Returning position=%" GST_TIME_FORMAT,
402           GST_TIME_ARGS (g_value_get_uint64 (value)));
403       break;
404     }
405     case PROP_DURATION:{
406       gint64 duration = 0;
407
408       gst_element_query_duration (self->transcodebin, GST_FORMAT_TIME,
409           &duration);
410       g_value_set_uint64 (value, duration);
411       GST_TRACE_OBJECT (self, "Returning duration=%" GST_TIME_FORMAT,
412           GST_TIME_ARGS (g_value_get_uint64 (value)));
413       break;
414     }
415     case PROP_PIPELINE:
416       g_value_set_object (value, self->transcodebin);
417       break;
418     case PROP_POSITION_UPDATE_INTERVAL:
419       GST_OBJECT_LOCK (self);
420       g_value_set_uint (value,
421           gst_transcoder_get_position_update_interval (self));
422       GST_OBJECT_UNLOCK (self);
423       break;
424     case PROP_PROFILE:
425       GST_OBJECT_LOCK (self);
426       g_value_set_object (value, self->profile);
427       GST_OBJECT_UNLOCK (self);
428       break;
429     case PROP_AVOID_REENCODING:
430     {
431       gboolean avoid_reencoding;
432
433       g_object_get (self->transcodebin, "avoid-reencoding", &avoid_reencoding,
434           NULL);
435       g_value_set_boolean (value, avoid_reencoding);
436       break;
437     }
438     default:
439       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
440       break;
441   }
442 }
443
444 static gboolean
445 main_loop_running_cb (gpointer user_data)
446 {
447   GstTranscoder *self = GST_TRANSCODER (user_data);
448
449   GST_TRACE_OBJECT (self, "Main loop running now");
450
451   GST_OBJECT_LOCK (self);
452   g_cond_signal (&self->cond);
453   GST_OBJECT_UNLOCK (self);
454
455   return G_SOURCE_REMOVE;
456 }
457
458 typedef struct
459 {
460   GstTranscoder *transcoder;
461   GstClockTime position;
462 } PositionUpdatedSignalData;
463
464 static void
465 position_updated_dispatch (gpointer user_data)
466 {
467   PositionUpdatedSignalData *data = user_data;
468
469   if (data->transcoder->target_state >= GST_STATE_PAUSED) {
470     g_signal_emit (data->transcoder, signals[SIGNAL_POSITION_UPDATED], 0,
471         data->position);
472     g_object_notify_by_pspec (G_OBJECT (data->transcoder),
473         param_specs[PROP_POSITION]);
474   }
475 }
476
477 static void
478 position_updated_signal_data_free (PositionUpdatedSignalData * data)
479 {
480   g_object_unref (data->transcoder);
481   g_free (data);
482 }
483
484 static gboolean
485 tick_cb (gpointer user_data)
486 {
487   GstTranscoder *self = GST_TRANSCODER (user_data);
488   gint64 position;
489
490   if (self->target_state < GST_STATE_PAUSED)
491     return G_SOURCE_CONTINUE;
492
493   if (!gst_element_query_position (self->transcodebin, GST_FORMAT_TIME,
494           &position)) {
495     GST_LOG_OBJECT (self, "Could not query position");
496     return G_SOURCE_CONTINUE;
497   }
498
499   GST_LOG_OBJECT (self, "Position %" GST_TIME_FORMAT, GST_TIME_ARGS (position));
500
501   if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
502           signals[SIGNAL_POSITION_UPDATED], 0, NULL, NULL, NULL) != 0) {
503     PositionUpdatedSignalData *data = g_new0 (PositionUpdatedSignalData, 1);
504
505     data->transcoder = g_object_ref (self);
506     data->position = position;
507     gst_transcoder_signal_dispatcher_dispatch (self->signal_dispatcher, self,
508         position_updated_dispatch, data,
509         (GDestroyNotify) position_updated_signal_data_free);
510   }
511
512   return G_SOURCE_CONTINUE;
513 }
514
515 static void
516 add_tick_source (GstTranscoder * self)
517 {
518   if (self->tick_source)
519     return;
520
521   if (!self->position_update_interval_ms)
522     return;
523
524   self->tick_source = g_timeout_source_new (self->position_update_interval_ms);
525   g_source_set_callback (self->tick_source, (GSourceFunc) tick_cb, self, NULL);
526   g_source_attach (self->tick_source, self->context);
527 }
528
529 static void
530 remove_tick_source (GstTranscoder * self)
531 {
532   if (!self->tick_source)
533     return;
534
535   g_source_destroy (self->tick_source);
536   g_source_unref (self->tick_source);
537   self->tick_source = NULL;
538 }
539
540 typedef struct
541 {
542   GstTranscoder *transcoder;
543   GError *err;
544   GstStructure *details;
545 } IssueSignalData;
546
547 static void
548 error_dispatch (gpointer user_data)
549 {
550   IssueSignalData *data = user_data;
551
552   g_signal_emit (data->transcoder, signals[SIGNAL_ERROR], 0, data->err,
553       data->details);
554 }
555
556 static void
557 free_issue_signal_data (IssueSignalData * data)
558 {
559   g_object_unref (data->transcoder);
560   if (data->details)
561     gst_structure_free (data->details);
562   g_clear_error (&data->err);
563   g_free (data);
564 }
565
566 static void
567 emit_error (GstTranscoder * self, GError * err, const GstStructure * details)
568 {
569   if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
570           signals[SIGNAL_ERROR], 0, NULL, NULL, NULL) != 0) {
571     IssueSignalData *data = g_new0 (IssueSignalData, 1);
572
573     data->transcoder = g_object_ref (self);
574     data->err = g_error_copy (err);
575     if (details)
576       data->details = gst_structure_copy (details);
577     gst_transcoder_signal_dispatcher_dispatch (self->signal_dispatcher, self,
578         error_dispatch, data, (GDestroyNotify) free_issue_signal_data);
579   }
580
581   g_error_free (err);
582
583   remove_tick_source (self);
584
585   self->target_state = GST_STATE_NULL;
586   self->current_state = GST_STATE_NULL;
587   self->is_live = FALSE;
588   self->is_eos = FALSE;
589   gst_element_set_state (self->transcodebin, GST_STATE_NULL);
590 }
591
592 static void
593 dump_dot_file (GstTranscoder * self, const gchar * name)
594 {
595   gchar *full_name;
596
597   full_name = g_strdup_printf ("gst-transcoder.%p.%s", self, name);
598
599   GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (self->transcodebin),
600       GST_DEBUG_GRAPH_SHOW_ALL, full_name);
601
602   g_free (full_name);
603 }
604
605 static void
606 warning_dispatch (gpointer user_data)
607 {
608   IssueSignalData *data = user_data;
609
610   g_signal_emit (data->transcoder, signals[SIGNAL_WARNING], 0, data->err,
611       data->details);
612 }
613
614 static void
615 emit_warning (GstTranscoder * self, GError * err, const GstStructure * details)
616 {
617   if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
618           signals[SIGNAL_WARNING], 0, NULL, NULL, NULL) != 0) {
619     IssueSignalData *data = g_new0 (IssueSignalData, 1);
620
621     data->transcoder = g_object_ref (self);
622     data->err = g_error_copy (err);
623     if (details)
624       data->details = gst_structure_copy (details);
625     gst_transcoder_signal_dispatcher_dispatch (self->signal_dispatcher, self,
626         warning_dispatch, data, (GDestroyNotify) free_issue_signal_data);
627   }
628
629   g_error_free (err);
630 }
631
632 static void
633 error_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
634 {
635   GError *err;
636   GstTranscoder *self = GST_TRANSCODER (user_data);
637   gchar *name, *debug, *message;
638   GstStructure *details = NULL;
639
640   dump_dot_file (self, "error");
641
642   gst_message_parse_error (msg, &err, &debug);
643   gst_message_parse_error_details (msg, (const GstStructure **) &details);
644
645   if (!details)
646     details = gst_structure_new_empty ("details");
647   else
648     details = gst_structure_copy (details);
649
650   name = gst_object_get_path_string (msg->src);
651   message = gst_error_get_message (err->domain, err->code);
652
653   gst_structure_set (details, "debug", G_TYPE_STRING, debug,
654       "msg-source-element-name", G_TYPE_STRING, "name",
655       "msg-source-type", G_TYPE_GTYPE, G_OBJECT_TYPE (msg->src),
656       "msg-error", G_TYPE_STRING, message, NULL);
657   emit_error (self, g_error_copy (err), details);
658
659   gst_structure_free (details);
660   g_clear_error (&err);
661   g_free (debug);
662   g_free (name);
663   g_free (message);
664 }
665
666 static void
667 warning_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
668 {
669   GstTranscoder *self = GST_TRANSCODER (user_data);
670   GError *err, *transcoder_err;
671   gchar *name, *debug, *message, *full_message;
672   const GstStructure *details = NULL;
673
674   dump_dot_file (self, "warning");
675
676   gst_message_parse_warning (msg, &err, &debug);
677   gst_message_parse_warning_details (msg, &details);
678
679   name = gst_object_get_path_string (msg->src);
680   message = gst_error_get_message (err->domain, err->code);
681
682   if (debug)
683     full_message =
684         g_strdup_printf ("Warning from element %s: %s\n%s\n%s", name, message,
685         err->message, debug);
686   else
687     full_message =
688         g_strdup_printf ("Warning from element %s: %s\n%s", name, message,
689         err->message);
690
691   GST_WARNING_OBJECT (self, "WARNING: from element %s: %s", name, err->message);
692   if (debug != NULL)
693     GST_WARNING_OBJECT (self, "Additional debug info: %s", debug);
694
695   transcoder_err =
696       g_error_new_literal (GST_TRANSCODER_ERROR, GST_TRANSCODER_ERROR_FAILED,
697       full_message);
698   emit_warning (self, transcoder_err, details);
699
700   g_clear_error (&err);
701   g_free (debug);
702   g_free (name);
703   g_free (full_message);
704   g_free (message);
705 }
706
707 static void
708 eos_dispatch (gpointer user_data)
709 {
710   g_signal_emit (user_data, signals[SIGNAL_DONE], 0);
711 }
712
713 static void
714 eos_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
715     gpointer user_data)
716 {
717   GstTranscoder *self = GST_TRANSCODER (user_data);
718
719   GST_DEBUG_OBJECT (self, "End of stream");
720
721   gst_element_query_duration (self->transcodebin, GST_FORMAT_TIME,
722       (gint64 *) & self->last_duration);
723   tick_cb (self);
724   remove_tick_source (self);
725
726   if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
727           signals[SIGNAL_DONE], 0, NULL, NULL, NULL) != 0) {
728     gst_transcoder_signal_dispatcher_dispatch (self->signal_dispatcher, self,
729         eos_dispatch, g_object_ref (self), (GDestroyNotify) g_object_unref);
730   }
731   self->is_eos = TRUE;
732 }
733
734 static void
735 clock_lost_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
736     gpointer user_data)
737 {
738   GstTranscoder *self = GST_TRANSCODER (user_data);
739   GstStateChangeReturn state_ret;
740
741   GST_DEBUG_OBJECT (self, "Clock lost");
742   if (self->target_state >= GST_STATE_PLAYING) {
743     state_ret = gst_element_set_state (self->transcodebin, GST_STATE_PAUSED);
744     if (state_ret != GST_STATE_CHANGE_FAILURE)
745       state_ret = gst_element_set_state (self->transcodebin, GST_STATE_PLAYING);
746
747     if (state_ret == GST_STATE_CHANGE_FAILURE)
748       emit_error (self, g_error_new (GST_TRANSCODER_ERROR,
749               GST_TRANSCODER_ERROR_FAILED, "Failed to handle clock loss"),
750           NULL);
751   }
752 }
753
754 typedef struct
755 {
756   GstTranscoder *transcoder;
757   GstClockTime duration;
758 } DurationChangedSignalData;
759
760 static void
761 duration_changed_dispatch (gpointer user_data)
762 {
763   DurationChangedSignalData *data = user_data;
764
765   if (data->transcoder->target_state >= GST_STATE_PAUSED) {
766     g_signal_emit (data->transcoder, signals[SIGNAL_DURATION_CHANGED], 0,
767         data->duration);
768     g_object_notify_by_pspec (G_OBJECT (data->transcoder),
769         param_specs[PROP_DURATION]);
770   }
771 }
772
773 static void
774 duration_changed_signal_data_free (DurationChangedSignalData * data)
775 {
776   g_object_unref (data->transcoder);
777   g_free (data);
778 }
779
780 static void
781 emit_duration_changed (GstTranscoder * self, GstClockTime duration)
782 {
783   GST_DEBUG_OBJECT (self, "Duration changed %" GST_TIME_FORMAT,
784       GST_TIME_ARGS (duration));
785
786   if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
787           signals[SIGNAL_DURATION_CHANGED], 0, NULL, NULL, NULL) != 0) {
788     DurationChangedSignalData *data = g_new0 (DurationChangedSignalData, 1);
789
790     data->transcoder = g_object_ref (self);
791     data->duration = duration;
792     gst_transcoder_signal_dispatcher_dispatch (self->signal_dispatcher, self,
793         duration_changed_dispatch, data,
794         (GDestroyNotify) duration_changed_signal_data_free);
795   }
796 }
797
798 static void
799 state_changed_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
800     gpointer user_data)
801 {
802   GstTranscoder *self = GST_TRANSCODER (user_data);
803   GstState old_state, new_state, pending_state;
804
805   gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
806
807   if (GST_MESSAGE_SRC (msg) == GST_OBJECT (self->transcodebin)) {
808     gchar *transition_name;
809
810     GST_DEBUG_OBJECT (self, "Changed state old: %s new: %s pending: %s",
811         gst_element_state_get_name (old_state),
812         gst_element_state_get_name (new_state),
813         gst_element_state_get_name (pending_state));
814
815     transition_name = g_strdup_printf ("%s_%s",
816         gst_element_state_get_name (old_state),
817         gst_element_state_get_name (new_state));
818     dump_dot_file (self, transition_name);
819     g_free (transition_name);
820
821     self->current_state = new_state;
822
823     if (new_state == GST_STATE_PLAYING
824         && pending_state == GST_STATE_VOID_PENDING) {
825       add_tick_source (self);
826     }
827   }
828 }
829
830 static void
831 duration_changed_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
832     gpointer user_data)
833 {
834   GstTranscoder *self = GST_TRANSCODER (user_data);
835   gint64 duration;
836
837   if (gst_element_query_duration (self->transcodebin, GST_FORMAT_TIME,
838           &duration)) {
839     emit_duration_changed (self, duration);
840   }
841 }
842
843 static void
844 latency_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
845     gpointer user_data)
846 {
847   GstTranscoder *self = GST_TRANSCODER (user_data);
848
849   GST_DEBUG_OBJECT (self, "Latency changed");
850
851   gst_bin_recalculate_latency (GST_BIN (self->transcodebin));
852 }
853
854 static void
855 request_state_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
856     gpointer user_data)
857 {
858   GstTranscoder *self = GST_TRANSCODER (user_data);
859   GstState state;
860   GstStateChangeReturn state_ret;
861
862   gst_message_parse_request_state (msg, &state);
863
864   GST_DEBUG_OBJECT (self, "State %s requested",
865       gst_element_state_get_name (state));
866
867   self->target_state = state;
868   state_ret = gst_element_set_state (self->transcodebin, state);
869   if (state_ret == GST_STATE_CHANGE_FAILURE)
870     emit_error (self, g_error_new (GST_TRANSCODER_ERROR,
871             GST_TRANSCODER_ERROR_FAILED,
872             "Failed to change to requested state %s",
873             gst_element_state_get_name (state)), NULL);
874 }
875
876 static void
877 element_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
878 {
879   GstTranscoder *self = GST_TRANSCODER (user_data);
880   const GstStructure *s;
881
882   s = gst_message_get_structure (msg);
883   if (gst_structure_has_name (s, "redirect")) {
884     const gchar *new_location;
885
886     new_location = gst_structure_get_string (s, "new-location");
887     if (!new_location) {
888       const GValue *locations_list, *location_val;
889       guint i, size;
890
891       locations_list = gst_structure_get_value (s, "locations");
892       size = gst_value_list_get_size (locations_list);
893       for (i = 0; i < size; ++i) {
894         const GstStructure *location_s;
895
896         location_val = gst_value_list_get_value (locations_list, i);
897         if (!GST_VALUE_HOLDS_STRUCTURE (location_val))
898           continue;
899
900         location_s = (const GstStructure *) g_value_get_boxed (location_val);
901         if (!gst_structure_has_name (location_s, "redirect"))
902           continue;
903
904         new_location = gst_structure_get_string (location_s, "new-location");
905         if (new_location)
906           break;
907       }
908     }
909
910     if (new_location) {
911       GST_FIXME_OBJECT (self, "Handle redirection to '%s'", new_location);
912     }
913   }
914 }
915
916
917 static gpointer
918 gst_transcoder_main (gpointer data)
919 {
920   GstTranscoder *self = GST_TRANSCODER (data);
921   GstBus *bus;
922   GSource *source;
923   GSource *bus_source;
924
925   GST_TRACE_OBJECT (self, "Starting main thread");
926
927   g_main_context_push_thread_default (self->context);
928
929   source = g_idle_source_new ();
930   g_source_set_callback (source, (GSourceFunc) main_loop_running_cb, self,
931       NULL);
932   g_source_attach (source, self->context);
933   g_source_unref (source);
934
935   self->bus = bus = gst_element_get_bus (self->transcodebin);
936   bus_source = gst_bus_create_watch (bus);
937   g_source_set_callback (bus_source, (GSourceFunc) gst_bus_async_signal_func,
938       NULL, NULL);
939   g_source_attach (bus_source, self->context);
940
941   g_signal_connect (G_OBJECT (bus), "message::error", G_CALLBACK (error_cb),
942       self);
943   g_signal_connect (G_OBJECT (bus), "message::warning", G_CALLBACK (warning_cb),
944       self);
945   g_signal_connect (G_OBJECT (bus), "message::eos", G_CALLBACK (eos_cb), self);
946   g_signal_connect (G_OBJECT (bus), "message::state-changed",
947       G_CALLBACK (state_changed_cb), self);
948   g_signal_connect (G_OBJECT (bus), "message::clock-lost",
949       G_CALLBACK (clock_lost_cb), self);
950   g_signal_connect (G_OBJECT (bus), "message::duration-changed",
951       G_CALLBACK (duration_changed_cb), self);
952   g_signal_connect (G_OBJECT (bus), "message::latency",
953       G_CALLBACK (latency_cb), self);
954   g_signal_connect (G_OBJECT (bus), "message::request-state",
955       G_CALLBACK (request_state_cb), self);
956   g_signal_connect (G_OBJECT (bus), "message::element",
957       G_CALLBACK (element_cb), self);
958
959   self->target_state = GST_STATE_NULL;
960   self->current_state = GST_STATE_NULL;
961   self->is_eos = FALSE;
962   self->is_live = FALSE;
963
964   GST_TRACE_OBJECT (self, "Starting main loop");
965   g_main_loop_run (self->loop);
966   GST_TRACE_OBJECT (self, "Stopped main loop");
967
968   g_source_destroy (bus_source);
969   g_source_unref (bus_source);
970   gst_object_unref (bus);
971
972   remove_tick_source (self);
973
974   g_main_context_pop_thread_default (self->context);
975
976   self->target_state = GST_STATE_NULL;
977   self->current_state = GST_STATE_NULL;
978   if (self->transcodebin) {
979     gst_element_set_state (self->transcodebin, GST_STATE_NULL);
980     g_clear_object (&self->transcodebin);
981   }
982
983   GST_TRACE_OBJECT (self, "Stopped main thread");
984
985   return NULL;
986 }
987
988 static gpointer
989 gst_transcoder_init_once (G_GNUC_UNUSED gpointer user_data)
990 {
991   gst_init (NULL, NULL);
992
993   GST_DEBUG_CATEGORY_INIT (gst_transcoder_debug, "gst-transcoder", 0,
994       "GstTranscoder");
995   gst_transcoder_error_quark ();
996
997   return NULL;
998 }
999
1000 static GstEncodingProfile *
1001 create_encoding_profile (const gchar * pname)
1002 {
1003   GstEncodingProfile *profile;
1004   GValue value = G_VALUE_INIT;
1005
1006   g_value_init (&value, GST_TYPE_ENCODING_PROFILE);
1007
1008   if (!gst_value_deserialize (&value, pname)) {
1009     g_value_reset (&value);
1010
1011     return NULL;
1012   }
1013
1014   profile = g_value_dup_object (&value);
1015   g_value_reset (&value);
1016
1017   return profile;
1018 }
1019
1020 /**
1021  * gst_transcoder_new:
1022  * @source_uri: The URI of the media stream to transcode
1023  * @dest_uri: The URI of the destination of the transcoded stream
1024  * @encoding_profile: The serialized #GstEncodingProfile defining the output
1025  * format. Have a look at the #GstEncodingProfile documentation to find more
1026  * about the serialization format.
1027  *
1028  * Returns: a new #GstTranscoder instance
1029  */
1030 GstTranscoder *
1031 gst_transcoder_new (const gchar * source_uri,
1032     const gchar * dest_uri, const gchar * encoding_profile)
1033 {
1034   GstEncodingProfile *profile;
1035
1036   profile = create_encoding_profile (encoding_profile);
1037
1038   return gst_transcoder_new_full (source_uri, dest_uri, profile, NULL);
1039 }
1040
1041 /**
1042  * gst_transcoder_new_full:
1043  * @source_uri: The URI of the media stream to transcode
1044  * @dest_uri: The URI of the destination of the transcoded stream
1045  * @profile: The #GstEncodingProfile defining the output format
1046  * have a look at the #GstEncodingProfile documentation to find more
1047  * about the serialization format.
1048  * @signal_dispatcher: The #GstTranscoderSignalDispatcher to be used
1049  * to dispatch the various signals.
1050  *
1051  * Returns: a new #GstTranscoder instance
1052  */
1053 GstTranscoder *
1054 gst_transcoder_new_full (const gchar * source_uri,
1055     const gchar * dest_uri, GstEncodingProfile * profile,
1056     GstTranscoderSignalDispatcher * signal_dispatcher)
1057 {
1058   static GOnce once = G_ONCE_INIT;
1059
1060   g_once (&once, gst_transcoder_init_once, NULL);
1061
1062   g_return_val_if_fail (source_uri, NULL);
1063   g_return_val_if_fail (dest_uri, NULL);
1064
1065   return g_object_new (GST_TYPE_TRANSCODER, "src-uri", source_uri,
1066       "dest-uri", dest_uri, "profile", profile,
1067       "signal-dispatcher", signal_dispatcher, NULL);
1068 }
1069
1070 typedef struct
1071 {
1072   GError *error;
1073   GMainLoop *loop;
1074 } RunSyncData;
1075
1076 static void
1077 _error_cb (GstTranscoder * self, GError * error, GstStructure * details,
1078     RunSyncData * data)
1079 {
1080   if (data->error == NULL)
1081     g_propagate_error (&data->error, error);
1082
1083   if (data->loop) {
1084     g_main_loop_quit (data->loop);
1085     data->loop = NULL;
1086   }
1087 }
1088
1089 static void
1090 _done_cb (GstTranscoder * self, RunSyncData * data)
1091 {
1092   if (data->loop) {
1093     g_main_loop_quit (data->loop);
1094     data->loop = NULL;
1095   }
1096 }
1097
1098 /**
1099  * gst_transcoder_run:
1100  * @self: The GstTranscoder to run
1101  * @error: (allow-none): An error to be set if transcoding fails
1102  *
1103  * Run the transcoder task synchonously. You can connect
1104  * to the 'position' signal to get information about the
1105  * progress of the transcoding.
1106  */
1107 gboolean
1108 gst_transcoder_run (GstTranscoder * self, GError ** error)
1109 {
1110   RunSyncData data = { 0, };
1111
1112   data.loop = g_main_loop_new (NULL, FALSE);
1113   g_signal_connect (self, "error", G_CALLBACK (_error_cb), &data);
1114   g_signal_connect (self, "done", G_CALLBACK (_done_cb), &data);
1115   gst_transcoder_run_async (self);
1116
1117   if (!data.error)
1118     g_main_loop_run (data.loop);
1119
1120   g_signal_handlers_disconnect_by_func (self, _error_cb, &data);
1121   g_signal_handlers_disconnect_by_func (self, _done_cb, &data);
1122
1123   if (data.error) {
1124     if (error)
1125       g_propagate_error (error, data.error);
1126
1127     g_clear_error (&data.error);
1128     return FALSE;
1129   }
1130
1131   return TRUE;
1132 }
1133
1134 /**
1135  * gst_transcoder_run_async:
1136  * @self: The GstTranscoder to run
1137  *
1138  * Run the transcoder task asynchronously. You should connect
1139  * to the 'done' signal to be notified about when the
1140  * transcoding is done, and to the 'error' signal to be
1141  * notified about any error.
1142  */
1143 void
1144 gst_transcoder_run_async (GstTranscoder * self)
1145 {
1146   GstStateChangeReturn state_ret;
1147
1148   GST_DEBUG_OBJECT (self, "Play");
1149
1150   if (!self->profile) {
1151     emit_error (self, g_error_new (GST_TRANSCODER_ERROR,
1152             GST_TRANSCODER_ERROR_FAILED, "No \"profile\" provided"), NULL);
1153
1154     return;
1155   }
1156
1157   self->target_state = GST_STATE_PLAYING;
1158   state_ret = gst_element_set_state (self->transcodebin, GST_STATE_PLAYING);
1159
1160   if (state_ret == GST_STATE_CHANGE_FAILURE) {
1161     emit_error (self, g_error_new (GST_TRANSCODER_ERROR,
1162             GST_TRANSCODER_ERROR_FAILED, "Could not start transcoding"), NULL);
1163     return;
1164   } else if (state_ret == GST_STATE_CHANGE_NO_PREROLL) {
1165     self->is_live = TRUE;
1166     GST_DEBUG_OBJECT (self, "Pipeline is live");
1167   }
1168
1169   return;
1170 }
1171
1172 static gboolean
1173 gst_transcoder_set_position_update_interval_internal (gpointer user_data)
1174 {
1175   GstTranscoder *self = user_data;
1176
1177   GST_OBJECT_LOCK (self);
1178
1179   if (self->tick_source) {
1180     remove_tick_source (self);
1181     add_tick_source (self);
1182   }
1183
1184   GST_OBJECT_UNLOCK (self);
1185
1186   return G_SOURCE_REMOVE;
1187 }
1188
1189 /**
1190  * gst_transcoder_set_position_update_interval:
1191  * @self: #GstTranscoder instance
1192  * @interval: interval in ms
1193  *
1194  * Set interval in milliseconds between two position-updated signals.
1195  * Pass 0 to stop updating the position.
1196  */
1197 void
1198 gst_transcoder_set_position_update_interval (GstTranscoder * self,
1199     guint interval)
1200 {
1201   g_return_if_fail (GST_IS_TRANSCODER (self));
1202   g_return_if_fail (interval <= 10000);
1203
1204   GST_OBJECT_LOCK (self);
1205   self->position_update_interval_ms = interval;
1206   GST_OBJECT_UNLOCK (self);
1207
1208   gst_transcoder_set_position_update_interval_internal (self);
1209 }
1210
1211 /**
1212  * gst_transcoder_get_position_update_interval:
1213  * @self: #GstTranscoder instance
1214  *
1215  * Returns: current position update interval in milliseconds
1216  */
1217 guint
1218 gst_transcoder_get_position_update_interval (GstTranscoder * self)
1219 {
1220   g_return_val_if_fail (GST_IS_TRANSCODER (self),
1221       DEFAULT_POSITION_UPDATE_INTERVAL_MS);
1222
1223   return self->position_update_interval_ms;
1224 }
1225
1226 /**
1227  * gst_transcoder_get_source_uri:
1228  * @self: #GstTranscoder instance
1229  *
1230  * Gets the URI of the currently-transcoding stream.
1231  *
1232  * Returns: (transfer full): a string containing the URI of the
1233  * source stream. g_free() after usage.
1234  */
1235 gchar *
1236 gst_transcoder_get_source_uri (GstTranscoder * self)
1237 {
1238   gchar *val;
1239
1240   g_return_val_if_fail (GST_IS_TRANSCODER (self), DEFAULT_URI);
1241
1242   g_object_get (self, "src-uri", &val, NULL);
1243
1244   return val;
1245 }
1246
1247 /**
1248  * gst_transcoder_get_dest_uri:
1249  * @self: #GstTranscoder instance
1250  *
1251  * Gets the URI of the destination of the transcoded stream.
1252  *
1253  * Returns: (transfer full): a string containing the URI of the
1254  * destination of the transcoded stream. g_free() after usage.
1255  */
1256 gchar *
1257 gst_transcoder_get_dest_uri (GstTranscoder * self)
1258 {
1259   gchar *val;
1260
1261   g_return_val_if_fail (GST_IS_TRANSCODER (self), DEFAULT_URI);
1262
1263   g_object_get (self, "dest-uri", &val, NULL);
1264
1265   return val;
1266 }
1267
1268 /**
1269  * gst_transcoder_get_position:
1270  * @self: #GstTranscoder instance
1271  *
1272  * Returns: the absolute position time, in nanoseconds, of the
1273  * transcoding stream.
1274  */
1275 GstClockTime
1276 gst_transcoder_get_position (GstTranscoder * self)
1277 {
1278   GstClockTime val;
1279
1280   g_return_val_if_fail (GST_IS_TRANSCODER (self), DEFAULT_POSITION);
1281
1282   g_object_get (self, "position", &val, NULL);
1283
1284   return val;
1285 }
1286
1287 /**
1288  * gst_transcoder_get_duration:
1289  * @self: #GstTranscoder instance
1290  *
1291  * Retrieves the duration of the media stream that self represents.
1292  *
1293  * Returns: the duration of the transcoding media stream, in
1294  * nanoseconds.
1295  */
1296 GstClockTime
1297 gst_transcoder_get_duration (GstTranscoder * self)
1298 {
1299   GstClockTime val;
1300
1301   g_return_val_if_fail (GST_IS_TRANSCODER (self), DEFAULT_DURATION);
1302
1303   g_object_get (self, "duration", &val, NULL);
1304
1305   return val;
1306 }
1307
1308 /**
1309  * gst_transcoder_get_pipeline:
1310  * @self: #GstTranscoder instance
1311  *
1312  * Returns: (transfer full): The internal uritranscodebin instance
1313  */
1314 GstElement *
1315 gst_transcoder_get_pipeline (GstTranscoder * self)
1316 {
1317   GstElement *val;
1318
1319   g_return_val_if_fail (GST_IS_TRANSCODER (self), NULL);
1320
1321   g_object_get (self, "pipeline", &val, NULL);
1322
1323   return val;
1324 }
1325
1326 /**
1327  * gst_transcoder_get_avoid_reencoding:
1328  * @self: The #GstTranscoder to check whether reencoding is avoided or not.
1329  *
1330  * Returns: %TRUE if the transcoder tries to avoid reencoding streams where
1331  * reencoding is not strictly needed, %FALSE otherwise.
1332  */
1333 gboolean
1334 gst_transcoder_get_avoid_reencoding (GstTranscoder * self)
1335 {
1336   gboolean val;
1337
1338   g_return_val_if_fail (GST_IS_TRANSCODER (self), FALSE);
1339
1340   g_object_get (self->transcodebin, "avoid-reencoding", &val, NULL);
1341
1342   return val;
1343 }
1344
1345 /**
1346  * gst_transcoder_set_avoid_reencoding:
1347  * @self: The #GstTranscoder to set whether reencoding should be avoided or not.
1348  * @avoid_reencoding: %TRUE if the transcoder should try to avoid reencoding
1349  * streams where * reencoding is not strictly needed, %FALSE otherwise.
1350  */
1351 void
1352 gst_transcoder_set_avoid_reencoding (GstTranscoder * self,
1353     gboolean avoid_reencoding)
1354 {
1355   g_return_if_fail (GST_IS_TRANSCODER (self));
1356
1357   g_object_set (self->transcodebin, "avoid-reencoding", avoid_reencoding, NULL);
1358 }
1359
1360 #define C_ENUM(v) ((gint) v)
1361 #define C_FLAGS(v) ((guint) v)
1362
1363 GType
1364 gst_transcoder_error_get_type (void)
1365 {
1366   static gsize id = 0;
1367   static const GEnumValue values[] = {
1368     {C_ENUM (GST_TRANSCODER_ERROR_FAILED), "GST_TRANSCODER_ERROR_FAILED",
1369         "failed"},
1370     {0, NULL, NULL}
1371   };
1372
1373   if (g_once_init_enter (&id)) {
1374     GType tmp = g_enum_register_static ("GstTranscoderError", values);
1375     g_once_init_leave (&id, tmp);
1376   }
1377
1378   return (GType) id;
1379 }
1380
1381 /**
1382  * gst_transcoder_error_get_name:
1383  * @error: a #GstTranscoderError
1384  *
1385  * Gets a string representing the given error.
1386  *
1387  * Returns: (transfer none): a string with the given error.
1388  */
1389 const gchar *
1390 gst_transcoder_error_get_name (GstTranscoderError error)
1391 {
1392   switch (error) {
1393     case GST_TRANSCODER_ERROR_FAILED:
1394       return "failed";
1395   }
1396
1397   g_assert_not_reached ();
1398   return NULL;
1399 }
1400
1401 G_DEFINE_INTERFACE (GstTranscoderSignalDispatcher,
1402     gst_transcoder_signal_dispatcher, G_TYPE_OBJECT);
1403
1404 static void
1405 gst_transcoder_signal_dispatcher_default_init (G_GNUC_UNUSED
1406     GstTranscoderSignalDispatcherInterface * iface)
1407 {
1408
1409 }
1410
1411 static void
1412 gst_transcoder_signal_dispatcher_dispatch (GstTranscoderSignalDispatcher * self,
1413     GstTranscoder * transcoder, void (*emitter) (gpointer data), gpointer data,
1414     GDestroyNotify destroy)
1415 {
1416   GstTranscoderSignalDispatcherInterface *iface;
1417
1418   if (!self) {
1419     emitter (data);
1420     if (destroy)
1421       destroy (data);
1422     return;
1423   }
1424
1425   g_return_if_fail (GST_IS_TRANSCODER_SIGNAL_DISPATCHER (self));
1426   iface = GST_TRANSCODER_SIGNAL_DISPATCHER_GET_INTERFACE (self);
1427   g_return_if_fail (iface->dispatch != NULL);
1428
1429   iface->dispatch (self, transcoder, emitter, data, destroy);
1430 }
1431
1432 struct _GstTranscoderGMainContextSignalDispatcher
1433 {
1434   GObject parent;
1435   GMainContext *application_context;
1436 };
1437
1438 struct _GstTranscoderGMainContextSignalDispatcherClass
1439 {
1440   GObjectClass parent_class;
1441 };
1442
1443 static void
1444     gst_transcoder_g_main_context_signal_dispatcher_interface_init
1445     (GstTranscoderSignalDispatcherInterface * iface);
1446
1447 enum
1448 {
1449   G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_0,
1450   G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_APPLICATION_CONTEXT,
1451   G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_LAST
1452 };
1453
1454 G_DEFINE_TYPE_WITH_CODE (GstTranscoderGMainContextSignalDispatcher,
1455     gst_transcoder_g_main_context_signal_dispatcher, G_TYPE_OBJECT,
1456     G_IMPLEMENT_INTERFACE (GST_TYPE_TRANSCODER_SIGNAL_DISPATCHER,
1457         gst_transcoder_g_main_context_signal_dispatcher_interface_init));
1458
1459 static GParamSpec
1460     * g_main_context_signal_dispatcher_param_specs
1461     [G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_LAST] = { NULL, };
1462
1463 static void
1464 gst_transcoder_g_main_context_signal_dispatcher_finalize (GObject * object)
1465 {
1466   GstTranscoderGMainContextSignalDispatcher *self =
1467       GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (object);
1468
1469   if (self->application_context)
1470     g_main_context_unref (self->application_context);
1471
1472   G_OBJECT_CLASS
1473       (gst_transcoder_g_main_context_signal_dispatcher_parent_class)->finalize
1474       (object);
1475 }
1476
1477 static void
1478 gst_transcoder_g_main_context_signal_dispatcher_set_property (GObject * object,
1479     guint prop_id, const GValue * value, GParamSpec * pspec)
1480 {
1481   GstTranscoderGMainContextSignalDispatcher *self =
1482       GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (object);
1483
1484   switch (prop_id) {
1485     case G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_APPLICATION_CONTEXT:
1486       self->application_context = g_value_dup_boxed (value);
1487       if (!self->application_context)
1488         self->application_context = g_main_context_ref_thread_default ();
1489       break;
1490     default:
1491       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1492       break;
1493   }
1494 }
1495
1496 static void
1497 gst_transcoder_g_main_context_signal_dispatcher_get_property (GObject * object,
1498     guint prop_id, GValue * value, GParamSpec * pspec)
1499 {
1500   GstTranscoderGMainContextSignalDispatcher *self =
1501       GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (object);
1502
1503   switch (prop_id) {
1504     case G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_APPLICATION_CONTEXT:
1505       g_value_set_boxed (value, self->application_context);
1506       break;
1507     default:
1508       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1509       break;
1510   }
1511 }
1512
1513 static void
1514     gst_transcoder_g_main_context_signal_dispatcher_class_init
1515     (GstTranscoderGMainContextSignalDispatcherClass * klass)
1516 {
1517   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1518
1519   gobject_class->finalize =
1520       gst_transcoder_g_main_context_signal_dispatcher_finalize;
1521   gobject_class->set_property =
1522       gst_transcoder_g_main_context_signal_dispatcher_set_property;
1523   gobject_class->get_property =
1524       gst_transcoder_g_main_context_signal_dispatcher_get_property;
1525
1526   g_main_context_signal_dispatcher_param_specs
1527       [G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_APPLICATION_CONTEXT] =
1528       g_param_spec_boxed ("application-context", "Application Context",
1529       "Application GMainContext to dispatch signals to", G_TYPE_MAIN_CONTEXT,
1530       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1531
1532   g_object_class_install_properties (gobject_class,
1533       G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_LAST,
1534       g_main_context_signal_dispatcher_param_specs);
1535 }
1536
1537 static void
1538     gst_transcoder_g_main_context_signal_dispatcher_init
1539     (G_GNUC_UNUSED GstTranscoderGMainContextSignalDispatcher * self)
1540 {
1541 }
1542
1543 typedef struct
1544 {
1545   void (*emitter) (gpointer data);
1546   gpointer data;
1547   GDestroyNotify destroy;
1548 } GMainContextSignalDispatcherData;
1549
1550 static gboolean
1551 g_main_context_signal_dispatcher_dispatch_gsourcefunc (gpointer user_data)
1552 {
1553   GMainContextSignalDispatcherData *data = user_data;
1554
1555   data->emitter (data->data);
1556
1557   return G_SOURCE_REMOVE;
1558 }
1559
1560 static void
1561 g_main_context_signal_dispatcher_dispatch_destroy (gpointer user_data)
1562 {
1563   GMainContextSignalDispatcherData *data = user_data;
1564
1565   if (data->destroy)
1566     data->destroy (data->data);
1567   g_free (data);
1568 }
1569
1570 /* *INDENT-OFF* */
1571 static void
1572 gst_transcoder_g_main_context_signal_dispatcher_dispatch (GstTranscoderSignalDispatcher * iface,
1573     G_GNUC_UNUSED GstTranscoder * transcoder, void (*emitter) (gpointer data),
1574     gpointer data, GDestroyNotify destroy)
1575 {
1576   GstTranscoderGMainContextSignalDispatcher *self =
1577       GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (iface);
1578   GMainContextSignalDispatcherData *gsourcefunc_data =
1579       g_new0 (GMainContextSignalDispatcherData, 1);
1580
1581   gsourcefunc_data->emitter = emitter;
1582   gsourcefunc_data->data = data;
1583   gsourcefunc_data->destroy = destroy;
1584
1585   g_main_context_invoke_full (self->application_context,
1586       G_PRIORITY_DEFAULT, g_main_context_signal_dispatcher_dispatch_gsourcefunc,
1587       gsourcefunc_data, g_main_context_signal_dispatcher_dispatch_destroy);
1588 }
1589
1590 static void
1591 gst_transcoder_g_main_context_signal_dispatcher_interface_init (GstTranscoderSignalDispatcherInterface * iface)
1592 {
1593   iface->dispatch = gst_transcoder_g_main_context_signal_dispatcher_dispatch;
1594 }
1595 /* *INDENT-ON* */
1596
1597 /**
1598  * gst_transcoder_g_main_context_signal_dispatcher_new:
1599  * @application_context: (allow-none): GMainContext to use or %NULL
1600  *
1601  * Returns: (transfer full):
1602  */
1603 GstTranscoderSignalDispatcher *
1604 gst_transcoder_g_main_context_signal_dispatcher_new (GMainContext *
1605     application_context)
1606 {
1607   return g_object_new (GST_TYPE_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER,
1608       "application-context", application_context, NULL);
1609 }