output-selector: Add pad-negotiation-mode property

Adds getcaps/setcaps to output-selector and adds a property
to select which type of negotiation should be done.

The available modes are:
 * none:   no negotiation (current behavior), getcaps return ANY and
           setcaps aren't set on any of the peers
 * all:    use all pads (default), getcaps returns the intersection of
           peer pads and setcaps is set on all peers
 * active: getcaps and setcaps are proxied to the active pad

https://bugzilla.gnome.org/show_bug.cgi?id=638381
This commit is contained in:
Thiago Santos 2011-01-04 12:42:50 -03:00 committed by Tim-Philipp Müller
parent c8ffd4e395
commit 757dc90faa
3 changed files with 276 additions and 10 deletions

View file

@ -49,13 +49,43 @@ GST_STATIC_PAD_TEMPLATE ("src%d",
GST_PAD_REQUEST, GST_PAD_REQUEST,
GST_STATIC_CAPS_ANY); GST_STATIC_CAPS_ANY);
enum GstOutputSelectorPadNegotiationMode
{
GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_NONE,
GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_ALL,
GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_ACTIVE
};
#define GST_TYPE_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE (gst_output_selector_pad_negotiation_mode_get_type())
static GType
gst_output_selector_pad_negotiation_mode_get_type (void)
{
static GType pad_negotiation_mode_type = 0;
static GEnumValue pad_negotiation_modes[] = {
{GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_NONE, "None", "none"},
{GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_ALL, "All", "all"},
{GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_ACTIVE, "Active", "active"},
{0, NULL, NULL}
};
if (!pad_negotiation_mode_type) {
pad_negotiation_mode_type =
g_enum_register_static ("GstOutputSelectorPadNegotiationMode",
pad_negotiation_modes);
}
return pad_negotiation_mode_type;
}
enum enum
{ {
PROP_0, PROP_0,
PROP_ACTIVE_PAD, PROP_ACTIVE_PAD,
PROP_RESEND_LATEST PROP_RESEND_LATEST,
PROP_PAD_NEGOTIATION_MODE
}; };
#define DEFAULT_PAD_NEGOTIATION_MODE GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_ALL
#define _do_init(bla) \ #define _do_init(bla) \
GST_DEBUG_CATEGORY_INIT (output_selector_debug, \ GST_DEBUG_CATEGORY_INIT (output_selector_debug, \
"output-selector", 0, "Output stream selector"); "output-selector", 0, "Output stream selector");
@ -79,6 +109,8 @@ static GstStateChangeReturn gst_output_selector_change_state (GstElement *
element, GstStateChange transition); element, GstStateChange transition);
static gboolean gst_output_selector_handle_sink_event (GstPad * pad, static gboolean gst_output_selector_handle_sink_event (GstPad * pad,
GstEvent * event); GstEvent * event);
static void gst_output_selector_switch_pad_negotiation_mode (GstOutputSelector *
sel, gint mode);
static void static void
gst_output_selector_base_init (gpointer g_class) gst_output_selector_base_init (gpointer g_class)
@ -113,6 +145,12 @@ gst_output_selector_class_init (GstOutputSelectorClass * klass)
g_param_spec_boolean ("resend-latest", "Resend latest buffer", g_param_spec_boolean ("resend-latest", "Resend latest buffer",
"Resend latest buffer after a switch to a new pad", FALSE, "Resend latest buffer after a switch to a new pad", FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_PAD_NEGOTIATION_MODE,
g_param_spec_enum ("pad-negotiation-mode", "Pad negotiation mode",
"The mode to be used for pad negotiation",
GST_TYPE_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE,
DEFAULT_PAD_NEGOTIATION_MODE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gstelement_class->request_new_pad = gstelement_class->request_new_pad =
GST_DEBUG_FUNCPTR (gst_output_selector_request_new_pad); GST_DEBUG_FUNCPTR (gst_output_selector_request_new_pad);
@ -135,12 +173,6 @@ gst_output_selector_init (GstOutputSelector * sel,
GST_DEBUG_FUNCPTR (gst_output_selector_handle_sink_event)); GST_DEBUG_FUNCPTR (gst_output_selector_handle_sink_event));
gst_pad_set_bufferalloc_function (sel->sinkpad, gst_pad_set_bufferalloc_function (sel->sinkpad,
GST_DEBUG_FUNCPTR (gst_output_selector_buffer_alloc)); GST_DEBUG_FUNCPTR (gst_output_selector_buffer_alloc));
/*
gst_pad_set_setcaps_function (sel->sinkpad,
GST_DEBUG_FUNCPTR (gst_pad_proxy_setcaps));
gst_pad_set_getcaps_function (sel->sinkpad,
GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
*/
gst_element_add_pad (GST_ELEMENT (sel), sel->sinkpad); gst_element_add_pad (GST_ELEMENT (sel), sel->sinkpad);
@ -152,6 +184,8 @@ gst_output_selector_init (GstOutputSelector * sel,
sel->resend_latest = FALSE; sel->resend_latest = FALSE;
sel->latest_buffer = NULL; sel->latest_buffer = NULL;
gst_output_selector_switch_pad_negotiation_mode (sel,
DEFAULT_PAD_NEGOTIATION_MODE);
} }
static void static void
@ -218,6 +252,11 @@ gst_output_selector_set_property (GObject * object, guint prop_id,
sel->resend_latest = g_value_get_boolean (value); sel->resend_latest = g_value_get_boolean (value);
break; break;
} }
case PROP_PAD_NEGOTIATION_MODE:{
gst_output_selector_switch_pad_negotiation_mode (sel,
g_value_get_enum (value));
break;
}
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -243,12 +282,75 @@ gst_output_selector_get_property (GObject * object, guint prop_id,
GST_OBJECT_UNLOCK (object); GST_OBJECT_UNLOCK (object);
break; break;
} }
case PROP_PAD_NEGOTIATION_MODE:
g_value_set_enum (value, sel->pad_negotiation_mode);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
} }
} }
static GstCaps *
gst_output_selector_sink_getcaps (GstPad * pad)
{
GstOutputSelector *sel = GST_OUTPUT_SELECTOR (GST_PAD_PARENT (pad));
GstPad *active;
GstCaps *caps;
GST_OBJECT_LOCK (sel);
if (sel->pending_srcpad)
active = gst_object_ref (sel->pending_srcpad);
else
active = gst_object_ref (sel->active_srcpad);
GST_OBJECT_UNLOCK (sel);
caps = gst_pad_peer_get_caps_reffed (active);
gst_object_unref (active);
if (caps == NULL) {
caps = gst_caps_new_any ();
}
return caps;
}
static gboolean
gst_output_selector_sink_setcaps (GstPad * pad, GstCaps * caps)
{
GstOutputSelector *sel = GST_OUTPUT_SELECTOR (GST_PAD_PARENT (pad));
GstPad *active;
gboolean ret;
GST_OBJECT_LOCK (sel);
if (sel->pending_srcpad)
active = gst_object_ref (sel->pending_srcpad);
else
active = gst_object_ref (sel->active_srcpad);
GST_OBJECT_UNLOCK (sel);
ret = gst_pad_set_caps (active, caps);
gst_object_unref (active);
return ret;
}
static void
gst_output_selector_switch_pad_negotiation_mode (GstOutputSelector * sel,
gint mode)
{
sel->pad_negotiation_mode = mode;
if (mode == GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_ALL) {
gst_pad_set_getcaps_function (sel->sinkpad, gst_pad_proxy_getcaps);
gst_pad_set_setcaps_function (sel->sinkpad, gst_pad_proxy_setcaps);
} else if (mode == GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_NONE) {
gst_pad_set_getcaps_function (sel->sinkpad, NULL);
gst_pad_set_setcaps_function (sel->sinkpad, NULL);
} else { /* active */
gst_pad_set_getcaps_function (sel->sinkpad,
gst_output_selector_sink_getcaps);
gst_pad_set_setcaps_function (sel->sinkpad,
gst_output_selector_sink_setcaps);
}
}
static GstFlowReturn static GstFlowReturn
gst_output_selector_buffer_alloc (GstPad * pad, guint64 offset, guint size, gst_output_selector_buffer_alloc (GstPad * pad, guint64 offset, guint size,
GstCaps * caps, GstBuffer ** buf) GstCaps * caps, GstBuffer ** buf)

