decodebin3: do not drop the eos event
[platform/upstream/gst-plugins-base.git] / gst / playback / gstdecodebin3.c
1 /* GStreamer
2  *
3  * Copyright (C) <2015> Centricular Ltd
4  *  @author: Edward Hervey <edward@centricular.com>
5  *  @author: Jan Schmidt <jan@centricular.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <glib.h>
28 #include <glib-object.h>
29 #include <glib/gprintf.h>
30 #include <gst/gst.h>
31 #include <gst/pbutils/pbutils.h>
32
33 #include "gstplayback.h"
34 #include "gstplay-enum.h"
35 #include "gstrawcaps.h"
36
37 /**
38  * SECTION:element-decodebin3
39  * @title: decodebin3
40  *
41  * #GstBin that auto-magically constructs a decoding pipeline using available
42  * decoders and demuxers via auto-plugging. The output is raw audio, video
43  * or subtitle streams.
44  *
45  * decodebin3 differs from the previous decodebin (decodebin2) in important ways:
46  *
47  * * supports publication and selection of stream information via
48  * GstStreamCollection messages and #GST_EVENT_SELECT_STREAM events.
49  *
50  * * dynamically switches stream connections internally, and
51  * reuses decoder elements when stream selections change, so that in
52  * the normal case it maintains 1 decoder of each type (video/audio/subtitle)
53  * and only creates new elements when streams change and an existing decoder
54  * is not capable of handling the new format.
55  *
56  * * supports multiple input pads for the parallel decoding of auxilliary streams
57  * not muxed with the primary stream.
58  *
59  * * does not handle network stream buffering. decodebin3 expects that network stream
60  * buffering is handled upstream, before data is passed to it.
61  *
62  * <emphasis>decodebin3 is still experimental API and a technology preview.
63  * Its behaviour and exposed API is subject to change.</emphasis>
64  *
65  */
66
67 /**
68  * Global design
69  *
70  * 1) From sink pad to elementary streams (GstParseBin)
71  *
72  * The input sink pads are fed to GstParseBin. GstParseBin will feed them
73  * through typefind. When the caps are detected (or changed) we recursively
74  * figure out which demuxer, parser or depayloader is needed until we get to
75  * elementary streams.
76  *
77  * All elementary streams (whether decoded or not, whether exposed or not) are
78  * fed through multiqueue. There is only *one* multiqueue in decodebin3.
79  *
80  * => MultiQueue is the cornerstone.
81  * => No buffering before multiqueue
82  *
83  * 2) Elementary streams
84  *
85  * After GstParseBin, there are 3 main components:
86  *  1) Input Streams (provided by GstParseBin)
87  *  2) Multiqueue slots
88  *  3) Output Streams
89  *
90  * Input Streams correspond to the stream coming from GstParseBin and that gets
91  * fed into a multiqueue slot.
92  *
93  * Output Streams correspond to the combination of a (optional) decoder and an
94  * output ghostpad. Output Streams can be moved from one multiqueue slot to
95  * another, can reconfigure itself (different decoders), and can be
96  * added/removed depending on the configuration (all streams outputted, only one
97  * of each type, ...).
98  *
99  * Multiqueue slots correspond to a pair of sink/src pad from multiqueue. For
100  * each 'active' Input Stream there is a corresponding slot.
101  * Slots might have different streams on input and output (due to internal
102  * buffering).
103  *
104  * Due to internal queuing/buffering/..., all those components (might) behave
105  * asynchronously. Therefore probes will be used on each component source pad to
106  * detect various key-points:
107  *  * EOS :
108  *     the stream is done => Mark that component as done, optionally freeing/removing it
109  *  * STREAM_START :
110  *     a new stream is starting => link it further if needed
111  *
112  * 3) Gradual replacement
113  *
114  * If the caps change at any point in decodebin (input sink pad, demuxer output,
115  * multiqueue output, ..), we gradually replace (if needed) the following elements.
116  *
117  * This is handled by the probes in various locations:
118  *  a) typefind output
119  *  b) multiqueue input (source pad of Input Streams)
120  *  c) multiqueue output (source pad of Multiqueue Slots)
121  *  d) final output (target of source ghostpads)
122  *
123  * When CAPS event arrive at those points, one of three things can happen:
124  * a) There is no elements downstream yet, just create/link-to following elements
125  * b) There are downstream elements, do a ACCEPT_CAPS query
126  *  b.1) The new CAPS are accepted, keep current configuration
127  *  b.2) The new CAPS are not accepted, remove following elements then do a)
128  *
129  *    Components:
130  *
131  *                                                   MultiQ     Output
132  *                     Input(s)                      Slots      Streams
133  *  /-------------------------------------------\   /-----\  /------------- \
134  *
135  * +-------------------------------------------------------------------------+
136  * |                                                                         |
137  * | +---------------------------------------------+                         |
138  * | |   GstParseBin(s)                            |                         |
139  * | |                +--------------+             |  +-----+                |
140  * | |                |              |---[parser]-[|--| Mul |---[ decoder ]-[|
141  * |]--[ typefind ]---|  demuxer(s)  |------------[|  | ti  |                |
142  * | |                |  (if needed) |---[parser]-[|--| qu  |                |
143  * | |                |              |---[parser]-[|--| eu  |---[ decoder ]-[|
144  * | |                +--------------+             |  +------             ^  |
145  * | +---------------------------------------------+        ^             |  |
146  * |                                               ^        |             |  |
147  * +-----------------------------------------------+--------+-------------+--+
148  *                                                 |        |             |
149  *                                                 |        |             |
150  *                                       Probes  --/--------/-------------/
151  *
152  * ATOMIC SWITCHING
153  *
154  * We want to ensure we re-use decoders when switching streams. This takes place
155  * at the multiqueue output level.
156  *
157  * MAIN CONCEPTS
158  *  1) Activating a stream (i.e. linking a slot to an output) is only done within
159  *    the streaming thread in the multiqueue_src_probe() and only if the
160       stream is in the REQUESTED selection.
161  *  2) Deactivating a stream (i.e. unlinking a slot from an output) is also done
162  *    within the stream thread, but only in a purposefully called IDLE probe
163  *    that calls reassign_slot().
164  *
165  * Based on those two principles, 3 "selection" of streams (stream-id) are used:
166  * 1) requested_selection
167  *    All streams within that list should be activated
168  * 2) active_selection
169  *    List of streams that are exposed by decodebin
170  * 3) to_activate
171  *    List of streams that will be moved to requested_selection in the
172  *    reassign_slot() method (i.e. once a stream was deactivated, and the output
173  *    was retargetted)
174  */
175
176
177 GST_DEBUG_CATEGORY_STATIC (decodebin3_debug);
178 #define GST_CAT_DEFAULT decodebin3_debug
179
180 #define GST_TYPE_DECODEBIN3      (gst_decodebin3_get_type ())
181
182 #define EXTRA_DEBUG 1
183
184 #define CUSTOM_FINAL_EOS_QUARK _custom_final_eos_quark_get ()
185 #define CUSTOM_FINAL_EOS_QUARK_DATA "custom-final-eos"
186 static GQuark
187 _custom_final_eos_quark_get (void)
188 {
189   static gsize g_quark;
190
191   if (g_once_init_enter (&g_quark)) {
192     gsize quark =
193         (gsize) g_quark_from_static_string ("decodebin3-custom-final-eos");
194     g_once_init_leave (&g_quark, quark);
195   }
196   return g_quark;
197 }
198
199 typedef struct _GstDecodebin3 GstDecodebin3;
200 typedef struct _GstDecodebin3Class GstDecodebin3Class;
201
202 typedef struct _DecodebinInputStream DecodebinInputStream;
203 typedef struct _DecodebinInput DecodebinInput;
204 typedef struct _DecodebinOutputStream DecodebinOutputStream;
205
206 struct _GstDecodebin3
207 {
208   GstBin bin;
209
210   /* input_lock protects the following variables */
211   GMutex input_lock;
212   /* Main input (static sink pad) */
213   DecodebinInput *main_input;
214   /* Supplementary input (request sink pads) */
215   GList *other_inputs;
216   /* counter for input */
217   guint32 input_counter;
218   /* Current stream group_id (default : GST_GROUP_ID_INVALID) */
219   /* FIXME : Needs to be resetted appropriately (when upstream changes ?) */
220   guint32 current_group_id;
221   /* End of variables protected by input_lock */
222
223   GstElement *multiqueue;
224
225   /* selection_lock protects access to following variables */
226   GMutex selection_lock;
227   GList *input_streams;         /* List of DecodebinInputStream for active collection */
228   GList *output_streams;        /* List of DecodebinOutputStream used for output */
229   GList *slots;                 /* List of MultiQueueSlot */
230   guint slot_id;
231
232   /* Active collection */
233   GstStreamCollection *collection;
234   /* requested selection of stream-id to activate post-multiqueue */
235   GList *requested_selection;
236   /* list of stream-id currently activated in output */
237   GList *active_selection;
238   /* List of stream-id that need to be activated (after a stream switch for ex) */
239   GList *to_activate;
240   /* Pending select streams event */
241   guint32 select_streams_seqnum;
242   /* pending list of streams to select (from downstream) */
243   GList *pending_select_streams;
244   /* TRUE if requested_selection was updated, will become FALSE once
245    * it has fully transitioned to active */
246   gboolean selection_updated;
247   /* End of variables protected by selection_lock */
248
249   /* List of pending collections.
250    * FIXME : Is this really needed ? */
251   GList *pending_collection;
252
253   /* Factories */
254   GMutex factories_lock;
255   guint32 factories_cookie;
256   /* All DECODABLE factories */
257   GList *factories;
258   /* Only DECODER factories */
259   GList *decoder_factories;
260   /* DECODABLE but not DECODER factories */
261   GList *decodable_factories;
262
263   /* counters for pads */
264   guint32 apadcount, vpadcount, tpadcount, opadcount;
265
266   /* Properties */
267   GstCaps *caps;
268 #ifdef TIZEN_FEATURE_FORCE_SW_DECODER
269   gboolean force_sw_decoders_for_video;
270   gboolean force_sw_decoders_for_audio;
271 #else
272   gboolean force_sw_decoders;
273 #endif
274 };
275
276 struct _GstDecodebin3Class
277 {
278   GstBinClass class;
279
280   gint (*select_stream) (GstDecodebin3 * dbin,
281     GstStreamCollection * collection, GstStream * stream);
282
283 #ifdef TIZEN_FEATURE_RESOURCE_MANAGER
284   gboolean (*request_resource) (GstDecodebin3 * dbin,
285     GstStreamCollection * collection, GstStream * stream);
286 #endif
287 };
288
289 /* Input of decodebin, controls input pad and parsebin */
290 struct _DecodebinInput
291 {
292   GstDecodebin3 *dbin;
293
294   gboolean is_main;
295
296   GstPad *ghost_sink;
297   GstPad *parsebin_sink;
298
299   GstStreamCollection *collection;      /* Active collection */
300
301   guint group_id;
302
303   GstElement *parsebin;
304
305   gulong pad_added_sigid;
306   gulong pad_removed_sigid;
307   gulong drained_sigid;
308
309   /* TRUE if the input got drained
310    * FIXME : When do we reset it if re-used ?
311    */
312   gboolean drained;
313
314   /* HACK : Remove these fields */
315   /* List of PendingPad structures */
316   GList *pending_pads;
317 };
318
319 /* Multiqueue Slots */
320 typedef struct _MultiQueueSlot
321 {
322   guint id;
323
324   GstDecodebin3 *dbin;
325   /* Type of stream handled by this slot */
326   GstStreamType type;
327
328   /* Linked input and output */
329   DecodebinInputStream *input;
330
331   /* pending => last stream received on sink pad */
332   GstStream *pending_stream;
333   /* active => last stream outputted on source pad */
334   GstStream *active_stream;
335
336   GstPad *sink_pad, *src_pad;
337
338   /* id of the MQ src_pad event probe */
339   gulong probe_id;
340
341   gboolean is_drained;
342
343   DecodebinOutputStream *output;
344 } MultiQueueSlot;
345
346 /* Streams that are exposed downstream (i.e. output) */
347 struct _DecodebinOutputStream
348 {
349   GstDecodebin3 *dbin;
350   /* The type of stream handled by this output stream */
351   GstStreamType type;
352
353   /* The slot to which this output stream is currently connected to */
354   MultiQueueSlot *slot;
355
356   GstElement *decoder;          /* Optional */
357   GstPad *decoder_sink, *decoder_src;
358   gboolean linked;
359
360   /* ghostpad */
361   GstPad *src_pad;
362   /* Flag if ghost pad is exposed */
363   gboolean src_exposed;
364
365   /* keyframe dropping probe */
366   gulong drop_probe_id;
367 };
368
369 /* Pending pads from parsebin */
370 typedef struct _PendingPad
371 {
372   GstDecodebin3 *dbin;
373   DecodebinInput *input;
374   GstPad *pad;
375
376   gulong buffer_probe;
377   gulong event_probe;
378   gboolean saw_eos;
379 } PendingPad;
380
381 /* properties */
382 enum
383 {
384   PROP_0,
385   PROP_CAPS,
386 #ifdef TIZEN_FEATURE_FORCE_SW_DECODER
387   PROP_FORCE_SW_DECODERS_FOR_VIDEO,
388   PROP_FORCE_SW_DECODERS_FOR_AUDIO,
389 #else
390   PROP_FORCE_SW_DECODERS,
391 #endif
392 };
393
394 /* signals */
395 enum
396 {
397   SIGNAL_SELECT_STREAM,
398   SIGNAL_ABOUT_TO_FINISH,
399 #ifdef TIZEN_FEATURE_RESOURCE_MANAGER
400   SIGNAL_REQUEST_RESOURCE,
401 #endif
402   LAST_SIGNAL
403 };
404 static guint gst_decodebin3_signals[LAST_SIGNAL] = { 0 };
405
406 #define SELECTION_LOCK(dbin) G_STMT_START {                             \
407     GST_LOG_OBJECT (dbin,                                               \
408                     "selection locking from thread %p",                 \
409                     g_thread_self ());                                  \
410     g_mutex_lock (&dbin->selection_lock);                               \
411     GST_LOG_OBJECT (dbin,                                               \
412                     "selection locked from thread %p",                  \
413                     g_thread_self ());                                  \
414   } G_STMT_END
415
416 #define SELECTION_UNLOCK(dbin) G_STMT_START {                           \
417     GST_LOG_OBJECT (dbin,                                               \
418                     "selection unlocking from thread %p",               \
419                     g_thread_self ());                                  \
420     g_mutex_unlock (&dbin->selection_lock);                             \
421   } G_STMT_END
422
423 #define INPUT_LOCK(dbin) G_STMT_START {                         \
424     GST_LOG_OBJECT (dbin,                                               \
425                     "input locking from thread %p",                     \
426                     g_thread_self ());                                  \
427     g_mutex_lock (&dbin->input_lock);                           \
428     GST_LOG_OBJECT (dbin,                                               \
429                     "input locked from thread %p",                      \
430                     g_thread_self ());                                  \
431   } G_STMT_END
432
433 #define INPUT_UNLOCK(dbin) G_STMT_START {                               \
434     GST_LOG_OBJECT (dbin,                                               \
435                     "input unlocking from thread %p",           \
436                     g_thread_self ());                                  \
437     g_mutex_unlock (&dbin->input_lock);                         \
438   } G_STMT_END
439
440 GType gst_decodebin3_get_type (void);
441 #define gst_decodebin3_parent_class parent_class
442 G_DEFINE_TYPE (GstDecodebin3, gst_decodebin3, GST_TYPE_BIN);
443
444 static GstStaticCaps default_raw_caps = GST_STATIC_CAPS (DEFAULT_RAW_CAPS);
445
446 #define DEFAULT_FORCE_SW_DECODERS FALSE
447
448 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
449     GST_PAD_SINK,
450     GST_PAD_ALWAYS,
451     GST_STATIC_CAPS_ANY);
452
453 static GstStaticPadTemplate request_sink_template =
454 GST_STATIC_PAD_TEMPLATE ("sink_%u",
455     GST_PAD_SINK,
456     GST_PAD_REQUEST,
457     GST_STATIC_CAPS_ANY);
458
459 static GstStaticPadTemplate video_src_template =
460 GST_STATIC_PAD_TEMPLATE ("video_%u",
461     GST_PAD_SRC,
462     GST_PAD_SOMETIMES,
463     GST_STATIC_CAPS_ANY);
464
465 static GstStaticPadTemplate audio_src_template =
466 GST_STATIC_PAD_TEMPLATE ("audio_%u",
467     GST_PAD_SRC,
468     GST_PAD_SOMETIMES,
469     GST_STATIC_CAPS_ANY);
470
471 static GstStaticPadTemplate text_src_template =
472 GST_STATIC_PAD_TEMPLATE ("text_%u",
473     GST_PAD_SRC,
474     GST_PAD_SOMETIMES,
475     GST_STATIC_CAPS_ANY);
476
477 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
478     GST_PAD_SRC,
479     GST_PAD_SOMETIMES,
480     GST_STATIC_CAPS_ANY);
481
482
483 static void gst_decodebin3_dispose (GObject * object);
484 static void gst_decodebin3_set_property (GObject * object, guint prop_id,
485     const GValue * value, GParamSpec * pspec);
486 static void gst_decodebin3_get_property (GObject * object, guint prop_id,
487     GValue * value, GParamSpec * pspec);
488 #ifdef TIZEN_FEATURE_RESOURCE_MANAGER
489 static gboolean gst_decodebin3_request_resource (GstElement * element,
490     GstStreamCollection * collection, GstStream * stream);
491 #endif
492
493 static gboolean parsebin_autoplug_continue_cb (GstElement *
494     parsebin, GstPad * pad, GstCaps * caps, GstDecodebin3 * dbin);
495
496 static gint
497 gst_decodebin3_select_stream (GstDecodebin3 * dbin,
498     GstStreamCollection * collection, GstStream * stream)
499 {
500   GST_LOG_OBJECT (dbin, "default select-stream, returning -1");
501
502   return -1;
503 }
504
505 static GstPad *gst_decodebin3_request_new_pad (GstElement * element,
506     GstPadTemplate * temp, const gchar * name, const GstCaps * caps);
507 static void gst_decodebin3_handle_message (GstBin * bin, GstMessage * message);
508 static GstStateChangeReturn gst_decodebin3_change_state (GstElement * element,
509     GstStateChange transition);
510 static gboolean gst_decodebin3_send_event (GstElement * element,
511     GstEvent * event);
512
513 static void gst_decode_bin_update_factories_list (GstDecodebin3 * dbin);
514 #if 0
515 static gboolean have_factory (GstDecodebin3 * dbin, GstCaps * caps,
516     GstElementFactoryListType ftype);
517 #endif
518
519 static void free_input (GstDecodebin3 * dbin, DecodebinInput * input);
520 static void free_input_async (GstDecodebin3 * dbin, DecodebinInput * input);
521 static DecodebinInput *create_new_input (GstDecodebin3 * dbin, gboolean main);
522 static gboolean set_input_group_id (DecodebinInput * input, guint32 * group_id);
523
524 static void reconfigure_output_stream (DecodebinOutputStream * output,
525     MultiQueueSlot * slot);
526 static void free_output_stream (GstDecodebin3 * dbin,
527     DecodebinOutputStream * output);
528 static DecodebinOutputStream *create_output_stream (GstDecodebin3 * dbin,
529     GstStreamType type);
530
531 static GstPadProbeReturn slot_unassign_probe (GstPad * pad,
532     GstPadProbeInfo * info, MultiQueueSlot * slot);
533 static gboolean reassign_slot (GstDecodebin3 * dbin, MultiQueueSlot * slot);
534 static MultiQueueSlot *get_slot_for_input (GstDecodebin3 * dbin,
535     DecodebinInputStream * input);
536 static void link_input_to_slot (DecodebinInputStream * input,
537     MultiQueueSlot * slot);
538 static void free_multiqueue_slot (GstDecodebin3 * dbin, MultiQueueSlot * slot);
539 static void free_multiqueue_slot_async (GstDecodebin3 * dbin,
540     MultiQueueSlot * slot);
541
542 static GstStreamCollection *get_merged_collection (GstDecodebin3 * dbin);
543 static void update_requested_selection (GstDecodebin3 * dbin);
544
545 /* FIXME: Really make all the parser stuff a self-contained helper object */
546 #include "gstdecodebin3-parse.c"
547
548 #ifdef TIZEN_FEATURE_RESOURCE_MANAGER
549 static gboolean gst_decodebin3_request_resource (GstElement * element,
550     GstStreamCollection * collection, GstStream * stream)
551 {
552   /* do not consider the resource limit */
553   return TRUE;
554 }
555
556 static gboolean
557 _gst_boolean_accumulator (GSignalInvocationHint * ihint,
558     GValue * return_accu, const GValue * handler_return, gpointer dummy)
559 {
560   gboolean myboolean;
561
562   myboolean = g_value_get_boolean (handler_return);
563   if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
564     g_value_set_boolean (return_accu, myboolean);
565
566   return myboolean;
567 }
568 #endif
569
570 static gboolean
571 _gst_int_accumulator (GSignalInvocationHint * ihint,
572     GValue * return_accu, const GValue * handler_return, gpointer dummy)
573 {
574   gint res = g_value_get_int (handler_return);
575
576   if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
577     g_value_set_int (return_accu, res);
578
579   if (res == -1)
580     return TRUE;
581
582   return FALSE;
583 }
584
585 static void
586 gst_decodebin3_class_init (GstDecodebin3Class * klass)
587 {
588   GObjectClass *gobject_klass = (GObjectClass *) klass;
589   GstElementClass *element_class = (GstElementClass *) klass;
590   GstBinClass *bin_klass = (GstBinClass *) klass;
591
592   gobject_klass->dispose = gst_decodebin3_dispose;
593   gobject_klass->set_property = gst_decodebin3_set_property;
594   gobject_klass->get_property = gst_decodebin3_get_property;
595
596   /* FIXME : ADD PROPERTIES ! */
597   g_object_class_install_property (gobject_klass, PROP_CAPS,
598       g_param_spec_boxed ("caps", "Caps",
599           "The caps on which to stop decoding. (NULL = default)",
600           GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
601 #ifdef TIZEN_FEATURE_FORCE_SW_DECODER
602   g_object_class_install_property (gobject_klass, PROP_FORCE_SW_DECODERS_FOR_VIDEO,
603       g_param_spec_boolean ("force-sw-decoders-for-video", "Video Software Decoders Only",
604           "Use only sofware decoders for video to process streams",
605           DEFAULT_FORCE_SW_DECODERS,
606           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
607   g_object_class_install_property (gobject_klass, PROP_FORCE_SW_DECODERS_FOR_AUDIO,
608       g_param_spec_boolean ("force-sw-decoders-for-audio", "Audio Software Decoders Only",
609           "Use only sofware decoders for audio to process streams",
610           DEFAULT_FORCE_SW_DECODERS,
611           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
612 #else
613   /**
614    * GstDecodeBin::force-sw-decoders:
615    *
616    * While auto-plugging, if set to %TRUE, those decoders within
617    * "Hardware" klass will be ignored. Otherwise they will be tried.
618    *
619    * Since: 1.18
620    */
621   g_object_class_install_property (gobject_klass, PROP_FORCE_SW_DECODERS,
622       g_param_spec_boolean ("force-sw-decoders", "Software Decoders Only",
623           "Use only sofware decoders to process streams",
624           DEFAULT_FORCE_SW_DECODERS,
625           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
626 #endif
627   /* FIXME : ADD SIGNALS ! */
628   /**
629    * GstDecodebin3::select-stream
630    * @decodebin: a #GstDecodebin3
631    * @collection: a #GstStreamCollection
632    * @stream: a #GstStream
633    *
634    * This signal is emitted whenever @decodebin needs to decide whether
635    * to expose a @stream of a given @collection.
636    *
637    * Returns: 1 if the stream should be selected, 0 if it shouldn't be selected.
638    * A value of -1 (default) lets @decodebin decide what to do with the stream.
639    * */
640   gst_decodebin3_signals[SIGNAL_SELECT_STREAM] =
641       g_signal_new ("select-stream", G_TYPE_FROM_CLASS (klass),
642       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDecodebin3Class, select_stream),
643       _gst_int_accumulator, NULL, g_cclosure_marshal_generic,
644       G_TYPE_INT, 2, GST_TYPE_STREAM_COLLECTION, GST_TYPE_STREAM);
645
646   /**
647    * GstDecodebin3::about-to-finish:
648    *
649    * This signal is emitted when the data for the selected URI is
650    * entirely buffered and it is safe to specify anothe URI.
651    */
652   gst_decodebin3_signals[SIGNAL_ABOUT_TO_FINISH] =
653       g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass),
654       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
655       0, G_TYPE_NONE);
656
657 #ifdef TIZEN_FEATURE_RESOURCE_MANAGER
658   gst_decodebin3_signals[SIGNAL_REQUEST_RESOURCE] =
659       g_signal_new ("request-resource", G_TYPE_FROM_CLASS (klass),
660       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDecodebin3Class, request_resource),
661       _gst_boolean_accumulator, NULL, g_cclosure_marshal_generic,
662       G_TYPE_BOOLEAN, 2, GST_TYPE_STREAM_COLLECTION, GST_TYPE_STREAM);
663 #endif
664
665   element_class->request_new_pad =
666       GST_DEBUG_FUNCPTR (gst_decodebin3_request_new_pad);
667   element_class->change_state = GST_DEBUG_FUNCPTR (gst_decodebin3_change_state);
668   element_class->send_event = GST_DEBUG_FUNCPTR (gst_decodebin3_send_event);
669
670   gst_element_class_add_pad_template (element_class,
671       gst_static_pad_template_get (&sink_template));
672   gst_element_class_add_pad_template (element_class,
673       gst_static_pad_template_get (&request_sink_template));
674   gst_element_class_add_pad_template (element_class,
675       gst_static_pad_template_get (&video_src_template));
676   gst_element_class_add_pad_template (element_class,
677       gst_static_pad_template_get (&audio_src_template));
678   gst_element_class_add_pad_template (element_class,
679       gst_static_pad_template_get (&text_src_template));
680   gst_element_class_add_pad_template (element_class,
681       gst_static_pad_template_get (&src_template));
682
683   gst_element_class_set_static_metadata (element_class,
684       "Decoder Bin 3", "Generic/Bin/Decoder",
685       "Autoplug and decode to raw media",
686       "Edward Hervey <edward@centricular.com>");
687
688   bin_klass->handle_message = gst_decodebin3_handle_message;
689
690   klass->select_stream = gst_decodebin3_select_stream;
691
692 #ifdef TIZEN_FEATURE_RESOURCE_MANAGER
693   klass->request_resource = gst_decodebin3_request_resource;
694 #endif
695 }
696
697 static void
698 gst_decodebin3_init (GstDecodebin3 * dbin)
699 {
700   /* Create main input */
701   dbin->main_input = create_new_input (dbin, TRUE);
702
703   dbin->multiqueue = gst_element_factory_make ("multiqueue", NULL);
704   g_object_set (dbin->multiqueue, "sync-by-running-time", TRUE,
705       "max-size-buffers", 0, "use-interleave", TRUE, NULL);
706   gst_bin_add ((GstBin *) dbin, dbin->multiqueue);
707
708   dbin->current_group_id = GST_GROUP_ID_INVALID;
709
710   g_mutex_init (&dbin->factories_lock);
711   g_mutex_init (&dbin->selection_lock);
712   g_mutex_init (&dbin->input_lock);
713
714   dbin->caps = gst_static_caps_get (&default_raw_caps);
715 #ifdef TIZEN_FEATURE_FORCE_SW_DECODER
716   dbin->force_sw_decoders_for_video = DEFAULT_FORCE_SW_DECODERS;
717   dbin->force_sw_decoders_for_audio = DEFAULT_FORCE_SW_DECODERS;
718 #else
719   dbin->force_sw_decoders = DEFAULT_FORCE_SW_DECODERS;
720 #endif
721
722   GST_OBJECT_FLAG_SET (dbin, GST_BIN_FLAG_STREAMS_AWARE);
723 }
724
725 static void
726 gst_decodebin3_dispose (GObject * object)
727 {
728   GstDecodebin3 *dbin = (GstDecodebin3 *) object;
729   GList *walk, *next;
730
731   if (dbin->factories)
732     gst_plugin_feature_list_free (dbin->factories);
733   if (dbin->decoder_factories)
734     g_list_free (dbin->decoder_factories);
735   if (dbin->decodable_factories)
736     g_list_free (dbin->decodable_factories);
737   g_list_free_full (dbin->requested_selection, g_free);
738   g_list_free (dbin->active_selection);
739   g_list_free (dbin->to_activate);
740   g_list_free (dbin->pending_select_streams);
741   g_clear_object (&dbin->collection);
742
743   free_input (dbin, dbin->main_input);
744
745   for (walk = dbin->other_inputs; walk; walk = next) {
746     DecodebinInput *input = walk->data;
747
748     next = g_list_next (walk);
749
750     free_input (dbin, input);
751     dbin->other_inputs = g_list_delete_link (dbin->other_inputs, walk);
752   }
753
754   G_OBJECT_CLASS (parent_class)->dispose (object);
755 }
756
757 static void
758 gst_decodebin3_set_property (GObject * object, guint prop_id,
759     const GValue * value, GParamSpec * pspec)
760 {
761   GstDecodebin3 *dbin = (GstDecodebin3 *) object;
762
763   /* FIXME : IMPLEMENT */
764   switch (prop_id) {
765     case PROP_CAPS:
766       GST_OBJECT_LOCK (dbin);
767       if (dbin->caps)
768         gst_caps_unref (dbin->caps);
769       dbin->caps = g_value_dup_boxed (value);
770       GST_OBJECT_UNLOCK (dbin);
771       break;
772 #ifdef TIZEN_FEATURE_FORCE_SW_DECODER
773     case PROP_FORCE_SW_DECODERS_FOR_VIDEO:
774       dbin->force_sw_decoders_for_video = g_value_get_boolean (value);
775       break;
776     case PROP_FORCE_SW_DECODERS_FOR_AUDIO:
777       dbin->force_sw_decoders_for_audio = g_value_get_boolean (value);
778       break;
779 #else
780     case PROP_FORCE_SW_DECODERS:
781       dbin->force_sw_decoders = g_value_get_boolean (value);
782       break;
783 #endif
784     default:
785       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
786       break;
787   }
788 }
789
790 static void
791 gst_decodebin3_get_property (GObject * object, guint prop_id, GValue * value,
792     GParamSpec * pspec)
793 {
794   GstDecodebin3 *dbin = (GstDecodebin3 *) object;
795
796   /* FIXME : IMPLEMENT */
797   switch (prop_id) {
798     case PROP_CAPS:
799       GST_OBJECT_LOCK (dbin);
800       g_value_set_boxed (value, dbin->caps);
801       GST_OBJECT_UNLOCK (dbin);
802       break;
803 #ifdef TIZEN_FEATURE_FORCE_SW_DECODER
804     case PROP_FORCE_SW_DECODERS_FOR_VIDEO:
805       g_value_set_boolean (value, dbin->force_sw_decoders_for_video);
806       break;
807     case PROP_FORCE_SW_DECODERS_FOR_AUDIO:
808       g_value_set_boolean (value, dbin->force_sw_decoders_for_audio);
809       break;
810 #else
811     case PROP_FORCE_SW_DECODERS:
812       g_value_set_boolean (value, dbin->force_sw_decoders);
813       break;
814 #endif
815     default:
816       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
817       break;
818   }
819 }
820
821 static gboolean
822 parsebin_autoplug_continue_cb (GstElement * parsebin, GstPad * pad,
823     GstCaps * caps, GstDecodebin3 * dbin)
824 {
825   GST_DEBUG_OBJECT (pad, "caps %" GST_PTR_FORMAT, caps);
826
827   /* If it matches our target caps, expose it */
828   if (gst_caps_can_intersect (caps, dbin->caps))
829     return FALSE;
830
831   return TRUE;
832 }
833
834 /* This method should be called whenever a STREAM_START event
835  * comes out of a given parsebin.
836  * The caller shall replace the group_id if the function returns TRUE */
837 static gboolean
838 set_input_group_id (DecodebinInput * input, guint32 * group_id)
839 {
840   GstDecodebin3 *dbin = input->dbin;
841
842   if (input->group_id != *group_id) {
843     if (input->group_id != GST_GROUP_ID_INVALID)
844       GST_WARNING_OBJECT (dbin,
845           "Group id changed (%" G_GUINT32_FORMAT " -> %" G_GUINT32_FORMAT
846           ") on input %p ", input->group_id, *group_id, input);
847     input->group_id = *group_id;
848   }
849
850   if (*group_id != dbin->current_group_id) {
851     if (dbin->current_group_id == GST_GROUP_ID_INVALID) {
852       GST_DEBUG_OBJECT (dbin, "Setting current group id to %" G_GUINT32_FORMAT,
853           *group_id);
854       dbin->current_group_id = *group_id;
855     }
856     *group_id = dbin->current_group_id;
857     return TRUE;
858   }
859
860   return FALSE;
861 }
862
863 static void
864 parsebin_drained_cb (GstElement * parsebin, DecodebinInput * input)
865 {
866   GstDecodebin3 *dbin = input->dbin;
867   gboolean all_drained;
868   GList *tmp;
869
870   GST_WARNING_OBJECT (dbin, "input %p drained", input);
871   input->drained = TRUE;
872
873   all_drained = dbin->main_input->drained;
874   for (tmp = dbin->other_inputs; tmp; tmp = tmp->next) {
875     DecodebinInput *data = (DecodebinInput *) tmp->data;
876
877     all_drained &= data->drained;
878   }
879
880   if (all_drained) {
881     GST_WARNING_OBJECT (dbin, "All inputs drained. Posting about-to-finish");
882     g_signal_emit (dbin, gst_decodebin3_signals[SIGNAL_ABOUT_TO_FINISH], 0,
883         NULL);
884   }
885 }
886
887 /* Call with INPUT_LOCK taken */
888 static gboolean
889 ensure_input_parsebin (GstDecodebin3 * dbin, DecodebinInput * input)
890 {
891   gboolean set_state = FALSE;
892
893   if (input->parsebin == NULL) {
894     input->parsebin = gst_element_factory_make ("parsebin", NULL);
895     if (input->parsebin == NULL)
896       goto no_parsebin;
897     input->parsebin = gst_object_ref (input->parsebin);
898     input->parsebin_sink = gst_element_get_static_pad (input->parsebin, "sink");
899     input->pad_added_sigid =
900         g_signal_connect (input->parsebin, "pad-added",
901         (GCallback) parsebin_pad_added_cb, input);
902     input->pad_removed_sigid =
903         g_signal_connect (input->parsebin, "pad-removed",
904         (GCallback) parsebin_pad_removed_cb, input);
905     input->drained_sigid =
906         g_signal_connect (input->parsebin, "drained",
907         (GCallback) parsebin_drained_cb, input);
908     g_signal_connect (input->parsebin, "autoplug-continue",
909         (GCallback) parsebin_autoplug_continue_cb, dbin);
910   }
911
912   if (GST_OBJECT_PARENT (GST_OBJECT (input->parsebin)) != GST_OBJECT (dbin)) {
913     gst_bin_add (GST_BIN (dbin), input->parsebin);
914     set_state = TRUE;
915   }
916
917   gst_ghost_pad_set_target (GST_GHOST_PAD (input->ghost_sink),
918       input->parsebin_sink);
919   if (set_state)
920     gst_element_sync_state_with_parent (input->parsebin);
921
922   return TRUE;
923
924   /* ERRORS */
925 no_parsebin:
926   {
927     gst_element_post_message ((GstElement *) dbin,
928         gst_missing_element_message_new ((GstElement *) dbin, "parsebin"));
929     return FALSE;
930   }
931 }
932
933 static GstPadLinkReturn
934 gst_decodebin3_input_pad_link (GstPad * pad, GstObject * parent, GstPad * peer)
935 {
936   GstDecodebin3 *dbin = (GstDecodebin3 *) parent;
937   GstPadLinkReturn res = GST_PAD_LINK_OK;
938   DecodebinInput *input;
939
940   GST_LOG_OBJECT (parent, "Got link on input pad %" GST_PTR_FORMAT
941       ". Creating parsebin if needed", pad);
942
943   if ((input = g_object_get_data (G_OBJECT (pad), "decodebin.input")) == NULL)
944     goto fail;
945
946   INPUT_LOCK (dbin);
947   if (!ensure_input_parsebin (dbin, input))
948     res = GST_PAD_LINK_REFUSED;
949   INPUT_UNLOCK (dbin);
950
951   return res;
952 fail:
953   GST_ERROR_OBJECT (parent, "Failed to retrieve input state from ghost pad");
954   return GST_PAD_LINK_REFUSED;
955 }
956
957 /* Drop duration query during _input_pad_unlink */
958 static GstPadProbeReturn
959 query_duration_drop_probe (GstPad * pad, GstPadProbeInfo * info,
960     DecodebinInput * input)
961 {
962   GstPadProbeReturn ret = GST_PAD_PROBE_OK;
963
964   if (GST_IS_QUERY (GST_PAD_PROBE_INFO_DATA (info))) {
965     GstQuery *query = GST_PAD_PROBE_INFO_QUERY (info);
966     if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION) {
967       GST_LOG_OBJECT (pad, "stop forwarding query duration");
968       ret = GST_PAD_PROBE_HANDLED;
969     }
970   }
971
972   return ret;
973 }
974
975 static void
976 gst_decodebin3_input_pad_unlink (GstPad * pad, GstObject * parent)
977 {
978   GstDecodebin3 *dbin = (GstDecodebin3 *) parent;
979   DecodebinInput *input;
980
981   GST_LOG_OBJECT (parent, "Got unlink on input pad %" GST_PTR_FORMAT
982       ". Removing parsebin.", pad);
983
984   if ((input = g_object_get_data (G_OBJECT (pad), "decodebin.input")) == NULL)
985     goto fail;
986
987   INPUT_LOCK (dbin);
988   if (input->parsebin == NULL) {
989     INPUT_UNLOCK (dbin);
990     return;
991   }
992
993   if (GST_OBJECT_PARENT (GST_OBJECT (input->parsebin)) == GST_OBJECT (dbin)) {
994     GstStreamCollection *collection = NULL;
995     gulong probe_id = gst_pad_add_probe (input->parsebin_sink,
996         GST_PAD_PROBE_TYPE_QUERY_UPSTREAM,
997         (GstPadProbeCallback) query_duration_drop_probe, input, NULL);
998
999     /* Clear stream-collection corresponding to current INPUT and post new
1000      * stream-collection message, if needed */
1001     if (input->collection) {
1002       gst_object_unref (input->collection);
1003       input->collection = NULL;
1004     }
1005
1006     SELECTION_LOCK (dbin);
1007     collection = get_merged_collection (dbin);
1008     if (collection && collection != dbin->collection) {
1009       GstMessage *msg;
1010       GST_DEBUG_OBJECT (dbin, "Update Stream Collection");
1011
1012       if (dbin->collection)
1013         gst_object_unref (dbin->collection);
1014       dbin->collection = collection;
1015
1016       msg =
1017           gst_message_new_stream_collection ((GstObject *) dbin,
1018           dbin->collection);
1019
1020       SELECTION_UNLOCK (dbin);
1021       gst_element_post_message (GST_ELEMENT_CAST (dbin), msg);
1022       update_requested_selection (dbin);
1023     } else
1024       SELECTION_UNLOCK (dbin);
1025
1026     gst_bin_remove (GST_BIN (dbin), input->parsebin);
1027     gst_element_set_state (input->parsebin, GST_STATE_NULL);
1028     g_signal_handler_disconnect (input->parsebin, input->pad_removed_sigid);
1029     g_signal_handler_disconnect (input->parsebin, input->pad_added_sigid);
1030     g_signal_handler_disconnect (input->parsebin, input->drained_sigid);
1031     gst_pad_remove_probe (input->parsebin_sink, probe_id);
1032     gst_object_unref (input->parsebin);
1033     gst_object_unref (input->parsebin_sink);
1034
1035     input->parsebin = NULL;
1036     input->parsebin_sink = NULL;
1037
1038     if (!input->is_main) {
1039       dbin->other_inputs = g_list_remove (dbin->other_inputs, input);
1040       free_input_async (dbin, input);
1041     }
1042   }
1043   INPUT_UNLOCK (dbin);
1044   return;
1045
1046 fail:
1047   GST_ERROR_OBJECT (parent, "Failed to retrieve input state from ghost pad");
1048   return;
1049 }
1050
1051 static void
1052 free_input (GstDecodebin3 * dbin, DecodebinInput * input)
1053 {
1054   GST_DEBUG ("Freeing input %p", input);
1055   gst_ghost_pad_set_target (GST_GHOST_PAD (input->ghost_sink), NULL);
1056   gst_element_remove_pad (GST_ELEMENT (dbin), input->ghost_sink);
1057   if (input->parsebin) {
1058     g_signal_handler_disconnect (input->parsebin, input->pad_removed_sigid);
1059     g_signal_handler_disconnect (input->parsebin, input->pad_added_sigid);
1060     g_signal_handler_disconnect (input->parsebin, input->drained_sigid);
1061     gst_element_set_state (input->parsebin, GST_STATE_NULL);
1062     gst_object_unref (input->parsebin);
1063     gst_object_unref (input->parsebin_sink);
1064   }
1065   if (input->collection)
1066     gst_object_unref (input->collection);
1067   g_free (input);
1068 }
1069
1070 static void
1071 free_input_async (GstDecodebin3 * dbin, DecodebinInput * input)
1072 {
1073   GST_LOG_OBJECT (dbin, "pushing input %p on thread pool to free", input);
1074   gst_element_call_async (GST_ELEMENT_CAST (dbin),
1075       (GstElementCallAsyncFunc) free_input, input, NULL);
1076 }
1077
1078 /* Call with INPUT_LOCK taken */
1079 static DecodebinInput *
1080 create_new_input (GstDecodebin3 * dbin, gboolean main)
1081 {
1082   DecodebinInput *input;
1083
1084   input = g_new0 (DecodebinInput, 1);
1085   input->dbin = dbin;
1086   input->is_main = main;
1087   input->group_id = GST_GROUP_ID_INVALID;
1088   if (main)
1089     input->ghost_sink = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1090   else {
1091     gchar *pad_name = g_strdup_printf ("sink_%u", dbin->input_counter++);
1092     input->ghost_sink = gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
1093     g_free (pad_name);
1094   }
1095   g_object_set_data (G_OBJECT (input->ghost_sink), "decodebin.input", input);
1096   gst_pad_set_link_function (input->ghost_sink, gst_decodebin3_input_pad_link);
1097   gst_pad_set_unlink_function (input->ghost_sink,
1098       gst_decodebin3_input_pad_unlink);
1099
1100   gst_pad_set_active (input->ghost_sink, TRUE);
1101   gst_element_add_pad ((GstElement *) dbin, input->ghost_sink);
1102
1103   return input;
1104
1105 }
1106
1107 static GstPad *
1108 gst_decodebin3_request_new_pad (GstElement * element, GstPadTemplate * temp,
1109     const gchar * name, const GstCaps * caps)
1110 {
1111   GstDecodebin3 *dbin = (GstDecodebin3 *) element;
1112   DecodebinInput *input;
1113   GstPad *res = NULL;
1114
1115   /* We are ignoring names for the time being, not sure it makes any sense
1116    * within the context of decodebin3 ... */
1117   input = create_new_input (dbin, FALSE);
1118   if (input) {
1119     INPUT_LOCK (dbin);
1120     dbin->other_inputs = g_list_append (dbin->other_inputs, input);
1121     res = input->ghost_sink;
1122     INPUT_UNLOCK (dbin);
1123   }
1124
1125   return res;
1126 }
1127
1128 /* Must be called with factories lock! */
1129 static void
1130 gst_decode_bin_update_factories_list (GstDecodebin3 * dbin)
1131 {
1132   guint cookie;
1133
1134   cookie = gst_registry_get_feature_list_cookie (gst_registry_get ());
1135   if (!dbin->factories || dbin->factories_cookie != cookie) {
1136     GList *tmp;
1137     if (dbin->factories)
1138       gst_plugin_feature_list_free (dbin->factories);
1139     if (dbin->decoder_factories)
1140       g_list_free (dbin->decoder_factories);
1141     if (dbin->decodable_factories)
1142       g_list_free (dbin->decodable_factories);
1143     dbin->factories =
1144         gst_element_factory_list_get_elements
1145         (GST_ELEMENT_FACTORY_TYPE_DECODABLE, GST_RANK_MARGINAL);
1146     dbin->factories =
1147         g_list_sort (dbin->factories, gst_plugin_feature_rank_compare_func);
1148     dbin->factories_cookie = cookie;
1149
1150     /* Filter decoder and other decodables */
1151     dbin->decoder_factories = NULL;
1152     dbin->decodable_factories = NULL;
1153     for (tmp = dbin->factories; tmp; tmp = tmp->next) {
1154       GstElementFactory *fact = (GstElementFactory *) tmp->data;
1155
1156       if (gst_element_factory_list_is_type (fact,
1157               GST_ELEMENT_FACTORY_TYPE_DECODER)) {
1158 #ifdef TIZEN_FEATURE_FORCE_SW_DECODER
1159         if  (!(dbin->force_sw_decoders_for_video &&
1160              gst_element_factory_list_is_type (fact, GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO) &&
1161              gst_element_factory_list_is_type (fact, GST_ELEMENT_FACTORY_TYPE_HARDWARE)) &&
1162              !(dbin->force_sw_decoders_for_audio &&
1163              gst_element_factory_list_is_type (fact, GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO) &&
1164              gst_element_factory_list_is_type (fact, GST_ELEMENT_FACTORY_TYPE_HARDWARE))) {
1165           dbin->decoder_factories =
1166               g_list_append (dbin->decoder_factories, fact);
1167         } else {
1168           GST_WARNING("%s is skipped", GST_OBJECT_NAME(fact));
1169         }
1170 #else
1171         if (!(dbin->force_sw_decoders
1172                 && gst_element_factory_list_is_type (fact,
1173                     GST_ELEMENT_FACTORY_TYPE_HARDWARE))) {
1174           dbin->decoder_factories =
1175               g_list_append (dbin->decoder_factories, fact);
1176         }
1177 #endif
1178       } else {
1179         dbin->decodable_factories =
1180             g_list_append (dbin->decodable_factories, fact);
1181       }
1182     }
1183   }
1184 }
1185
1186 /* Must be called with appropriate lock if list is a protected variable */
1187 static const gchar *
1188 stream_in_list (GList * list, const gchar * sid)
1189 {
1190   GList *tmp;
1191
1192 #if EXTRA_DEBUG
1193   for (tmp = list; tmp; tmp = tmp->next) {
1194     gchar *osid = (gchar *) tmp->data;
1195     GST_DEBUG ("Checking %s against %s", sid, osid);
1196   }
1197 #endif
1198
1199   for (tmp = list; tmp; tmp = tmp->next) {
1200     const gchar *osid = (gchar *) tmp->data;
1201     if (!g_strcmp0 (sid, osid))
1202       return osid;
1203   }
1204
1205   return NULL;
1206 }
1207
1208 static void
1209 update_requested_selection (GstDecodebin3 * dbin)
1210 {
1211   guint i, nb;
1212   GList *tmp = NULL;
1213   GstStreamType used_types = 0;
1214   GstStreamCollection *collection;
1215
1216   /* 1. Is there a pending SELECT_STREAMS we can return straight away since
1217    *  the switch handler will take care of the pending selection */
1218   SELECTION_LOCK (dbin);
1219   if (dbin->pending_select_streams) {
1220     GST_DEBUG_OBJECT (dbin,
1221         "No need to create pending selection, SELECT_STREAMS underway");
1222     goto beach;
1223   }
1224
1225   collection = dbin->collection;
1226   if (G_UNLIKELY (collection == NULL)) {
1227     GST_DEBUG_OBJECT (dbin, "No current GstStreamCollection");
1228     goto beach;
1229   }
1230   nb = gst_stream_collection_get_size (collection);
1231
1232   /* 2. If not, are we in EXPOSE_ALL_MODE ? If so, match everything */
1233   GST_FIXME_OBJECT (dbin, "Implement EXPOSE_ALL_MODE");
1234
1235   /* 3. If not, check if we already have some of the streams in the
1236    * existing active/requested selection */
1237   for (i = 0; i < nb; i++) {
1238     GstStream *stream = gst_stream_collection_get_stream (collection, i);
1239     const gchar *sid = gst_stream_get_stream_id (stream);
1240     gint request = -1;
1241     /* Fire select-stream signal to see if outside components want to
1242      * hint at which streams should be selected */
1243     g_signal_emit (G_OBJECT (dbin),
1244         gst_decodebin3_signals[SIGNAL_SELECT_STREAM], 0, collection, stream,
1245         &request);
1246     GST_DEBUG_OBJECT (dbin, "stream %s , request:%d", sid, request);
1247     if (request == 1 || (request == -1
1248             && (stream_in_list (dbin->requested_selection, sid)
1249                 || stream_in_list (dbin->active_selection, sid)))) {
1250       GstStreamType curtype = gst_stream_get_stream_type (stream);
1251       if (request == 1)
1252         GST_DEBUG_OBJECT (dbin,
1253             "Using stream requested by 'select-stream' signal : %s", sid);
1254       else
1255         GST_DEBUG_OBJECT (dbin,
1256             "Re-using stream already present in requested or active selection : %s",
1257             sid);
1258       tmp = g_list_append (tmp, (gchar *) sid);
1259       used_types |= curtype;
1260     }
1261   }
1262
1263   /* 4. If not, match one stream of each type */
1264   for (i = 0; i < nb; i++) {
1265     GstStream *stream = gst_stream_collection_get_stream (collection, i);
1266     GstStreamType curtype = gst_stream_get_stream_type (stream);
1267     if (!(used_types & curtype)) {
1268       const gchar *sid = gst_stream_get_stream_id (stream);
1269       GST_DEBUG_OBJECT (dbin, "Selecting stream '%s' of type %s",
1270           sid, gst_stream_type_get_name (curtype));
1271       tmp = g_list_append (tmp, (gchar *) sid);
1272       used_types |= curtype;
1273     }
1274   }
1275
1276 beach:
1277   /* Finally set the requested selection */
1278   if (tmp) {
1279     if (dbin->requested_selection) {
1280       GST_FIXME_OBJECT (dbin,
1281           "Replacing non-NULL requested_selection, what should we do ??");
1282       g_list_free_full (dbin->requested_selection, g_free);
1283     }
1284     dbin->requested_selection =
1285         g_list_copy_deep (tmp, (GCopyFunc) g_strdup, NULL);
1286     dbin->selection_updated = TRUE;
1287     g_list_free (tmp);
1288   }
1289   SELECTION_UNLOCK (dbin);
1290 }
1291
1292 /* sort_streams:
1293  * GCompareFunc to use with lists of GstStream.
1294  * Sorts GstStreams by stream type and SELECT flag and stream-id
1295  * First video, then audio, then others.
1296  *
1297  * Return: negative if a<b, 0 if a==b, positive if a>b
1298  */
1299 static gint
1300 sort_streams (GstStream * sa, GstStream * sb)
1301 {
1302   GstStreamType typea, typeb;
1303   GstStreamFlags flaga, flagb;
1304   const gchar *ida, *idb;
1305   gint ret = 0;
1306
1307   typea = gst_stream_get_stream_type (sa);
1308   typeb = gst_stream_get_stream_type (sb);
1309
1310   GST_LOG ("sa(%s), sb(%s)", gst_stream_get_stream_id (sa),
1311       gst_stream_get_stream_id (sb));
1312
1313   /* Sort by stream type. First video, then audio, then others(text, container, unknown) */
1314   if (typea != typeb) {
1315     if (typea & GST_STREAM_TYPE_VIDEO)
1316       ret = -1;
1317     else if (typea & GST_STREAM_TYPE_AUDIO)
1318       ret = (!(typeb & GST_STREAM_TYPE_VIDEO)) ? -1 : 1;
1319     else if (typea & GST_STREAM_TYPE_TEXT)
1320       ret = (!(typeb & GST_STREAM_TYPE_VIDEO)
1321           && !(typeb & GST_STREAM_TYPE_AUDIO)) ? -1 : 1;
1322     else if (typea & GST_STREAM_TYPE_CONTAINER)
1323       ret = (typeb & GST_STREAM_TYPE_UNKNOWN) ? -1 : 1;
1324     else
1325       ret = 1;
1326
1327     if (ret != 0) {
1328       GST_LOG ("Sort by stream-type: %d", ret);
1329       return ret;
1330     }
1331   }
1332
1333   /* Sort by SELECT flag, if stream type is same. */
1334   flaga = gst_stream_get_stream_flags (sa);
1335   flagb = gst_stream_get_stream_flags (sb);
1336
1337   ret =
1338       (flaga & GST_STREAM_FLAG_SELECT) ? ((flagb & GST_STREAM_FLAG_SELECT) ? 0 :
1339       -1) : ((flagb & GST_STREAM_FLAG_SELECT) ? 1 : 0);
1340
1341   if (ret != 0) {
1342     GST_LOG ("Sort by SELECT flag: %d", ret);
1343     return ret;
1344   }
1345
1346   /* Sort by stream-id, if otherwise the same. */
1347   ida = gst_stream_get_stream_id (sa);
1348   idb = gst_stream_get_stream_id (sb);
1349   ret = g_strcmp0 (ida, idb);
1350
1351   GST_LOG ("Sort by stream-id: %d", ret);
1352
1353   return ret;
1354 }
1355
1356 /* Call with INPUT_LOCK taken */
1357 static GstStreamCollection *
1358 get_merged_collection (GstDecodebin3 * dbin)
1359 {
1360   gboolean needs_merge = FALSE;
1361   GstStreamCollection *res = NULL;
1362   GList *tmp;
1363   GList *unsorted_streams = NULL;
1364   guint i, nb_stream;
1365
1366   /* First check if we need to do a merge or just return the only collection */
1367   res = dbin->main_input->collection;
1368
1369   for (tmp = dbin->other_inputs; tmp; tmp = tmp->next) {
1370     DecodebinInput *input = (DecodebinInput *) tmp->data;
1371     if (input->collection) {
1372       if (res) {
1373         needs_merge = TRUE;
1374         break;
1375       }
1376       res = input->collection;
1377     }
1378   }
1379
1380   if (!needs_merge) {
1381     GST_DEBUG_OBJECT (dbin, "No need to merge, returning %p", res);
1382     return res ? gst_object_ref (res) : NULL;
1383   }
1384
1385   /* We really need to create a new collection */
1386   /* FIXME : Some numbering scheme maybe ?? */
1387   res = gst_stream_collection_new ("decodebin3");
1388   if (dbin->main_input->collection) {
1389     nb_stream = gst_stream_collection_get_size (dbin->main_input->collection);
1390     GST_DEBUG_OBJECT (dbin, "main input %p %d", dbin->main_input, nb_stream);
1391     for (i = 0; i < nb_stream; i++) {
1392       GstStream *stream =
1393           gst_stream_collection_get_stream (dbin->main_input->collection, i);
1394       unsorted_streams = g_list_append (unsorted_streams, stream);
1395     }
1396   }
1397
1398   for (tmp = dbin->other_inputs; tmp; tmp = tmp->next) {
1399     DecodebinInput *input = (DecodebinInput *) tmp->data;
1400     GST_DEBUG_OBJECT (dbin, "input %p , collection %p", input,
1401         input->collection);
1402     if (input->collection) {
1403       nb_stream = gst_stream_collection_get_size (input->collection);
1404       GST_DEBUG_OBJECT (dbin, "nb_stream : %d", nb_stream);
1405       for (i = 0; i < nb_stream; i++) {
1406         GstStream *stream =
1407             gst_stream_collection_get_stream (input->collection, i);
1408         unsorted_streams = g_list_append (unsorted_streams, stream);
1409       }
1410     }
1411   }
1412
1413   /* re-order streams : video, then audio, then others */
1414   unsorted_streams =
1415       g_list_sort (unsorted_streams, (GCompareFunc) sort_streams);
1416   for (tmp = unsorted_streams; tmp; tmp = tmp->next) {
1417     GstStream *stream = (GstStream *) tmp->data;
1418     GST_DEBUG_OBJECT (dbin, "Adding #stream(%s) to collection",
1419         gst_stream_get_stream_id (stream));
1420     gst_stream_collection_add_stream (res, gst_object_ref (stream));
1421   }
1422
1423   if (unsorted_streams)
1424     g_list_free (unsorted_streams);
1425
1426   return res;
1427 }
1428
1429 /* Call with INPUT_LOCK taken */
1430 static DecodebinInput *
1431 find_message_parsebin (GstDecodebin3 * dbin, GstElement * child)
1432 {
1433   DecodebinInput *input = NULL;
1434   GstElement *parent = gst_object_ref (child);
1435   GList *tmp;
1436
1437   do {
1438     GstElement *next_parent;
1439
1440     GST_DEBUG_OBJECT (dbin, "parent %s",
1441         parent ? GST_ELEMENT_NAME (parent) : "<NONE>");
1442
1443     if (parent == dbin->main_input->parsebin) {
1444       input = dbin->main_input;
1445       break;
1446     }
1447     for (tmp = dbin->other_inputs; tmp; tmp = tmp->next) {
1448       DecodebinInput *cur = (DecodebinInput *) tmp->data;
1449       if (parent == cur->parsebin) {
1450         input = cur;
1451         break;
1452       }
1453     }
1454     next_parent = (GstElement *) gst_element_get_parent (parent);
1455     gst_object_unref (parent);
1456     parent = next_parent;
1457
1458   } while (parent && parent != (GstElement *) dbin);
1459
1460   if (parent)
1461     gst_object_unref (parent);
1462
1463   return input;
1464 }
1465
1466 static const gchar *
1467 stream_in_collection (GstDecodebin3 * dbin, gchar * sid)
1468 {
1469   guint i, len;
1470
1471   if (dbin->collection == NULL)
1472     return NULL;
1473   len = gst_stream_collection_get_size (dbin->collection);
1474   for (i = 0; i < len; i++) {
1475     GstStream *stream = gst_stream_collection_get_stream (dbin->collection, i);
1476     const gchar *osid = gst_stream_get_stream_id (stream);
1477     if (!g_strcmp0 (sid, osid))
1478       return osid;
1479   }
1480
1481   return NULL;
1482 }
1483
1484 /* Call with INPUT_LOCK taken */
1485 static void
1486 handle_stream_collection (GstDecodebin3 * dbin,
1487     GstStreamCollection * collection, GstElement * child)
1488 {
1489 #ifndef GST_DISABLE_GST_DEBUG
1490   const gchar *upstream_id;
1491   guint i;
1492 #endif
1493   DecodebinInput *input = find_message_parsebin (dbin, child);
1494
1495   if (!input) {
1496     GST_DEBUG_OBJECT (dbin,
1497         "Couldn't find corresponding input, most likely shutting down");
1498     return;
1499   }
1500
1501   /* Replace collection in input */
1502   if (input->collection)
1503     gst_object_unref (input->collection);
1504   input->collection = gst_object_ref (collection);
1505   GST_DEBUG_OBJECT (dbin, "Setting collection %p on input %p", collection,
1506       input);
1507
1508   /* Merge collection if needed */
1509   collection = get_merged_collection (dbin);
1510
1511 #ifndef GST_DISABLE_GST_DEBUG
1512   /* Just some debugging */
1513   upstream_id = gst_stream_collection_get_upstream_id (collection);
1514   GST_DEBUG ("Received Stream Collection. Upstream_id : %s", upstream_id);
1515   GST_DEBUG ("From input %p", input);
1516   GST_DEBUG ("  %d streams", gst_stream_collection_get_size (collection));
1517   for (i = 0; i < gst_stream_collection_get_size (collection); i++) {
1518     GstStream *stream = gst_stream_collection_get_stream (collection, i);
1519     GstTagList *taglist;
1520     GstCaps *caps;
1521
1522     GST_DEBUG ("   Stream '%s'", gst_stream_get_stream_id (stream));
1523     GST_DEBUG ("     type  : %s",
1524         gst_stream_type_get_name (gst_stream_get_stream_type (stream)));
1525     GST_DEBUG ("     flags : 0x%x", gst_stream_get_stream_flags (stream));
1526     taglist = gst_stream_get_tags (stream);
1527     GST_DEBUG ("     tags  : %" GST_PTR_FORMAT, taglist);
1528     caps = gst_stream_get_caps (stream);
1529     GST_DEBUG ("     caps  : %" GST_PTR_FORMAT, caps);
1530     if (taglist)
1531       gst_tag_list_unref (taglist);
1532     if (caps)
1533       gst_caps_unref (caps);
1534   }
1535 #endif
1536
1537   /* Store collection for later usage */
1538   SELECTION_LOCK (dbin);
1539   if (dbin->collection == NULL) {
1540     dbin->collection = collection;
1541   } else {
1542     /* We need to check who emitted this collection (the owner).
1543      * If we already had a collection from that user, this one is an update,
1544      * that is to say that we need to figure out how we are going to re-use
1545      * the streams/slot */
1546     GST_FIXME_OBJECT (dbin, "New collection but already had one ...");
1547     /* FIXME : When do we switch from pending collection to active collection ?
1548      * When all streams from active collection are drained in multiqueue output ? */
1549     gst_object_unref (dbin->collection);
1550     dbin->collection = collection;
1551     /* dbin->pending_collection = */
1552     /*     g_list_append (dbin->pending_collection, collection); */
1553   }
1554   SELECTION_UNLOCK (dbin);
1555 }
1556
1557 static void
1558 gst_decodebin3_handle_message (GstBin * bin, GstMessage * message)
1559 {
1560   GstDecodebin3 *dbin = (GstDecodebin3 *) bin;
1561   gboolean posting_collection = FALSE;
1562
1563   GST_DEBUG_OBJECT (bin, "Got Message %s", GST_MESSAGE_TYPE_NAME (message));
1564
1565   switch (GST_MESSAGE_TYPE (message)) {
1566     case GST_MESSAGE_STREAM_COLLECTION:
1567     {
1568       GstStreamCollection *collection = NULL;
1569       gst_message_parse_stream_collection (message, &collection);
1570       if (collection) {
1571         INPUT_LOCK (dbin);
1572         handle_stream_collection (dbin, collection,
1573             (GstElement *) GST_MESSAGE_SRC (message));
1574         posting_collection = TRUE;
1575         INPUT_UNLOCK (dbin);
1576       }
1577
1578       SELECTION_LOCK (dbin);
1579       if (dbin->collection && collection != dbin->collection) {
1580         /* Replace collection message, we most likely aggregated it */
1581         GstMessage *new_msg;
1582         new_msg =
1583             gst_message_new_stream_collection ((GstObject *) dbin,
1584             dbin->collection);
1585         gst_message_unref (message);
1586         message = new_msg;
1587       }
1588       SELECTION_UNLOCK (dbin);
1589
1590       if (collection)
1591         gst_object_unref (collection);
1592       break;
1593     }
1594     default:
1595       break;
1596   }
1597
1598   GST_BIN_CLASS (parent_class)->handle_message (bin, message);
1599
1600   if (posting_collection) {
1601     /* Figure out a selection for that collection */
1602     update_requested_selection (dbin);
1603   }
1604 }
1605
1606 static DecodebinOutputStream *
1607 find_free_compatible_output (GstDecodebin3 * dbin, GstStream * stream)
1608 {
1609   GList *tmp;
1610   GstStreamType stype = gst_stream_get_stream_type (stream);
1611
1612   for (tmp = dbin->output_streams; tmp; tmp = tmp->next) {
1613     DecodebinOutputStream *output = (DecodebinOutputStream *) tmp->data;
1614     if (output->type == stype && output->slot && output->slot->active_stream) {
1615       GstStream *tstream = output->slot->active_stream;
1616       if (!stream_in_list (dbin->requested_selection,
1617               (gchar *) gst_stream_get_stream_id (tstream))) {
1618         return output;
1619       }
1620     }
1621   }
1622
1623   return NULL;
1624 }
1625
1626 /* Give a certain slot, figure out if it should be linked to an
1627  * output stream
1628  * CALL WITH SELECTION LOCK TAKEN !*/
1629 static DecodebinOutputStream *
1630 get_output_for_slot (MultiQueueSlot * slot)
1631 {
1632   GstDecodebin3 *dbin = slot->dbin;
1633   DecodebinOutputStream *output = NULL;
1634   const gchar *stream_id;
1635   GstCaps *caps;
1636   gchar *id_in_list = NULL;
1637
1638   /* If we already have a configured output, just use it */
1639   if (slot->output != NULL)
1640     return slot->output;
1641
1642   /*
1643    * FIXME
1644    *
1645    * This method needs to be split into multiple parts
1646    *
1647    * 1) Figure out whether stream should be exposed or not
1648    *   This is based on autoplug-continue, EXPOSE_ALL_MODE, or presence
1649    *   in the default stream attribution
1650    *
1651    * 2) Figure out whether an output stream should be created, whether
1652    *   we can re-use the output stream already linked to the slot, or
1653    *   whether we need to get re-assigned another (currently used) output
1654    *   stream.
1655    */
1656
1657   stream_id = gst_stream_get_stream_id (slot->active_stream);
1658   caps = gst_stream_get_caps (slot->active_stream);
1659   GST_DEBUG_OBJECT (dbin, "stream %s , %" GST_PTR_FORMAT, stream_id, caps);
1660   gst_caps_unref (caps);
1661
1662   /* 0. Emit autoplug-continue signal for pending caps ? */
1663   GST_FIXME_OBJECT (dbin, "emit autoplug-continue");
1664
1665   /* 1. if in EXPOSE_ALL_MODE, just accept */
1666   GST_FIXME_OBJECT (dbin, "Handle EXPOSE_ALL_MODE");
1667
1668 #if 0
1669   /* FIXME : The idea around this was to avoid activating a stream for
1670    *     which we have no decoder. Unfortunately it is way too
1671    *     expensive. Need to figure out a better solution */
1672   /* 2. Is there a potential decoder (if one is required) */
1673   if (!gst_caps_can_intersect (caps, dbin->caps)
1674       && !have_factory (dbin, (GstCaps *) caps,
1675           GST_ELEMENT_FACTORY_TYPE_DECODER)) {
1676     GST_WARNING_OBJECT (dbin, "Don't have a decoder for %" GST_PTR_FORMAT,
1677         caps);
1678     SELECTION_UNLOCK (dbin);
1679     gst_element_post_message (GST_ELEMENT_CAST (dbin),
1680         gst_missing_decoder_message_new (GST_ELEMENT_CAST (dbin), caps));
1681     SELECTION_LOCK (dbin);
1682     return NULL;
1683   }
1684 #endif
1685
1686   /* 3. In default mode check if we should expose */
1687   id_in_list = (gchar *) stream_in_list (dbin->requested_selection, stream_id);
1688   if (id_in_list) {
1689     /* Check if we can steal an existing output stream we could re-use.
1690      * that is:
1691      * * an output stream whose slot->stream is not in requested
1692      * * and is of the same type as this stream
1693      */
1694     output = find_free_compatible_output (dbin, slot->active_stream);
1695     if (output) {
1696       /* Move this output from its current slot to this slot */
1697       dbin->to_activate =
1698           g_list_append (dbin->to_activate, (gchar *) stream_id);
1699       dbin->requested_selection =
1700           g_list_remove (dbin->requested_selection, id_in_list);
1701       g_free (id_in_list);
1702       SELECTION_UNLOCK (dbin);
1703       gst_pad_add_probe (output->slot->src_pad, GST_PAD_PROBE_TYPE_IDLE,
1704           (GstPadProbeCallback) slot_unassign_probe, output->slot, NULL);
1705       SELECTION_LOCK (dbin);
1706       return NULL;
1707     }
1708
1709     output = create_output_stream (dbin, slot->type);
1710     output->slot = slot;
1711     GST_DEBUG ("Linking slot %p to new output %p", slot, output);
1712     slot->output = output;
1713     dbin->active_selection =
1714         g_list_append (dbin->active_selection, (gchar *) stream_id);
1715   } else
1716     GST_DEBUG ("Not creating any output for slot %p", slot);
1717
1718   return output;
1719 }
1720
1721 /* Returns SELECTED_STREAMS message if active_selection is equal to
1722  * requested_selection, else NULL.
1723  * Must be called with LOCK taken */
1724 static GstMessage *
1725 is_selection_done (GstDecodebin3 * dbin)
1726 {
1727   GList *tmp;
1728   GstMessage *msg;
1729
1730   if (!dbin->selection_updated)
1731     return NULL;
1732
1733   GST_LOG_OBJECT (dbin, "Checking");
1734
1735   if (dbin->to_activate != NULL) {
1736     GST_DEBUG ("Still have streams to activate");
1737     return NULL;
1738   }
1739   for (tmp = dbin->requested_selection; tmp; tmp = tmp->next) {
1740     GST_DEBUG ("Checking requested stream %s", (gchar *) tmp->data);
1741     if (!stream_in_list (dbin->active_selection, (gchar *) tmp->data)) {
1742       GST_DEBUG ("Not in active selection, returning");
1743       return NULL;
1744     }
1745   }
1746
1747   GST_DEBUG_OBJECT (dbin, "Selection active, creating message");
1748
1749   /* We are completely active */
1750   msg = gst_message_new_streams_selected ((GstObject *) dbin, dbin->collection);
1751   GST_MESSAGE_SEQNUM (msg) = dbin->select_streams_seqnum;
1752   for (tmp = dbin->output_streams; tmp; tmp = tmp->next) {
1753     DecodebinOutputStream *output = (DecodebinOutputStream *) tmp->data;
1754     if (output->slot) {
1755       GST_DEBUG_OBJECT (dbin, "Adding stream %s",
1756           gst_stream_get_stream_id (output->slot->active_stream));
1757
1758       gst_message_streams_selected_add (msg, output->slot->active_stream);
1759     } else
1760       GST_WARNING_OBJECT (dbin, "No valid slot for output %p", output);
1761   }
1762   dbin->selection_updated = FALSE;
1763   return msg;
1764 }
1765
1766 /* Must be called with SELECTION_LOCK taken */
1767 static void
1768 check_all_slot_for_eos (GstDecodebin3 * dbin)
1769 {
1770   gboolean all_drained = TRUE;
1771   GList *iter;
1772
1773   GST_DEBUG_OBJECT (dbin, "check slot for eos");
1774
1775   for (iter = dbin->slots; iter; iter = iter->next) {
1776     MultiQueueSlot *slot = iter->data;
1777
1778     if (!slot->output)
1779       continue;
1780
1781     if (slot->is_drained) {
1782       GST_LOG_OBJECT (slot->sink_pad, "slot %p is drained", slot);
1783       continue;
1784     }
1785
1786     all_drained = FALSE;
1787     break;
1788   }
1789
1790   if (all_drained) {
1791     INPUT_LOCK (dbin);
1792     if (!pending_pads_are_eos (dbin->main_input))
1793       all_drained = FALSE;
1794
1795     if (all_drained) {
1796       for (iter = dbin->other_inputs; iter; iter = iter->next) {
1797         if (!pending_pads_are_eos ((DecodebinInput *) iter->data)) {
1798           all_drained = FALSE;
1799           break;
1800         }
1801       }
1802     }
1803     INPUT_UNLOCK (dbin);
1804   }
1805
1806   if (all_drained) {
1807     GST_DEBUG_OBJECT (dbin,
1808         "All active slots are drained, and no pending input, push EOS");
1809
1810     for (iter = dbin->input_streams; iter; iter = iter->next) {
1811       DecodebinInputStream *input = (DecodebinInputStream *) iter->data;
1812       GstPad *peer = gst_pad_get_peer (input->srcpad);
1813
1814       /* Send EOS to all slots */
1815       if (peer) {
1816         GstEvent *stream_start, *eos;
1817
1818         stream_start =
1819             gst_pad_get_sticky_event (input->srcpad, GST_EVENT_STREAM_START, 0);
1820
1821         /* First forward a custom STREAM_START event to reset the EOS status (if any) */
1822         if (stream_start) {
1823           GstStructure *s;
1824           GstEvent *custom_stream_start = gst_event_copy (stream_start);
1825           gst_event_unref (stream_start);
1826           s = (GstStructure *) gst_event_get_structure (custom_stream_start);
1827           gst_structure_set (s, "decodebin3-flushing-stream-start",
1828               G_TYPE_BOOLEAN, TRUE, NULL);
1829           gst_pad_send_event (peer, custom_stream_start);
1830         }
1831
1832         eos = gst_event_new_eos ();
1833         gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (eos),
1834             CUSTOM_FINAL_EOS_QUARK, (gchar *) CUSTOM_FINAL_EOS_QUARK_DATA,
1835             NULL);
1836         gst_pad_send_event (peer, eos);
1837         gst_object_unref (peer);
1838       } else
1839         GST_DEBUG_OBJECT (dbin, "no output");
1840     }
1841   }
1842 }
1843
1844 static GstPadProbeReturn
1845 multiqueue_src_probe (GstPad * pad, GstPadProbeInfo * info,
1846     MultiQueueSlot * slot)
1847 {
1848   GstPadProbeReturn ret = GST_PAD_PROBE_OK;
1849   GstDecodebin3 *dbin = slot->dbin;
1850
1851   if (GST_IS_EVENT (GST_PAD_PROBE_INFO_DATA (info))) {
1852     GstEvent *ev = GST_PAD_PROBE_INFO_EVENT (info);
1853
1854     GST_DEBUG_OBJECT (pad, "Got event %p %s", ev, GST_EVENT_TYPE_NAME (ev));
1855     switch (GST_EVENT_TYPE (ev)) {
1856       case GST_EVENT_STREAM_START:
1857       {
1858         GstStream *stream = NULL;
1859         const GstStructure *s = gst_event_get_structure (ev);
1860
1861         /* Drop STREAM_START events used to cleanup multiqueue */
1862         if (s
1863             && gst_structure_has_field (s,
1864                 "decodebin3-flushing-stream-start")) {
1865           ret = GST_PAD_PROBE_HANDLED;
1866           gst_event_unref (ev);
1867           break;
1868         }
1869
1870         gst_event_parse_stream (ev, &stream);
1871         if (stream == NULL) {
1872           GST_ERROR_OBJECT (pad,
1873               "Got a STREAM_START event without a GstStream");
1874           break;
1875         }
1876         slot->is_drained = FALSE;
1877         GST_DEBUG_OBJECT (pad, "Stream Start '%s'",
1878             gst_stream_get_stream_id (stream));
1879         if (slot->active_stream == NULL) {
1880           slot->active_stream = stream;
1881         } else if (slot->active_stream != stream) {
1882           GST_FIXME_OBJECT (pad, "Handle stream changes (%s => %s) !",
1883               gst_stream_get_stream_id (slot->active_stream),
1884               gst_stream_get_stream_id (stream));
1885           gst_object_unref (slot->active_stream);
1886           slot->active_stream = stream;
1887         } else
1888           gst_object_unref (stream);
1889 #if 0                           /* Disabled because stream-start is pushed for every buffer on every unlinked pad */
1890         {
1891           gboolean is_active, is_requested;
1892           /* Quick check to see if we're in the current selection */
1893           /* FIXME : Re-check all slot<=>output mappings based on requested_selection */
1894           SELECTION_LOCK (dbin);
1895           GST_DEBUG_OBJECT (dbin, "Checking active selection");
1896           is_active = stream_in_list (dbin->active_selection, stream_id);
1897           GST_DEBUG_OBJECT (dbin, "Checking requested selection");
1898           is_requested = stream_in_list (dbin->requested_selection, stream_id);
1899           SELECTION_UNLOCK (dbin);
1900           if (is_active)
1901             GST_DEBUG_OBJECT (pad, "Slot in ACTIVE selection (output:%p)",
1902                 slot->output);
1903           if (is_requested)
1904             GST_DEBUG_OBJECT (pad, "Slot in REQUESTED selection (output:%p)",
1905                 slot->output);
1906           else if (slot->output) {
1907             GST_DEBUG_OBJECT (pad,
1908                 "Slot needs to be deactivated ? It's no longer in requested selection");
1909           } else if (!is_active)
1910             GST_DEBUG_OBJECT (pad,
1911                 "Slot in neither active nor requested selection");
1912         }
1913 #endif
1914       }
1915         break;
1916       case GST_EVENT_CAPS:
1917       {
1918         /* Configure the output slot if needed */
1919         DecodebinOutputStream *output;
1920         GstMessage *msg = NULL;
1921         SELECTION_LOCK (dbin);
1922         output = get_output_for_slot (slot);
1923         if (output) {
1924           reconfigure_output_stream (output, slot);
1925           msg = is_selection_done (dbin);
1926         }
1927         SELECTION_UNLOCK (dbin);
1928         if (msg)
1929           gst_element_post_message ((GstElement *) slot->dbin, msg);
1930       }
1931         break;
1932       case GST_EVENT_EOS:
1933       {
1934         gboolean was_drained = slot->is_drained;
1935         slot->is_drained = TRUE;
1936
1937         /* Custom EOS handling first */
1938         if (gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (ev),
1939                 CUSTOM_EOS_QUARK)) {
1940           /* remove custom-eos */
1941           gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (ev),
1942               CUSTOM_EOS_QUARK, NULL, NULL);
1943           GST_LOG_OBJECT (pad, "Received custom EOS");
1944 #ifndef TIZEN_FEATURE_DISABLE_EOS_DROP
1945           ret = GST_PAD_PROBE_HANDLED;
1946 #endif
1947           SELECTION_LOCK (dbin);
1948           if (slot->input == NULL) {
1949             GST_DEBUG_OBJECT (pad,
1950                 "Got custom-eos from null input stream, remove output stream");
1951             /* Remove the output */
1952             if (slot->output) {
1953               DecodebinOutputStream *output = slot->output;
1954               dbin->output_streams =
1955                   g_list_remove (dbin->output_streams, output);
1956               free_output_stream (dbin, output);
1957             }
1958             slot->probe_id = 0;
1959             dbin->slots = g_list_remove (dbin->slots, slot);
1960             free_multiqueue_slot_async (dbin, slot);
1961             ret = GST_PAD_PROBE_REMOVE;
1962           } else if (!was_drained) {
1963             check_all_slot_for_eos (dbin);
1964           }
1965           if (ret == GST_PAD_PROBE_HANDLED)
1966             gst_event_unref (ev);
1967           SELECTION_UNLOCK (dbin);
1968           break;
1969         }
1970
1971         GST_FIXME_OBJECT (pad, "EOS on multiqueue source pad. input:%p",
1972             slot->input);
1973         if (slot->input == NULL) {
1974           GstPad *peer;
1975           GST_DEBUG_OBJECT (pad,
1976               "last EOS for input, forwarding and removing slot");
1977           peer = gst_pad_get_peer (pad);
1978           if (peer) {
1979             gst_pad_send_event (peer, ev);
1980             gst_object_unref (peer);
1981           } else {
1982             gst_event_unref (ev);
1983           }
1984           SELECTION_LOCK (dbin);
1985           /* FIXME : Shouldn't we try to re-assign the output instead of just
1986            * removing it ? */
1987           /* Remove the output */
1988           if (slot->output) {
1989             DecodebinOutputStream *output = slot->output;
1990             dbin->output_streams = g_list_remove (dbin->output_streams, output);
1991             free_output_stream (dbin, output);
1992           }
1993           slot->probe_id = 0;
1994           dbin->slots = g_list_remove (dbin->slots, slot);
1995           SELECTION_UNLOCK (dbin);
1996
1997           free_multiqueue_slot_async (dbin, slot);
1998           ret = GST_PAD_PROBE_REMOVE;
1999         } else if (gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (ev),
2000                 CUSTOM_FINAL_EOS_QUARK)) {
2001           GST_DEBUG_OBJECT (pad, "Got final eos, propagating downstream");
2002         } else {
2003           GST_DEBUG_OBJECT (pad, "Got regular eos (all_inputs_are_eos)");
2004 #ifndef TIZEN_FEATURE_DISABLE_EOS_DROP
2005           /* drop current event as eos will be sent in check_all_slot_for_eos
2006            * when all output streams are also eos */
2007           ret = GST_PAD_PROBE_DROP;
2008 #endif
2009           SELECTION_LOCK (dbin);
2010           check_all_slot_for_eos (dbin);
2011           SELECTION_UNLOCK (dbin);
2012         }
2013       }
2014         break;
2015       default:
2016         break;
2017     }
2018   } else if (GST_IS_QUERY (GST_PAD_PROBE_INFO_DATA (info))) {
2019     GstQuery *query = GST_PAD_PROBE_INFO_QUERY (info);
2020     switch (GST_QUERY_TYPE (query)) {
2021       case GST_QUERY_CAPS:
2022       {
2023         GST_DEBUG_OBJECT (pad, "Intercepting CAPS query");
2024         gst_query_set_caps_result (query, GST_CAPS_ANY);
2025         ret = GST_PAD_PROBE_HANDLED;
2026       }
2027         break;
2028
2029       case GST_QUERY_ACCEPT_CAPS:
2030       {
2031         GST_DEBUG_OBJECT (pad, "Intercepting Accept Caps query");
2032         /* If the current decoder doesn't accept caps, we'll reconfigure
2033          * on the actual caps event. So accept any caps. */
2034         gst_query_set_accept_caps_result (query, TRUE);
2035         ret = GST_PAD_PROBE_HANDLED;
2036       }
2037       default:
2038         break;
2039     }
2040   }
2041
2042   return ret;
2043 }
2044
2045 /* Create a new multiqueue slot for the given type
2046  *
2047  * It is up to the caller to know whether that slot is needed or not
2048  * (and release it when no longer needed) */
2049 static MultiQueueSlot *
2050 create_new_slot (GstDecodebin3 * dbin, GstStreamType type)
2051 {
2052   MultiQueueSlot *slot;
2053   GstIterator *it = NULL;
2054   GValue item = { 0, };
2055
2056   GST_DEBUG_OBJECT (dbin, "Creating new slot for type %s",
2057       gst_stream_type_get_name (type));
2058   slot = g_new0 (MultiQueueSlot, 1);
2059   slot->dbin = dbin;
2060
2061   slot->id = dbin->slot_id++;
2062
2063   slot->type = type;
2064   slot->sink_pad = gst_element_get_request_pad (dbin->multiqueue, "sink_%u");
2065   if (slot->sink_pad == NULL)
2066     goto fail;
2067
2068   it = gst_pad_iterate_internal_links (slot->sink_pad);
2069   if (!it || (gst_iterator_next (it, &item)) != GST_ITERATOR_OK
2070       || ((slot->src_pad = g_value_dup_object (&item)) == NULL)) {
2071     GST_ERROR ("Couldn't get srcpad from multiqueue for sink pad %s:%s",
2072         GST_DEBUG_PAD_NAME (slot->src_pad));
2073     goto fail;
2074   }
2075   gst_iterator_free (it);
2076   g_value_reset (&item);
2077
2078   g_object_set (slot->sink_pad, "group-id", (guint) type, NULL);
2079
2080   /* Add event probe */
2081   slot->probe_id =
2082       gst_pad_add_probe (slot->src_pad,
2083       GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM | GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM,
2084       (GstPadProbeCallback) multiqueue_src_probe, slot, NULL);
2085
2086   GST_DEBUG ("Created new slot %u (%p) (%s:%s)", slot->id, slot,
2087       GST_DEBUG_PAD_NAME (slot->src_pad));
2088
2089   dbin->slots = g_list_append (dbin->slots, slot);
2090
2091   return slot;
2092
2093   /* ERRORS */
2094 fail:
2095   {
2096     if (slot->sink_pad)
2097       gst_element_release_request_pad (dbin->multiqueue, slot->sink_pad);
2098     g_free (slot);
2099     return NULL;
2100   }
2101 }
2102
2103 /* Must be called with SELECTION_LOCK */
2104 static MultiQueueSlot *
2105 get_slot_for_input (GstDecodebin3 * dbin, DecodebinInputStream * input)
2106 {
2107   GList *tmp;
2108   MultiQueueSlot *empty_slot = NULL;
2109   GstStreamType input_type = 0;
2110   gchar *stream_id = NULL;
2111
2112   GST_DEBUG_OBJECT (dbin, "input %p (stream %p %s)",
2113       input, input->active_stream,
2114       input->
2115       active_stream ? gst_stream_get_stream_id (input->active_stream) : "");
2116
2117   if (input->active_stream) {
2118     input_type = gst_stream_get_stream_type (input->active_stream);
2119     stream_id = (gchar *) gst_stream_get_stream_id (input->active_stream);
2120   }
2121
2122   /* Go over existing slots and check if there is already one for it */
2123   for (tmp = dbin->slots; tmp; tmp = tmp->next) {
2124     MultiQueueSlot *slot = (MultiQueueSlot *) tmp->data;
2125     /* Already used input, return that one */
2126     if (slot->input == input) {
2127       GST_DEBUG_OBJECT (dbin, "Returning already specified slot %d", slot->id);
2128       return slot;
2129     }
2130   }
2131
2132   /* Go amongst all unused slots of the right type and try to find a candidate */
2133   for (tmp = dbin->slots; tmp; tmp = tmp->next) {
2134     MultiQueueSlot *slot = (MultiQueueSlot *) tmp->data;
2135     if (slot->input == NULL && input_type == slot->type) {
2136       /* Remember this empty slot for later */
2137       empty_slot = slot;
2138       /* Check if available slot is of the same stream_id */
2139       GST_LOG_OBJECT (dbin, "Checking candidate slot %d (active_stream:%p)",
2140           slot->id, slot->active_stream);
2141       if (stream_id && slot->active_stream) {
2142         gchar *ostream_id =
2143             (gchar *) gst_stream_get_stream_id (slot->active_stream);
2144         GST_DEBUG_OBJECT (dbin, "Checking slot %d %s against %s", slot->id,
2145             ostream_id, stream_id);
2146         if (!g_strcmp0 (stream_id, ostream_id))
2147           break;
2148       }
2149     }
2150   }
2151
2152   if (empty_slot) {
2153     GST_DEBUG_OBJECT (dbin, "Re-using existing unused slot %d", empty_slot->id);
2154     empty_slot->input = input;
2155     return empty_slot;
2156   }
2157
2158   if (input_type)
2159     return create_new_slot (dbin, input_type);
2160
2161   return NULL;
2162 }
2163
2164 static void
2165 link_input_to_slot (DecodebinInputStream * input, MultiQueueSlot * slot)
2166 {
2167   if (slot->input != NULL && slot->input != input) {
2168     GST_ERROR_OBJECT (slot->dbin,
2169         "Trying to link input to an already used slot");
2170     return;
2171   }
2172   gst_pad_link_full (input->srcpad, slot->sink_pad, GST_PAD_LINK_CHECK_NOTHING);
2173   slot->pending_stream = input->active_stream;
2174   slot->input = input;
2175 }
2176
2177 #if 0
2178 static gboolean
2179 have_factory (GstDecodebin3 * dbin, GstCaps * caps,
2180     GstElementFactoryListType ftype)
2181 {
2182   gboolean ret = FALSE;
2183   GList *res;
2184
2185   g_mutex_lock (&dbin->factories_lock);
2186   gst_decode_bin_update_factories_list (dbin);
2187   if (ftype == GST_ELEMENT_FACTORY_TYPE_DECODER)
2188     res =
2189         gst_element_factory_list_filter (dbin->decoder_factories,
2190         caps, GST_PAD_SINK, TRUE);
2191   else
2192     res =
2193         gst_element_factory_list_filter (dbin->decodable_factories,
2194         caps, GST_PAD_SINK, TRUE);
2195   g_mutex_unlock (&dbin->factories_lock);
2196
2197   if (res) {
2198     ret = TRUE;
2199     gst_plugin_feature_list_free (res);
2200   }
2201
2202   return ret;
2203 }
2204 #endif
2205
2206 static GstElement *
2207 create_element (GstDecodebin3 * dbin, GstStream * stream,
2208     GstElementFactoryListType ftype)
2209 {
2210   GList *res;
2211   GstElement *element = NULL;
2212   GstCaps *caps;
2213
2214   g_mutex_lock (&dbin->factories_lock);
2215   gst_decode_bin_update_factories_list (dbin);
2216   caps = gst_stream_get_caps (stream);
2217   if (ftype == GST_ELEMENT_FACTORY_TYPE_DECODER)
2218     res =
2219         gst_element_factory_list_filter (dbin->decoder_factories,
2220         caps, GST_PAD_SINK, TRUE);
2221   else
2222     res =
2223         gst_element_factory_list_filter (dbin->decodable_factories,
2224         caps, GST_PAD_SINK, TRUE);
2225   g_mutex_unlock (&dbin->factories_lock);
2226
2227   if (res) {
2228 #ifdef TIZEN_FEATURE_RESOURCE_MANAGER
2229     if (gst_element_factory_list_is_type (res->data,
2230               GST_ELEMENT_FACTORY_TYPE_HARDWARE)) {
2231         gboolean result = FALSE;
2232         g_signal_emit (G_OBJECT (dbin),
2233             gst_decodebin3_signals[SIGNAL_REQUEST_RESOURCE], 0, dbin->collection, stream,
2234             &result);
2235         if (!result) {
2236           GST_WARNING_OBJECT (dbin, "Failed to get HW resource.");
2237           gst_plugin_feature_list_free (res);
2238           gst_caps_unref (caps);
2239           return NULL;
2240         }
2241     }
2242 #endif
2243     element =
2244         gst_element_factory_create ((GstElementFactory *) res->data, NULL);
2245     GST_DEBUG ("Created element '%s'", GST_ELEMENT_NAME (element));
2246     gst_plugin_feature_list_free (res);
2247   } else {
2248     GST_DEBUG ("Could not find an element for caps %" GST_PTR_FORMAT, caps);
2249   }
2250
2251   gst_caps_unref (caps);
2252   return element;
2253 }
2254
2255 /* FIXME : VERY NAIVE. ASSUMING FIRST ONE WILL WORK */
2256 static GstElement *
2257 create_decoder (GstDecodebin3 * dbin, GstStream * stream)
2258 {
2259   return create_element (dbin, stream, GST_ELEMENT_FACTORY_TYPE_DECODER);
2260 }
2261
2262 static GstPadProbeReturn
2263 keyframe_waiter_probe (GstPad * pad, GstPadProbeInfo * info,
2264     DecodebinOutputStream * output)
2265 {
2266   GstBuffer *buf = GST_PAD_PROBE_INFO_BUFFER (info);
2267   /* If we have a keyframe, remove the probe and let all data through */
2268   /* FIXME : HANDLE HEADER BUFFER ?? */
2269   if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ||
2270       GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_HEADER)) {
2271     GST_DEBUG_OBJECT (pad,
2272         "Buffer is keyframe or header, letting through and removing probe");
2273     output->drop_probe_id = 0;
2274     return GST_PAD_PROBE_REMOVE;
2275   }
2276   GST_DEBUG_OBJECT (pad, "Buffer is not a keyframe, dropping");
2277   return GST_PAD_PROBE_DROP;
2278 }
2279
2280 static void
2281 reconfigure_output_stream (DecodebinOutputStream * output,
2282     MultiQueueSlot * slot)
2283 {
2284   GstDecodebin3 *dbin = output->dbin;
2285   GstCaps *new_caps = (GstCaps *) gst_stream_get_caps (slot->active_stream);
2286   gboolean needs_decoder;
2287
2288   needs_decoder = gst_caps_can_intersect (new_caps, dbin->caps) != TRUE;
2289
2290   GST_DEBUG_OBJECT (dbin,
2291       "Reconfiguring output %p to slot %p, needs_decoder:%d", output, slot,
2292       needs_decoder);
2293
2294   /* FIXME : Maybe make the output un-hook itself automatically ? */
2295   if (output->slot != NULL && output->slot != slot) {
2296     GST_WARNING_OBJECT (dbin,
2297         "Output still linked to another slot (%p)", output->slot);
2298     gst_caps_unref (new_caps);
2299     return;
2300   }
2301
2302   /* Check if existing config is reusable as-is by checking if
2303    * the existing decoder accepts the new caps, if not delete
2304    * it and create a new one */
2305   if (output->decoder) {
2306     gboolean can_reuse_decoder;
2307
2308     if (needs_decoder) {
2309       can_reuse_decoder =
2310           gst_pad_query_accept_caps (output->decoder_sink, new_caps);
2311     } else
2312       can_reuse_decoder = FALSE;
2313
2314     if (can_reuse_decoder) {
2315       if (output->type & GST_STREAM_TYPE_VIDEO && output->drop_probe_id == 0) {
2316         GST_DEBUG_OBJECT (dbin, "Adding keyframe-waiter probe");
2317         output->drop_probe_id =
2318             gst_pad_add_probe (slot->src_pad, GST_PAD_PROBE_TYPE_BUFFER,
2319             (GstPadProbeCallback) keyframe_waiter_probe, output, NULL);
2320       }
2321       GST_DEBUG_OBJECT (dbin, "Reusing existing decoder for slot %p", slot);
2322       if (output->linked == FALSE) {
2323         gst_pad_link_full (slot->src_pad, output->decoder_sink,
2324             GST_PAD_LINK_CHECK_NOTHING);
2325         output->linked = TRUE;
2326       }
2327       gst_caps_unref (new_caps);
2328       return;
2329     }
2330
2331     GST_DEBUG_OBJECT (dbin, "Removing old decoder for slot %p", slot);
2332
2333     if (output->linked)
2334       gst_pad_unlink (slot->src_pad, output->decoder_sink);
2335     output->linked = FALSE;
2336     if (output->drop_probe_id) {
2337       gst_pad_remove_probe (slot->src_pad, output->drop_probe_id);
2338       output->drop_probe_id = 0;
2339     }
2340
2341     if (!gst_ghost_pad_set_target ((GstGhostPad *) output->src_pad, NULL)) {
2342       GST_ERROR_OBJECT (dbin, "Could not release decoder pad");
2343       gst_caps_unref (new_caps);
2344       goto cleanup;
2345     }
2346
2347     gst_element_set_locked_state (output->decoder, TRUE);
2348     gst_element_set_state (output->decoder, GST_STATE_NULL);
2349
2350     gst_bin_remove ((GstBin *) dbin, output->decoder);
2351     output->decoder = NULL;
2352   }
2353
2354   gst_caps_unref (new_caps);
2355
2356   gst_object_replace ((GstObject **) & output->decoder_sink, NULL);
2357   gst_object_replace ((GstObject **) & output->decoder_src, NULL);
2358
2359   /* If a decoder is required, create one */
2360   if (needs_decoder) {
2361     /* If we don't have a decoder yet, instantiate one */
2362     output->decoder = create_decoder (dbin, slot->active_stream);
2363     if (output->decoder == NULL) {
2364       GstCaps *caps;
2365
2366       SELECTION_UNLOCK (dbin);
2367       /* FIXME : Should we be smarter if there's a missing decoder ?
2368        * Should we deactivate that stream ? */
2369       caps = gst_stream_get_caps (slot->active_stream);
2370       gst_element_post_message (GST_ELEMENT_CAST (dbin),
2371           gst_missing_decoder_message_new (GST_ELEMENT_CAST (dbin), caps));
2372       gst_caps_unref (caps);
2373       SELECTION_LOCK (dbin);
2374       goto cleanup;
2375     }
2376     if (!gst_bin_add ((GstBin *) dbin, output->decoder)) {
2377       GST_ERROR_OBJECT (dbin, "could not add decoder to pipeline");
2378       goto cleanup;
2379     }
2380     output->decoder_sink = gst_element_get_static_pad (output->decoder, "sink");
2381     output->decoder_src = gst_element_get_static_pad (output->decoder, "src");
2382     if (output->type & GST_STREAM_TYPE_VIDEO) {
2383       GST_DEBUG_OBJECT (dbin, "Adding keyframe-waiter probe");
2384       output->drop_probe_id =
2385           gst_pad_add_probe (slot->src_pad, GST_PAD_PROBE_TYPE_BUFFER,
2386           (GstPadProbeCallback) keyframe_waiter_probe, output, NULL);
2387     }
2388     if (gst_pad_link_full (slot->src_pad, output->decoder_sink,
2389             GST_PAD_LINK_CHECK_NOTHING) != GST_PAD_LINK_OK) {
2390       GST_ERROR_OBJECT (dbin, "could not link to %s:%s",
2391           GST_DEBUG_PAD_NAME (output->decoder_sink));
2392       goto cleanup;
2393     }
2394   } else {
2395     output->decoder_src = gst_object_ref (slot->src_pad);
2396     output->decoder_sink = NULL;
2397   }
2398   output->linked = TRUE;
2399   if (!gst_ghost_pad_set_target ((GstGhostPad *) output->src_pad,
2400           output->decoder_src)) {
2401     GST_ERROR_OBJECT (dbin, "Could not expose decoder pad");
2402     goto cleanup;
2403   }
2404   if (output->src_exposed == FALSE) {
2405     output->src_exposed = TRUE;
2406     gst_element_add_pad (GST_ELEMENT_CAST (dbin), output->src_pad);
2407   }
2408
2409   if (output->decoder)
2410     gst_element_sync_state_with_parent (output->decoder);
2411
2412   output->slot = slot;
2413   return;
2414
2415 cleanup:
2416   {
2417     GST_DEBUG_OBJECT (dbin, "Cleanup");
2418     if (output->decoder_sink) {
2419       gst_object_unref (output->decoder_sink);
2420       output->decoder_sink = NULL;
2421     }
2422     if (output->decoder_src) {
2423       gst_object_unref (output->decoder_src);
2424       output->decoder_src = NULL;
2425     }
2426     if (output->decoder) {
2427       gst_element_set_state (output->decoder, GST_STATE_NULL);
2428       gst_bin_remove ((GstBin *) dbin, output->decoder);
2429       output->decoder = NULL;
2430     }
2431   }
2432 }
2433
2434 static GstPadProbeReturn
2435 idle_reconfigure (GstPad * pad, GstPadProbeInfo * info, MultiQueueSlot * slot)
2436 {
2437   GstMessage *msg = NULL;
2438   DecodebinOutputStream *output;
2439
2440   SELECTION_LOCK (slot->dbin);
2441   output = get_output_for_slot (slot);
2442
2443   GST_DEBUG_OBJECT (pad, "output : %p", output);
2444
2445   if (output) {
2446     reconfigure_output_stream (output, slot);
2447     msg = is_selection_done (slot->dbin);
2448   }
2449   SELECTION_UNLOCK (slot->dbin);
2450   if (msg)
2451     gst_element_post_message ((GstElement *) slot->dbin, msg);
2452
2453   return GST_PAD_PROBE_REMOVE;
2454 }
2455
2456 static MultiQueueSlot *
2457 find_slot_for_stream_id (GstDecodebin3 * dbin, const gchar * sid)
2458 {
2459   GList *tmp;
2460
2461   for (tmp = dbin->slots; tmp; tmp = tmp->next) {
2462     MultiQueueSlot *slot = (MultiQueueSlot *) tmp->data;
2463     const gchar *stream_id;
2464     if (slot->active_stream) {
2465       stream_id = gst_stream_get_stream_id (slot->active_stream);
2466       if (!g_strcmp0 (sid, stream_id))
2467         return slot;
2468     }
2469     if (slot->pending_stream && slot->pending_stream != slot->active_stream) {
2470       stream_id = gst_stream_get_stream_id (slot->pending_stream);
2471       if (!g_strcmp0 (sid, stream_id))
2472         return slot;
2473     }
2474   }
2475
2476   return NULL;
2477 }
2478
2479 /* This function handles the reassignment of a slot. Call this from
2480  * the streaming thread of a slot. */
2481 static gboolean
2482 reassign_slot (GstDecodebin3 * dbin, MultiQueueSlot * slot)
2483 {
2484   DecodebinOutputStream *output;
2485   MultiQueueSlot *target_slot = NULL;
2486   GList *tmp;
2487   const gchar *sid, *tsid;
2488
2489   SELECTION_LOCK (dbin);
2490   output = slot->output;
2491
2492   if (G_UNLIKELY (slot->active_stream == NULL)) {
2493     GST_DEBUG_OBJECT (slot->src_pad,
2494         "Called on inactive slot (active_stream == NULL)");
2495     SELECTION_UNLOCK (dbin);
2496     return FALSE;
2497   }
2498
2499   if (G_UNLIKELY (output == NULL)) {
2500     GST_DEBUG_OBJECT (slot->src_pad,
2501         "Slot doesn't have any output to be removed");
2502     SELECTION_UNLOCK (dbin);
2503     return FALSE;
2504   }
2505
2506   sid = gst_stream_get_stream_id (slot->active_stream);
2507   GST_DEBUG_OBJECT (slot->src_pad, "slot %s %p", sid, slot);
2508
2509   /* Recheck whether this stream is still in the list of streams to deactivate */
2510   if (stream_in_list (dbin->requested_selection, sid)) {
2511     /* Stream is in the list of requested streams, don't remove */
2512     SELECTION_UNLOCK (dbin);
2513     GST_DEBUG_OBJECT (slot->src_pad,
2514         "Stream '%s' doesn't need to be deactivated", sid);
2515     return FALSE;
2516   }
2517
2518   /* Unlink slot from output */
2519   /* FIXME : Handle flushing ? */
2520   /* FIXME : Handle outputs without decoders */
2521   GST_DEBUG_OBJECT (slot->src_pad, "Unlinking from decoder %p",
2522       output->decoder_sink);
2523   if (output->decoder_sink)
2524     gst_pad_unlink (slot->src_pad, output->decoder_sink);
2525   output->linked = FALSE;
2526   slot->output = NULL;
2527   output->slot = NULL;
2528   /* Remove sid from active selection */
2529   for (tmp = dbin->active_selection; tmp; tmp = tmp->next)
2530     if (!g_strcmp0 (sid, tmp->data)) {
2531       dbin->active_selection = g_list_delete_link (dbin->active_selection, tmp);
2532       break;
2533     }
2534
2535   /* Can we re-assign this output to a requested stream ? */
2536   GST_DEBUG_OBJECT (slot->src_pad, "Attempting to re-assing output stream");
2537   for (tmp = dbin->to_activate; tmp; tmp = tmp->next) {
2538     MultiQueueSlot *tslot = find_slot_for_stream_id (dbin, tmp->data);
2539     GST_LOG_OBJECT (tslot->src_pad, "Checking slot %p (output:%p , stream:%s)",
2540         tslot, tslot->output, gst_stream_get_stream_id (tslot->active_stream));
2541     if (tslot && tslot->type == output->type && tslot->output == NULL) {
2542       GST_DEBUG_OBJECT (tslot->src_pad, "Using as reassigned slot");
2543       target_slot = tslot;
2544       tsid = tmp->data;
2545       /* Pass target stream id to requested selection */
2546       dbin->requested_selection =
2547           g_list_append (dbin->requested_selection, g_strdup (tmp->data));
2548       dbin->to_activate = g_list_remove (dbin->to_activate, tmp->data);
2549       break;
2550     }
2551   }
2552
2553   if (target_slot) {
2554     GST_DEBUG_OBJECT (slot->src_pad, "Assigning output to slot %p '%s'",
2555         target_slot, tsid);
2556     target_slot->output = output;
2557     output->slot = target_slot;
2558     dbin->active_selection =
2559         g_list_append (dbin->active_selection, (gchar *) tsid);
2560     SELECTION_UNLOCK (dbin);
2561
2562     /* Wakeup the target slot so that it retries to send events/buffers
2563      * thereby triggering the output reconfiguration codepath */
2564     gst_pad_add_probe (target_slot->src_pad, GST_PAD_PROBE_TYPE_IDLE,
2565         (GstPadProbeCallback) idle_reconfigure, target_slot, NULL);
2566     /* gst_pad_send_event (target_slot->src_pad, gst_event_new_reconfigure ()); */
2567   } else {
2568     GstMessage *msg;
2569
2570     dbin->output_streams = g_list_remove (dbin->output_streams, output);
2571     free_output_stream (dbin, output);
2572     msg = is_selection_done (slot->dbin);
2573     SELECTION_UNLOCK (dbin);
2574
2575     if (msg)
2576       gst_element_post_message ((GstElement *) slot->dbin, msg);
2577   }
2578
2579   return TRUE;
2580 }
2581
2582 /* Idle probe called when a slot should be unassigned from its output stream.
2583  * This is needed to ensure nothing is flowing when unlinking the slot.
2584  *
2585  * Also, this method will search for a pending stream which could re-use
2586  * the output stream. */
2587 static GstPadProbeReturn
2588 slot_unassign_probe (GstPad * pad, GstPadProbeInfo * info,
2589     MultiQueueSlot * slot)
2590 {
2591   GstDecodebin3 *dbin = slot->dbin;
2592
2593   reassign_slot (dbin, slot);
2594
2595   return GST_PAD_PROBE_REMOVE;
2596 }
2597
2598 static gboolean
2599 handle_stream_switch (GstDecodebin3 * dbin, GList * select_streams,
2600     guint32 seqnum)
2601 {
2602   gboolean ret = TRUE;
2603   GList *tmp;
2604   /* List of slots to (de)activate. */
2605   GList *to_deactivate = NULL;
2606   GList *to_activate = NULL;
2607   /* List of unknown stream id, most likely means the event
2608    * should be sent upstream so that elements can expose the requested stream */
2609   GList *unknown = NULL;
2610   GList *to_reassign = NULL;
2611   GList *future_request_streams = NULL;
2612   GList *pending_streams = NULL;
2613   GList *slots_to_reassign = NULL;
2614
2615   SELECTION_LOCK (dbin);
2616   if (G_UNLIKELY (seqnum != dbin->select_streams_seqnum)) {
2617     GST_DEBUG_OBJECT (dbin, "New SELECT_STREAMS has arrived in the meantime");
2618     SELECTION_UNLOCK (dbin);
2619     return TRUE;
2620   }
2621   /* Remove pending select_streams */
2622   g_list_free (dbin->pending_select_streams);
2623   dbin->pending_select_streams = NULL;
2624
2625   /* COMPARE the requested streams to the active and requested streams
2626    * on multiqueue. */
2627
2628   /* First check the slots to activate and which ones are unknown */
2629   for (tmp = select_streams; tmp; tmp = tmp->next) {
2630     const gchar *sid = (const gchar *) tmp->data;
2631     MultiQueueSlot *slot;
2632     GST_DEBUG_OBJECT (dbin, "Checking stream '%s'", sid);
2633     slot = find_slot_for_stream_id (dbin, sid);
2634     /* Find the corresponding slot */
2635     if (slot == NULL) {
2636       if (stream_in_collection (dbin, (gchar *) sid)) {
2637         pending_streams = g_list_append (pending_streams, (gchar *) sid);
2638       } else {
2639         GST_DEBUG_OBJECT (dbin, "We don't have a slot for stream '%s'", sid);
2640         unknown = g_list_append (unknown, (gchar *) sid);
2641       }
2642     } else if (slot->output == NULL) {
2643       GST_DEBUG_OBJECT (dbin, "We need to activate slot %p for stream '%s')",
2644           slot, sid);
2645       to_activate = g_list_append (to_activate, slot);
2646     } else {
2647       GST_DEBUG_OBJECT (dbin,
2648           "Stream '%s' from slot %p is already active on output %p", sid, slot,
2649           slot->output);
2650       future_request_streams =
2651           g_list_append (future_request_streams, (gchar *) sid);
2652     }
2653   }
2654
2655   for (tmp = dbin->slots; tmp; tmp = tmp->next) {
2656     MultiQueueSlot *slot = (MultiQueueSlot *) tmp->data;
2657     /* For slots that have an output, check if it's part of the streams to
2658      * be active */
2659     if (slot->output) {
2660       gboolean slot_to_deactivate = TRUE;
2661
2662       if (slot->active_stream) {
2663         if (stream_in_list (select_streams,
2664                 gst_stream_get_stream_id (slot->active_stream)))
2665           slot_to_deactivate = FALSE;
2666       }
2667       if (slot_to_deactivate && slot->pending_stream
2668           && slot->pending_stream != slot->active_stream) {
2669         if (stream_in_list (select_streams,
2670                 gst_stream_get_stream_id (slot->pending_stream)))
2671           slot_to_deactivate = FALSE;
2672       }
2673       if (slot_to_deactivate) {
2674         GST_DEBUG_OBJECT (dbin,
2675             "Slot %p (%s) should be deactivated, no longer used", slot,
2676             slot->
2677             active_stream ? gst_stream_get_stream_id (slot->active_stream) :
2678             "NULL");
2679         to_deactivate = g_list_append (to_deactivate, slot);
2680       }
2681     }
2682   }
2683
2684   if (to_deactivate != NULL) {
2685     GST_DEBUG_OBJECT (dbin, "Check if we can reassign slots");
2686     /* We need to compare what needs to be activated and deactivated in order
2687      * to determine whether there are outputs that can be transferred */
2688     /* Take the stream-id of the slots that are to be activated, for which there
2689      * is a slot of the same type that needs to be deactivated */
2690     tmp = to_deactivate;
2691     while (tmp) {
2692       MultiQueueSlot *slot_to_deactivate = (MultiQueueSlot *) tmp->data;
2693       gboolean removeit = FALSE;
2694       GList *tmp2, *next;
2695       GST_DEBUG_OBJECT (dbin,
2696           "Checking if slot to deactivate (%p) has a candidate slot to activate",
2697           slot_to_deactivate);
2698       for (tmp2 = to_activate; tmp2; tmp2 = tmp2->next) {
2699         MultiQueueSlot *slot_to_activate = (MultiQueueSlot *) tmp2->data;
2700         GST_DEBUG_OBJECT (dbin, "Comparing to slot %p", slot_to_activate);
2701         if (slot_to_activate->type == slot_to_deactivate->type) {
2702           GST_DEBUG_OBJECT (dbin, "Re-using");
2703           to_reassign = g_list_append (to_reassign, (gchar *)
2704               gst_stream_get_stream_id (slot_to_activate->active_stream));
2705           slots_to_reassign =
2706               g_list_append (slots_to_reassign, slot_to_deactivate);
2707           to_activate = g_list_remove (to_activate, slot_to_activate);
2708           removeit = TRUE;
2709           break;
2710         }
2711       }
2712       next = tmp->next;
2713       if (removeit)
2714         to_deactivate = g_list_delete_link (to_deactivate, tmp);
2715       tmp = next;
2716     }
2717   }
2718
2719   for (tmp = to_deactivate; tmp; tmp = tmp->next) {
2720     MultiQueueSlot *slot = (MultiQueueSlot *) tmp->data;
2721     GST_DEBUG_OBJECT (dbin,
2722         "Really need to deactivate slot %p, but no available alternative",
2723         slot);
2724
2725     slots_to_reassign = g_list_append (slots_to_reassign, slot);
2726   }
2727
2728   /* The only slots left to activate are the ones that won't be reassigned and
2729    * therefore really need to have a new output created */
2730   for (tmp = to_activate; tmp; tmp = tmp->next) {
2731     MultiQueueSlot *slot = (MultiQueueSlot *) tmp->data;
2732     if (slot->active_stream)
2733       future_request_streams =
2734           g_list_append (future_request_streams,
2735           (gchar *) gst_stream_get_stream_id (slot->active_stream));
2736     else if (slot->pending_stream)
2737       future_request_streams =
2738           g_list_append (future_request_streams,
2739           (gchar *) gst_stream_get_stream_id (slot->pending_stream));
2740     else
2741       GST_ERROR_OBJECT (dbin, "No stream for slot %p !!", slot);
2742   }
2743
2744   if (to_activate == NULL && pending_streams != NULL) {
2745     GST_DEBUG_OBJECT (dbin, "Stream switch requested for future collection");
2746     if (dbin->requested_selection)
2747       g_list_free_full (dbin->requested_selection, g_free);
2748     dbin->requested_selection =
2749         g_list_copy_deep (select_streams, (GCopyFunc) g_strdup, NULL);
2750     g_list_free (to_deactivate);
2751     g_list_free (pending_streams);
2752     to_deactivate = NULL;
2753     pending_streams = NULL;
2754   } else {
2755     if (dbin->requested_selection)
2756       g_list_free_full (dbin->requested_selection, g_free);
2757     dbin->requested_selection =
2758         g_list_copy_deep (future_request_streams, (GCopyFunc) g_strdup, NULL);
2759     dbin->requested_selection =
2760         g_list_concat (dbin->requested_selection,
2761         g_list_copy_deep (pending_streams, (GCopyFunc) g_strdup, NULL));
2762     if (dbin->to_activate)
2763       g_list_free (dbin->to_activate);
2764     dbin->to_activate = g_list_copy (to_reassign);
2765   }
2766
2767   dbin->selection_updated = TRUE;
2768   SELECTION_UNLOCK (dbin);
2769
2770   if (unknown) {
2771     GST_FIXME_OBJECT (dbin, "Got request for an unknown stream");
2772     g_list_free (unknown);
2773   }
2774
2775   if (to_activate && !slots_to_reassign) {
2776     for (tmp = to_activate; tmp; tmp = tmp->next) {
2777       MultiQueueSlot *slot = (MultiQueueSlot *) tmp->data;
2778       gst_pad_add_probe (slot->src_pad, GST_PAD_PROBE_TYPE_IDLE,
2779           (GstPadProbeCallback) idle_reconfigure, slot, NULL);
2780     }
2781   }
2782
2783   /* For all streams to deactivate, add an idle probe where we will do
2784    * the unassignment and switch over */
2785   for (tmp = slots_to_reassign; tmp; tmp = tmp->next) {
2786     MultiQueueSlot *slot = (MultiQueueSlot *) tmp->data;
2787     gst_pad_add_probe (slot->src_pad, GST_PAD_PROBE_TYPE_IDLE,
2788         (GstPadProbeCallback) slot_unassign_probe, slot, NULL);
2789   }
2790
2791   if (to_deactivate)
2792     g_list_free (to_deactivate);
2793   if (to_activate)
2794     g_list_free (to_activate);
2795   if (to_reassign)
2796     g_list_free (to_reassign);
2797   if (future_request_streams)
2798     g_list_free (future_request_streams);
2799   if (pending_streams)
2800     g_list_free (pending_streams);
2801   if (slots_to_reassign)
2802     g_list_free (slots_to_reassign);
2803
2804   return ret;
2805 }
2806
2807 static GstPadProbeReturn
2808 ghost_pad_event_probe (GstPad * pad, GstPadProbeInfo * info,
2809     DecodebinOutputStream * output)
2810 {
2811   GstPadProbeReturn ret = GST_PAD_PROBE_OK;
2812   GstDecodebin3 *dbin = output->dbin;
2813   GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
2814
2815   GST_DEBUG_OBJECT (pad, "Got event %p %s", event, GST_EVENT_TYPE_NAME (event));
2816
2817   switch (GST_EVENT_TYPE (event)) {
2818     case GST_EVENT_SELECT_STREAMS:
2819     {
2820       GstPad *peer;
2821       GList *streams = NULL;
2822       guint32 seqnum = gst_event_get_seqnum (event);
2823
2824       SELECTION_LOCK (dbin);
2825       if (seqnum == dbin->select_streams_seqnum) {
2826         SELECTION_UNLOCK (dbin);
2827         GST_DEBUG_OBJECT (pad,
2828             "Already handled/handling that SELECT_STREAMS event");
2829         gst_event_unref (event);
2830         ret = GST_PAD_PROBE_HANDLED;
2831         break;
2832       }
2833       dbin->select_streams_seqnum = seqnum;
2834       if (dbin->pending_select_streams != NULL) {
2835         GST_LOG_OBJECT (dbin, "Replacing pending select streams");
2836         g_list_free (dbin->pending_select_streams);
2837         dbin->pending_select_streams = NULL;
2838       }
2839       gst_event_parse_select_streams (event, &streams);
2840       dbin->pending_select_streams = g_list_copy (streams);
2841       SELECTION_UNLOCK (dbin);
2842
2843       /* Send event upstream */
2844       if ((peer = gst_pad_get_peer (pad))) {
2845         gst_pad_send_event (peer, event);
2846         gst_object_unref (peer);
2847       } else {
2848         gst_event_unref (event);
2849       }
2850       /* Finally handle the switch */
2851       if (streams) {
2852         handle_stream_switch (dbin, streams, seqnum);
2853         g_list_free_full (streams, g_free);
2854       }
2855       ret = GST_PAD_PROBE_HANDLED;
2856     }
2857       break;
2858     default:
2859       break;
2860   }
2861
2862   return ret;
2863 }
2864
2865 static gboolean
2866 gst_decodebin3_send_event (GstElement * element, GstEvent * event)
2867 {
2868   GST_DEBUG_OBJECT (element, "event %s", GST_EVENT_TYPE_NAME (event));
2869   if (GST_EVENT_TYPE (event) == GST_EVENT_SELECT_STREAMS) {
2870     GstDecodebin3 *dbin = (GstDecodebin3 *) element;
2871     GList *streams = NULL;
2872     guint32 seqnum = gst_event_get_seqnum (event);
2873
2874     SELECTION_LOCK (dbin);
2875     if (seqnum == dbin->select_streams_seqnum) {
2876       SELECTION_UNLOCK (dbin);
2877       GST_DEBUG_OBJECT (dbin,
2878           "Already handled/handling that SELECT_STREAMS event");
2879       return TRUE;
2880     }
2881     dbin->select_streams_seqnum = seqnum;
2882     if (dbin->pending_select_streams != NULL) {
2883       GST_LOG_OBJECT (dbin, "Replacing pending select streams");
2884       g_list_free (dbin->pending_select_streams);
2885       dbin->pending_select_streams = NULL;
2886     }
2887     gst_event_parse_select_streams (event, &streams);
2888     dbin->pending_select_streams = g_list_copy (streams);
2889     SELECTION_UNLOCK (dbin);
2890
2891     /* FIXME : We don't have an upstream ?? */
2892 #if 0
2893     /* Send event upstream */
2894     if ((peer = gst_pad_get_peer (pad))) {
2895       gst_pad_send_event (peer, event);
2896       gst_object_unref (peer);
2897     }
2898 #endif
2899     /* Finally handle the switch */
2900     if (streams) {
2901       handle_stream_switch (dbin, streams, seqnum);
2902       g_list_free_full (streams, g_free);
2903     }
2904
2905     gst_event_unref (event);
2906     return TRUE;
2907   }
2908   return GST_ELEMENT_CLASS (parent_class)->send_event (element, event);
2909 }
2910
2911
2912 static void
2913 free_multiqueue_slot (GstDecodebin3 * dbin, MultiQueueSlot * slot)
2914 {
2915   if (slot->probe_id)
2916     gst_pad_remove_probe (slot->src_pad, slot->probe_id);
2917   if (slot->input) {
2918     if (slot->input->srcpad)
2919       gst_pad_unlink (slot->input->srcpad, slot->sink_pad);
2920   }
2921
2922   gst_element_release_request_pad (dbin->multiqueue, slot->sink_pad);
2923   gst_object_replace ((GstObject **) & slot->sink_pad, NULL);
2924   gst_object_replace ((GstObject **) & slot->src_pad, NULL);
2925   gst_object_replace ((GstObject **) & slot->active_stream, NULL);
2926   g_free (slot);
2927 }
2928
2929 static void
2930 free_multiqueue_slot_async (GstDecodebin3 * dbin, MultiQueueSlot * slot)
2931 {
2932   GST_LOG_OBJECT (dbin, "pushing multiqueue slot on thread pool to free");
2933   gst_element_call_async (GST_ELEMENT_CAST (dbin),
2934       (GstElementCallAsyncFunc) free_multiqueue_slot, slot, NULL);
2935 }
2936
2937 /* Create a DecodebinOutputStream for a given type
2938  * Note: It will be empty initially, it needs to be configured
2939  * afterwards */
2940 static DecodebinOutputStream *
2941 create_output_stream (GstDecodebin3 * dbin, GstStreamType type)
2942 {
2943   DecodebinOutputStream *res = g_new0 (DecodebinOutputStream, 1);
2944   gchar *pad_name;
2945   const gchar *prefix;
2946   GstStaticPadTemplate *templ;
2947   GstPadTemplate *ptmpl;
2948   guint32 *counter;
2949   GstPad *internal_pad;
2950
2951   GST_DEBUG_OBJECT (dbin, "Created new output stream %p for type %s",
2952       res, gst_stream_type_get_name (type));
2953
2954   res->type = type;
2955   res->dbin = dbin;
2956
2957   if (type & GST_STREAM_TYPE_VIDEO) {
2958     templ = &video_src_template;
2959     counter = &dbin->vpadcount;
2960     prefix = "video";
2961   } else if (type & GST_STREAM_TYPE_AUDIO) {
2962     templ = &audio_src_template;
2963     counter = &dbin->apadcount;
2964     prefix = "audio";
2965   } else if (type & GST_STREAM_TYPE_TEXT) {
2966     templ = &text_src_template;
2967     counter = &dbin->tpadcount;
2968     prefix = "text";
2969   } else {
2970     templ = &src_template;
2971     counter = &dbin->opadcount;
2972     prefix = "src";
2973   }
2974
2975   pad_name = g_strdup_printf ("%s_%u", prefix, *counter);
2976   *counter += 1;
2977   ptmpl = gst_static_pad_template_get (templ);
2978   res->src_pad = gst_ghost_pad_new_no_target_from_template (pad_name, ptmpl);
2979   gst_object_unref (ptmpl);
2980   g_free (pad_name);
2981   gst_pad_set_active (res->src_pad, TRUE);
2982   /* Put an event probe on the internal proxy pad to detect upstream
2983    * events */
2984   internal_pad =
2985       (GstPad *) gst_proxy_pad_get_internal ((GstProxyPad *) res->src_pad);
2986   gst_pad_add_probe (internal_pad, GST_PAD_PROBE_TYPE_EVENT_UPSTREAM,
2987       (GstPadProbeCallback) ghost_pad_event_probe, res, NULL);
2988   gst_object_unref (internal_pad);
2989
2990   dbin->output_streams = g_list_append (dbin->output_streams, res);
2991
2992   return res;
2993 }
2994
2995 static void
2996 free_output_stream (GstDecodebin3 * dbin, DecodebinOutputStream * output)
2997 {
2998   if (output->slot) {
2999     if (output->decoder_sink && output->decoder)
3000       gst_pad_unlink (output->slot->src_pad, output->decoder_sink);
3001
3002     output->slot->output = NULL;
3003     output->slot = NULL;
3004   }
3005   gst_object_replace ((GstObject **) & output->decoder_sink, NULL);
3006   gst_ghost_pad_set_target ((GstGhostPad *) output->src_pad, NULL);
3007   gst_object_replace ((GstObject **) & output->decoder_src, NULL);
3008   if (output->src_exposed) {
3009     gst_element_remove_pad ((GstElement *) dbin, output->src_pad);
3010   }
3011   if (output->decoder) {
3012     gst_element_set_locked_state (output->decoder, TRUE);
3013     gst_element_set_state (output->decoder, GST_STATE_NULL);
3014     gst_bin_remove ((GstBin *) dbin, output->decoder);
3015   }
3016   g_free (output);
3017 }
3018
3019 static GstStateChangeReturn
3020 gst_decodebin3_change_state (GstElement * element, GstStateChange transition)
3021 {
3022   GstDecodebin3 *dbin = (GstDecodebin3 *) element;
3023   GstStateChangeReturn ret;
3024
3025   /* Upwards */
3026   switch (transition) {
3027     default:
3028       break;
3029   }
3030   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3031   if (ret == GST_STATE_CHANGE_FAILURE)
3032     goto beach;
3033
3034   switch (transition) {
3035     case GST_STATE_CHANGE_PAUSED_TO_READY:
3036     {
3037       GList *tmp;
3038
3039       /* Free output streams */
3040       for (tmp = dbin->output_streams; tmp; tmp = tmp->next) {
3041         DecodebinOutputStream *output = (DecodebinOutputStream *) tmp->data;
3042         free_output_stream (dbin, output);
3043       }
3044       g_list_free (dbin->output_streams);
3045       dbin->output_streams = NULL;
3046       /* Free multiqueue slots */
3047       for (tmp = dbin->slots; tmp; tmp = tmp->next) {
3048         MultiQueueSlot *slot = (MultiQueueSlot *) tmp->data;
3049         free_multiqueue_slot (dbin, slot);
3050       }
3051       g_list_free (dbin->slots);
3052       dbin->slots = NULL;
3053       dbin->current_group_id = GST_GROUP_ID_INVALID;
3054       /* Free inputs */
3055     }
3056       break;
3057     default:
3058       break;
3059   }
3060 beach:
3061   return ret;
3062 }
3063
3064 gboolean
3065 gst_decodebin3_plugin_init (GstPlugin * plugin)
3066 {
3067   GST_DEBUG_CATEGORY_INIT (decodebin3_debug, "decodebin3", 0, "decoder bin");
3068
3069   return gst_element_register (plugin, "decodebin3", GST_RANK_NONE,
3070       GST_TYPE_DECODEBIN3);
3071 }