2016-06-29 16:14:51 +00:00
|
|
|
/* GStreamer
|
|
|
|
*
|
|
|
|
* Copyright (C) <2015> Centricular Ltd
|
|
|
|
* @author: Edward Hervey <edward@centricular.com>
|
|
|
|
* @author: Jan Schmidt <jan@centricular.com>
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Library General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Library General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Library General Public
|
|
|
|
* License along with this library; if not, write to the
|
|
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
|
|
* Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/* Not needed for now - we're including gstdecodebin3-parse.c into gstdecodebin3.c */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <glib.h>
|
|
|
|
#include <glib-object.h>
|
|
|
|
#include <glib/gprintf.h>
|
|
|
|
#include <gst/gst.h>
|
|
|
|
#include <gst/pbutils/pbutils.h>
|
|
|
|
|
|
|
|
#include "gstplayback.h"
|
|
|
|
#endif
|
|
|
|
|
2018-05-09 16:54:36 +00:00
|
|
|
#define CUSTOM_EOS_QUARK _custom_eos_quark_get ()
|
|
|
|
#define CUSTOM_EOS_QUARK_DATA "custom-eos"
|
|
|
|
static GQuark
|
|
|
|
_custom_eos_quark_get (void)
|
|
|
|
{
|
|
|
|
static gsize g_quark;
|
|
|
|
|
|
|
|
if (g_once_init_enter (&g_quark)) {
|
|
|
|
gsize quark = (gsize) g_quark_from_static_string ("decodebin3-custom-eos");
|
|
|
|
g_once_init_leave (&g_quark, quark);
|
|
|
|
}
|
|
|
|
return g_quark;
|
|
|
|
}
|
|
|
|
|
2016-06-29 16:14:51 +00:00
|
|
|
/* Streams that come from demuxers (input/upstream) */
|
|
|
|
/* FIXME : All this is hardcoded. Switch to tree of chains */
|
|
|
|
struct _DecodebinInputStream
|
|
|
|
{
|
|
|
|
GstDecodebin3 *dbin;
|
|
|
|
GstStream *pending_stream; /* Extra ref */
|
|
|
|
GstStream *active_stream;
|
|
|
|
|
|
|
|
DecodebinInput *input;
|
|
|
|
|
|
|
|
GstPad *srcpad; /* From demuxer */
|
|
|
|
|
|
|
|
/* id of the pad event probe */
|
|
|
|
gulong output_event_probe_id;
|
|
|
|
|
|
|
|
/* id of the buffer blocking probe on the input (demuxer src) pad */
|
|
|
|
gulong input_buffer_probe_id;
|
|
|
|
|
|
|
|
/* Whether we saw an EOS on input. This should be treated accordingly
|
|
|
|
* when the stream is no longer used */
|
|
|
|
gboolean saw_eos;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void parsebin_pad_added_cb (GstElement * demux, GstPad * pad,
|
|
|
|
DecodebinInput * input);
|
|
|
|
static void parsebin_pad_removed_cb (GstElement * demux, GstPad * pad,
|
|
|
|
DecodebinInput * input);
|
|
|
|
|
2017-06-15 16:48:42 +00:00
|
|
|
/* WITH SELECTION_LOCK TAKEN! */
|
2016-06-29 16:14:51 +00:00
|
|
|
static gboolean
|
|
|
|
pending_pads_are_eos (DecodebinInput * input)
|
|
|
|
{
|
|
|
|
GList *tmp;
|
|
|
|
|
|
|
|
for (tmp = input->pending_pads; tmp; tmp = tmp->next) {
|
|
|
|
PendingPad *ppad = (PendingPad *) tmp->data;
|
|
|
|
if (ppad->saw_eos == FALSE)
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2017-06-15 16:48:42 +00:00
|
|
|
/* WITH SELECTION_LOCK TAKEN! */
|
2016-06-29 16:14:51 +00:00
|
|
|
static gboolean
|
|
|
|
all_inputs_are_eos (GstDecodebin3 * dbin)
|
|
|
|
{
|
|
|
|
GList *tmp;
|
|
|
|
/* First check input streams */
|
|
|
|
for (tmp = dbin->input_streams; tmp; tmp = tmp->next) {
|
|
|
|
DecodebinInputStream *input = (DecodebinInputStream *) tmp->data;
|
|
|
|
if (input->saw_eos == FALSE)
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check pending pads */
|
|
|
|
if (!pending_pads_are_eos (dbin->main_input))
|
|
|
|
return FALSE;
|
|
|
|
for (tmp = dbin->other_inputs; tmp; tmp = tmp->next)
|
|
|
|
if (!pending_pads_are_eos ((DecodebinInput *) tmp->data))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
GST_DEBUG_OBJECT (dbin, "All streams are EOS");
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2017-06-15 16:48:42 +00:00
|
|
|
/* WITH SELECTION_LOCK TAKEN! */
|
2016-06-29 16:14:51 +00:00
|
|
|
static void
|
|
|
|
check_all_streams_for_eos (GstDecodebin3 * dbin)
|
|
|
|
{
|
|
|
|
GList *tmp;
|
2020-12-11 09:13:59 +00:00
|
|
|
GList *outputpads = NULL;
|
2016-06-29 16:14:51 +00:00
|
|
|
|
|
|
|
if (!all_inputs_are_eos (dbin))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* We know all streams are EOS, properly clean up everything */
|
2020-12-11 09:13:59 +00:00
|
|
|
|
|
|
|
/* We grab all peer pads *while* the selection lock is taken and then we will
|
|
|
|
push EOS downstream with the selection lock released */
|
2016-06-29 16:14:51 +00:00
|
|
|
for (tmp = dbin->input_streams; tmp; tmp = tmp->next) {
|
|
|
|
DecodebinInputStream *input = (DecodebinInputStream *) tmp->data;
|
|
|
|
GstPad *peer = gst_pad_get_peer (input->srcpad);
|
|
|
|
|
2020-12-11 09:13:59 +00:00
|
|
|
/* Keep a reference to the peer pad */
|
|
|
|
if (peer)
|
|
|
|
outputpads = g_list_append (outputpads, peer);
|
|
|
|
}
|
|
|
|
|
|
|
|
SELECTION_UNLOCK (dbin);
|
|
|
|
/* */
|
|
|
|
for (tmp = outputpads; tmp; tmp = tmp->next) {
|
|
|
|
GstPad *peer = (GstPad *) tmp->data;
|
|
|
|
|
2016-06-29 16:14:51 +00:00
|
|
|
/* Send EOS and then remove elements */
|
2020-12-11 09:13:59 +00:00
|
|
|
gst_pad_send_event (peer, gst_event_new_eos ());
|
|
|
|
GST_FIXME_OBJECT (peer, "Remove input stream");
|
|
|
|
gst_object_unref (peer);
|
2016-06-29 16:14:51 +00:00
|
|
|
}
|
2020-12-11 09:13:59 +00:00
|
|
|
SELECTION_LOCK (dbin);
|
|
|
|
|
|
|
|
g_list_free (outputpads);
|
2016-06-29 16:14:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the intersection of parser caps and available (sorted) decoders */
|
|
|
|
static GstCaps *
|
|
|
|
get_parser_caps_filter (GstDecodebin3 * dbin, GstCaps * caps)
|
|
|
|
{
|
|
|
|
GList *tmp;
|
2020-11-19 13:35:32 +00:00
|
|
|
GstCaps *filter_caps;
|
2016-06-29 16:14:51 +00:00
|
|
|
|
2020-11-14 08:43:59 +00:00
|
|
|
/* If no filter was provided, it can handle anything */
|
|
|
|
if (!caps || gst_caps_is_any (caps))
|
|
|
|
return gst_caps_new_any ();
|
|
|
|
|
2020-11-19 13:35:32 +00:00
|
|
|
filter_caps = gst_caps_new_empty ();
|
|
|
|
|
2016-06-29 16:14:51 +00:00
|
|
|
g_mutex_lock (&dbin->factories_lock);
|
|
|
|
gst_decode_bin_update_factories_list (dbin);
|
|
|
|
for (tmp = dbin->decoder_factories; tmp; tmp = tmp->next) {
|
|
|
|
GstElementFactory *factory = (GstElementFactory *) tmp->data;
|
|
|
|
GstCaps *tcaps, *intersection;
|
|
|
|
const GList *tmps;
|
|
|
|
|
|
|
|
GST_LOG ("Trying factory %s",
|
|
|
|
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
|
|
|
|
for (tmps = gst_element_factory_get_static_pad_templates (factory); tmps;
|
|
|
|
tmps = tmps->next) {
|
|
|
|
GstStaticPadTemplate *st = (GstStaticPadTemplate *) tmps->data;
|
|
|
|
if (st->direction != GST_PAD_SINK || st->presence != GST_PAD_ALWAYS)
|
|
|
|
continue;
|
|
|
|
tcaps = gst_static_pad_template_get_caps (st);
|
|
|
|
intersection =
|
|
|
|
gst_caps_intersect_full (tcaps, caps, GST_CAPS_INTERSECT_FIRST);
|
|
|
|
filter_caps = gst_caps_merge (filter_caps, intersection);
|
|
|
|
gst_caps_unref (tcaps);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g_mutex_unlock (&dbin->factories_lock);
|
|
|
|
GST_DEBUG_OBJECT (dbin, "Got filter caps %" GST_PTR_FORMAT, filter_caps);
|
|
|
|
return filter_caps;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
check_parser_caps_filter (GstDecodebin3 * dbin, GstCaps * caps)
|
|
|
|
{
|
|
|
|
GList *tmp;
|
|
|
|
gboolean res = FALSE;
|
|
|
|
|
|
|
|
g_mutex_lock (&dbin->factories_lock);
|
|
|
|
gst_decode_bin_update_factories_list (dbin);
|
|
|
|
for (tmp = dbin->decoder_factories; tmp; tmp = tmp->next) {
|
|
|
|
GstElementFactory *factory = (GstElementFactory *) tmp->data;
|
|
|
|
GstCaps *tcaps;
|
|
|
|
const GList *tmps;
|
|
|
|
|
|
|
|
GST_LOG ("Trying factory %s",
|
|
|
|
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
|
|
|
|
for (tmps = gst_element_factory_get_static_pad_templates (factory); tmps;
|
|
|
|
tmps = tmps->next) {
|
|
|
|
GstStaticPadTemplate *st = (GstStaticPadTemplate *) tmps->data;
|
|
|
|
if (st->direction != GST_PAD_SINK || st->presence != GST_PAD_ALWAYS)
|
|
|
|
continue;
|
|
|
|
tcaps = gst_static_pad_template_get_caps (st);
|
|
|
|
if (gst_caps_can_intersect (tcaps, caps)) {
|
|
|
|
res = TRUE;
|
|
|
|
gst_caps_unref (tcaps);
|
|
|
|
goto beach;
|
|
|
|
}
|
|
|
|
gst_caps_unref (tcaps);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
beach:
|
|
|
|
g_mutex_unlock (&dbin->factories_lock);
|
|
|
|
GST_DEBUG_OBJECT (dbin, "Can intersect : %d", res);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Probe on the output of a parser chain (the last
|
|
|
|
* src pad) */
|
|
|
|
static GstPadProbeReturn
|
|
|
|
parse_chain_output_probe (GstPad * pad, GstPadProbeInfo * info,
|
|
|
|
DecodebinInputStream * input)
|
|
|
|
{
|
|
|
|
GstPadProbeReturn ret = GST_PAD_PROBE_OK;
|
|
|
|
|
|
|
|
if (GST_IS_EVENT (GST_PAD_PROBE_INFO_DATA (info))) {
|
|
|
|
GstEvent *ev = GST_PAD_PROBE_INFO_EVENT (info);
|
|
|
|
|
|
|
|
GST_DEBUG_OBJECT (pad, "Got event %s", GST_EVENT_TYPE_NAME (ev));
|
|
|
|
switch (GST_EVENT_TYPE (ev)) {
|
|
|
|
case GST_EVENT_STREAM_START:
|
|
|
|
{
|
|
|
|
GstStream *stream = NULL;
|
2017-12-07 14:23:17 +00:00
|
|
|
guint group_id = GST_GROUP_ID_INVALID;
|
2017-02-28 13:13:49 +00:00
|
|
|
|
|
|
|
if (!gst_event_parse_group_id (ev, &group_id)) {
|
|
|
|
GST_FIXME_OBJECT (pad,
|
|
|
|
"Consider implementing group-id handling on stream-start event");
|
|
|
|
group_id = gst_util_group_id_next ();
|
|
|
|
}
|
|
|
|
|
2016-06-29 16:14:51 +00:00
|
|
|
GST_DEBUG_OBJECT (pad, "Got stream-start, group_id:%d, input %p",
|
|
|
|
group_id, input->input);
|
|
|
|
if (set_input_group_id (input->input, &group_id)) {
|
|
|
|
ev = gst_event_make_writable (ev);
|
|
|
|
gst_event_set_group_id (ev, group_id);
|
|
|
|
GST_PAD_PROBE_INFO_DATA (info) = ev;
|
|
|
|
}
|
|
|
|
input->saw_eos = FALSE;
|
|
|
|
|
|
|
|
gst_event_parse_stream (ev, &stream);
|
|
|
|
/* FIXME : Would we ever end up with a stream already set on the input ?? */
|
|
|
|
if (stream) {
|
|
|
|
if (input->active_stream != stream) {
|
|
|
|
MultiQueueSlot *slot;
|
|
|
|
if (input->active_stream)
|
|
|
|
gst_object_unref (input->active_stream);
|
|
|
|
input->active_stream = stream;
|
|
|
|
/* We have the beginning of a stream, get a multiqueue slot and link to it */
|
2017-07-18 09:34:22 +00:00
|
|
|
SELECTION_LOCK (input->dbin);
|
2016-06-29 16:14:51 +00:00
|
|
|
slot = get_slot_for_input (input->dbin, input);
|
|
|
|
link_input_to_slot (input, slot);
|
2017-07-18 09:34:22 +00:00
|
|
|
SELECTION_UNLOCK (input->dbin);
|
2016-06-29 16:14:51 +00:00
|
|
|
} else
|
|
|
|
gst_object_unref (stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case GST_EVENT_CAPS:
|
|
|
|
{
|
|
|
|
GstCaps *caps = NULL;
|
|
|
|
gst_event_parse_caps (ev, &caps);
|
|
|
|
GST_DEBUG_OBJECT (pad, "caps %" GST_PTR_FORMAT, caps);
|
|
|
|
if (caps && input->active_stream)
|
|
|
|
gst_stream_set_caps (input->active_stream, caps);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case GST_EVENT_EOS:
|
2016-12-03 12:42:30 +00:00
|
|
|
input->saw_eos = TRUE;
|
|
|
|
if (all_inputs_are_eos (input->dbin)) {
|
2016-06-29 16:14:51 +00:00
|
|
|
GST_DEBUG_OBJECT (pad, "real input pad, marking as EOS");
|
2017-06-15 16:48:42 +00:00
|
|
|
SELECTION_LOCK (input->dbin);
|
2016-06-29 16:14:51 +00:00
|
|
|
check_all_streams_for_eos (input->dbin);
|
2017-06-15 16:48:42 +00:00
|
|
|
SELECTION_UNLOCK (input->dbin);
|
2016-06-29 16:14:51 +00:00
|
|
|
} else {
|
2016-12-03 12:42:30 +00:00
|
|
|
GstPad *peer = gst_pad_get_peer (input->srcpad);
|
|
|
|
if (peer) {
|
|
|
|
/* Send custom-eos event to multiqueue slot */
|
|
|
|
GstEvent *event;
|
2016-10-13 11:10:09 +00:00
|
|
|
|
2016-06-29 16:14:51 +00:00
|
|
|
GST_DEBUG_OBJECT (pad,
|
2016-12-03 12:42:30 +00:00
|
|
|
"Got EOS end of input stream, post custom-eos");
|
2017-08-09 14:23:03 +00:00
|
|
|
event = gst_event_new_eos ();
|
|
|
|
gst_event_set_seqnum (event, gst_event_get_seqnum (ev));
|
2018-05-09 16:54:36 +00:00
|
|
|
gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (event),
|
|
|
|
CUSTOM_EOS_QUARK, (gchar *) CUSTOM_EOS_QUARK_DATA, NULL);
|
2016-12-03 12:42:30 +00:00
|
|
|
gst_pad_send_event (peer, event);
|
|
|
|
gst_object_unref (peer);
|
2016-06-29 16:14:51 +00:00
|
|
|
} else {
|
2016-12-03 12:42:30 +00:00
|
|
|
GST_FIXME_OBJECT (pad, "No peer, what should we do ?");
|
2016-06-29 16:14:51 +00:00
|
|
|
}
|
|
|
|
}
|
2016-12-03 12:42:30 +00:00
|
|
|
ret = GST_PAD_PROBE_DROP;
|
2016-06-29 16:14:51 +00:00
|
|
|
break;
|
2016-11-13 04:15:38 +00:00
|
|
|
case GST_EVENT_FLUSH_STOP:
|
|
|
|
GST_DEBUG_OBJECT (pad, "Clear saw_eos flag");
|
|
|
|
input->saw_eos = FALSE;
|
2016-06-29 16:14:51 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (GST_IS_QUERY (GST_PAD_PROBE_INFO_DATA (info))) {
|
|
|
|
GstQuery *q = GST_PAD_PROBE_INFO_QUERY (info);
|
|
|
|
GST_DEBUG_OBJECT (pad, "Seeing query %s", GST_QUERY_TYPE_NAME (q));
|
|
|
|
/* If we have a parser, we want to reply to the caps query */
|
|
|
|
/* FIXME: Set a flag when the input stream is created for
|
|
|
|
* streams where we shouldn't reply to these queries */
|
|
|
|
if (GST_QUERY_TYPE (q) == GST_QUERY_CAPS
|
|
|
|
&& (info->type & GST_PAD_PROBE_TYPE_PULL)) {
|
|
|
|
GstCaps *filter = NULL;
|
|
|
|
GstCaps *allowed;
|
|
|
|
gst_query_parse_caps (q, &filter);
|
|
|
|
allowed = get_parser_caps_filter (input->dbin, filter);
|
|
|
|
GST_DEBUG_OBJECT (pad,
|
|
|
|
"Intercepting caps query, setting %" GST_PTR_FORMAT, allowed);
|
|
|
|
gst_query_set_caps_result (q, allowed);
|
|
|
|
gst_caps_unref (allowed);
|
|
|
|
ret = GST_PAD_PROBE_HANDLED;
|
|
|
|
} else if (GST_QUERY_TYPE (q) == GST_QUERY_ACCEPT_CAPS) {
|
|
|
|
GstCaps *prop = NULL;
|
|
|
|
gst_query_parse_accept_caps (q, &prop);
|
|
|
|
/* Fast check against target caps */
|
|
|
|
if (gst_caps_can_intersect (prop, input->dbin->caps))
|
|
|
|
gst_query_set_accept_caps_result (q, TRUE);
|
|
|
|
else {
|
|
|
|
gboolean accepted = check_parser_caps_filter (input->dbin, prop);
|
|
|
|
/* check against caps filter */
|
|
|
|
gst_query_set_accept_caps_result (q, accepted);
|
|
|
|
GST_DEBUG_OBJECT (pad, "ACCEPT_CAPS query, returning %d", accepted);
|
|
|
|
}
|
|
|
|
ret = GST_PAD_PROBE_HANDLED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static DecodebinInputStream *
|
|
|
|
create_input_stream (GstDecodebin3 * dbin, GstStream * stream, GstPad * pad,
|
|
|
|
DecodebinInput * input)
|
|
|
|
{
|
|
|
|
DecodebinInputStream *res = g_new0 (DecodebinInputStream, 1);
|
|
|
|
|
|
|
|
GST_DEBUG_OBJECT (pad, "Creating input stream for stream %p %s (input:%p)",
|
|
|
|
stream, gst_stream_get_stream_id (stream), input);
|
|
|
|
|
|
|
|
res->dbin = dbin;
|
|
|
|
res->input = input;
|
|
|
|
res->pending_stream = gst_object_ref (stream);
|
|
|
|
res->srcpad = pad;
|
|
|
|
|
2016-11-13 04:15:38 +00:00
|
|
|
/* Put probe on output source pad (for detecting EOS/STREAM_START/FLUSH) */
|
2016-06-29 16:14:51 +00:00
|
|
|
res->output_event_probe_id =
|
|
|
|
gst_pad_add_probe (pad,
|
2016-11-13 04:15:38 +00:00
|
|
|
GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM | GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM
|
|
|
|
| GST_PAD_PROBE_TYPE_EVENT_FLUSH,
|
2016-06-29 16:14:51 +00:00
|
|
|
(GstPadProbeCallback) parse_chain_output_probe, res, NULL);
|
|
|
|
|
|
|
|
/* Add to list of current input streams */
|
2017-06-15 16:48:42 +00:00
|
|
|
SELECTION_LOCK (dbin);
|
2016-06-29 16:14:51 +00:00
|
|
|
dbin->input_streams = g_list_append (dbin->input_streams, res);
|
2017-06-15 16:48:42 +00:00
|
|
|
SELECTION_UNLOCK (dbin);
|
2016-06-29 16:14:51 +00:00
|
|
|
GST_DEBUG_OBJECT (pad, "Done creating input stream");
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2017-06-15 16:48:42 +00:00
|
|
|
/* WITH SELECTION_LOCK TAKEN! */
|
2016-06-29 16:14:51 +00:00
|
|
|
static void
|
|
|
|
remove_input_stream (GstDecodebin3 * dbin, DecodebinInputStream * stream)
|
|
|
|
{
|
|
|
|
MultiQueueSlot *slot;
|
|
|
|
|
|
|
|
GST_DEBUG_OBJECT (dbin, "Removing input stream %p (%s)", stream,
|
|
|
|
stream->active_stream ? gst_stream_get_stream_id (stream->active_stream) :
|
|
|
|
"<NONE>");
|
|
|
|
|
|
|
|
/* Unlink from slot */
|
|
|
|
if (stream->srcpad) {
|
|
|
|
GstPad *peer;
|
|
|
|
peer = gst_pad_get_peer (stream->srcpad);
|
|
|
|
if (peer) {
|
|
|
|
gst_pad_unlink (stream->srcpad, peer);
|
|
|
|
gst_object_unref (peer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
slot = get_slot_for_input (dbin, stream);
|
|
|
|
if (slot) {
|
|
|
|
slot->pending_stream = NULL;
|
|
|
|
slot->input = NULL;
|
|
|
|
GST_DEBUG_OBJECT (dbin, "slot %p cleared", slot);
|
|
|
|
}
|
|
|
|
|
2016-07-14 08:34:30 +00:00
|
|
|
if (stream->active_stream)
|
|
|
|
gst_object_unref (stream->active_stream);
|
|
|
|
if (stream->pending_stream)
|
|
|
|
gst_object_unref (stream->pending_stream);
|
|
|
|
|
2016-06-29 16:14:51 +00:00
|
|
|
dbin->input_streams = g_list_remove (dbin->input_streams, stream);
|
|
|
|
|
|
|
|
g_free (stream);
|
|
|
|
}
|
|
|
|
|
2021-10-27 11:49:44 +00:00
|
|
|
static void
|
|
|
|
unblock_pending_input (DecodebinInput * input)
|
2016-06-29 16:14:51 +00:00
|
|
|
{
|
|
|
|
GstDecodebin3 *dbin = input->dbin;
|
2016-10-13 11:10:09 +00:00
|
|
|
GList *tmp, *unused_slot = NULL;
|
2016-06-29 16:14:51 +00:00
|
|
|
|
|
|
|
/* 1. Re-use existing streams if/when possible */
|
|
|
|
GST_FIXME_OBJECT (dbin, "Re-use existing input streams if/when possible");
|
|
|
|
|
|
|
|
/* 2. Remove unused streams (push EOS) */
|
|
|
|
GST_DEBUG_OBJECT (dbin, "Removing unused streams");
|
2017-06-15 16:48:42 +00:00
|
|
|
SELECTION_LOCK (dbin);
|
2016-06-29 16:14:51 +00:00
|
|
|
tmp = dbin->input_streams;
|
|
|
|
while (tmp != NULL) {
|
|
|
|
DecodebinInputStream *input_stream = (DecodebinInputStream *) tmp->data;
|
|
|
|
GList *next = tmp->next;
|
|
|
|
|
2017-07-19 10:58:28 +00:00
|
|
|
if (input_stream->input != input) {
|
|
|
|
tmp = next;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-06-29 16:14:51 +00:00
|
|
|
GST_DEBUG_OBJECT (dbin, "Checking input stream %p", input_stream);
|
|
|
|
if (input_stream->input_buffer_probe_id) {
|
|
|
|
GST_DEBUG_OBJECT (dbin,
|
|
|
|
"Removing pad block on input %p pad %" GST_PTR_FORMAT, input_stream,
|
|
|
|
input_stream->srcpad);
|
|
|
|
gst_pad_remove_probe (input_stream->srcpad,
|
|
|
|
input_stream->input_buffer_probe_id);
|
|
|
|
}
|
|
|
|
input_stream->input_buffer_probe_id = 0;
|
|
|
|
|
|
|
|
if (input_stream->saw_eos) {
|
|
|
|
remove_input_stream (dbin, input_stream);
|
|
|
|
tmp = dbin->input_streams;
|
|
|
|
} else
|
|
|
|
tmp = next;
|
|
|
|
}
|
2017-06-15 16:48:42 +00:00
|
|
|
SELECTION_UNLOCK (dbin);
|
2016-06-29 16:14:51 +00:00
|
|
|
|
|
|
|
GST_DEBUG_OBJECT (dbin, "Creating new streams (if needed)");
|
|
|
|
/* 3. Create new streams */
|
|
|
|
for (tmp = input->pending_pads; tmp; tmp = tmp->next) {
|
|
|
|
GstStream *stream;
|
|
|
|
PendingPad *ppad = (PendingPad *) tmp->data;
|
|
|
|
|
|
|
|
stream = gst_pad_get_stream (ppad->pad);
|
|
|
|
if (stream == NULL) {
|
|
|
|
GST_ERROR_OBJECT (dbin, "No stream for pad ????");
|
|
|
|
} else {
|
|
|
|
MultiQueueSlot *slot;
|
|
|
|
DecodebinInputStream *input_stream;
|
|
|
|
/* The remaining pads in pending_pads are the ones that require a new
|
|
|
|
* input stream */
|
|
|
|
input_stream = create_input_stream (dbin, stream, ppad->pad, ppad->input);
|
|
|
|
/* See if we can link it straight away */
|
|
|
|
input_stream->active_stream = stream;
|
2016-10-13 11:10:09 +00:00
|
|
|
|
2017-07-18 09:34:22 +00:00
|
|
|
SELECTION_LOCK (dbin);
|
2016-06-29 16:14:51 +00:00
|
|
|
slot = get_slot_for_input (dbin, input_stream);
|
|
|
|
link_input_to_slot (input_stream, slot);
|
2017-07-18 09:34:22 +00:00
|
|
|
SELECTION_UNLOCK (dbin);
|
2016-10-13 11:10:09 +00:00
|
|
|
|
2016-06-29 16:14:51 +00:00
|
|
|
/* Remove the buffer and event probe */
|
|
|
|
gst_pad_remove_probe (ppad->pad, ppad->buffer_probe);
|
|
|
|
gst_pad_remove_probe (ppad->pad, ppad->event_probe);
|
|
|
|
g_free (ppad);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_list_free (input->pending_pads);
|
|
|
|
input->pending_pads = NULL;
|
|
|
|
|
|
|
|
/* Weed out unused multiqueue slots */
|
2017-07-18 09:34:22 +00:00
|
|
|
SELECTION_LOCK (dbin);
|
2016-06-29 16:14:51 +00:00
|
|
|
for (tmp = dbin->slots; tmp; tmp = tmp->next) {
|
|
|
|
MultiQueueSlot *slot = (MultiQueueSlot *) tmp->data;
|
2016-12-03 14:01:53 +00:00
|
|
|
GST_LOG_OBJECT (dbin, "Slot %d input:%p", slot->id, slot->input);
|
2016-06-29 16:14:51 +00:00
|
|
|
if (slot->input == NULL) {
|
2016-10-13 11:10:09 +00:00
|
|
|
unused_slot =
|
|
|
|
g_list_append (unused_slot, gst_object_ref (slot->sink_pad));
|
2016-06-29 16:14:51 +00:00
|
|
|
}
|
|
|
|
}
|
2017-07-18 09:34:22 +00:00
|
|
|
SELECTION_UNLOCK (dbin);
|
2016-10-13 11:10:09 +00:00
|
|
|
|
|
|
|
for (tmp = unused_slot; tmp; tmp = tmp->next) {
|
|
|
|
GstPad *sink_pad = (GstPad *) tmp->data;
|
|
|
|
GST_DEBUG_OBJECT (sink_pad, "Sending EOS to unused slot");
|
|
|
|
gst_pad_send_event (sink_pad, gst_event_new_eos ());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unused_slot)
|
|
|
|
g_list_free_full (unused_slot, (GDestroyNotify) gst_object_unref);
|
2016-06-29 16:14:51 +00:00
|
|
|
|
2021-10-27 11:49:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* FIXME : HACK, REMOVE, USE INPUT CHAINS */
|
|
|
|
static GstPadProbeReturn
|
|
|
|
parsebin_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
|
|
|
|
DecodebinInput * input)
|
|
|
|
{
|
|
|
|
/* Any data out the demuxer means it's not creating pads
|
|
|
|
* any more right now */
|
|
|
|
GST_DEBUG_OBJECT (pad, "Got a buffer ! UNBLOCK !");
|
|
|
|
unblock_pending_input (input);
|
|
|
|
|
2016-06-29 16:14:51 +00:00
|
|
|
return GST_PAD_PROBE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GstPadProbeReturn
|
|
|
|
parsebin_pending_event_probe (GstPad * pad, GstPadProbeInfo * info,
|
|
|
|
PendingPad * ppad)
|
|
|
|
{
|
|
|
|
GstDecodebin3 *dbin = ppad->dbin;
|
|
|
|
/* We drop all events by default */
|
|
|
|
GstPadProbeReturn ret = GST_PAD_PROBE_DROP;
|
|
|
|
GstEvent *ev = GST_PAD_PROBE_INFO_EVENT (info);
|
|
|
|
|
|
|
|
GST_DEBUG_OBJECT (pad, "Got event %p %s", ev, GST_EVENT_TYPE_NAME (ev));
|
|
|
|
switch (GST_EVENT_TYPE (ev)) {
|
|
|
|
case GST_EVENT_EOS:
|
|
|
|
{
|
|
|
|
GST_DEBUG_OBJECT (pad, "Pending pad marked as EOS, removing");
|
|
|
|
ppad->input->pending_pads =
|
|
|
|
g_list_remove (ppad->input->pending_pads, ppad);
|
|
|
|
gst_pad_remove_probe (ppad->pad, ppad->buffer_probe);
|
|
|
|
gst_pad_remove_probe (ppad->pad, ppad->event_probe);
|
|
|
|
g_free (ppad);
|
|
|
|
|
2020-12-11 09:13:59 +00:00
|
|
|
SELECTION_LOCK (dbin);
|
2016-06-29 16:14:51 +00:00
|
|
|
check_all_streams_for_eos (dbin);
|
2020-12-11 09:13:59 +00:00
|
|
|
SELECTION_UNLOCK (dbin);
|
2016-06-29 16:14:51 +00:00
|
|
|
}
|
|
|
|
break;
|
2021-08-01 16:20:06 +00:00
|
|
|
case GST_EVENT_GAP:
|
2021-10-27 11:49:44 +00:00
|
|
|
GST_DEBUG_OBJECT (pad, "Got a gap event! UNBLOCK !");
|
|
|
|
unblock_pending_input (ppad->input);
|
|
|
|
ret = GST_PAD_PROBE_OK;
|
2021-08-01 16:20:06 +00:00
|
|
|
break;
|
2016-06-29 16:14:51 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
parsebin_pad_added_cb (GstElement * demux, GstPad * pad, DecodebinInput * input)
|
|
|
|
{
|
|
|
|
GstDecodebin3 *dbin = input->dbin;
|
|
|
|
PendingPad *ppad;
|
|
|
|
GList *tmp;
|
|
|
|
|
|
|
|
GST_DEBUG_OBJECT (dbin, "New pad %s:%s (input:%p)", GST_DEBUG_PAD_NAME (pad),
|
|
|
|
input);
|
|
|
|
|
|
|
|
ppad = g_new0 (PendingPad, 1);
|
|
|
|
ppad->dbin = dbin;
|
|
|
|
ppad->input = input;
|
|
|
|
ppad->pad = pad;
|
|
|
|
|
|
|
|
ppad->event_probe =
|
|
|
|
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
|
|
|
|
(GstPadProbeCallback) parsebin_pending_event_probe, ppad, NULL);
|
|
|
|
ppad->buffer_probe =
|
|
|
|
gst_pad_add_probe (pad,
|
2021-10-27 11:49:44 +00:00
|
|
|
GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER,
|
2016-06-29 16:14:51 +00:00
|
|
|
(GstPadProbeCallback) parsebin_buffer_probe, input, NULL);
|
|
|
|
|
|
|
|
input->pending_pads = g_list_append (input->pending_pads, ppad);
|
|
|
|
|
|
|
|
/* Check if all existing input streams have a buffer probe set */
|
|
|
|
for (tmp = dbin->input_streams; tmp; tmp = tmp->next) {
|
|
|
|
DecodebinInputStream *input_stream = (DecodebinInputStream *) tmp->data;
|
2017-07-19 10:58:28 +00:00
|
|
|
if (input_stream->input == input &&
|
|
|
|
input_stream->input_buffer_probe_id == 0) {
|
2016-06-29 16:14:51 +00:00
|
|
|
GST_DEBUG_OBJECT (input_stream->srcpad, "Adding blocking buffer probe");
|
|
|
|
input_stream->input_buffer_probe_id =
|
|
|
|
gst_pad_add_probe (input_stream->srcpad,
|
|
|
|
GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER,
|
|
|
|
(GstPadProbeCallback) parsebin_buffer_probe, input_stream->input,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
parsebin_pad_removed_cb (GstElement * demux, GstPad * pad, DecodebinInput * inp)
|
|
|
|
{
|
|
|
|
GstDecodebin3 *dbin = inp->dbin;
|
|
|
|
DecodebinInputStream *input = NULL;
|
|
|
|
GList *tmp;
|
|
|
|
GST_DEBUG_OBJECT (pad, "removed");
|
|
|
|
|
|
|
|
for (tmp = dbin->input_streams; tmp; tmp = tmp->next) {
|
|
|
|
DecodebinInputStream *cand = (DecodebinInputStream *) tmp->data;
|
|
|
|
if (cand->srcpad == pad)
|
|
|
|
input = cand;
|
|
|
|
}
|
|
|
|
/* If there are no pending pads, this means we will definitely not need this
|
|
|
|
* stream anymore */
|
|
|
|
if (input) {
|
|
|
|
GST_DEBUG_OBJECT (pad, "stream %p", input);
|
|
|
|
if (inp->pending_pads == NULL) {
|
2016-12-03 13:46:20 +00:00
|
|
|
MultiQueueSlot *slot;
|
|
|
|
|
2016-06-29 16:14:51 +00:00
|
|
|
GST_DEBUG_OBJECT (pad, "Remove input stream %p", input);
|
2016-12-03 13:46:20 +00:00
|
|
|
|
|
|
|
SELECTION_LOCK (dbin);
|
|
|
|
slot = get_slot_for_input (dbin, input);
|
|
|
|
|
2016-06-29 16:14:51 +00:00
|
|
|
remove_input_stream (dbin, input);
|
2016-12-03 13:46:20 +00:00
|
|
|
if (slot && g_list_find (dbin->slots, slot) && slot->is_drained) {
|
|
|
|
/* if slot is still there and already drained, remove it in here */
|
|
|
|
if (slot->output) {
|
|
|
|
DecodebinOutputStream *output = slot->output;
|
|
|
|
GST_DEBUG_OBJECT (pad,
|
|
|
|
"Multiqueue was drained, Remove output stream");
|
|
|
|
|
|
|
|
dbin->output_streams = g_list_remove (dbin->output_streams, output);
|
|
|
|
free_output_stream (dbin, output);
|
|
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (pad, "No pending pad, Remove multiqueue slot");
|
2017-01-20 10:49:46 +00:00
|
|
|
if (slot->probe_id)
|
|
|
|
gst_pad_remove_probe (slot->src_pad, slot->probe_id);
|
|
|
|
slot->probe_id = 0;
|
2016-12-03 13:46:20 +00:00
|
|
|
dbin->slots = g_list_remove (dbin->slots, slot);
|
|
|
|
free_multiqueue_slot_async (dbin, slot);
|
|
|
|
}
|
|
|
|
SELECTION_UNLOCK (dbin);
|
2016-06-29 16:14:51 +00:00
|
|
|
} else {
|
|
|
|
input->srcpad = NULL;
|
|
|
|
if (input->input_buffer_probe_id)
|
|
|
|
gst_pad_remove_probe (pad, input->input_buffer_probe_id);
|
|
|
|
input->input_buffer_probe_id = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|