decodebin: Add autoplug-query signal to handle queries for yet unconnected elements

This allows playbin to answer the CAPS query with the possible sink
caps for example, and allows decoders to chose more optimal caps.
This commit is contained in:
Sebastian Dröge 2013-03-29 18:27:03 +01:00
parent 9ce0818c7d
commit 0932391d3f
2 changed files with 198 additions and 12 deletions

View file

@ -3,6 +3,8 @@
* Copyright (C) <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
* Copyright (C) <2011> Hewlett-Packard Development Company, L.P.
* Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
* Copyright (C) <2013> Collabora Ltd.
* Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -202,6 +204,10 @@ struct _GstDecodeBinClass
/* signal fired to select from the proposed list of factories */
GstAutoplugSelectResult (*autoplug_select) (GstElement * element,
GstPad * pad, GstCaps * caps, GstElementFactory * factory);
/* signal fired when a autoplugged element that is not linked downstream
* or exposed wants to query something */
gboolean (*autoplug_query) (GstElement * element, GstPad * pad,
GstQuery * query);
/* fired when the last group is drained */
void (*drained) (GstElement * element);
@ -215,6 +221,7 @@ enum
SIGNAL_AUTOPLUG_FACTORIES,
SIGNAL_AUTOPLUG_SELECT,
SIGNAL_AUTOPLUG_SORT,
SIGNAL_AUTOPLUG_QUERY,
SIGNAL_DRAINED,
LAST_SIGNAL
};
@ -285,6 +292,8 @@ static GValueArray *gst_decode_bin_autoplug_sort (GstElement * element,
GstPad * pad, GstCaps * caps, GValueArray * factories);
static GstAutoplugSelectResult gst_decode_bin_autoplug_select (GstElement *
element, GstPad * pad, GstCaps * caps, GstElementFactory * factory);
static gboolean gst_decode_bin_autoplug_query (GstElement * element,
GstPad * pad, GstQuery * query);
static void gst_decode_bin_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
@ -419,6 +428,9 @@ struct _GstDecodeChain
all new pads will be ignored! */
GList *pending_pads; /* Pads that have no fixed caps yet */
GstDecodePad *current_pad; /* Current ending pad of the chain that can't
* be exposed yet but would be the same as endpad
* once it can be exposed */
GstDecodePad *endpad; /* Pad of this chain that could be exposed */
gboolean deadend; /* This chain is incomplete and can't be completed,
e.g. no suitable decoder could be found
@ -489,12 +501,14 @@ G_DEFINE_TYPE (GstDecodePad, gst_decode_pad, GST_TYPE_GHOST_PAD);
#define GST_TYPE_DECODE_PAD (gst_decode_pad_get_type ())
#define GST_DECODE_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DECODE_PAD,GstDecodePad))
static GstDecodePad *gst_decode_pad_new (GstDecodeBin * dbin, GstPad * pad,
static GstDecodePad *gst_decode_pad_new (GstDecodeBin * dbin,
GstDecodeChain * chain);
static void gst_decode_pad_activate (GstDecodePad * dpad,
GstDecodeChain * chain);
static void gst_decode_pad_unblock (GstDecodePad * dpad);
static void gst_decode_pad_set_blocked (GstDecodePad * dpad, gboolean blocked);
static gboolean gst_decode_pad_query (GstPad * pad, GstObject * parent,
GstQuery * query);
static void gst_pending_pad_free (GstPendingPad * ppad);
static GstPadProbeReturn pad_event_cb (GstPad * pad, GstPadProbeInfo * info,
@ -550,6 +564,22 @@ _gst_boolean_accumulator (GSignalInvocationHint * ihint,
return myboolean;
}
static gboolean
_gst_boolean_or_accumulator (GSignalInvocationHint * ihint,
GValue * return_accu, const GValue * handler_return, gpointer dummy)
{
gboolean myboolean;
gboolean retboolean;
myboolean = g_value_get_boolean (handler_return);
retboolean = g_value_get_boolean (return_accu);
if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
g_value_set_boolean (return_accu, myboolean || retboolean);
return TRUE;
}
/* we collect the first result */
static gboolean
_gst_array_accumulator (GSignalInvocationHint * ihint,
@ -758,6 +788,27 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass)
GST_TYPE_AUTOPLUG_SELECT_RESULT, 3, GST_TYPE_PAD, GST_TYPE_CAPS,
GST_TYPE_ELEMENT_FACTORY);
/**
* GstDecodeBin::autoplug-query:
* @bin: The decodebin.
* @child: The child element doing the query
* @pad: The #GstPad.
* @query: The #GstQuery.
*
* This signal is emitted whenever an autoplugged element that is
* not linked downstream yet and not exposed does a query. It can
* be used to tell the element about the downstream supported caps
* for example.
*
* Returns: #TRUE if the query was handled, #FALSE otherwise.
*/
gst_decode_bin_signals[SIGNAL_AUTOPLUG_QUERY] =
g_signal_new ("autoplug-query", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDecodeBinClass, autoplug_query),
_gst_boolean_or_accumulator, NULL, g_cclosure_marshal_generic,
G_TYPE_BOOLEAN, 2, GST_TYPE_PAD,
GST_TYPE_QUERY | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
* GstDecodeBin::drained
* @bin: The decodebin
@ -913,6 +964,7 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass)
GST_DEBUG_FUNCPTR (gst_decode_bin_autoplug_factories);
klass->autoplug_sort = GST_DEBUG_FUNCPTR (gst_decode_bin_autoplug_sort);
klass->autoplug_select = GST_DEBUG_FUNCPTR (gst_decode_bin_autoplug_select);
klass->autoplug_query = GST_DEBUG_FUNCPTR (gst_decode_bin_autoplug_query);
gst_element_class_add_pad_template (gstelement_klass,
gst_static_pad_template_get (&decoder_bin_sink_template));
@ -1362,6 +1414,14 @@ gst_decode_bin_autoplug_select (GstElement * element, GstPad * pad,
return GST_AUTOPLUG_SELECT_TRY;
}
static gboolean
gst_decode_bin_autoplug_query (GstElement * element, GstPad * pad,
GstQuery * query)
{
/* No query handled here */
return FALSE;
}
/********
* Discovery methods
*****/
@ -1429,6 +1489,10 @@ analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad,
GstDecodeGroup *group;
GstDecodeChain *oldchain = chain;
if (chain->current_pad)
gst_object_unref (chain->current_pad);
chain->current_pad = NULL;
/* we are adding a new pad for a demuxer (see is_demuxer_element(),
* start a new chain for it */
CHAIN_MUTEX_LOCK (oldchain);
@ -1450,7 +1514,11 @@ analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad,
if (gst_caps_is_any (caps))
goto any_caps;
dpad = gst_decode_pad_new (dbin, pad, chain);
if (!chain->current_pad)
chain->current_pad = gst_decode_pad_new (dbin, chain);
dpad = gst_object_ref (chain->current_pad);
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (dpad), pad);
/* 1. Emit 'autoplug-continue' the result will tell us if this pads needs
* further autoplugging. Only do this for fixed caps, for unfixed caps
@ -2798,6 +2866,11 @@ gst_decode_chain_free_internal (GstDecodeChain * chain, gboolean hide)
}
}
if (!hide && chain->current_pad) {
gst_object_unref (chain->current_pad);
chain->current_pad = NULL;
}
if (chain->pad) {
gst_object_unref (chain->pad);
chain->pad = NULL;
@ -3764,13 +3837,15 @@ gst_decode_bin_expose (GstDecodeBin * dbin)
g_free (padname);
/* 2. activate and add */
if (!dpad->exposed
&& !gst_element_add_pad (GST_ELEMENT (dbin), GST_PAD_CAST (dpad))) {
/* not really fatal, we can try to add the other pads */
g_warning ("error adding pad to decodebin");
continue;
if (!dpad->exposed) {
dpad->exposed = TRUE;
if (!gst_element_add_pad (GST_ELEMENT (dbin), GST_PAD_CAST (dpad))) {
/* not really fatal, we can try to add the other pads */
g_warning ("error adding pad to decodebin");
dpad->exposed = FALSE;
continue;
}
}
dpad->exposed = TRUE;
/* 3. emit signal */
GST_INFO_OBJECT (dpad, "added new decoded pad");
@ -4035,27 +4110,71 @@ gst_decode_pad_unblock (GstDecodePad * dpad)
gst_decode_pad_set_blocked (dpad, FALSE);
}
static gboolean
gst_decode_pad_query (GstPad * pad, GstObject * parent, GstQuery * query)
{
GstDecodePad *dpad = GST_DECODE_PAD (parent);
gboolean ret = FALSE;
if (!dpad->exposed && !dpad->chain->deadend) {
ret = FALSE;
g_signal_emit (G_OBJECT (dpad->dbin),
gst_decode_bin_signals[SIGNAL_AUTOPLUG_QUERY], 0, dpad, query, &ret);
GST_DEBUG_OBJECT (dpad->dbin, "autoplug-query returned %d", ret);
if (ret) {
GstCaps *result, *filter;
GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (dpad));
gst_query_parse_caps (query, &filter);
gst_query_parse_caps_result (query, &result);
result =
gst_caps_merge (gst_caps_ref (result),
gst_pad_get_pad_template_caps (target));
if (filter) {
GstCaps *intersection =
gst_caps_intersect_full (filter, result, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (result);
result = intersection;
}
gst_query_set_caps_result (query, result);
gst_caps_unref (result);
gst_object_unref (target);
}
}
/* If exposed or nothing handled the query use the default handler */
if (!ret)
ret = gst_pad_query_default (pad, parent, query);
return ret;
}
/*gst_decode_pad_new:
*
* Creates a new GstDecodePad for the given pad.
*/
static GstDecodePad *
gst_decode_pad_new (GstDecodeBin * dbin, GstPad * pad, GstDecodeChain * chain)
gst_decode_pad_new (GstDecodeBin * dbin, GstDecodeChain * chain)
{
GstDecodePad *dpad;
GstProxyPad *ppad;
GstPadTemplate *pad_tmpl;
GST_DEBUG_OBJECT (dbin, "making new decodepad");
pad_tmpl = gst_static_pad_template_get (&decoder_bin_src_template);
dpad =
g_object_new (GST_TYPE_DECODE_PAD, "direction", GST_PAD_DIRECTION (pad),
g_object_new (GST_TYPE_DECODE_PAD, "direction", GST_PAD_SRC,
"template", pad_tmpl, NULL);
gst_ghost_pad_construct (GST_GHOST_PAD_CAST (dpad));
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (dpad), pad);
dpad->chain = chain;
dpad->dbin = dbin;
gst_object_unref (pad_tmpl);
ppad = gst_proxy_pad_get_internal (GST_PROXY_PAD (dpad));
gst_pad_set_query_function (GST_PAD_CAST (ppad), gst_decode_pad_query);
gst_object_unref (ppad);
return dpad;
}

