3 * Copyright (C) <2015> Centricular Ltd
4 * @author: Edward Hervey <edward@centricular.com>
5 * @author: Jan Schmidt <jan@centricular.com>
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.
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.
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.
24 /* Not needed for now - we're including gstdecodebin3-parse.c into gstdecodebin3.c */
30 #include <glib-object.h>
31 #include <glib/gprintf.h>
33 #include <gst/pbutils/pbutils.h>
35 #include "gstplayback.h"
38 /* Streams that come from demuxers (input/upstream) */
39 /* FIXME : All this is hardcoded. Switch to tree of chains */
40 struct _DecodebinInputStream
43 GstStream *pending_stream; /* Extra ref */
44 GstStream *active_stream;
46 DecodebinInput *input;
48 GstPad *srcpad; /* From demuxer */
50 /* id of the pad event probe */
51 gulong output_event_probe_id;
53 /* id of the buffer blocking probe on the input (demuxer src) pad */
54 gulong input_buffer_probe_id;
56 /* Whether we saw an EOS on input. This should be treated accordingly
57 * when the stream is no longer used */
61 static void parsebin_pad_added_cb (GstElement * demux, GstPad * pad,
62 DecodebinInput * input);
63 static void parsebin_pad_removed_cb (GstElement * demux, GstPad * pad,
64 DecodebinInput * input);
66 /* WITH SELECTION_LOCK TAKEN! */
68 pending_pads_are_eos (DecodebinInput * input)
72 for (tmp = input->pending_pads; tmp; tmp = tmp->next) {
73 PendingPad *ppad = (PendingPad *) tmp->data;
74 if (ppad->saw_eos == FALSE)
81 /* WITH SELECTION_LOCK TAKEN! */
83 all_inputs_are_eos (GstDecodebin3 * dbin)
86 /* First check input streams */
87 for (tmp = dbin->input_streams; tmp; tmp = tmp->next) {
88 DecodebinInputStream *input = (DecodebinInputStream *) tmp->data;
89 if (input->saw_eos == FALSE)
93 /* Check pending pads */
94 if (!pending_pads_are_eos (dbin->main_input))
96 for (tmp = dbin->other_inputs; tmp; tmp = tmp->next)
97 if (!pending_pads_are_eos ((DecodebinInput *) tmp->data))
100 GST_DEBUG_OBJECT (dbin, "All streams are EOS");
104 /* WITH SELECTION_LOCK TAKEN! */
106 check_all_streams_for_eos (GstDecodebin3 * dbin)
110 if (!all_inputs_are_eos (dbin))
113 /* We know all streams are EOS, properly clean up everything */
114 for (tmp = dbin->input_streams; tmp; tmp = tmp->next) {
115 DecodebinInputStream *input = (DecodebinInputStream *) tmp->data;
116 GstPad *peer = gst_pad_get_peer (input->srcpad);
118 /* Send EOS and then remove elements */
120 gst_pad_send_event (peer, gst_event_new_eos ());
121 gst_object_unref (peer);
123 GST_FIXME_OBJECT (input->srcpad, "Remove input stream");
127 /* Get the intersection of parser caps and available (sorted) decoders */
129 get_parser_caps_filter (GstDecodebin3 * dbin, GstCaps * caps)
132 GstCaps *filter_caps = gst_caps_new_empty ();
134 g_mutex_lock (&dbin->factories_lock);
135 gst_decode_bin_update_factories_list (dbin);
136 for (tmp = dbin->decoder_factories; tmp; tmp = tmp->next) {
137 GstElementFactory *factory = (GstElementFactory *) tmp->data;
138 GstCaps *tcaps, *intersection;
141 GST_LOG ("Trying factory %s",
142 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
143 for (tmps = gst_element_factory_get_static_pad_templates (factory); tmps;
145 GstStaticPadTemplate *st = (GstStaticPadTemplate *) tmps->data;
146 if (st->direction != GST_PAD_SINK || st->presence != GST_PAD_ALWAYS)
148 tcaps = gst_static_pad_template_get_caps (st);
150 gst_caps_intersect_full (tcaps, caps, GST_CAPS_INTERSECT_FIRST);
151 filter_caps = gst_caps_merge (filter_caps, intersection);
152 gst_caps_unref (tcaps);
155 g_mutex_unlock (&dbin->factories_lock);
156 GST_DEBUG_OBJECT (dbin, "Got filter caps %" GST_PTR_FORMAT, filter_caps);
161 check_parser_caps_filter (GstDecodebin3 * dbin, GstCaps * caps)
164 gboolean res = FALSE;
166 g_mutex_lock (&dbin->factories_lock);
167 gst_decode_bin_update_factories_list (dbin);
168 for (tmp = dbin->decoder_factories; tmp; tmp = tmp->next) {
169 GstElementFactory *factory = (GstElementFactory *) tmp->data;
173 GST_LOG ("Trying factory %s",
174 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
175 for (tmps = gst_element_factory_get_static_pad_templates (factory); tmps;
177 GstStaticPadTemplate *st = (GstStaticPadTemplate *) tmps->data;
178 if (st->direction != GST_PAD_SINK || st->presence != GST_PAD_ALWAYS)
180 tcaps = gst_static_pad_template_get_caps (st);
181 if (gst_caps_can_intersect (tcaps, caps)) {
183 gst_caps_unref (tcaps);
186 gst_caps_unref (tcaps);
190 g_mutex_unlock (&dbin->factories_lock);
191 GST_DEBUG_OBJECT (dbin, "Can intersect : %d", res);
195 /* Probe on the output of a parser chain (the last
197 static GstPadProbeReturn
198 parse_chain_output_probe (GstPad * pad, GstPadProbeInfo * info,
199 DecodebinInputStream * input)
201 GstPadProbeReturn ret = GST_PAD_PROBE_OK;
203 if (GST_IS_EVENT (GST_PAD_PROBE_INFO_DATA (info))) {
204 GstEvent *ev = GST_PAD_PROBE_INFO_EVENT (info);
206 GST_DEBUG_OBJECT (pad, "Got event %s", GST_EVENT_TYPE_NAME (ev));
207 switch (GST_EVENT_TYPE (ev)) {
208 case GST_EVENT_STREAM_START:
210 GstStream *stream = NULL;
211 guint group_id = G_MAXUINT32;
213 if (!gst_event_parse_group_id (ev, &group_id)) {
214 GST_FIXME_OBJECT (pad,
215 "Consider implementing group-id handling on stream-start event");
216 group_id = gst_util_group_id_next ();
219 GST_DEBUG_OBJECT (pad, "Got stream-start, group_id:%d, input %p",
220 group_id, input->input);
221 if (set_input_group_id (input->input, &group_id)) {
222 ev = gst_event_make_writable (ev);
223 gst_event_set_group_id (ev, group_id);
224 GST_PAD_PROBE_INFO_DATA (info) = ev;
226 input->saw_eos = FALSE;
228 gst_event_parse_stream (ev, &stream);
229 /* FIXME : Would we ever end up with a stream already set on the input ?? */
231 if (input->active_stream != stream) {
232 MultiQueueSlot *slot;
233 if (input->active_stream)
234 gst_object_unref (input->active_stream);
235 input->active_stream = stream;
236 /* We have the beginning of a stream, get a multiqueue slot and link to it */
237 SELECTION_LOCK (input->dbin);
238 slot = get_slot_for_input (input->dbin, input);
239 link_input_to_slot (input, slot);
240 SELECTION_UNLOCK (input->dbin);
242 gst_object_unref (stream);
248 GstCaps *caps = NULL;
249 gst_event_parse_caps (ev, &caps);
250 GST_DEBUG_OBJECT (pad, "caps %" GST_PTR_FORMAT, caps);
251 if (caps && input->active_stream)
252 gst_stream_set_caps (input->active_stream, caps);
256 input->saw_eos = TRUE;
257 if (all_inputs_are_eos (input->dbin)) {
258 GST_DEBUG_OBJECT (pad, "real input pad, marking as EOS");
259 SELECTION_LOCK (input->dbin);
260 check_all_streams_for_eos (input->dbin);
261 SELECTION_UNLOCK (input->dbin);
263 GstPad *peer = gst_pad_get_peer (input->srcpad);
265 /* Send custom-eos event to multiqueue slot */
269 GST_DEBUG_OBJECT (pad,
270 "Got EOS end of input stream, post custom-eos");
271 s = gst_structure_new_empty ("decodebin3-custom-eos");
272 event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
273 gst_pad_send_event (peer, event);
274 gst_object_unref (peer);
276 GST_FIXME_OBJECT (pad, "No peer, what should we do ?");
279 ret = GST_PAD_PROBE_DROP;
281 case GST_EVENT_FLUSH_STOP:
282 GST_DEBUG_OBJECT (pad, "Clear saw_eos flag");
283 input->saw_eos = FALSE;
287 } else if (GST_IS_QUERY (GST_PAD_PROBE_INFO_DATA (info))) {
288 GstQuery *q = GST_PAD_PROBE_INFO_QUERY (info);
289 GST_DEBUG_OBJECT (pad, "Seeing query %s", GST_QUERY_TYPE_NAME (q));
290 /* If we have a parser, we want to reply to the caps query */
291 /* FIXME: Set a flag when the input stream is created for
292 * streams where we shouldn't reply to these queries */
293 if (GST_QUERY_TYPE (q) == GST_QUERY_CAPS
294 && (info->type & GST_PAD_PROBE_TYPE_PULL)) {
295 GstCaps *filter = NULL;
297 gst_query_parse_caps (q, &filter);
298 allowed = get_parser_caps_filter (input->dbin, filter);
299 GST_DEBUG_OBJECT (pad,
300 "Intercepting caps query, setting %" GST_PTR_FORMAT, allowed);
301 gst_query_set_caps_result (q, allowed);
302 gst_caps_unref (allowed);
303 ret = GST_PAD_PROBE_HANDLED;
304 } else if (GST_QUERY_TYPE (q) == GST_QUERY_ACCEPT_CAPS) {
305 GstCaps *prop = NULL;
306 gst_query_parse_accept_caps (q, &prop);
307 /* Fast check against target caps */
308 if (gst_caps_can_intersect (prop, input->dbin->caps))
309 gst_query_set_accept_caps_result (q, TRUE);
311 gboolean accepted = check_parser_caps_filter (input->dbin, prop);
312 /* check against caps filter */
313 gst_query_set_accept_caps_result (q, accepted);
314 GST_DEBUG_OBJECT (pad, "ACCEPT_CAPS query, returning %d", accepted);
316 ret = GST_PAD_PROBE_HANDLED;
323 static DecodebinInputStream *
324 create_input_stream (GstDecodebin3 * dbin, GstStream * stream, GstPad * pad,
325 DecodebinInput * input)
327 DecodebinInputStream *res = g_new0 (DecodebinInputStream, 1);
329 GST_DEBUG_OBJECT (pad, "Creating input stream for stream %p %s (input:%p)",
330 stream, gst_stream_get_stream_id (stream), input);
334 res->pending_stream = gst_object_ref (stream);
337 /* Put probe on output source pad (for detecting EOS/STREAM_START/FLUSH) */
338 res->output_event_probe_id =
339 gst_pad_add_probe (pad,
340 GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM | GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM
341 | GST_PAD_PROBE_TYPE_EVENT_FLUSH,
342 (GstPadProbeCallback) parse_chain_output_probe, res, NULL);
344 /* Add to list of current input streams */
345 SELECTION_LOCK (dbin);
346 dbin->input_streams = g_list_append (dbin->input_streams, res);
347 SELECTION_UNLOCK (dbin);
348 GST_DEBUG_OBJECT (pad, "Done creating input stream");
353 /* WITH SELECTION_LOCK TAKEN! */
355 remove_input_stream (GstDecodebin3 * dbin, DecodebinInputStream * stream)
357 MultiQueueSlot *slot;
359 GST_DEBUG_OBJECT (dbin, "Removing input stream %p (%s)", stream,
360 stream->active_stream ? gst_stream_get_stream_id (stream->active_stream) :
363 /* Unlink from slot */
364 if (stream->srcpad) {
366 peer = gst_pad_get_peer (stream->srcpad);
368 gst_pad_unlink (stream->srcpad, peer);
369 gst_object_unref (peer);
373 slot = get_slot_for_input (dbin, stream);
375 slot->pending_stream = NULL;
377 GST_DEBUG_OBJECT (dbin, "slot %p cleared", slot);
380 if (stream->active_stream)
381 gst_object_unref (stream->active_stream);
382 if (stream->pending_stream)
383 gst_object_unref (stream->pending_stream);
385 dbin->input_streams = g_list_remove (dbin->input_streams, stream);
391 /* FIXME : HACK, REMOVE, USE INPUT CHAINS */
392 static GstPadProbeReturn
393 parsebin_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
394 DecodebinInput * input)
396 GstDecodebin3 *dbin = input->dbin;
397 GList *tmp, *unused_slot = NULL;
399 GST_DEBUG_OBJECT (pad, "Got a buffer ! UNBLOCK !");
401 /* Any data out the demuxer means it's not creating pads
402 * any more right now */
404 /* 1. Re-use existing streams if/when possible */
405 GST_FIXME_OBJECT (dbin, "Re-use existing input streams if/when possible");
407 /* 2. Remove unused streams (push EOS) */
408 GST_DEBUG_OBJECT (dbin, "Removing unused streams");
409 SELECTION_LOCK (dbin);
410 tmp = dbin->input_streams;
411 while (tmp != NULL) {
412 DecodebinInputStream *input_stream = (DecodebinInputStream *) tmp->data;
413 GList *next = tmp->next;
415 if (input_stream->input != input) {
420 GST_DEBUG_OBJECT (dbin, "Checking input stream %p", input_stream);
421 if (input_stream->input_buffer_probe_id) {
422 GST_DEBUG_OBJECT (dbin,
423 "Removing pad block on input %p pad %" GST_PTR_FORMAT, input_stream,
424 input_stream->srcpad);
425 gst_pad_remove_probe (input_stream->srcpad,
426 input_stream->input_buffer_probe_id);
428 input_stream->input_buffer_probe_id = 0;
430 if (input_stream->saw_eos) {
431 remove_input_stream (dbin, input_stream);
432 tmp = dbin->input_streams;
436 SELECTION_UNLOCK (dbin);
438 GST_DEBUG_OBJECT (dbin, "Creating new streams (if needed)");
439 /* 3. Create new streams */
440 for (tmp = input->pending_pads; tmp; tmp = tmp->next) {
442 PendingPad *ppad = (PendingPad *) tmp->data;
444 stream = gst_pad_get_stream (ppad->pad);
445 if (stream == NULL) {
446 GST_ERROR_OBJECT (dbin, "No stream for pad ????");
448 MultiQueueSlot *slot;
449 DecodebinInputStream *input_stream;
450 /* The remaining pads in pending_pads are the ones that require a new
452 input_stream = create_input_stream (dbin, stream, ppad->pad, ppad->input);
453 /* See if we can link it straight away */
454 input_stream->active_stream = stream;
456 SELECTION_LOCK (dbin);
457 slot = get_slot_for_input (dbin, input_stream);
458 link_input_to_slot (input_stream, slot);
459 SELECTION_UNLOCK (dbin);
461 /* Remove the buffer and event probe */
462 gst_pad_remove_probe (ppad->pad, ppad->buffer_probe);
463 gst_pad_remove_probe (ppad->pad, ppad->event_probe);
468 g_list_free (input->pending_pads);
469 input->pending_pads = NULL;
471 /* Weed out unused multiqueue slots */
472 SELECTION_LOCK (dbin);
473 for (tmp = dbin->slots; tmp; tmp = tmp->next) {
474 MultiQueueSlot *slot = (MultiQueueSlot *) tmp->data;
475 GST_LOG_OBJECT (dbin, "Slot %d input:%p", slot->id, slot->input);
476 if (slot->input == NULL) {
478 g_list_append (unused_slot, gst_object_ref (slot->sink_pad));
481 SELECTION_UNLOCK (dbin);
483 for (tmp = unused_slot; tmp; tmp = tmp->next) {
484 GstPad *sink_pad = (GstPad *) tmp->data;
485 GST_DEBUG_OBJECT (sink_pad, "Sending EOS to unused slot");
486 gst_pad_send_event (sink_pad, gst_event_new_eos ());
490 g_list_free_full (unused_slot, (GDestroyNotify) gst_object_unref);
492 return GST_PAD_PROBE_OK;
495 static GstPadProbeReturn
496 parsebin_pending_event_probe (GstPad * pad, GstPadProbeInfo * info,
499 GstDecodebin3 *dbin = ppad->dbin;
500 /* We drop all events by default */
501 GstPadProbeReturn ret = GST_PAD_PROBE_DROP;
502 GstEvent *ev = GST_PAD_PROBE_INFO_EVENT (info);
504 GST_DEBUG_OBJECT (pad, "Got event %p %s", ev, GST_EVENT_TYPE_NAME (ev));
505 switch (GST_EVENT_TYPE (ev)) {
508 GST_DEBUG_OBJECT (pad, "Pending pad marked as EOS, removing");
509 ppad->input->pending_pads =
510 g_list_remove (ppad->input->pending_pads, ppad);
511 gst_pad_remove_probe (ppad->pad, ppad->buffer_probe);
512 gst_pad_remove_probe (ppad->pad, ppad->event_probe);
515 check_all_streams_for_eos (dbin);
526 parsebin_pad_added_cb (GstElement * demux, GstPad * pad, DecodebinInput * input)
528 GstDecodebin3 *dbin = input->dbin;
532 GST_DEBUG_OBJECT (dbin, "New pad %s:%s (input:%p)", GST_DEBUG_PAD_NAME (pad),
535 ppad = g_new0 (PendingPad, 1);
541 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
542 (GstPadProbeCallback) parsebin_pending_event_probe, ppad, NULL);
544 gst_pad_add_probe (pad,
545 GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER,
546 (GstPadProbeCallback) parsebin_buffer_probe, input, NULL);
548 input->pending_pads = g_list_append (input->pending_pads, ppad);
550 /* Check if all existing input streams have a buffer probe set */
551 for (tmp = dbin->input_streams; tmp; tmp = tmp->next) {
552 DecodebinInputStream *input_stream = (DecodebinInputStream *) tmp->data;
553 if (input_stream->input == input &&
554 input_stream->input_buffer_probe_id == 0) {
555 GST_DEBUG_OBJECT (input_stream->srcpad, "Adding blocking buffer probe");
556 input_stream->input_buffer_probe_id =
557 gst_pad_add_probe (input_stream->srcpad,
558 GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER,
559 (GstPadProbeCallback) parsebin_buffer_probe, input_stream->input,
566 parsebin_pad_removed_cb (GstElement * demux, GstPad * pad, DecodebinInput * inp)
568 GstDecodebin3 *dbin = inp->dbin;
569 DecodebinInputStream *input = NULL;
571 GST_DEBUG_OBJECT (pad, "removed");
573 for (tmp = dbin->input_streams; tmp; tmp = tmp->next) {
574 DecodebinInputStream *cand = (DecodebinInputStream *) tmp->data;
575 if (cand->srcpad == pad)
578 /* If there are no pending pads, this means we will definitely not need this
581 GST_DEBUG_OBJECT (pad, "stream %p", input);
582 if (inp->pending_pads == NULL) {
583 MultiQueueSlot *slot;
585 GST_DEBUG_OBJECT (pad, "Remove input stream %p", input);
587 SELECTION_LOCK (dbin);
588 slot = get_slot_for_input (dbin, input);
590 remove_input_stream (dbin, input);
591 if (slot && g_list_find (dbin->slots, slot) && slot->is_drained) {
592 /* if slot is still there and already drained, remove it in here */
594 DecodebinOutputStream *output = slot->output;
595 GST_DEBUG_OBJECT (pad,
596 "Multiqueue was drained, Remove output stream");
598 dbin->output_streams = g_list_remove (dbin->output_streams, output);
599 free_output_stream (dbin, output);
601 GST_DEBUG_OBJECT (pad, "No pending pad, Remove multiqueue slot");
603 gst_pad_remove_probe (slot->src_pad, slot->probe_id);
605 dbin->slots = g_list_remove (dbin->slots, slot);
606 free_multiqueue_slot_async (dbin, slot);
608 SELECTION_UNLOCK (dbin);
610 input->srcpad = NULL;
611 if (input->input_buffer_probe_id)
612 gst_pad_remove_probe (pad, input->input_buffer_probe_id);
613 input->input_buffer_probe_id = 0;