mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-25 17:50:36 +00:00
56e05f63f5
We can't keep the lock otherwise this would lock other actions. In order to keep it safe, we grab a list of peer pads to send EOS to with the lock taken, then send to the peer pads with the lock released. Also make sure the selection lock is taken for another call to this function Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/issues/847 Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/merge_requests/971>
654 lines
21 KiB
C
654 lines
21 KiB
C
/* 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
|
|
|
|
#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;
|
|
}
|
|
|
|
/* 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);
|
|
|
|
/* WITH SELECTION_LOCK TAKEN! */
|
|
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;
|
|
}
|
|
|
|
/* WITH SELECTION_LOCK TAKEN! */
|
|
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;
|
|
}
|
|
|
|
/* WITH SELECTION_LOCK TAKEN! */
|
|
static void
|
|
check_all_streams_for_eos (GstDecodebin3 * dbin)
|
|
{
|
|
GList *tmp;
|
|
GList *outputpads = NULL;
|
|
|
|
if (!all_inputs_are_eos (dbin))
|
|
return;
|
|
|
|
/* We know all streams are EOS, properly clean up everything */
|
|
|
|
/* We grab all peer pads *while* the selection lock is taken and then we will
|
|
push EOS downstream with the selection lock released */
|
|
for (tmp = dbin->input_streams; tmp; tmp = tmp->next) {
|
|
DecodebinInputStream *input = (DecodebinInputStream *) tmp->data;
|
|
GstPad *peer = gst_pad_get_peer (input->srcpad);
|
|
|
|
/* 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;
|
|
|
|
/* Send EOS and then remove elements */
|
|
gst_pad_send_event (peer, gst_event_new_eos ());
|
|
GST_FIXME_OBJECT (peer, "Remove input stream");
|
|
gst_object_unref (peer);
|
|
}
|
|
SELECTION_LOCK (dbin);
|
|
|
|
g_list_free (outputpads);
|
|
}
|
|
|
|
/* Get the intersection of parser caps and available (sorted) decoders */
|
|
static GstCaps *
|
|
get_parser_caps_filter (GstDecodebin3 * dbin, GstCaps * caps)
|
|
{
|
|
GList *tmp;
|
|
GstCaps *filter_caps;
|
|
|
|
/* If no filter was provided, it can handle anything */
|
|
if (!caps || gst_caps_is_any (caps))
|
|
return gst_caps_new_any ();
|
|
|
|
filter_caps = gst_caps_new_empty ();
|
|
|
|
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;
|
|
guint group_id = GST_GROUP_ID_INVALID;
|
|
|
|
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 ();
|
|
}
|
|
|
|
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 */
|
|
SELECTION_LOCK (input->dbin);
|
|
slot = get_slot_for_input (input->dbin, input);
|
|
link_input_to_slot (input, slot);
|
|
SELECTION_UNLOCK (input->dbin);
|
|
} 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:
|
|
input->saw_eos = TRUE;
|
|
if (all_inputs_are_eos (input->dbin)) {
|
|
GST_DEBUG_OBJECT (pad, "real input pad, marking as EOS");
|
|
SELECTION_LOCK (input->dbin);
|
|
check_all_streams_for_eos (input->dbin);
|
|
SELECTION_UNLOCK (input->dbin);
|
|
} else {
|
|
GstPad *peer = gst_pad_get_peer (input->srcpad);
|
|
if (peer) {
|
|
/* Send custom-eos event to multiqueue slot */
|
|
GstEvent *event;
|
|
|
|
GST_DEBUG_OBJECT (pad,
|
|
"Got EOS end of input stream, post custom-eos");
|
|
event = gst_event_new_eos ();
|
|
gst_event_set_seqnum (event, gst_event_get_seqnum (ev));
|
|
gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (event),
|
|
CUSTOM_EOS_QUARK, (gchar *) CUSTOM_EOS_QUARK_DATA, NULL);
|
|
gst_pad_send_event (peer, event);
|
|
gst_object_unref (peer);
|
|
} else {
|
|
GST_FIXME_OBJECT (pad, "No peer, what should we do ?");
|
|
}
|
|
}
|
|
ret = GST_PAD_PROBE_DROP;
|
|
break;
|
|
case GST_EVENT_FLUSH_STOP:
|
|
GST_DEBUG_OBJECT (pad, "Clear saw_eos flag");
|
|
input->saw_eos = FALSE;
|
|
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;
|
|
|
|
/* Put probe on output source pad (for detecting EOS/STREAM_START/FLUSH) */
|
|
res->output_event_probe_id =
|
|
gst_pad_add_probe (pad,
|
|
GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM | GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM
|
|
| GST_PAD_PROBE_TYPE_EVENT_FLUSH,
|
|
(GstPadProbeCallback) parse_chain_output_probe, res, NULL);
|
|
|
|
/* Add to list of current input streams */
|
|
SELECTION_LOCK (dbin);
|
|
dbin->input_streams = g_list_append (dbin->input_streams, res);
|
|
SELECTION_UNLOCK (dbin);
|
|
GST_DEBUG_OBJECT (pad, "Done creating input stream");
|
|
|
|
return res;
|
|
}
|
|
|
|
/* WITH SELECTION_LOCK TAKEN! */
|
|
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);
|
|
}
|
|
|
|
if (stream->active_stream)
|
|
gst_object_unref (stream->active_stream);
|
|
if (stream->pending_stream)
|
|
gst_object_unref (stream->pending_stream);
|
|
|
|
dbin->input_streams = g_list_remove (dbin->input_streams, stream);
|
|
|
|
g_free (stream);
|
|
}
|
|
|
|
|
|
/* FIXME : HACK, REMOVE, USE INPUT CHAINS */
|
|
static GstPadProbeReturn
|
|
parsebin_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
|
|
DecodebinInput * input)
|
|
{
|
|
GstDecodebin3 *dbin = input->dbin;
|
|
GList *tmp, *unused_slot = NULL;
|
|
|
|
GST_DEBUG_OBJECT (pad, "Got a buffer ! UNBLOCK !");
|
|
|
|
/* Any data out the demuxer means it's not creating pads
|
|
* any more right now */
|
|
|
|
/* 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");
|
|
SELECTION_LOCK (dbin);
|
|
tmp = dbin->input_streams;
|
|
while (tmp != NULL) {
|
|
DecodebinInputStream *input_stream = (DecodebinInputStream *) tmp->data;
|
|
GList *next = tmp->next;
|
|
|
|
if (input_stream->input != input) {
|
|
tmp = next;
|
|
continue;
|
|
}
|
|
|
|
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;
|
|
}
|
|
SELECTION_UNLOCK (dbin);
|
|
|
|
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;
|
|
|
|
SELECTION_LOCK (dbin);
|
|
slot = get_slot_for_input (dbin, input_stream);
|
|
link_input_to_slot (input_stream, slot);
|
|
SELECTION_UNLOCK (dbin);
|
|
|
|
/* 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 */
|
|
SELECTION_LOCK (dbin);
|
|
for (tmp = dbin->slots; tmp; tmp = tmp->next) {
|
|
MultiQueueSlot *slot = (MultiQueueSlot *) tmp->data;
|
|
GST_LOG_OBJECT (dbin, "Slot %d input:%p", slot->id, slot->input);
|
|
if (slot->input == NULL) {
|
|
unused_slot =
|
|
g_list_append (unused_slot, gst_object_ref (slot->sink_pad));
|
|
}
|
|
}
|
|
SELECTION_UNLOCK (dbin);
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
SELECTION_LOCK (dbin);
|
|
check_all_streams_for_eos (dbin);
|
|
SELECTION_UNLOCK (dbin);
|
|
}
|
|
break;
|
|
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,
|
|
GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER,
|
|
(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;
|
|
if (input_stream->input == input &&
|
|
input_stream->input_buffer_probe_id == 0) {
|
|
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) {
|
|
MultiQueueSlot *slot;
|
|
|
|
GST_DEBUG_OBJECT (pad, "Remove input stream %p", input);
|
|
|
|
SELECTION_LOCK (dbin);
|
|
slot = get_slot_for_input (dbin, input);
|
|
|
|
remove_input_stream (dbin, input);
|
|
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");
|
|
if (slot->probe_id)
|
|
gst_pad_remove_probe (slot->src_pad, slot->probe_id);
|
|
slot->probe_id = 0;
|
|
dbin->slots = g_list_remove (dbin->slots, slot);
|
|
free_multiqueue_slot_async (dbin, slot);
|
|
}
|
|
SELECTION_UNLOCK (dbin);
|
|
} 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;
|
|
}
|
|
}
|
|
}
|