From 757dc90faa163a235e1d16791a0a0f8c74715768 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Tue, 4 Jan 2011 12:42:50 -0300 Subject: [PATCH] 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 --- plugins/elements/gstoutputselector.c | 116 ++++++++++++++++-- plugins/elements/gstoutputselector.h | 2 + tests/check/elements/selector.c | 168 ++++++++++++++++++++++++++- 3 files changed, 276 insertions(+), 10 deletions(-) diff --git a/plugins/elements/gstoutputselector.c b/plugins/elements/gstoutputselector.c index c28ddb2e30..9d2fbfef86 100644 --- a/plugins/elements/gstoutputselector.c +++ b/plugins/elements/gstoutputselector.c @@ -49,13 +49,43 @@ GST_STATIC_PAD_TEMPLATE ("src%d", GST_PAD_REQUEST, 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 { PROP_0, 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) \ GST_DEBUG_CATEGORY_INIT (output_selector_debug, \ "output-selector", 0, "Output stream selector"); @@ -79,6 +109,8 @@ static GstStateChangeReturn gst_output_selector_change_state (GstElement * element, GstStateChange transition); static gboolean gst_output_selector_handle_sink_event (GstPad * pad, GstEvent * event); +static void gst_output_selector_switch_pad_negotiation_mode (GstOutputSelector * + sel, gint mode); static void 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", "Resend latest buffer after a switch to a new pad", FALSE, 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 = 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_pad_set_bufferalloc_function (sel->sinkpad, 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); @@ -152,6 +184,8 @@ gst_output_selector_init (GstOutputSelector * sel, sel->resend_latest = FALSE; sel->latest_buffer = NULL; + gst_output_selector_switch_pad_negotiation_mode (sel, + DEFAULT_PAD_NEGOTIATION_MODE); } static void @@ -218,6 +252,11 @@ gst_output_selector_set_property (GObject * object, guint prop_id, sel->resend_latest = g_value_get_boolean (value); break; } + case PROP_PAD_NEGOTIATION_MODE:{ + gst_output_selector_switch_pad_negotiation_mode (sel, + g_value_get_enum (value)); + break; + } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -243,12 +282,75 @@ gst_output_selector_get_property (GObject * object, guint prop_id, GST_OBJECT_UNLOCK (object); break; } + case PROP_PAD_NEGOTIATION_MODE: + g_value_set_enum (value, sel->pad_negotiation_mode); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 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 gst_output_selector_buffer_alloc (GstPad * pad, guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf) diff --git a/plugins/elements/gstoutputselector.h b/plugins/elements/gstoutputselector.h index 5389d47354..8c55b44944 100644 --- a/plugins/elements/gstoutputselector.h +++ b/plugins/elements/gstoutputselector.h @@ -47,6 +47,8 @@ struct _GstOutputSelector { GstPad *pending_srcpad; guint nb_srcpads; + gint pad_negotiation_mode; + GstSegment segment; /* resend latest buffer after switch */ diff --git a/tests/check/elements/selector.c b/tests/check/elements/selector.c index 56d2840ec1..3c96890be4 100644 --- a/tests/check/elements/selector.c +++ b/tests/check/elements/selector.c @@ -61,13 +61,16 @@ probe_cb (GstPad * pad, GstMiniObject * obj, gpointer user_data) /* Create and link output pad: selector:src%d ! output_pad */ static GstPad * -setup_output_pad (GstElement * element) +setup_output_pad (GstElement * element, GstStaticPadTemplate * tmpl) { GstPad *srcpad = NULL, *output_pad = NULL; gulong probe_id = 0; + if (tmpl == NULL) + tmpl = &sinktemplate; + /* 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"); /* add probe */ @@ -244,7 +247,7 @@ run_output_selector_buffer_count (gint num_output_pads, input_pads = g_list_append (input_pads, input_pad); gst_pad_set_active (input_pad, TRUE); 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 */ @@ -369,6 +372,157 @@ GST_START_TEST (test_input_selector_buffer_count); 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 * 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_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; } -- 2.34.1