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) <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
* Copyright (C) <2011> Hewlett-Packard Development Company, L.P. * Copyright (C) <2011> Hewlett-Packard Development Company, L.P.
* Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd. * 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 * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * 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 */ /* signal fired to select from the proposed list of factories */
GstAutoplugSelectResult (*autoplug_select) (GstElement * element, GstAutoplugSelectResult (*autoplug_select) (GstElement * element,
GstPad * pad, GstCaps * caps, GstElementFactory * factory); 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 */ /* fired when the last group is drained */
void (*drained) (GstElement * element); void (*drained) (GstElement * element);
@ -215,6 +221,7 @@ enum
SIGNAL_AUTOPLUG_FACTORIES, SIGNAL_AUTOPLUG_FACTORIES,
SIGNAL_AUTOPLUG_SELECT, SIGNAL_AUTOPLUG_SELECT,
SIGNAL_AUTOPLUG_SORT, SIGNAL_AUTOPLUG_SORT,
SIGNAL_AUTOPLUG_QUERY,
SIGNAL_DRAINED, SIGNAL_DRAINED,
LAST_SIGNAL LAST_SIGNAL
}; };
@ -285,6 +292,8 @@ static GValueArray *gst_decode_bin_autoplug_sort (GstElement * element,
GstPad * pad, GstCaps * caps, GValueArray * factories); GstPad * pad, GstCaps * caps, GValueArray * factories);
static GstAutoplugSelectResult gst_decode_bin_autoplug_select (GstElement * static GstAutoplugSelectResult gst_decode_bin_autoplug_select (GstElement *
element, GstPad * pad, GstCaps * caps, GstElementFactory * factory); 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, static void gst_decode_bin_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec); const GValue * value, GParamSpec * pspec);
@ -419,6 +428,9 @@ struct _GstDecodeChain
all new pads will be ignored! */ all new pads will be ignored! */
GList *pending_pads; /* Pads that have no fixed caps yet */ 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 */ GstDecodePad *endpad; /* Pad of this chain that could be exposed */
gboolean deadend; /* This chain is incomplete and can't be completed, gboolean deadend; /* This chain is incomplete and can't be completed,
e.g. no suitable decoder could be found 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_TYPE_DECODE_PAD (gst_decode_pad_get_type ())
#define GST_DECODE_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DECODE_PAD,GstDecodePad)) #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); GstDecodeChain * chain);
static void gst_decode_pad_activate (GstDecodePad * dpad, static void gst_decode_pad_activate (GstDecodePad * dpad,
GstDecodeChain * chain); GstDecodeChain * chain);
static void gst_decode_pad_unblock (GstDecodePad * dpad); static void gst_decode_pad_unblock (GstDecodePad * dpad);
static void gst_decode_pad_set_blocked (GstDecodePad * dpad, gboolean blocked); 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 void gst_pending_pad_free (GstPendingPad * ppad);
static GstPadProbeReturn pad_event_cb (GstPad * pad, GstPadProbeInfo * info, static GstPadProbeReturn pad_event_cb (GstPad * pad, GstPadProbeInfo * info,
@ -550,6 +564,22 @@ _gst_boolean_accumulator (GSignalInvocationHint * ihint,
return myboolean; 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 */ /* we collect the first result */
static gboolean static gboolean
_gst_array_accumulator (GSignalInvocationHint * ihint, _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_AUTOPLUG_SELECT_RESULT, 3, GST_TYPE_PAD, GST_TYPE_CAPS,
GST_TYPE_ELEMENT_FACTORY); 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 * GstDecodeBin::drained
* @bin: The decodebin * @bin: The decodebin
@ -913,6 +964,7 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass)
GST_DEBUG_FUNCPTR (gst_decode_bin_autoplug_factories); GST_DEBUG_FUNCPTR (gst_decode_bin_autoplug_factories);
klass->autoplug_sort = GST_DEBUG_FUNCPTR (gst_decode_bin_autoplug_sort); klass->autoplug_sort = GST_DEBUG_FUNCPTR (gst_decode_bin_autoplug_sort);
klass->autoplug_select = GST_DEBUG_FUNCPTR (gst_decode_bin_autoplug_select); 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_element_class_add_pad_template (gstelement_klass,
gst_static_pad_template_get (&decoder_bin_sink_template)); 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; 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 * Discovery methods
*****/ *****/
@ -1429,6 +1489,10 @@ analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad,
GstDecodeGroup *group; GstDecodeGroup *group;
GstDecodeChain *oldchain = chain; 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(), /* we are adding a new pad for a demuxer (see is_demuxer_element(),
* start a new chain for it */ * start a new chain for it */
CHAIN_MUTEX_LOCK (oldchain); CHAIN_MUTEX_LOCK (oldchain);
@ -1450,7 +1514,11 @@ analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad,
if (gst_caps_is_any (caps)) if (gst_caps_is_any (caps))
goto 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 /* 1. Emit 'autoplug-continue' the result will tell us if this pads needs
* further autoplugging. Only do this for fixed caps, for unfixed caps * 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) { if (chain->pad) {
gst_object_unref (chain->pad); gst_object_unref (chain->pad);
chain->pad = NULL; chain->pad = NULL;
@ -3764,13 +3837,15 @@ gst_decode_bin_expose (GstDecodeBin * dbin)
g_free (padname); g_free (padname);
/* 2. activate and add */ /* 2. activate and add */
if (!dpad->exposed if (!dpad->exposed) {
&& !gst_element_add_pad (GST_ELEMENT (dbin), GST_PAD_CAST (dpad))) { dpad->exposed = TRUE;
/* not really fatal, we can try to add the other pads */ if (!gst_element_add_pad (GST_ELEMENT (dbin), GST_PAD_CAST (dpad))) {
g_warning ("error adding pad to decodebin"); /* not really fatal, we can try to add the other pads */
continue; g_warning ("error adding pad to decodebin");
dpad->exposed = FALSE;
continue;
}
} }
dpad->exposed = TRUE;
/* 3. emit signal */ /* 3. emit signal */
GST_INFO_OBJECT (dpad, "added new decoded pad"); 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); 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: /*gst_decode_pad_new:
* *
* Creates a new GstDecodePad for the given pad. * Creates a new GstDecodePad for the given pad.
*/ */
static GstDecodePad * static GstDecodePad *
gst_decode_pad_new (GstDecodeBin * dbin, GstPad * pad, GstDecodeChain * chain) gst_decode_pad_new (GstDecodeBin * dbin, GstDecodeChain * chain)
{ {
GstDecodePad *dpad; GstDecodePad *dpad;
GstProxyPad *ppad;
GstPadTemplate *pad_tmpl; GstPadTemplate *pad_tmpl;
GST_DEBUG_OBJECT (dbin, "making new decodepad"); GST_DEBUG_OBJECT (dbin, "making new decodepad");
pad_tmpl = gst_static_pad_template_get (&decoder_bin_src_template); pad_tmpl = gst_static_pad_template_get (&decoder_bin_src_template);
dpad = 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); "template", pad_tmpl, NULL);
gst_ghost_pad_construct (GST_GHOST_PAD_CAST (dpad)); gst_ghost_pad_construct (GST_GHOST_PAD_CAST (dpad));
gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (dpad), pad);
dpad->chain = chain; dpad->chain = chain;
dpad->dbin = dbin; dpad->dbin = dbin;
gst_object_unref (pad_tmpl); 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; return dpad;
} }