View file

@ -47,6 +47,8 @@ struct _GstOutputSelector {
GstPad *pending_srcpad; GstPad *pending_srcpad;
guint nb_srcpads; guint nb_srcpads;
gint pad_negotiation_mode;
GstSegment segment; GstSegment segment;
/* resend latest buffer after switch */ /* resend latest buffer after switch */

View file

@ -61,13 +61,16 @@ probe_cb (GstPad * pad, GstMiniObject * obj, gpointer user_data)
/* Create and link output pad: selector:src%d ! output_pad */ /* Create and link output pad: selector:src%d ! output_pad */
static GstPad * static GstPad *
setup_output_pad (GstElement * element) setup_output_pad (GstElement * element, GstStaticPadTemplate * tmpl)
{ {
GstPad *srcpad = NULL, *output_pad = NULL; GstPad *srcpad = NULL, *output_pad = NULL;
gulong probe_id = 0; gulong probe_id = 0;
if (tmpl == NULL)
tmpl = &sinktemplate;
/* create output_pad */ /* create output_pad */
output_pad = gst_pad_new_from_static_template (&sinktemplate, "sink"); output_pad = gst_pad_new_from_static_template (tmpl, "sink");
fail_if (output_pad == NULL, "Could not create a output_pad"); fail_if (output_pad == NULL, "Could not create a output_pad");
/* add probe */ /* add probe */
@ -244,7 +247,7 @@ run_output_selector_buffer_count (gint num_output_pads,
input_pads = g_list_append (input_pads, input_pad); input_pads = g_list_append (input_pads, input_pad);
gst_pad_set_active (input_pad, TRUE); gst_pad_set_active (input_pad, TRUE);
for (i = 0; i < num_output_pads; i++) { for (i = 0; i < num_output_pads; i++) {
output_pads = g_list_append (output_pads, setup_output_pad (sel)); output_pads = g_list_append (output_pads, setup_output_pad (sel, NULL));
} }
/* run the test */ /* run the test */
@ -369,6 +372,157 @@ GST_START_TEST (test_input_selector_buffer_count);
GST_END_TEST; GST_END_TEST;
GstElement *sel;
GstPad *input_pad;
GList *output_pads = NULL; /* list of sinkpads linked to output-selector */
#define OUTPUT_SELECTOR_NUM_PADS 2
static GstStaticPadTemplate sinktmpl_nego_a = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("format/abc; format/xyz"));
static GstStaticPadTemplate sinktmpl_nego_b = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("format/abc"));
static void
setup_output_selector (void)
{
sel = gst_check_setup_element ("output-selector");
input_pad = gst_check_setup_src_pad (sel, &srctemplate, NULL);
gst_pad_set_active (input_pad, TRUE);
output_pads = g_list_append (output_pads, setup_output_pad (sel,
&sinktmpl_nego_a));
output_pads = g_list_append (output_pads, setup_output_pad (sel,
&sinktmpl_nego_b));
}
static void
teardown_output_selector (void)
{
gst_pad_set_active (input_pad, FALSE);
gst_object_unref (input_pad);
gst_check_teardown_src_pad (sel);
g_list_foreach (output_pads, (GFunc) cleanup_pad, sel);
g_list_free (output_pads);
gst_check_teardown_element (sel);
output_pads = NULL;
}
GST_START_TEST (test_output_selector_getcaps_none);
{
GList *walker;
/* set pad negotiation mode to none */
g_object_set (sel, "pad-negotiation-mode", 0, NULL);
fail_unless (gst_element_set_state (sel,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"could not set to playing");
for (walker = output_pads; walker; walker = g_list_next (walker)) {
GstCaps *caps;
GstPad *pad;
pad = gst_pad_get_peer ((GstPad *) walker->data);
g_object_set (sel, "active-pad", pad, NULL);
caps = gst_pad_peer_get_caps (input_pad);
/* in 'none' mode, the getcaps returns the template, which is ANY */
g_assert (gst_caps_is_any (caps));
gst_caps_unref (caps);
}
fail_unless (gst_element_set_state (sel,
GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
}
GST_END_TEST;
GST_START_TEST (test_output_selector_getcaps_all);
{
GList *walker;
GstCaps *expected;
/* set pad negotiation mode to 'all' */
g_object_set (sel, "pad-negotiation-mode", 1, NULL);
fail_unless (gst_element_set_state (sel,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"could not set to playing");
/* in 'all' mode, the intersection of the srcpad caps should be returned on
* the sinkpad's getcaps */
expected = gst_caps_new_simple ("format/abc", NULL);
for (walker = output_pads; walker; walker = g_list_next (walker)) {
GstCaps *caps;
GstPad *pad;
pad = gst_pad_get_peer ((GstPad *) walker->data);
g_object_set (sel, "active-pad", pad, NULL);
caps = gst_pad_peer_get_caps (input_pad);
g_assert (gst_caps_is_equal (caps, expected));
gst_caps_unref (caps);
}
gst_caps_unref (expected);
fail_unless (gst_element_set_state (sel,
GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
}
GST_END_TEST;
GST_START_TEST (test_output_selector_getcaps_active);
{
GList *walker;
GstCaps *expected;
/* set pad negotiation mode to 'active' */
g_object_set (sel, "pad-negotiation-mode", 2, NULL);
fail_unless (gst_element_set_state (sel,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"could not set to playing");
for (walker = output_pads; walker; walker = g_list_next (walker)) {
GstCaps *caps;
GstPad *pad;
pad = gst_pad_get_peer ((GstPad *) walker->data);
g_object_set (sel, "active-pad", pad, NULL);
/* in 'active' mode, the active srcpad peer's caps should be returned on
* the sinkpad's getcaps */
expected = gst_pad_template_get_caps (gst_pad_get_pad_template ((GstPad *)
walker->data));
caps = gst_pad_peer_get_caps (input_pad);
g_assert (gst_caps_is_equal (caps, expected));
gst_caps_unref (caps);
}
fail_unless (gst_element_set_state (sel,
GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
}
GST_END_TEST;
static Suite * static Suite *
selector_suite (void) selector_suite (void)
{ {
@ -379,6 +533,14 @@ selector_suite (void)
tcase_add_test (tc_chain, test_output_selector_buffer_count); tcase_add_test (tc_chain, test_output_selector_buffer_count);
tcase_add_test (tc_chain, test_input_selector_buffer_count); tcase_add_test (tc_chain, test_input_selector_buffer_count);
tc_chain = tcase_create ("output-selector-negotiation");
tcase_add_checked_fixture (tc_chain, setup_output_selector,
teardown_output_selector);
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_output_selector_getcaps_none);
tcase_add_test (tc_chain, test_output_selector_getcaps_all);
tcase_add_test (tc_chain, test_output_selector_getcaps_active);
return s; return s;
} }