1 /* GStreamer
2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 /* TODO/FIXME:
21 *
22 * * BUFFERING MESSAGES
23 * ** How/Where do we deal with buffering messages from a new/prerolling
24 * source ? Ideally we want to re-use the same sourcebin ?
25 * ** Remember last buffering messages per source handler, if the SourceEntry
26 * group_id is the one being currently outputted on the source ghostpads,
27 * post the (last) buffering messages.
28 * If no group_id is being outputted (still prerolling), then output
29 * the messages directly
30 *
31 * * ASYNC HANDLING
32 * ** URIDECODEBIN3 is not async-aware.
33 *
34 * * GAPLESS HANDLING
35 * ** Correlate group_id and URI to know when/which stream is being outputted/started
36 */
37
38 /**
39 * SECTION:element-uridecodebin3
40 * @title: uridecodebin3
41 *
42 * Decodes data from a URI into raw media. It selects a source element that can
43 * handle the given #GstURIDecodeBin3:uri scheme and connects it to a decodebin.
44 */
45
46 #ifdef HAVE_CONFIG_H
47 # include "config.h"
48 #endif
49
50 #include <string.h>
51
52 #include <gst/gst.h>
53 #include <gst/gst-i18n-plugin.h>
54 #include <gst/pbutils/missing-plugins.h>
55
56 #include "gstplay-enum.h"
57 #include "gstrawcaps.h"
58 #include "gstplayback.h"
59 #include "gstplaybackutils.h"
60
61 #define GST_TYPE_URI_DECODE_BIN3 \
62 (gst_uri_decode_bin3_get_type())
63 #define GST_URI_DECODE_BIN3(obj) \
64 (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_URI_DECODE_BIN3,GstURIDecodeBin3))
65 #define GST_URI_DECODE_BIN3_CLASS(klass) \
66 (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_URI_DECODE_BIN3,GstURIDecodeBin3Class))
67 #define GST_IS_URI_DECODE_BIN3(obj) \
68 (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_URI_DECODE_BIN3))
69 #define GST_IS_URI_DECODE_BIN3_CLASS(klass) \
70 (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_URI_DECODE_BIN3))
71 #define GST_URI_DECODE_BIN3_CAST(obj) ((GstURIDecodeBin3 *) (obj))
72
73 typedef struct _GstSourceGroup GstSourceGroup;
74 typedef struct _GstURIDecodeBin3 GstURIDecodeBin3;
75 typedef struct _GstURIDecodeBin3Class GstURIDecodeBin3Class;
76
77 #define GST_URI_DECODE_BIN3_LOCK(dec) (g_mutex_lock(&((GstURIDecodeBin3*)(dec))->lock))
78 #define GST_URI_DECODE_BIN3_UNLOCK(dec) (g_mutex_unlock(&((GstURIDecodeBin3*)(dec))->lock))
79
80 typedef struct _GstPlayItem GstPlayItem;
81 typedef struct _GstSourceItem GstSourceItem;
82 typedef struct _GstSourceHandler GstSourceHandler;
83 typedef struct _OutputPad OutputPad;
84
85 /* A structure describing a play item, which travels through the elements
86 * over time. */
87 struct _GstPlayItem
88 {
89 GstURIDecodeBin3 *uridecodebin;
90
91 /* Main URI */
92 GstSourceItem *main_item;
93
94 /* Auxiliary URI */
95 /* FIXME : Replace by a list later */
96 GstSourceItem *sub_item;
97
98 /* The group_id used to identify this play item via STREAM_START events
99 * This is the group_id which will be used externally (i.e. rewritten
100 * to outgoing STREAM_START events and in emitted signals).
101 * The urisourcebin-specific group_id is located in GstSourceItem */
102 guint group_id;
103
104 /* Is this play item the one being currently outputted by decodebin3
105 * and on our source ghostpads */
106 gboolean currently_outputted;
107 };
108
109 struct _GstSourceItem
110 {
111 /* The GstPlayItem to which this GstSourceItem belongs to */
112 GstPlayItem *play_item;
113
114 gchar *uri;
115
116 /* The urisourcebin controlling this uri
117 * Can be NULL */
118 GstSourceHandler *handler;
119
120 /* Last buffering information */
121 gint last_perc;
122 GstMessage *last_buffering_message;
123
124 /* The groupid created by urisourcebin for this uri */
125 guint internal_groupid;
126
127 /* FIXME : Add tag lists and other uri-specific items here ? */
128 };
129
130 /* Structure wrapping everything related to a urisourcebin */
131 struct _GstSourceHandler
132 {
133 GstURIDecodeBin3 *uridecodebin;
134
135 GstElement *urisourcebin;
136
137 /* Signal handlers */
138 gulong pad_added_id;
139 gulong pad_removed_id;
140 gulong source_setup_id;
141 gulong about_to_finish_id;
142
143 /* TRUE if the controlled urisourcebin was added to uridecodebin */
144 gboolean active;
145
146 /* whether urisourcebin is drained or not.
147 * Reset if/when setting a new URI */
148 gboolean drained;
149
150 /* Whether urisourcebin posted EOS on all pads and
151 * there is no pending entry */
152 gboolean is_eos;
153
154 /* TRUE if the urisourcebin handles main item */
155 gboolean is_main_source;
156
157 /* buffering message stored for after switching */
158 GstMessage *pending_buffering_msg;
159 };
160
161 /* Controls an output source pad */
162 struct _OutputPad
163 {
164 GstURIDecodeBin3 *uridecodebin;
165
166 GstPad *target_pad;
167 GstPad *ghost_pad;
168
169 /* Downstream event probe id */
170 gulong probe_id;
171
172 /* TRUE if the pad saw EOS. Resetted to FALSE on STREAM_START */
173 gboolean is_eos;
174
175 /* The last seen (i.e. current) group_id
176 * Can be (guint)-1 if no group_id was seen yet */
177 guint current_group_id;
178 };
179
180 /**
181 * GstURIDecodeBin3
182 *
183 * uridecodebin3 element struct
184 */
185 struct _GstURIDecodeBin3
186 {
187 GstBin parent_instance;
188
189 GMutex lock; /* lock for constructing */
190
191 /* Properties */
192 GstElement *source;
193 guint64 connection_speed; /* In bits/sec (0 = unknown) */
194 GstCaps *caps;
195 guint64 buffer_duration; /* When buffering, buffer duration (ns) */
196 guint buffer_size; /* When buffering, buffer size (bytes) */
197 gboolean download;
198 gboolean use_buffering;
199 guint64 ring_buffer_max_size;
200
201 GList *play_items; /* List of GstPlayItem ordered by time of
202 * creation. Head of list is therefore the
203 * current (or pending if initial) one being
204 * outputted */
205 GstPlayItem *current; /* Currently active GstPlayItem. Can be NULL
206 * if no entry is active yet (i.e. no source
207 * pads) */
208
209 /* sources.
210 * FIXME : Replace by a more modular system later on */
211 GstSourceHandler *main_handler;
212 GstSourceHandler *sub_handler;
213
214 /* URI handling
215 * FIXME : Switch to a playlist-based API */
216 gchar *uri;
217 gboolean uri_changed; /* TRUE if uri changed */
218 gchar *suburi;
219 gboolean suburi_changed; /* TRUE if suburi changed */
220
221 /* A global decodebin3 that's used to actually do decoding */
222 GstElement *decodebin;
223
224 /* db3 signals */
225 gulong db_pad_added_id;
226 gulong db_pad_removed_id;
227 gulong db_select_stream_id;
228 gulong db_about_to_finish_id;
229
230 GList *output_pads; /* List of OutputPad */
231
232 GList *source_handlers; /* List of SourceHandler */
233
234 /* Whether we already signalled about-to-finish or not
235 * FIXME: Track this by group-id ! */
236 gboolean posted_about_to_finish;
237 };
238
239 struct _GstURIDecodeBin3Class
240 {
241 GstBinClass parent_class;
242
243 gint (*select_stream) (GstURIDecodeBin3 * dbin,
244 GstStreamCollection * collection, GstStream * stream);
245 };
246
247 GST_DEBUG_CATEGORY_STATIC (gst_uri_decode_bin3_debug);
248 #define GST_CAT_DEFAULT gst_uri_decode_bin3_debug
249
250 /* signals */
251 enum
252 {
253 SIGNAL_SELECT_STREAM,
254 SIGNAL_SOURCE_SETUP,
255 SIGNAL_ABOUT_TO_FINISH,
256 LAST_SIGNAL
257 };
258
259 #if 0
260 static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS ("audio/x-raw(ANY)");
261 static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw(ANY)");
262 #endif
263
264 /* properties */
265 #define DEFAULT_PROP_URI NULL
266 #define DEFAULT_PROP_SUBURI NULL
267 #define DEFAULT_PROP_SOURCE NULL
268 #define DEFAULT_CONNECTION_SPEED 0
269 #define DEFAULT_CAPS (gst_static_caps_get (&default_raw_caps))
270 #define DEFAULT_BUFFER_DURATION -1
271 #define DEFAULT_BUFFER_SIZE -1
272 #define DEFAULT_DOWNLOAD FALSE
273 #define DEFAULT_USE_BUFFERING FALSE
274 #define DEFAULT_RING_BUFFER_MAX_SIZE 0
275
276 enum
277 {
278 PROP_0,
279 PROP_URI,
280 PROP_CURRENT_URI,
281 PROP_SUBURI,
282 PROP_CURRENT_SUBURI,
283 PROP_SOURCE,
284 PROP_CONNECTION_SPEED,
285 PROP_BUFFER_SIZE,
286 PROP_BUFFER_DURATION,
287 PROP_DOWNLOAD,
288 PROP_USE_BUFFERING,
289 PROP_RING_BUFFER_MAX_SIZE,
290 PROP_CAPS
291 };
292
293 static guint gst_uri_decode_bin3_signals[LAST_SIGNAL] = { 0 };
294
295 static GstStaticCaps default_raw_caps = GST_STATIC_CAPS (DEFAULT_RAW_CAPS);
296
297 static GstStaticPadTemplate video_src_template =
298 GST_STATIC_PAD_TEMPLATE ("video_%u",
299 GST_PAD_SRC,
300 GST_PAD_SOMETIMES,
301 GST_STATIC_CAPS_ANY);
302
303 static GstStaticPadTemplate audio_src_template =
304 GST_STATIC_PAD_TEMPLATE ("audio_%u",
305 GST_PAD_SRC,
306 GST_PAD_SOMETIMES,
307 GST_STATIC_CAPS_ANY);
308
309 static GstStaticPadTemplate text_src_template =
310 GST_STATIC_PAD_TEMPLATE ("text_%u",
311 GST_PAD_SRC,
312 GST_PAD_SOMETIMES,
313 GST_STATIC_CAPS_ANY);
314
315 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
316 GST_PAD_SRC,
317 GST_PAD_SOMETIMES,
318 GST_STATIC_CAPS_ANY);
319
320 GType gst_uri_decode_bin3_get_type (void);
321 #define gst_uri_decode_bin3_parent_class parent_class
322 G_DEFINE_TYPE (GstURIDecodeBin3, gst_uri_decode_bin3, GST_TYPE_BIN);
323
324 #define REMOVE_SIGNAL(obj,id) \
325 if (id) { \
326 g_signal_handler_disconnect (obj, id); \
327 id = 0; \
328 }
329
330 static void gst_uri_decode_bin3_set_property (GObject * object, guint prop_id,
331 const GValue * value, GParamSpec * pspec);
332 static void gst_uri_decode_bin3_get_property (GObject * object, guint prop_id,
333 GValue * value, GParamSpec * pspec);
334 static void gst_uri_decode_bin3_finalize (GObject * obj);
335
336 static GstStateChangeReturn gst_uri_decode_bin3_change_state (GstElement *
337 element, GstStateChange transition);
338
339 static gboolean
_gst_int_accumulator(GSignalInvocationHint * ihint,GValue * return_accu,const GValue * handler_return,gpointer dummy)340 _gst_int_accumulator (GSignalInvocationHint * ihint,
341 GValue * return_accu, const GValue * handler_return, gpointer dummy)
342 {
343 gint res = g_value_get_int (handler_return);
344
345 if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
346 g_value_set_int (return_accu, res);
347
348 if (res == -1)
349 return TRUE;
350
351 return FALSE;
352 }
353
354
355 static void
gst_uri_decode_bin3_class_init(GstURIDecodeBin3Class * klass)356 gst_uri_decode_bin3_class_init (GstURIDecodeBin3Class * klass)
357 {
358 GObjectClass *gobject_class;
359 GstElementClass *gstelement_class;
360
361 gobject_class = G_OBJECT_CLASS (klass);
362 gstelement_class = GST_ELEMENT_CLASS (klass);
363
364 gobject_class->set_property = gst_uri_decode_bin3_set_property;
365 gobject_class->get_property = gst_uri_decode_bin3_get_property;
366 gobject_class->finalize = gst_uri_decode_bin3_finalize;
367
368 g_object_class_install_property (gobject_class, PROP_URI,
369 g_param_spec_string ("uri", "URI", "URI to decode",
370 DEFAULT_PROP_URI, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
371
372 g_object_class_install_property (gobject_class, PROP_CURRENT_URI,
373 g_param_spec_string ("current-uri", "Current URI",
374 "The currently playing URI", NULL,
375 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
376
377 g_object_class_install_property (gobject_class, PROP_SUBURI,
378 g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle",
379 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
380
381 g_object_class_install_property (gobject_class, PROP_CURRENT_SUBURI,
382 g_param_spec_string ("current-suburi", "Current .sub-URI",
383 "The currently playing URI of a subtitle",
384 NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
385
386 g_object_class_install_property (gobject_class, PROP_SOURCE,
387 g_param_spec_object ("source", "Source", "Source object used",
388 GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
389
390 g_object_class_install_property (gobject_class, PROP_CONNECTION_SPEED,
391 g_param_spec_uint64 ("connection-speed", "Connection Speed",
392 "Network connection speed in kbps (0 = unknown)",
393 0, G_MAXUINT64 / 1000, DEFAULT_CONNECTION_SPEED,
394 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
395
396 g_object_class_install_property (gobject_class, PROP_BUFFER_SIZE,
397 g_param_spec_int ("buffer-size", "Buffer size (bytes)",
398 "Buffer size when buffering streams (-1 default value)",
399 -1, G_MAXINT, DEFAULT_BUFFER_SIZE,
400 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
401
402 g_object_class_install_property (gobject_class, PROP_BUFFER_DURATION,
403 g_param_spec_int64 ("buffer-duration", "Buffer duration (ns)",
404 "Buffer duration when buffering streams (-1 default value)",
405 -1, G_MAXINT64, DEFAULT_BUFFER_DURATION,
406 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
407
408 /**
409 * GstURIDecodeBin3::download:
410 *
411 * For certain media type, enable download buffering.
412 */
413 g_object_class_install_property (gobject_class, PROP_DOWNLOAD,
414 g_param_spec_boolean ("download", "Download",
415 "Attempt download buffering when buffering network streams",
416 DEFAULT_DOWNLOAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
417 /**
418 * GstURIDecodeBin3::use-buffering:
419 *
420 * Emit BUFFERING messages based on low-/high-percent thresholds of the
421 * demuxed or parsed data.
422 * When download buffering is activated and used for the current media
423 * type, this property does nothing. Otherwise perform buffering on the
424 * demuxed or parsed media.
425 */
426 g_object_class_install_property (gobject_class, PROP_USE_BUFFERING,
427 g_param_spec_boolean ("use-buffering", "Use Buffering",
428 "Perform buffering on demuxed/parsed media",
429 DEFAULT_USE_BUFFERING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
430
431 /**
432 * GstURIDecodeBin3::ring-buffer-max-size
433 *
434 * The maximum size of the ring buffer in kilobytes. If set to 0, the ring
435 * buffer is disabled. Default is 0.
436 */
437 g_object_class_install_property (gobject_class, PROP_RING_BUFFER_MAX_SIZE,
438 g_param_spec_uint64 ("ring-buffer-max-size",
439 "Max. ring buffer size (bytes)",
440 "Max. amount of data in the ring buffer (bytes, 0 = ring buffer disabled)",
441 0, G_MAXUINT, DEFAULT_RING_BUFFER_MAX_SIZE,
442 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
443
444 g_object_class_install_property (gobject_class, PROP_CAPS,
445 g_param_spec_boxed ("caps", "Caps",
446 "The caps on which to stop decoding. (NULL = default)",
447 GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
448
449 /**
450 * GstURIDecodebin3::select-stream
451 * @decodebin: a #GstURIDecodebin3
452 * @collection: a #GstStreamCollection
453 * @stream: a #GstStream
454 *
455 * This signal is emitted whenever @decodebin needs to decide whether
456 * to expose a @stream of a given @collection.
457 *
458 * Returns: 1 if the stream should be selected, 0 if it shouldn't be selected.
459 * A value of -1 (default) lets @decodebin decide what to do with the stream.
460 * */
461 gst_uri_decode_bin3_signals[SIGNAL_SELECT_STREAM] =
462 g_signal_new ("select-stream", G_TYPE_FROM_CLASS (klass),
463 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURIDecodeBin3Class, select_stream),
464 _gst_int_accumulator, NULL, g_cclosure_marshal_generic,
465 G_TYPE_INT, 2, GST_TYPE_STREAM_COLLECTION, GST_TYPE_STREAM);
466
467 /**
468 * GstURIDecodeBin3::source-setup:
469 * @bin: the uridecodebin.
470 * @source: source element
471 *
472 * This signal is emitted after a source element has been created, so
473 * it can be configured by setting additional properties (e.g. set a
474 * proxy server for an http source, or set the device and read speed for
475 * an audio cd source).
476 */
477 gst_uri_decode_bin3_signals[SIGNAL_SOURCE_SETUP] =
478 g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass),
479 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
480 g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
481 /**
482 * GstURIDecodeBin3::about-to-finish:
483 *
484 * This signal is emitted when the data for the selected URI is
485 * entirely buffered and it is safe to specify anothe URI.
486 */
487 gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH] =
488 g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass),
489 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
490 0, G_TYPE_NONE);
491
492
493 gst_element_class_add_static_pad_template (gstelement_class,
494 &video_src_template);
495 gst_element_class_add_static_pad_template (gstelement_class,
496 &audio_src_template);
497 gst_element_class_add_static_pad_template (gstelement_class,
498 &text_src_template);
499 gst_element_class_add_static_pad_template (gstelement_class, &src_template);
500 gst_element_class_set_static_metadata (gstelement_class,
501 "URI Decoder", "Generic/Bin/Decoder",
502 "Autoplug and decode an URI to raw media",
503 "Edward Hervey <edward@centricular.com>, Jan Schmidt <jan@centricular.com>");
504
505 gstelement_class->change_state = gst_uri_decode_bin3_change_state;
506
507 }
508
509 static GstPadProbeReturn
db_src_probe(GstPad * pad,GstPadProbeInfo * info,OutputPad * output)510 db_src_probe (GstPad * pad, GstPadProbeInfo * info, OutputPad * output)
511 {
512 /* FIXME : IMPLEMENT */
513
514 /* EOS : Mark pad as EOS */
515
516 /* STREAM_START : Store group_id and check if currently active
517 * PlayEntry changed */
518
519 return GST_PAD_PROBE_OK;
520 }
521
522 static OutputPad *
add_output_pad(GstURIDecodeBin3 * dec,GstPad * target_pad)523 add_output_pad (GstURIDecodeBin3 * dec, GstPad * target_pad)
524 {
525 OutputPad *output;
526 gchar *pad_name;
527
528 output = g_slice_new0 (OutputPad);
529
530 GST_LOG_OBJECT (dec, "Created output %p", output);
531
532 output->uridecodebin = dec;
533 output->target_pad = target_pad;
534 output->current_group_id = (guint) - 1;
535 pad_name = gst_pad_get_name (target_pad);
536 output->ghost_pad = gst_ghost_pad_new (pad_name, target_pad);
537 g_free (pad_name);
538
539 gst_pad_set_active (output->ghost_pad, TRUE);
540 gst_element_add_pad (GST_ELEMENT (dec), output->ghost_pad);
541
542 output->probe_id =
543 gst_pad_add_probe (output->target_pad,
544 GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, (GstPadProbeCallback) db_src_probe,
545 output, NULL);
546
547 /* FIXME: LOCK TO PROTECT PAD LIST */
548 dec->output_pads = g_list_append (dec->output_pads, output);
549
550 return output;
551 }
552
553 static void
db_pad_added_cb(GstElement * element,GstPad * pad,GstURIDecodeBin3 * dec)554 db_pad_added_cb (GstElement * element, GstPad * pad, GstURIDecodeBin3 * dec)
555 {
556 GST_DEBUG_OBJECT (dec, "Wrapping new pad %s:%s", GST_DEBUG_PAD_NAME (pad));
557
558 if (GST_PAD_IS_SRC (pad))
559 add_output_pad (dec, pad);
560 }
561
562 static void
db_pad_removed_cb(GstElement * element,GstPad * pad,GstURIDecodeBin3 * dec)563 db_pad_removed_cb (GstElement * element, GstPad * pad, GstURIDecodeBin3 * dec)
564 {
565 GList *tmp;
566 OutputPad *output = NULL;
567
568 if (!GST_PAD_IS_SRC (pad))
569 return;
570
571 GST_DEBUG_OBJECT (dec, "pad %s:%s", GST_DEBUG_PAD_NAME (pad));
572 /* FIXME: LOCK for list access */
573
574 for (tmp = dec->output_pads; tmp; tmp = tmp->next) {
575 OutputPad *cand = (OutputPad *) tmp->data;
576
577 if (cand->target_pad == pad) {
578 output = cand;
579 dec->output_pads = g_list_delete_link (dec->output_pads, tmp);
580 break;
581 }
582 }
583
584 if (output) {
585 GST_LOG_OBJECT (element, "Removing output %p", output);
586 /* Remove source ghost pad */
587 gst_ghost_pad_set_target ((GstGhostPad *) output->ghost_pad, NULL);
588 gst_element_remove_pad ((GstElement *) dec, output->ghost_pad);
589
590 /* FIXME : Update global/current PlayEntry group_id (did we switch ?) */
591
592 /* Remove event probe */
593 gst_pad_remove_probe (output->target_pad, output->probe_id);
594
595 g_slice_free (OutputPad, output);
596 }
597 }
598
599 static gint
db_select_stream_cb(GstElement * decodebin,GstStreamCollection * collection,GstStream * stream,GstURIDecodeBin3 * uridecodebin)600 db_select_stream_cb (GstElement * decodebin,
601 GstStreamCollection * collection, GstStream * stream,
602 GstURIDecodeBin3 * uridecodebin)
603 {
604 gint response = -1;
605
606 g_signal_emit (uridecodebin,
607 gst_uri_decode_bin3_signals[SIGNAL_SELECT_STREAM], 0, collection, stream,
608 &response);
609 return response;
610 }
611
612 static void
db_about_to_finish_cb(GstElement * decodebin,GstURIDecodeBin3 * uridecodebin)613 db_about_to_finish_cb (GstElement * decodebin, GstURIDecodeBin3 * uridecodebin)
614 {
615 if (!uridecodebin->posted_about_to_finish) {
616 uridecodebin->posted_about_to_finish = TRUE;
617 g_signal_emit (uridecodebin,
618 gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
619 }
620 }
621
622 static void
gst_uri_decode_bin3_init(GstURIDecodeBin3 * dec)623 gst_uri_decode_bin3_init (GstURIDecodeBin3 * dec)
624 {
625 g_mutex_init (&dec->lock);
626
627 dec->caps = gst_static_caps_get (&default_raw_caps);
628
629 dec->decodebin = gst_element_factory_make ("decodebin3", NULL);
630 gst_bin_add (GST_BIN_CAST (dec), dec->decodebin);
631 dec->db_pad_added_id =
632 g_signal_connect (dec->decodebin, "pad-added",
633 G_CALLBACK (db_pad_added_cb), dec);
634 dec->db_pad_removed_id =
635 g_signal_connect (dec->decodebin, "pad-removed",
636 G_CALLBACK (db_pad_removed_cb), dec);
637 dec->db_select_stream_id =
638 g_signal_connect (dec->decodebin, "select-stream",
639 G_CALLBACK (db_select_stream_cb), dec);
640 dec->db_about_to_finish_id =
641 g_signal_connect (dec->decodebin, "about-to-finish",
642 G_CALLBACK (db_about_to_finish_cb), dec);
643
644 GST_OBJECT_FLAG_SET (dec, GST_ELEMENT_FLAG_SOURCE);
645 gst_bin_set_suppressed_flags (GST_BIN (dec),
646 GST_ELEMENT_FLAG_SOURCE | GST_ELEMENT_FLAG_SINK);
647 }
648
649 static void
gst_uri_decode_bin3_finalize(GObject * obj)650 gst_uri_decode_bin3_finalize (GObject * obj)
651 {
652 GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (obj);
653
654 g_mutex_clear (&dec->lock);
655 g_free (dec->uri);
656 g_free (dec->suburi);
657
658 G_OBJECT_CLASS (parent_class)->finalize (obj);
659 }
660
661 static GstStateChangeReturn
activate_source_item(GstSourceItem * item)662 activate_source_item (GstSourceItem * item)
663 {
664 GstSourceHandler *handler = item->handler;
665
666 if (handler == NULL) {
667 GST_WARNING ("Can't activate item without a handler");
668 return GST_STATE_CHANGE_FAILURE;
669 }
670
671 g_object_set (handler->urisourcebin, "uri", item->uri, NULL);
672 if (!handler->active) {
673 gst_bin_add ((GstBin *) handler->uridecodebin, handler->urisourcebin);
674 /* if (!gst_element_sync_state_with_parent (handler->urisourcebin)) */
675 /* return GST_STATE_CHANGE_FAILURE; */
676 handler->active = TRUE;
677 }
678
679 return GST_STATE_CHANGE_SUCCESS;
680 }
681
682 static void
src_pad_added_cb(GstElement * element,GstPad * pad,GstSourceHandler * handler)683 src_pad_added_cb (GstElement * element, GstPad * pad,
684 GstSourceHandler * handler)
685 {
686 GstURIDecodeBin3 *uridecodebin;
687 GstPad *sinkpad = NULL;
688 GstPadLinkReturn res;
689
690 uridecodebin = handler->uridecodebin;
691
692 GST_DEBUG_OBJECT (uridecodebin,
693 "New pad %" GST_PTR_FORMAT " from source %" GST_PTR_FORMAT, pad, element);
694
695 /* FIXME: Add probe to unify group_id and detect EOS */
696
697 /* Try to link to main sink pad only if it's from a main handler */
698 if (handler->is_main_source) {
699 sinkpad = gst_element_get_static_pad (uridecodebin->decodebin, "sink");
700 if (gst_pad_is_linked (sinkpad)) {
701 gst_object_unref (sinkpad);
702 sinkpad = NULL;
703 }
704 }
705
706 if (sinkpad == NULL)
707 sinkpad = gst_element_get_request_pad (uridecodebin->decodebin, "sink_%u");
708
709 if (sinkpad) {
710 GST_DEBUG_OBJECT (uridecodebin,
711 "Linking %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT, pad, sinkpad);
712 res = gst_pad_link (pad, sinkpad);
713 gst_object_unref (sinkpad);
714 if (GST_PAD_LINK_FAILED (res))
715 goto link_failed;
716 }
717 return;
718
719 link_failed:
720 {
721 GST_ERROR_OBJECT (uridecodebin,
722 "failed to link pad %s:%s to decodebin, reason %s (%d)",
723 GST_DEBUG_PAD_NAME (pad), gst_pad_link_get_name (res), res);
724 return;
725 }
726 }
727
728 static void
src_pad_removed_cb(GstElement * element,GstPad * pad,GstSourceHandler * handler)729 src_pad_removed_cb (GstElement * element, GstPad * pad,
730 GstSourceHandler * handler)
731 {
732 /* FIXME : IMPLEMENT */
733 }
734
735 static void
src_source_setup_cb(GstElement * element,GstElement * source,GstSourceHandler * handler)736 src_source_setup_cb (GstElement * element, GstElement * source,
737 GstSourceHandler * handler)
738 {
739 g_signal_emit (handler->uridecodebin,
740 gst_uri_decode_bin3_signals[SIGNAL_SOURCE_SETUP], 0, source, NULL);
741 }
742
743 static void
src_about_to_finish_cb(GstElement * element,GstSourceHandler * handler)744 src_about_to_finish_cb (GstElement * element, GstSourceHandler * handler)
745 {
746 /* FIXME : check if all sources are done */
747 if (!handler->uridecodebin->posted_about_to_finish) {
748 handler->uridecodebin->posted_about_to_finish = TRUE;
749 g_signal_emit (handler->uridecodebin,
750 gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
751 }
752 }
753
754 static GstSourceHandler *
new_source_handler(GstURIDecodeBin3 * uridecodebin,gboolean is_main)755 new_source_handler (GstURIDecodeBin3 * uridecodebin, gboolean is_main)
756 {
757 GstSourceHandler *handler;
758
759 handler = g_slice_new0 (GstSourceHandler);
760
761 handler->uridecodebin = uridecodebin;
762 handler->is_main_source = is_main;
763 handler->urisourcebin = gst_element_factory_make ("urisourcebin", NULL);
764 /* Set pending properties */
765 g_object_set (handler->urisourcebin,
766 "connection-speed", uridecodebin->connection_speed / 1000,
767 "download", uridecodebin->download,
768 "use-buffering", uridecodebin->use_buffering,
769 "buffer-duration", uridecodebin->buffer_duration,
770 "buffer-size", uridecodebin->buffer_size,
771 "ring-buffer-max-size", uridecodebin->ring_buffer_max_size, NULL);
772
773 handler->pad_added_id =
774 g_signal_connect (handler->urisourcebin, "pad-added",
775 (GCallback) src_pad_added_cb, handler);
776 handler->pad_removed_id =
777 g_signal_connect (handler->urisourcebin, "pad-removed",
778 (GCallback) src_pad_removed_cb, handler);
779 handler->source_setup_id =
780 g_signal_connect (handler->urisourcebin, "source-setup",
781 (GCallback) src_source_setup_cb, handler);
782 handler->about_to_finish_id =
783 g_signal_connect (handler->urisourcebin, "about-to-finish",
784 (GCallback) src_about_to_finish_cb, handler);
785
786 uridecodebin->source_handlers =
787 g_list_append (uridecodebin->source_handlers, handler);
788
789 return handler;
790 }
791
792 static void
gst_uri_decode_bin3_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)793 gst_uri_decode_bin3_set_property (GObject * object, guint prop_id,
794 const GValue * value, GParamSpec * pspec)
795 {
796 GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (object);
797
798 switch (prop_id) {
799 case PROP_URI:
800 if (dec->uri)
801 g_free (dec->uri);
802 dec->uri = g_value_dup_string (value);
803 break;
804 case PROP_SUBURI:
805 if (dec->suburi)
806 g_free (dec->suburi);
807 dec->suburi = g_value_dup_string (value);
808 break;
809 case PROP_CONNECTION_SPEED:
810 GST_URI_DECODE_BIN3_LOCK (dec);
811 dec->connection_speed = g_value_get_uint64 (value) * 1000;
812 GST_URI_DECODE_BIN3_UNLOCK (dec);
813 break;
814 case PROP_BUFFER_SIZE:
815 dec->buffer_size = g_value_get_int (value);
816 break;
817 case PROP_BUFFER_DURATION:
818 dec->buffer_duration = g_value_get_int64 (value);
819 break;
820 case PROP_DOWNLOAD:
821 dec->download = g_value_get_boolean (value);
822 break;
823 case PROP_USE_BUFFERING:
824 dec->use_buffering = g_value_get_boolean (value);
825 break;
826 case PROP_RING_BUFFER_MAX_SIZE:
827 dec->ring_buffer_max_size = g_value_get_uint64 (value);
828 break;
829 case PROP_CAPS:
830 GST_OBJECT_LOCK (dec);
831 if (dec->caps)
832 gst_caps_unref (dec->caps);
833 dec->caps = g_value_dup_boxed (value);
834 GST_OBJECT_UNLOCK (dec);
835 break;
836 default:
837 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
838 break;
839 }
840 }
841
842 static void
gst_uri_decode_bin3_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)843 gst_uri_decode_bin3_get_property (GObject * object, guint prop_id,
844 GValue * value, GParamSpec * pspec)
845 {
846 GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (object);
847
848 switch (prop_id) {
849 case PROP_URI:
850 {
851 g_value_set_string (value, dec->uri);
852 break;
853 }
854 case PROP_CURRENT_URI:
855 {
856 g_value_set_string (value, dec->suburi);
857 break;
858 }
859 case PROP_SUBURI:
860 {
861 /* FIXME : Return current uri */
862 g_value_set_string (value, dec->uri);
863 break;
864 }
865 case PROP_CURRENT_SUBURI:
866 {
867 /* FIXME : Return current suburi */
868 g_value_set_string (value, dec->suburi);
869 break;
870 }
871 case PROP_SOURCE:
872 {
873 GST_OBJECT_LOCK (dec);
874 g_value_set_object (value, dec->source);
875 GST_OBJECT_UNLOCK (dec);
876 break;
877 }
878 case PROP_CONNECTION_SPEED:
879 GST_URI_DECODE_BIN3_LOCK (dec);
880 g_value_set_uint64 (value, dec->connection_speed / 1000);
881 GST_URI_DECODE_BIN3_UNLOCK (dec);
882 break;
883 case PROP_BUFFER_SIZE:
884 GST_OBJECT_LOCK (dec);
885 g_value_set_int (value, dec->buffer_size);
886 GST_OBJECT_UNLOCK (dec);
887 break;
888 case PROP_BUFFER_DURATION:
889 GST_OBJECT_LOCK (dec);
890 g_value_set_int64 (value, dec->buffer_duration);
891 GST_OBJECT_UNLOCK (dec);
892 break;
893 case PROP_DOWNLOAD:
894 g_value_set_boolean (value, dec->download);
895 break;
896 case PROP_USE_BUFFERING:
897 g_value_set_boolean (value, dec->use_buffering);
898 break;
899 case PROP_RING_BUFFER_MAX_SIZE:
900 g_value_set_uint64 (value, dec->ring_buffer_max_size);
901 break;
902 case PROP_CAPS:
903 GST_OBJECT_LOCK (dec);
904 g_value_set_boxed (value, dec->caps);
905 GST_OBJECT_UNLOCK (dec);
906 break;
907 default:
908 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
909 break;
910 }
911 }
912
913 static void
free_source_handler(GstURIDecodeBin3 * uridecodebin,GstSourceHandler * handler)914 free_source_handler (GstURIDecodeBin3 * uridecodebin,
915 GstSourceHandler * handler)
916 {
917 GST_LOG_OBJECT (uridecodebin, "source handler %p", handler);
918 if (handler->active) {
919 GST_LOG_OBJECT (uridecodebin, "Removing %" GST_PTR_FORMAT,
920 handler->urisourcebin);
921 gst_element_set_state (handler->urisourcebin, GST_STATE_NULL);
922 gst_bin_remove ((GstBin *) uridecodebin, handler->urisourcebin);
923 }
924 uridecodebin->source_handlers =
925 g_list_remove (uridecodebin->source_handlers, handler);
926 g_slice_free (GstSourceHandler, handler);
927 }
928
929 static GstSourceItem *
new_source_item(GstURIDecodeBin3 * dec,GstPlayItem * item,gchar * uri)930 new_source_item (GstURIDecodeBin3 * dec, GstPlayItem * item, gchar * uri)
931 {
932 GstSourceItem *sourceitem = g_slice_new0 (GstSourceItem);
933
934 sourceitem->play_item = item;
935 sourceitem->uri = uri;
936
937 return sourceitem;
938 }
939
940 static void
free_source_item(GstURIDecodeBin3 * uridecodebin,GstSourceItem * item)941 free_source_item (GstURIDecodeBin3 * uridecodebin, GstSourceItem * item)
942 {
943 GST_LOG_OBJECT (uridecodebin, "source item %p", item);
944 if (item->handler)
945 free_source_handler (uridecodebin, item->handler);
946 g_slice_free (GstSourceItem, item);
947 }
948
949 static GstPlayItem *
new_play_item(GstURIDecodeBin3 * dec,gchar * uri,gchar * suburi)950 new_play_item (GstURIDecodeBin3 * dec, gchar * uri, gchar * suburi)
951 {
952 GstPlayItem *item = g_slice_new0 (GstPlayItem);
953
954 item->uridecodebin = dec;
955 item->main_item = new_source_item (dec, item, uri);
956 if (suburi)
957 item->sub_item = new_source_item (dec, item, suburi);
958
959 return item;
960 }
961
962 static void
free_play_item(GstURIDecodeBin3 * dec,GstPlayItem * item)963 free_play_item (GstURIDecodeBin3 * dec, GstPlayItem * item)
964 {
965 GST_LOG_OBJECT (dec, "play item %p", item);
966 if (item->main_item)
967 free_source_item (dec, item->main_item);
968 if (item->sub_item)
969 free_source_item (dec, item->sub_item);
970
971 g_slice_free (GstPlayItem, item);
972 }
973
974 /* Sync source handlers for the given play item. Might require creating/removing some
975 * and/or configure the handlers accordingly */
976 static GstStateChangeReturn
assign_handlers_to_item(GstURIDecodeBin3 * dec,GstPlayItem * item)977 assign_handlers_to_item (GstURIDecodeBin3 * dec, GstPlayItem * item)
978 {
979 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
980
981 /* FIXME : Go over existing handlers to see if we can assign some to the
982 * given item */
983
984 /* Create missing handlers */
985 if (item->main_item->handler == NULL) {
986 item->main_item->handler = new_source_handler (dec, TRUE);
987 ret = activate_source_item (item->main_item);
988 if (ret == GST_STATE_CHANGE_FAILURE)
989 return ret;
990 }
991
992 if (item->sub_item && item->sub_item->handler) {
993 item->sub_item->handler = new_source_handler (dec, FALSE);
994 ret = activate_source_item (item->sub_item);
995 }
996
997 return ret;
998 }
999
1000 /* Called to activate the next play item */
1001 static GstStateChangeReturn
activate_next_play_item(GstURIDecodeBin3 * dec)1002 activate_next_play_item (GstURIDecodeBin3 * dec)
1003 {
1004 GstPlayItem *item;
1005 GstStateChangeReturn ret;
1006
1007 /* If there is no current play entry, create one from the uri/suburi
1008 * FIXME : Use a playlist API in the future */
1009 item = new_play_item (dec, dec->uri, dec->suburi);
1010
1011 ret = assign_handlers_to_item (dec, item);
1012 if (ret == GST_STATE_CHANGE_FAILURE) {
1013 free_play_item (dec, item);
1014 return ret;
1015 }
1016
1017 dec->play_items = g_list_append (dec->play_items, item);
1018
1019 return ret;
1020 }
1021
1022 static void
free_play_items(GstURIDecodeBin3 * dec)1023 free_play_items (GstURIDecodeBin3 * dec)
1024 {
1025 GList *tmp;
1026
1027 for (tmp = dec->play_items; tmp; tmp = tmp->next) {
1028 GstPlayItem *item = (GstPlayItem *) tmp->data;
1029 free_play_item (dec, item);
1030 }
1031
1032 g_list_free (dec->play_items);
1033 dec->play_items = NULL;
1034 }
1035
1036 static GstStateChangeReturn
gst_uri_decode_bin3_change_state(GstElement * element,GstStateChange transition)1037 gst_uri_decode_bin3_change_state (GstElement * element,
1038 GstStateChange transition)
1039 {
1040 GstStateChangeReturn ret;
1041 GstURIDecodeBin3 *uridecodebin = (GstURIDecodeBin3 *) element;
1042
1043 switch (transition) {
1044 case GST_STATE_CHANGE_READY_TO_PAUSED:
1045 ret = activate_next_play_item (uridecodebin);
1046 if (ret == GST_STATE_CHANGE_FAILURE)
1047 goto failure;
1048 default:
1049 break;
1050 }
1051
1052 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1053 if (ret == GST_STATE_CHANGE_FAILURE)
1054 goto failure;
1055
1056 switch (transition) {
1057 case GST_STATE_CHANGE_PAUSED_TO_READY:
1058 /* FIXME: Cleanup everything */
1059 free_play_items (uridecodebin);
1060 /* Free play item */
1061 uridecodebin->posted_about_to_finish = FALSE;
1062 break;
1063 default:
1064 break;
1065 }
1066
1067 return ret;
1068
1069 /* ERRORS */
1070 failure:
1071 {
1072 if (transition == GST_STATE_CHANGE_READY_TO_PAUSED)
1073 free_play_items (uridecodebin);
1074 return ret;
1075 }
1076 }
1077
1078
1079 gboolean
gst_uri_decode_bin3_plugin_init(GstPlugin * plugin)1080 gst_uri_decode_bin3_plugin_init (GstPlugin * plugin)
1081 {
1082 GST_DEBUG_CATEGORY_INIT (gst_uri_decode_bin3_debug, "uridecodebin3", 0,
1083 "URI decoder element 3");
1084
1085 return gst_element_register (plugin, "uridecodebin3", GST_RANK_NONE,
1086 GST_TYPE_URI_DECODE_BIN3);
1087 }
1088