diff --git a/gst/playback/gstplaysink.c b/gst/playback/gstplaysink.c index 3322ea49a5..b59ae8ae91 100644 --- a/gst/playback/gstplaysink.c +++ b/gst/playback/gstplaysink.c @@ -216,6 +216,11 @@ struct _GstPlaySink gint xoverlay_x, xoverlay_y, xoverlay_width, xoverlay_height; gboolean xoverlay_handle_events_set; gboolean xoverlay_handle_events; + + /* colorbalance proxy interface */ + GstColorBalance *colorbalance_element; + GList *colorbalance_channels; /* CONTRAST, BRIGHTNESS, HUE, SATURATION */ + gint colorbalance_values[4]; }; struct _GstPlaySinkClass @@ -344,6 +349,9 @@ static void gst_play_sink_xoverlay_init (gpointer g_iface, gpointer g_iface_data); static void gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data); +static void gst_play_sink_colorbalance_init (gpointer g_iface, + gpointer g_iface_data); + static void _do_init (GType type) { @@ -362,11 +370,16 @@ _do_init (GType type) gst_play_sink_navigation_init, NULL, NULL }; + static const GInterfaceInfo col_info = { + gst_play_sink_colorbalance_init, + NULL, NULL + }; g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, &impl_info); g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info); g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &xov_info); g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &nav_info); + g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &col_info); } G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN, @@ -553,6 +566,8 @@ gst_play_sink_class_init (GstPlaySinkClass * klass) static void gst_play_sink_init (GstPlaySink * playsink) { + GstColorBalanceChannel *channel; + /* init groups */ playsink->video_sink = NULL; playsink->audio_sink = NULL; @@ -570,6 +585,46 @@ gst_play_sink_init (GstPlaySink * playsink) g_static_rec_mutex_init (&playsink->lock); GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_IS_SINK); + + channel = + GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, + NULL)); + channel->label = g_strdup ("CONTRAST"); + channel->min_value = -1000; + channel->max_value = 1000; + playsink->colorbalance_channels = + g_list_append (playsink->colorbalance_channels, channel); + playsink->colorbalance_values[0] = 0; + + channel = + GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, + NULL)); + channel->label = g_strdup ("BRIGHTNESS"); + channel->min_value = -1000; + channel->max_value = 1000; + playsink->colorbalance_channels = + g_list_append (playsink->colorbalance_channels, channel); + playsink->colorbalance_values[1] = 0; + + channel = + GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, + NULL)); + channel->label = g_strdup ("HUE"); + channel->min_value = -1000; + channel->max_value = 1000; + playsink->colorbalance_channels = + g_list_append (playsink->colorbalance_channels, channel); + playsink->colorbalance_values[2] = 0; + + channel = + GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, + NULL)); + channel->label = g_strdup ("SATURATION"); + channel->min_value = -1000; + channel->max_value = 1000; + playsink->colorbalance_channels = + g_list_append (playsink->colorbalance_channels, channel); + playsink->colorbalance_values[3] = 0; } static void @@ -661,6 +716,11 @@ gst_play_sink_dispose (GObject * object) playsink->stream_synchronizer = NULL; + g_list_foreach (playsink->colorbalance_channels, (GFunc) gst_object_unref, + NULL); + g_list_free (playsink->colorbalance_channels); + playsink->colorbalance_channels = NULL; + G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object); } @@ -1314,32 +1374,117 @@ static void iterate_color_balance_elements (gpointer data, gpointer user_data) { gboolean valid = is_valid_color_balance_element (data); - gboolean *valid_out = user_data; + GstColorBalance **cb_out = user_data; - *valid_out = *valid_out && valid; + if (valid) { + if (*cb_out + && gst_color_balance_get_balance_type (*cb_out) == + GST_COLOR_BALANCE_SOFTWARE) { + gst_object_unref (*cb_out); + *cb_out = GST_COLOR_BALANCE (gst_object_ref (data)); + } else if (!*cb_out) { + *cb_out = GST_COLOR_BALANCE (gst_object_ref (data)); + } + } gst_object_unref (data); } -static gboolean -has_color_balance_element (GstElement * element) +static GstColorBalance * +find_color_balance_element (GstElement * element) { GstIterator *it; - gboolean valid = FALSE; + GstColorBalance *cb = NULL; - if (GST_IS_COLOR_BALANCE (element)) - return is_valid_color_balance_element (element); + if (GST_IS_COLOR_BALANCE (element) + && is_valid_color_balance_element (element)) + return GST_COLOR_BALANCE (gst_object_ref (element)); else if (!GST_IS_BIN (element)) return FALSE; it = gst_bin_iterate_all_by_interface (GST_BIN (element), GST_TYPE_COLOR_BALANCE); while (gst_iterator_foreach (it, iterate_color_balance_elements, - &valid) == GST_ITERATOR_RESYNC) + &cb) == GST_ITERATOR_RESYNC) gst_iterator_resync (it); gst_iterator_free (it); - return valid; + return cb; +} + +static void +colorbalance_value_changed_cb (GstColorBalance * balance, + GstColorBalanceChannel * channel, gint value, GstPlaySink * playsink) +{ + GList *l; + gint i; + + for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) { + GstColorBalanceChannel *proxy = l->data; + + if (g_strrstr (channel->label, proxy->label)) { + gdouble new_val; + + /* Convert to [0, 1] range */ + new_val = + ((gdouble) value - + (gdouble) channel->min_value) / ((gdouble) channel->max_value - + (gdouble) channel->min_value); + /* Convert to proxy range */ + new_val = + proxy->min_value + new_val * ((gdouble) proxy->max_value - + (gdouble) proxy->min_value); + playsink->colorbalance_values[i] = (gint) (0.5 + new_val); + + gst_color_balance_value_changed (GST_COLOR_BALANCE (playsink), proxy, + playsink->colorbalance_values[i]); + break; + } + } +} + +static void +update_colorbalance (GstPlaySink * playsink) +{ + GstColorBalance *balance = NULL; + GList *l; + gint i; + + GST_OBJECT_LOCK (playsink); + if (playsink->colorbalance_element) { + balance = + GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element)); + } + GST_OBJECT_UNLOCK (playsink); + if (!balance) + return; + + g_signal_handlers_disconnect_by_func (balance, + G_CALLBACK (colorbalance_value_changed_cb), playsink); + + for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) { + GstColorBalanceChannel *proxy = l->data; + GstColorBalanceChannel *channel = NULL; + const GList *channels, *k; + + channels = gst_color_balance_list_channels (balance); + for (k = channels; k; k = k->next) { + GstColorBalanceChannel *tmp = k->data; + + if (g_strrstr (tmp->label, proxy->label)) { + channel = tmp; + break; + } + } + + g_assert (channel); + + gst_color_balance_set_value (balance, channel, + playsink->colorbalance_values[i]); + } + + g_signal_connect (balance, "value-changed", + G_CALLBACK (colorbalance_value_changed_cb), playsink); } /* make the element (bin) that contains the elements needed to perform @@ -1475,17 +1620,34 @@ gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async) head = prev = chain->queue; } + GST_OBJECT_LOCK (playsink); + if (playsink->colorbalance_element) { + g_signal_handlers_disconnect_by_func (playsink->colorbalance_element, + G_CALLBACK (colorbalance_value_changed_cb), playsink); + gst_object_unref (playsink->colorbalance_element); + } + playsink->colorbalance_element = find_color_balance_element (chain->sink); + GST_OBJECT_UNLOCK (playsink); + if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO) - || (!has_color_balance_element (chain->sink) + || (!playsink->colorbalance_element && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) { gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO); - gboolean use_balance = !has_color_balance_element (chain->sink) + gboolean use_balance = !playsink->colorbalance_element && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE); GST_DEBUG_OBJECT (playsink, "creating videoconverter"); chain->conv = g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv", "use-converters", use_converters, "use-balance", use_balance, NULL); + + GST_OBJECT_LOCK (playsink); + if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance) + playsink->colorbalance_element = + GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT + (chain->conv)->balance)); + GST_OBJECT_UNLOCK (playsink); + gst_bin_add (bin, chain->conv); if (prev) { if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink", @@ -1497,6 +1659,8 @@ gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async) prev = chain->conv; } + update_colorbalance (playsink); + if (prev) { GST_DEBUG_OBJECT (playsink, "linking to sink"); if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL, @@ -1632,10 +1796,30 @@ setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async) if (elem) g_object_set (elem, "force-aspect-ratio", TRUE, NULL); - if (chain->conv) - g_object_set (chain->conv, "use-balance", - !has_color_balance_element (chain->sink) - && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE), NULL); + GST_OBJECT_LOCK (playsink); + if (playsink->colorbalance_element) { + g_signal_handlers_disconnect_by_func (playsink->colorbalance_element, + G_CALLBACK (colorbalance_value_changed_cb), playsink); + gst_object_unref (playsink->colorbalance_element); + } + playsink->colorbalance_element = find_color_balance_element (chain->sink); + GST_OBJECT_UNLOCK (playsink); + + if (chain->conv) { + gboolean use_balance = !playsink->colorbalance_element + && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE); + + g_object_set (chain->conv, "use-balance", use_balance, NULL); + + GST_OBJECT_LOCK (playsink); + if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance) + playsink->colorbalance_element = + GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT + (chain->conv)->balance)); + GST_OBJECT_UNLOCK (playsink); + } + + update_colorbalance (playsink); return TRUE; } @@ -2363,6 +2547,13 @@ gst_play_sink_reconfigure (GstPlaySink * playsink) if (playsink->xoverlay_element) gst_object_unref (playsink->xoverlay_element); playsink->xoverlay_element = NULL; + + if (playsink->colorbalance_element) { + g_signal_handlers_disconnect_by_func (playsink->colorbalance_element, + G_CALLBACK (colorbalance_value_changed_cb), playsink); + gst_object_unref (playsink->colorbalance_element); + } + playsink->colorbalance_element = NULL; GST_OBJECT_UNLOCK (playsink); if (((flags & GST_PLAY_FLAG_VIDEO) @@ -3727,6 +3918,13 @@ gst_play_sink_change_state (GstElement * element, GstStateChange transition) if (playsink->xoverlay_element) gst_object_unref (playsink->xoverlay_element); playsink->xoverlay_element = NULL; + + if (playsink->colorbalance_element) { + g_signal_handlers_disconnect_by_func (playsink->colorbalance_element, + G_CALLBACK (colorbalance_value_changed_cb), playsink); + gst_object_unref (playsink->colorbalance_element); + } + playsink->colorbalance_element = NULL; GST_OBJECT_UNLOCK (playsink); ret = GST_STATE_CHANGE_SUCCESS; @@ -4072,7 +4270,7 @@ gst_play_sink_implements_interface_supported (GstImplementsInterface * iface, GType type) { if (type == GST_TYPE_X_OVERLAY || type == GST_TYPE_STREAM_VOLUME || - type == GST_TYPE_NAVIGATION) + type == GST_TYPE_NAVIGATION || type == GST_TYPE_COLOR_BALANCE) return TRUE; else return FALSE; @@ -4122,6 +4320,127 @@ gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data) iface->send_event = gst_play_sink_navigation_send_event; } +static const GList * +gst_play_sink_colorbalance_list_channels (GstColorBalance * balance) +{ + GstPlaySink *playsink = GST_PLAY_SINK (balance); + + return playsink->colorbalance_channels; +} + +static void +gst_play_sink_colorbalance_set_value (GstColorBalance * balance, + GstColorBalanceChannel * proxy, gint value) +{ + GstPlaySink *playsink = GST_PLAY_SINK (balance); + GList *l; + gint i; + GstColorBalance *balance_element = NULL; + + GST_OBJECT_LOCK (playsink); + if (playsink->colorbalance_element) + balance_element = + GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element)); + GST_OBJECT_UNLOCK (playsink); + + for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) { + GstColorBalanceChannel *proxy_tmp = l->data; + gdouble new_val; + + if (proxy_tmp != proxy) + continue; + + playsink->colorbalance_values[i] = value; + + if (balance_element) { + GstColorBalanceChannel *channel = NULL; + const GList *channels, *k; + + channels = gst_color_balance_list_channels (balance_element); + for (k = channels; k; k = k->next) { + GstColorBalanceChannel *tmp = l->data; + + if (g_strrstr (tmp->label, proxy->label)) { + channel = tmp; + break; + } + } + + g_assert (channel); + + /* Convert to [0, 1] range */ + new_val = + ((gdouble) value - + (gdouble) proxy->min_value) / ((gdouble) proxy->max_value - + (gdouble) proxy->min_value); + /* Convert to channel range */ + new_val = + channel->min_value + new_val * ((gdouble) channel->max_value - + (gdouble) channel->min_value); + + gst_color_balance_set_value (balance_element, channel, + (gint) (new_val + 0.5)); + + gst_object_unref (balance_element); + } + + gst_color_balance_value_changed (balance, proxy, value); + break; + } +} + +static gint +gst_play_sink_colorbalance_get_value (GstColorBalance * balance, + GstColorBalanceChannel * proxy) +{ + GstPlaySink *playsink = GST_PLAY_SINK (balance); + GList *l; + gint i; + + for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) { + GstColorBalanceChannel *proxy_tmp = l->data; + + if (proxy_tmp != proxy) + continue; + + return playsink->colorbalance_values[i]; + } + + g_return_val_if_reached (0); +} + +static GstColorBalanceType +gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance) +{ + GstPlaySink *playsink = GST_PLAY_SINK (balance); + GstColorBalance *balance_element = NULL; + GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE; + + GST_OBJECT_LOCK (playsink); + if (playsink->colorbalance_element) + balance_element = + GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element)); + GST_OBJECT_UNLOCK (playsink); + + if (balance_element) { + t = gst_color_balance_get_balance_type (balance_element); + gst_object_unref (balance_element); + } + + return t; +} + +static void +gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data) +{ + GstColorBalanceClass *iface = (GstColorBalanceClass *) g_iface; + + iface->list_channels = gst_play_sink_colorbalance_list_channels; + iface->set_value = gst_play_sink_colorbalance_set_value; + iface->get_value = gst_play_sink_colorbalance_get_value; + iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type; +} + gboolean gst_play_sink_plugin_init (GstPlugin * plugin) {