View file

@ -137,6 +137,10 @@ struct _GstURIDecodeBinClass
/* signal fired to select from the proposed list of factories */
GstAutoplugSelectResult (*autoplug_select) (GstElement * element,
GstPad * pad, GstCaps * caps, GstElementFactory * factory);
/* signal fired when a autoplugged element that is not linked downstream
* or exposed wants to query something */
gboolean (*autoplug_query) (GstElement * element, GstPad * pad,
GstQuery * query);
/* emitted when all data is decoded */
void (*drained) (GstElement * element);
@ -159,8 +163,9 @@ enum
SIGNAL_AUTOPLUG_CONTINUE,
SIGNAL_AUTOPLUG_FACTORIES,
SIGNAL_AUTOPLUG_SELECT,
SIGNAL_DRAINED,
SIGNAL_AUTOPLUG_SORT,
SIGNAL_AUTOPLUG_QUERY,
SIGNAL_DRAINED,
SIGNAL_SOURCE_SETUP,
LAST_SIGNAL
};
@ -229,6 +234,22 @@ _gst_boolean_accumulator (GSignalInvocationHint * ihint,
return myboolean;
}
static gboolean
_gst_boolean_or_accumulator (GSignalInvocationHint * ihint,
GValue * return_accu, const GValue * handler_return, gpointer dummy)
{
gboolean myboolean;
gboolean retboolean;
myboolean = g_value_get_boolean (handler_return);
retboolean = g_value_get_boolean (return_accu);
if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
g_value_set_boolean (return_accu, myboolean || retboolean);
return TRUE;
}
static gboolean
_gst_array_accumulator (GSignalInvocationHint * ihint,
GValue * return_accu, const GValue * handler_return, gpointer dummy)
@ -350,6 +371,14 @@ gst_uri_decode_bin_autoplug_select (GstElement * element, GstPad * pad,
return GST_AUTOPLUG_SELECT_TRY;
}
static gboolean
gst_uri_decode_bin_autoplug_query (GstElement * element, GstPad * pad,
GstQuery * query)
{
/* No query handled here */
return FALSE;
}
static void
gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass)
{
@ -611,6 +640,27 @@ gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass)
GST_TYPE_AUTOPLUG_SELECT_RESULT, 3, GST_TYPE_PAD, GST_TYPE_CAPS,
GST_TYPE_ELEMENT_FACTORY);
/**
* GstDecodeBin::autoplug-query:
* @bin: The decodebin.
* @child: The child element doing the query
* @pad: The #GstPad.
* @query: The #GstQuery.
*
* This signal is emitted whenever an autoplugged element that is
* not linked downstream yet and not exposed does a query. It can
* be used to tell the element about the downstream supported caps
* for example.
*
* Returns: #TRUE if the query was handled, #FALSE otherwise.
*/
gst_uri_decode_bin_signals[SIGNAL_AUTOPLUG_QUERY] =
g_signal_new ("autoplug-query", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURIDecodeBinClass, autoplug_query),
_gst_boolean_or_accumulator, NULL, g_cclosure_marshal_generic,
G_TYPE_BOOLEAN, 2, GST_TYPE_PAD,
GST_TYPE_QUERY | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
* GstURIDecodeBin::drained:
*
@ -660,6 +710,7 @@ gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass)
klass->autoplug_sort = GST_DEBUG_FUNCPTR (gst_uri_decode_bin_autoplug_sort);
klass->autoplug_select =
GST_DEBUG_FUNCPTR (gst_uri_decode_bin_autoplug_select);
klass->autoplug_query = GST_DEBUG_FUNCPTR (gst_uri_decode_bin_autoplug_query);
}
static void
@ -1643,6 +1694,20 @@ proxy_autoplug_select_signal (GstElement * element, GstPad * pad,
return result;
}
static gboolean
proxy_autoplug_query_signal (GstElement * element, GstPad * pad,
GstQuery * query, GstURIDecodeBin * dec)
{
gboolean ret = FALSE;
g_signal_emit (dec,
gst_uri_decode_bin_signals[SIGNAL_AUTOPLUG_QUERY], 0, pad, query, &ret);
GST_DEBUG_OBJECT (dec, "autoplug-query returned %d", ret);
return ret;
}
static void
proxy_drained_signal (GstElement * element, GstURIDecodeBin * dec)
{
@ -1688,6 +1753,8 @@ make_decoder (GstURIDecodeBin * decoder)
G_CALLBACK (proxy_autoplug_sort_signal), decoder);
g_signal_connect (decodebin, "autoplug-select",
G_CALLBACK (proxy_autoplug_select_signal), decoder);
g_signal_connect (decodebin, "autoplug-query",
G_CALLBACK (proxy_autoplug_query_signal), decoder);
g_signal_connect (decodebin, "drained",
G_CALLBACK (proxy_drained_signal), decoder);