View file

@ -137,6 +137,10 @@ struct _GstURIDecodeBinClass
/* signal fired to select from the proposed list of factories */ /* signal fired to select from the proposed list of factories */
GstAutoplugSelectResult (*autoplug_select) (GstElement * element, GstAutoplugSelectResult (*autoplug_select) (GstElement * element,
GstPad * pad, GstCaps * caps, GstElementFactory * factory); 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 */ /* emitted when all data is decoded */
void (*drained) (GstElement * element); void (*drained) (GstElement * element);
@ -159,8 +163,9 @@ enum
SIGNAL_AUTOPLUG_CONTINUE, SIGNAL_AUTOPLUG_CONTINUE,
SIGNAL_AUTOPLUG_FACTORIES, SIGNAL_AUTOPLUG_FACTORIES,
SIGNAL_AUTOPLUG_SELECT, SIGNAL_AUTOPLUG_SELECT,
SIGNAL_DRAINED,
SIGNAL_AUTOPLUG_SORT, SIGNAL_AUTOPLUG_SORT,
SIGNAL_AUTOPLUG_QUERY,
SIGNAL_DRAINED,
SIGNAL_SOURCE_SETUP, SIGNAL_SOURCE_SETUP,
LAST_SIGNAL LAST_SIGNAL
}; };
@ -229,6 +234,22 @@ _gst_boolean_accumulator (GSignalInvocationHint * ihint,
return myboolean; 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 static gboolean
_gst_array_accumulator (GSignalInvocationHint * ihint, _gst_array_accumulator (GSignalInvocationHint * ihint,
GValue * return_accu, const GValue * handler_return, gpointer dummy) 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; 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 static void
gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass) 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_AUTOPLUG_SELECT_RESULT, 3, GST_TYPE_PAD, GST_TYPE_CAPS,
GST_TYPE_ELEMENT_FACTORY); 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: * 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_sort = GST_DEBUG_FUNCPTR (gst_uri_decode_bin_autoplug_sort);
klass->autoplug_select = klass->autoplug_select =
GST_DEBUG_FUNCPTR (gst_uri_decode_bin_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 static void
@ -1643,6 +1694,20 @@ proxy_autoplug_select_signal (GstElement * element, GstPad * pad,
return result; 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 static void
proxy_drained_signal (GstElement * element, GstURIDecodeBin * dec) proxy_drained_signal (GstElement * element, GstURIDecodeBin * dec)
{ {
@ -1688,6 +1753,8 @@ make_decoder (GstURIDecodeBin * decoder)
G_CALLBACK (proxy_autoplug_sort_signal), decoder); G_CALLBACK (proxy_autoplug_sort_signal), decoder);
g_signal_connect (decodebin, "autoplug-select", g_signal_connect (decodebin, "autoplug-select",
G_CALLBACK (proxy_autoplug_select_signal), decoder); 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_signal_connect (decodebin, "drained",
G_CALLBACK (proxy_drained_signal), decoder); G_CALLBACK (proxy_drained_signal), decoder);