-/* 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
* @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,
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,
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,
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);
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
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
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;
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)
/* 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");
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);
}