android: make it ready for androgenizer
[platform/upstream/gstreamer.git] / plugins / elements / gstoutputselector.c
index f5c89ee..9a4db2a 100644 (file)
@@ -1,4 +1,4 @@
-/* GStreamer
+/* GStreamer output selector
  * Copyright (C) 2008 Nokia Corporation. (contact <stefan.kost@nokia.com>)
  *
  * This library is free software; you can redistribute it and/or
@@ -22,6 +22,8 @@
  * @see_also: #GstOutputSelector, #GstInputSelector
  *
  * Direct input stream to one out of N output pads.
+ *
+ * Since: 0.10.32
  */
 
 #ifdef HAVE_CONFIG_H
 GST_DEBUG_CATEGORY_STATIC (output_selector_debug);
 #define GST_CAT_DEFAULT output_selector_debug
 
-static const GstElementDetails gst_output_selector_details =
-GST_ELEMENT_DETAILS ("Output selector",
-    "Generic",
-    "1-to-N output stream selectoring",
-    "Stefan Kost <stefan.kost@nokia.com>");
-
 static GstStaticPadTemplate gst_output_selector_sink_factory =
 GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
@@ -53,16 +49,49 @@ 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_LAST
+  PROP_PAD_NEGOTIATION_MODE
 };
 
-GST_BOILERPLATE (GstOutputSelector, gst_output_selector, GstElement,
-    GST_TYPE_ELEMENT);
+#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");
+
+GST_BOILERPLATE_FULL (GstOutputSelector, gst_output_selector, GstElement,
+    GST_TYPE_ELEMENT, _do_init);
 
 static void gst_output_selector_dispose (GObject * object);
 static void gst_output_selector_set_property (GObject * object,
@@ -80,13 +109,17 @@ 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)
 {
   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
 
-  gst_element_class_set_details (element_class, &gst_output_selector_details);
+  gst_element_class_set_details_simple (element_class, "Output selector",
+      "Generic", "1-to-N output stream selector",
+      "Stefan Kost <stefan.kost@nokia.com>");
   gst_element_class_add_pad_template (element_class,
       gst_static_pad_template_get (&gst_output_selector_sink_factory));
   gst_element_class_add_pad_template (element_class,
@@ -99,22 +132,25 @@ gst_output_selector_class_init (GstOutputSelectorClass * klass)
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
 
-  parent_class = g_type_class_peek_parent (klass);
-
   gobject_class->dispose = gst_output_selector_dispose;
 
-  gobject_class->set_property =
-      GST_DEBUG_FUNCPTR (gst_output_selector_set_property);
-  gobject_class->get_property =
-      GST_DEBUG_FUNCPTR (gst_output_selector_get_property);
+  gobject_class->set_property = gst_output_selector_set_property;
+  gobject_class->get_property = gst_output_selector_get_property;
 
   g_object_class_install_property (gobject_class, PROP_ACTIVE_PAD,
       g_param_spec_object ("active-pad", "Active pad",
-          "Currently active src pad", GST_TYPE_PAD, G_PARAM_READWRITE));
+          "Currently active src pad", GST_TYPE_PAD,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   g_object_class_install_property (gobject_class, PROP_RESEND_LATEST,
       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_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);
@@ -122,9 +158,6 @@ gst_output_selector_class_init (GstOutputSelectorClass * klass)
       GST_DEBUG_FUNCPTR (gst_output_selector_release_pad);
 
   gstelement_class->change_state = gst_output_selector_change_state;
-
-  GST_DEBUG_CATEGORY_INIT (output_selector_debug,
-      "output-selector", 0, "An output stream selector element");
 }
 
 static void
@@ -140,23 +173,19 @@ 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);
 
   /* srcpad management */
   sel->active_srcpad = NULL;
   sel->nb_srcpads = 0;
-  gst_segment_init (&sel->segment, GST_FORMAT_UNDEFINED);
+  gst_segment_init (&sel->segment, GST_FORMAT_TIME);
   sel->pending_srcpad = NULL;
 
   sel->resend_latest = FALSE;
   sel->latest_buffer = NULL;
+  gst_output_selector_switch_pad_negotiation_mode (sel,
+      DEFAULT_PAD_NEGOTIATION_MODE);
 }
 
 static void
@@ -223,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;
@@ -248,12 +282,79 @@ 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 = NULL;
+  GstCaps *caps = NULL;
+
+  GST_OBJECT_LOCK (sel);
+  if (sel->pending_srcpad)
+    active = gst_object_ref (sel->pending_srcpad);
+  else if (sel->active_srcpad)
+    active = gst_object_ref (sel->active_srcpad);
+  GST_OBJECT_UNLOCK (sel);
+
+  if (active) {
+    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 = NULL;
+  gboolean ret = TRUE;
+
+  GST_OBJECT_LOCK (sel);
+  if (sel->pending_srcpad)
+    active = gst_object_ref (sel->pending_srcpad);
+  else if (sel->active_srcpad)
+    active = gst_object_ref (sel->active_srcpad);
+  GST_OBJECT_UNLOCK (sel);
+
+  if (active) {
+    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)
@@ -376,8 +477,7 @@ gst_output_selector_switch (GstOutputSelector * osel)
     /* Resend latest buffer to newly switched pad */
     if (osel->resend_latest && osel->latest_buffer) {
       GST_INFO ("resending latest buffer");
-      gst_pad_push (osel->active_srcpad, osel->latest_buffer);
-      osel->latest_buffer = NULL;
+      gst_pad_push (osel->active_srcpad, gst_buffer_ref (osel->latest_buffer));
     }
   } else {
     GST_WARNING_OBJECT (osel, "switch failed, pad not linked");
@@ -395,7 +495,18 @@ gst_output_selector_chain (GstPad * pad, GstBuffer * buf)
 
   osel = GST_OUTPUT_SELECTOR (gst_pad_get_parent (pad));
 
-  if (osel->pending_srcpad) {
+  /*
+   * The _switch function might push a buffer if 'resend-latest' is true.
+   *
+   * Elements/Applications (e.g. camerabin) might use pad probes to
+   * switch output-selector's active pad. If we simply switch and don't
+   * recheck any pending pad switch the following codepath could end
+   * up pushing a buffer on a non-active pad. This is bad.
+   *
+   * So we always should check the pending_srcpad before going further down
+   * the chain and pushing the new buffer
+   */
+  while (osel->pending_srcpad) {
     /* Do the switch */
     gst_output_selector_switch (osel);
   }