#define parent_class gst_play_sink_video_convert_parent_class
G_DEFINE_TYPE (GstPlaySinkVideoConvert, gst_play_sink_video_convert,
- GST_TYPE_BIN);
+ GST_TYPE_PLAY_SINK_CONVERT_BIN);
-static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS_ANY);
-
-static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS_ANY);
-
-static gboolean
-is_raw_caps (GstCaps * caps)
+enum
{
- gint i, n;
- GstStructure *s;
- const gchar *name;
-
- n = gst_caps_get_size (caps);
- for (i = 0; i < n; i++) {
- s = gst_caps_get_structure (caps, i);
- name = gst_structure_get_name (s);
- if (!g_str_has_prefix (name, "video/x-raw"))
- return FALSE;
- }
-
- return TRUE;
-}
+ PROP_0,
+ PROP_USE_CONVERTERS,
+ PROP_USE_BALANCE,
+};
-static void
-post_missing_element_message (GstPlaySinkVideoConvert * self,
- const gchar * name)
-{
- GstMessage *msg;
-
- msg = gst_missing_element_message_new (GST_ELEMENT_CAST (self), name);
- gst_element_post_message (GST_ELEMENT_CAST (self), msg);
-}
-
-static void
-distribute_running_time (GstElement * element, const GstSegment * segment)
-{
- GstEvent *event;
- GstPad *pad;
-
- pad = gst_element_get_static_pad (element, "sink");
-
- if (segment->accum) {
- event = gst_event_new_new_segment_full (FALSE, segment->rate,
- segment->applied_rate, segment->format, 0, segment->accum, 0);
- gst_pad_send_event (pad, event);
- }
-
- event = gst_event_new_new_segment_full (FALSE, segment->rate,
- segment->applied_rate, segment->format,
- segment->start, segment->stop, segment->time);
- gst_pad_send_event (pad, event);
-
- gst_object_unref (pad);
-}
-
-static void
-gst_play_sink_video_convert_add_identity (GstPlaySinkVideoConvert * self)
-{
- self->identity = gst_element_factory_make ("identity", "identity");
- if (self->identity == NULL) {
- post_missing_element_message (self, "identity");
- GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN,
- (_("Missing element '%s' - check your GStreamer installation."),
- "identity"), ("video rendering might fail"));
- } else {
- gst_bin_add (GST_BIN_CAST (self), self->identity);
- gst_element_sync_state_with_parent (self->identity);
- distribute_running_time (self->identity, &self->segment);
- }
-}
-
-static void
-gst_play_sink_video_convert_set_targets (GstPlaySinkVideoConvert * self,
- GstElement * head, GstElement * tail)
+static gboolean
+gst_play_sink_video_convert_add_conversion_elements (GstPlaySinkVideoConvert *
+ self)
{
- GstPad *pad;
+ GstPlaySinkConvertBin *cbin = GST_PLAY_SINK_CONVERT_BIN (self);
+ GstElement *el, *prev = NULL;
- pad = gst_element_get_static_pad (head, "sink");
- gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad);
- gst_object_unref (pad);
+ g_assert (cbin->conversion_elements == NULL);
- pad = gst_element_get_static_pad (tail, "src");
- gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad);
- gst_object_unref (pad);
-}
+ GST_DEBUG_OBJECT (self,
+ "Building video conversion with use-converters %d, use-balance %d",
+ self->use_converters, self->use_balance);
-static void
-pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkVideoConvert * self)
-{
- GstPad *peer;
- GstCaps *caps;
- gboolean raw;
- GstBin *bin = GST_BIN_CAST (self);
- GstElement *head = NULL, *prev = NULL;
-
- GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
- self->sink_proxypad_blocked = blocked;
- GST_DEBUG_OBJECT (self, "Pad blocked: %d", blocked);
- if (!blocked)
- goto done;
-
- /* There must be a peer at this point */
- peer = gst_pad_get_peer (self->sinkpad);
- caps = gst_pad_get_negotiated_caps (peer);
- if (!caps)
- caps = gst_pad_get_caps_reffed (peer);
- gst_object_unref (peer);
-
- raw = is_raw_caps (caps);
- GST_DEBUG_OBJECT (self, "Caps %" GST_PTR_FORMAT " are raw: %d", caps, raw);
- gst_caps_unref (caps);
-
- if (raw == self->raw)
- goto unblock;
- self->raw = raw;
-
- if (raw) {
-
- GST_DEBUG_OBJECT (self, "Creating raw conversion pipeline");
-
- gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
- gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
-
- self->conv = gst_element_factory_make (COLORSPACE, "conv");
- if (self->conv == NULL) {
- post_missing_element_message (self, COLORSPACE);
- GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN,
- (_("Missing element '%s' - check your GStreamer installation."),
- COLORSPACE), ("video rendering might fail"));
- } else {
- gst_bin_add (bin, self->conv);
- gst_element_sync_state_with_parent (self->conv);
- distribute_running_time (self->conv, &self->segment);
- prev = head = self->conv;
- }
+ if (self->use_converters) {
+ el = gst_play_sink_convert_bin_add_conversion_element_factory (cbin,
+ COLORSPACE, "conv");
+ if (el)
+ prev = el;
- self->scale = gst_element_factory_make ("videoscale", "scale");
- if (self->scale == NULL) {
- post_missing_element_message (self, "videoscale");
- GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN,
- (_("Missing element '%s' - check your GStreamer installation."),
- "videoscale"), ("possibly a liboil version mismatch?"));
- } else {
+ el = gst_play_sink_convert_bin_add_conversion_element_factory (cbin,
+ "videoscale", "scale");
+ if (el) {
/* Add black borders if necessary to keep the DAR */
- g_object_set (self->scale, "add-borders", TRUE, NULL);
- gst_bin_add (bin, self->scale);
- gst_element_sync_state_with_parent (self->scale);
- distribute_running_time (self->scale, &self->segment);
+ g_object_set (el, "add-borders", TRUE, NULL);
if (prev) {
- if (!gst_element_link_pads_full (prev, "src", self->scale, "sink",
+ if (!gst_element_link_pads_full (prev, "src", el, "sink",
GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
goto link_failed;
- } else {
- head = self->scale;
}
- prev = self->scale;
- }
-
- GST_DEBUG_OBJECT (self, "Raw conversion pipeline created");
- } else {
-
- GST_DEBUG_OBJECT (self, "Removing raw conversion pipeline");
-
- gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
- gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
-
- if (self->conv) {
- gst_element_set_state (self->conv, GST_STATE_NULL);
- gst_bin_remove (bin, self->conv);
- self->conv = NULL;
+ prev = el;
}
- if (self->scale) {
- gst_element_set_state (self->scale, GST_STATE_NULL);
- gst_bin_remove (bin, self->scale);
- self->scale = NULL;
- }
-
- GST_DEBUG_OBJECT (self, "Raw conversion pipeline removed");
- }
-
- g_assert ((head != NULL) == (prev != NULL));
-
- /* to make things simple and avoid counterintuitive pad juggling,
- ensure there is at least one element in the list */
- if (!head) {
- gst_play_sink_video_convert_add_identity (self);
- prev = head = self->identity;
}
- gst_play_sink_video_convert_set_targets (self, head, prev);
-
-unblock:
- gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
- (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
- (GDestroyNotify) gst_object_unref);
-
-done:
- GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
- return;
-
-link_failed:
- {
- GST_ELEMENT_ERROR (self, CORE, PAD,
- (NULL), ("Failed to configure the video converter."));
-
- /* use a simple identity, better than nothing */
- gst_play_sink_video_convert_add_identity (self);
- gst_play_sink_video_convert_set_targets (self, self->identity,
- self->identity);
-
- gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
- (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
- (GDestroyNotify) gst_object_unref);
-
- GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
- return;
- }
-}
-
-static gboolean
-gst_play_sink_video_convert_sink_event (GstPad * pad, GstEvent * event)
-{
- GstPlaySinkVideoConvert *self =
- GST_PLAY_SINK_VIDEO_CONVERT (gst_pad_get_parent (pad));
- gboolean ret;
-
- ret = gst_proxy_pad_event_default (pad, gst_event_ref (event));
-
- if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) {
- gboolean update;
- gdouble rate, applied_rate;
- GstFormat format;
- gint64 start, stop, position;
-
- GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
- gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
- &format, &start, &stop, &position);
-
- GST_DEBUG_OBJECT (self, "Segment before %" GST_SEGMENT_FORMAT,
- &self->segment);
- gst_segment_set_newsegment_full (&self->segment, update, rate, applied_rate,
- format, start, stop, position);
- GST_DEBUG_OBJECT (self, "Segment after %" GST_SEGMENT_FORMAT,
- &self->segment);
- GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
- } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
- GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
- GST_DEBUG_OBJECT (self, "Resetting segment");
- gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
- GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
- }
-
- gst_event_unref (event);
- gst_object_unref (self);
-
- return ret;
-}
-
-static gboolean
-gst_play_sink_video_convert_sink_setcaps (GstPad * pad, GstCaps * caps)
-{
- GstPlaySinkVideoConvert *self =
- GST_PLAY_SINK_VIDEO_CONVERT (gst_pad_get_parent (pad));
- gboolean ret;
- GstStructure *s;
- const gchar *name;
- gboolean reconfigure = FALSE;
-
- GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
- s = gst_caps_get_structure (caps, 0);
- name = gst_structure_get_name (s);
-
- if (g_str_has_prefix (name, "video/x-raw-")) {
- if (!self->raw && !gst_pad_is_blocked (self->sink_proxypad)) {
- GST_DEBUG_OBJECT (self, "Changing caps from non-raw to raw");
- reconfigure = TRUE;
- gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE,
- (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
- (GDestroyNotify) gst_object_unref);
- }
- } else {
- if (self->raw && !gst_pad_is_blocked (self->sink_proxypad)) {
- GST_DEBUG_OBJECT (self, "Changing caps from raw to non-raw");
- reconfigure = TRUE;
- gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE,
- (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
- (GDestroyNotify) gst_object_unref);
- }
- }
-
- /* Otherwise the setcaps below fails */
- if (reconfigure) {
- gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
- gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
- }
- GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
-
- ret = gst_ghost_pad_setcaps_default (pad, caps);
-
- GST_DEBUG_OBJECT (self, "Setting sink caps %" GST_PTR_FORMAT ": %d", caps,
- ret);
-
- gst_object_unref (self);
-
- return ret;
-}
-
-static GstCaps *
-gst_play_sink_video_convert_getcaps (GstPad * pad)
-{
- GstPlaySinkVideoConvert *self =
- GST_PLAY_SINK_VIDEO_CONVERT (gst_pad_get_parent (pad));
- GstCaps *ret;
- GstPad *otherpad, *peer;
-
- GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
- otherpad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (pad));
- if (!otherpad) {
- if (pad == self->srcpad) {
- otherpad = self->sink_proxypad;
- } else if (pad == self->sinkpad) {
- otherpad =
- GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
- (self->sinkpad)));
- } else {
- GST_ERROR_OBJECT (pad, "Not one of our pads");
+ if (self->use_balance && self->balance) {
+ el = self->balance;
+ gst_play_sink_convert_bin_add_conversion_element (cbin, el);
+ if (prev) {
+ if (!gst_element_link_pads_full (prev, "src", el, "sink",
+ GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
+ goto link_failed;
}
- }
- GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
-
- if (otherpad) {
- peer = gst_pad_get_peer (otherpad);
- if (peer) {
- ret = gst_pad_get_caps_reffed (peer);
- gst_object_unref (peer);
- } else {
- ret = gst_caps_new_any ();
+ prev = el;
+
+ el = gst_play_sink_convert_bin_add_conversion_element_factory (cbin,
+ COLORSPACE, "conv");
+ if (prev) {
+ if (!gst_element_link_pads_full (prev, "src", el, "sink",
+ GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
+ goto link_failed;
}
- gst_object_unref (otherpad);
- } else {
- GST_WARNING_OBJECT (self, "Could not traverse bin");
- ret = gst_caps_new_any ();
+ if (el)
+ prev = el;
}
- gst_object_unref (self);
+ return TRUE;
- return ret;
+link_failed:
+ return FALSE;
}
static void
{
GstPlaySinkVideoConvert *self = GST_PLAY_SINK_VIDEO_CONVERT_CAST (object);
- gst_object_unref (self->sink_proxypad);
- g_mutex_free (self->lock);
+ if (self->balance)
+ gst_object_unref (self->balance);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
-static GstStateChangeReturn
-gst_play_sink_video_convert_change_state (GstElement * element,
- GstStateChange transition)
+static void
+gst_play_sink_video_convert_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
{
- GstStateChangeReturn ret;
- GstPlaySinkVideoConvert *self = GST_PLAY_SINK_VIDEO_CONVERT_CAST (element);
-
- switch (transition) {
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
- if (gst_pad_is_blocked (self->sink_proxypad))
- gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
- (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
- (GDestroyNotify) gst_object_unref);
- GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
+ GstPlaySinkVideoConvert *self = GST_PLAY_SINK_VIDEO_CONVERT_CAST (object);
+ gboolean v, changed = FALSE;
+
+ GST_PLAY_SINK_CONVERT_BIN_LOCK (self);
+ switch (prop_id) {
+ case PROP_USE_CONVERTERS:
+ v = g_value_get_boolean (value);
+ if (v != self->use_converters) {
+ self->use_converters = v;
+ changed = TRUE;
+ }
+ break;
+ case PROP_USE_BALANCE:
+ v = g_value_get_boolean (value);
+ if (v != self->use_balance) {
+ self->use_balance = v;
+ changed = TRUE;
+ }
break;
default:
break;
}
- ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
- if (ret == GST_STATE_CHANGE_FAILURE)
- return ret;
-
- switch (transition) {
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
- gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
- if (self->conv) {
- gst_element_set_state (self->conv, GST_STATE_NULL);
- gst_bin_remove (GST_BIN_CAST (self), self->conv);
- self->conv = NULL;
- }
- if (self->scale) {
- gst_element_set_state (self->scale, GST_STATE_NULL);
- gst_bin_remove (GST_BIN_CAST (self), self->scale);
- self->scale = NULL;
- }
- if (!self->identity) {
- gst_play_sink_video_convert_add_identity (self);
- }
- gst_play_sink_video_convert_set_targets (self, self->identity,
- self->identity);
- self->raw = FALSE;
- GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
- break;
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
- if (!gst_pad_is_blocked (self->sink_proxypad))
- gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE,
- (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
- (GDestroyNotify) gst_object_unref);
- GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
+ if (changed) {
+ GstPlaySinkConvertBin *cbin = GST_PLAY_SINK_CONVERT_BIN (self);
+ GST_DEBUG_OBJECT (self, "Rebuilding converter bin");
+ gst_play_sink_convert_bin_remove_elements (cbin);
+ gst_play_sink_video_convert_add_conversion_elements (self);
+ gst_play_sink_convert_bin_add_identity (cbin);
+ gst_play_sink_convert_bin_cache_converter_caps (cbin);
+ }
+ GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self);
+}
+
+static void
+gst_play_sink_video_convert_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstPlaySinkVideoConvert *self = GST_PLAY_SINK_VIDEO_CONVERT_CAST (object);
+
+ GST_PLAY_SINK_CONVERT_BIN_LOCK (self);
+ switch (prop_id) {
+ case PROP_USE_CONVERTERS:
+ g_value_set_boolean (value, self->use_converters);
break;
- case GST_STATE_CHANGE_READY_TO_NULL:
- if (self->identity) {
- gst_element_set_state (self->identity, GST_STATE_NULL);
- gst_bin_remove (GST_BIN_CAST (self), self->identity);
- self->identity = NULL;
- }
+ case PROP_USE_BALANCE:
+ g_value_set_boolean (value, self->use_balance);
break;
default:
break;
}
-
- return ret;
+ GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self);
}
static void
gstelement_class = (GstElementClass *) klass;
gobject_class->finalize = gst_play_sink_video_convert_finalize;
+ gobject_class->set_property = gst_play_sink_video_convert_set_property;
+ gobject_class->get_property = gst_play_sink_video_convert_get_property;
+
+ g_object_class_install_property (gobject_class, PROP_USE_CONVERTERS,
+ g_param_spec_boolean ("use-converters", "Use converters",
+ "Whether to use conversion elements", FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_USE_BALANCE,
+ g_param_spec_boolean ("use-balance", "Use balance",
+ "Whether to use a videobalance element", FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&srctemplate));
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&sinktemplate));
gst_element_class_set_details_simple (gstelement_class,
"Player Sink Video Converter", "Video/Bin/Converter",
"Convenience bin for video conversion",
"Sebastian Dröge <sebastian.droege@collabora.co.uk>");
-
- gstelement_class->change_state =
- GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_change_state);
}
static void
gst_play_sink_video_convert_init (GstPlaySinkVideoConvert * self)
{
- GstPadTemplate *templ;
-
- self->lock = g_mutex_new ();
- gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
-
- templ = gst_static_pad_template_get (&sinktemplate);
- self->sinkpad = gst_ghost_pad_new_no_target_from_template ("sink", templ);
- gst_pad_set_event_function (self->sinkpad,
- GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_sink_event));
- gst_pad_set_setcaps_function (self->sinkpad,
- GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_sink_setcaps));
- gst_pad_set_getcaps_function (self->sinkpad,
- GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_getcaps));
-
- self->sink_proxypad =
- GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (self->sinkpad)));
-
- gst_element_add_pad (GST_ELEMENT_CAST (self), self->sinkpad);
- gst_object_unref (templ);
-
- templ = gst_static_pad_template_get (&srctemplate);
- self->srcpad = gst_ghost_pad_new_no_target_from_template ("src", templ);
- gst_pad_set_getcaps_function (self->srcpad,
- GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_getcaps));
- gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad);
- gst_object_unref (templ);
-
- gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
- self->sink_proxypad);
+ GstPlaySinkConvertBin *cbin = GST_PLAY_SINK_CONVERT_BIN (self);
+ cbin->audio = FALSE;
+
+ /* FIXME: Only create this on demand but for now we need
+ * it to always exist because of playsink's color balance
+ * proxying logic.
+ */
+ self->balance = gst_element_factory_make ("videobalance", "videobalance");
+ if (self->balance)
+ gst_object_ref_sink (self->balance);
+
+ gst_play_sink_video_convert_add_conversion_elements (self);
+ gst_play_sink_convert_bin_cache_converter_caps (cbin);
}