output-selector: Add pad-negotiation-mode property
authorThiago Santos <thiago.sousa.santos@collabora.co.uk>
Tue, 4 Jan 2011 15:42:50 +0000 (12:42 -0300)
committerTim-Philipp Müller <tim.muller@collabora.co.uk>
Thu, 6 Jan 2011 18:42:29 +0000 (18:42 +0000)
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
plugins/elements/gstoutputselector.h
tests/check/elements/selector.c

index c28ddb2..9d2fbfe 100644 (file)
@@ -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)
index 5389d47..8c55b44 100644 (file)
@@ -47,6 +47,8 @@ struct _GstOutputSelector {
   GstPad *pending_srcpad;
   guint nb_srcpads;
 
+  gint pad_negotiation_mode;
+
   GstSegment segment;
 
   /* resend latest buffer after switch */
index 56d2840..3c96890 100644 (file)
@@ -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;
 }