10 KiB
Playback tutorial 5: Color Balance
Goal
Brightness, Contrast, Hue and Saturation are common video adjustments, which are collectively known as Color Balance settings in GStreamer. This tutorial shows:
- How to find out the available color balance channels
- How to change them
Introduction
has already explained the concept of GObject interfaces: applications use them to find out if certain functionality is available, regardless of the actual element which implements it.
playbin
implements the Color Balance interface (GstColorBalance
),
which allows access to the color balance settings. If any of the
elements in the playbin
pipeline support this interface,
playbin
simply forwards it to the application, otherwise, a
colorbalance element is inserted in the pipeline.
This interface allows querying for the available color balance channels
(GstColorBalanceChannel
), along with their name and valid range of
values, and then modify the current value of any of them.
Color balance example
Copy this code into a text file named playback-tutorial-5.c
.
playback-tutorial-5.c
#include <string.h>
#include <stdio.h>
#include <gst/gst.h>
#include <gst/video/colorbalance.h>
typedef struct _CustomData {
GstElement *pipeline;
GMainLoop *loop;
} CustomData;
/* Process a color balance command */
static void update_color_channel (const gchar *channel_name, gboolean increase, GstColorBalance *cb) {
gdouble step;
gint value;
GstColorBalanceChannel *channel = NULL;
const GList *channels, *l;
/* Retrieve the list of channels and locate the requested one */
channels = gst_color_balance_list_channels (cb);
for (l = channels; l != NULL; l = l->next) {
GstColorBalanceChannel *tmp = (GstColorBalanceChannel *)l->data;
if (g_strrstr (tmp->label, channel_name)) {
channel = tmp;
break;
}
}
if (!channel)
return;
/* Change the channel's value */
step = 0.1 * (channel->max_value - channel->min_value);
value = gst_color_balance_get_value (cb, channel);
if (increase) {
value = (gint)(value + step);
if (value > channel->max_value)
value = channel->max_value;
} else {
value = (gint)(value - step);
if (value < channel->min_value)
value = channel->min_value;
}
gst_color_balance_set_value (cb, channel, value);
}
/* Output the current values of all Color Balance channels */
static void print_current_values (GstElement *pipeline) {
const GList *channels, *l;
/* Output Color Balance values */
channels = gst_color_balance_list_channels (GST_COLOR_BALANCE (pipeline));
for (l = channels; l != NULL; l = l->next) {
GstColorBalanceChannel *channel = (GstColorBalanceChannel *)l->data;
gint value = gst_color_balance_get_value (GST_COLOR_BALANCE (pipeline), channel);
g_print ("%s: %3d%% ", channel->label,
100 * (value - channel->min_value) / (channel->max_value - channel->min_value));
}
g_print ("\n");
}
/* Process keyboard input */
static gboolean handle_keyboard (GIOChannel *source, GIOCondition cond, CustomData *data) {
gchar *str = NULL;
if (g_io_channel_read_line (source, &str, NULL, NULL, NULL) != G_IO_STATUS_NORMAL) {
return TRUE;
}
switch (g_ascii_tolower (str[0])) {
case 'c':
update_color_channel ("CONTRAST", g_ascii_isupper (str[0]), GST_COLOR_BALANCE (data->pipeline));
break;
case 'b':
update_color_channel ("BRIGHTNESS", g_ascii_isupper (str[0]), GST_COLOR_BALANCE (data->pipeline));
break;
case 'h':
update_color_channel ("HUE", g_ascii_isupper (str[0]), GST_COLOR_BALANCE (data->pipeline));
break;
case 's':
update_color_channel ("SATURATION", g_ascii_isupper (str[0]), GST_COLOR_BALANCE (data->pipeline));
break;
case 'q':
g_main_loop_quit (data->loop);
break;
default:
break;
}
g_free (str);
print_current_values (data->pipeline);
return TRUE;
}
int main(int argc, char *argv[]) {
CustomData data;
GstStateChangeReturn ret;
GIOChannel *io_stdin;
/* Initialize GStreamer */
gst_init (&argc, &argv);
/* Initialize our data structure */
memset (&data, 0, sizeof (data));
/* Print usage map */
g_print (
"USAGE: Choose one of the following options, then press enter:\n"
" 'C' to increase contrast, 'c' to decrease contrast\n"
" 'B' to increase brightness, 'b' to decrease brightness\n"
" 'H' to increase hue, 'h' to decrease hue\n"
" 'S' to increase saturation, 's' to decrease saturation\n"
" 'Q' to quit\n");
/* Build the pipeline */
data.pipeline = gst_parse_launch ("playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm", NULL);
/* Add a keyboard watch so we get notified of keystrokes */
#ifdef G_OS_WIN32
io_stdin = g_io_channel_win32_new_fd (fileno (stdin));
#else
io_stdin = g_io_channel_unix_new (fileno (stdin));
#endif
g_io_add_watch (io_stdin, G_IO_IN, (GIOFunc)handle_keyboard, &data);
/* Start playing */
ret = gst_element_set_state (data.pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
g_printerr ("Unable to set the pipeline to the playing state.\n");
gst_object_unref (data.pipeline);
return -1;
}
print_current_values (data.pipeline);
/* Create a GLib Main Loop and set it to run */
data.loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (data.loop);
/* Free resources */
g_main_loop_unref (data.loop);
g_io_channel_unref (io_stdin);
gst_element_set_state (data.pipeline, GST_STATE_NULL);
gst_object_unref (data.pipeline);
return 0;
}
If you need help to compile this code, refer to the Building the tutorials section for your platform: Mac or Windows or use this specific command on Linux:
gcc playback-tutorial-5.c -o playback-tutorial-5 `pkg-config --cflags --libs gstreamer-1.0 gstreamer-video-1.0`
If you need help to run this code, refer to the Running the tutorials section for your platform: Mac OS X, Windows, for iOS or for android.
This tutorial opens a window and displays a movie, with accompanying audio. The media is fetched from the Internet, so the window might take a few seconds to appear, depending on your connection speed.
The console should print all commands (Each command is a single upper-case or lower-case letter) and list all available Color Balance channels, typically, CONTRAST, BRIGHTNESS, HUE and SATURATION. Type each command (letter) followed by the Enter key.
Required libraries:
gstreamer-1.0 gstreamer-video-1.0
Walkthrough
The main()
function is fairly simple. A playbin
pipeline is
instantiated and set to run, and a keyboard watch is installed so
keystrokes can be monitored.
/* Output the current values of all Color Balance channels */
static void print_current_values (GstElement *pipeline) {
const GList *channels, *l;
/* Output Color Balance values */
channels = gst_color_balance_list_channels (GST_COLOR_BALANCE (pipeline));
for (l = channels; l != NULL; l = l->next) {
GstColorBalanceChannel *channel = (GstColorBalanceChannel *)l->data;
gint value = gst_color_balance_get_value (GST_COLOR_BALANCE (pipeline), channel);
g_print ("%s: %3d%% ", channel->label,
100 * (value - channel->min_value) / (channel->max_value - channel->min_value));
}
g_print ("\n");
}
This method prints the current value for all channels, and exemplifies
how to retrieve the list of channels. This is accomplished through the
gst_color_balance_list_channels()
method. It returns a GList
which
needs to be traversed.
Each element in the list is a GstColorBalanceChannel
structure,
informing of the channel’s name, minimum value and maximum value.
gst_color_balance_get_value()
can then be called on each channel to
retrieve the current value.
In this example, the minimum and maximum values are used to output the current value as a percentage.
/* Process a color balance command */
static void update_color_channel (const gchar *channel_name, gboolean increase, GstColorBalance *cb) {
gdouble step;
gint value;
GstColorBalanceChannel *channel = NULL;
const GList *channels, *l;
/* Retrieve the list of channels and locate the requested one */
channels = gst_color_balance_list_channels (cb);
for (l = channels; l != NULL; l = l->next) {
GstColorBalanceChannel *tmp = (GstColorBalanceChannel *)l->data;
if (g_strrstr (tmp->label, channel_name)) {
channel = tmp;
break;
}
}
if (!channel)
return;
This method locates the specified channel by name and increases or decreases it as requested. Again, the list of channels is retrieved and parsed looking for the channel with the specified name. Obviously, this list could be parsed only once and the pointers to the channels be stored and indexed by something more efficient than a string.
/* Change the channel's value */
step = 0.1 * (channel->max_value - channel->min_value);
value = gst_color_balance_get_value (cb, channel);
if (increase) {
value = (gint)(value + step);
if (value > channel->max_value)
value = channel->max_value;
} else {
value = (gint)(value - step);
if (value < channel->min_value)
value = channel->min_value;
}
gst_color_balance_set_value (cb, channel, value);
}
The current value for the channel is then retrieved, changed (the
increment is proportional to its dynamic range), clamped (to avoid
out-of-range values) and set using gst_color_balance_set_value()
.
And there is not much more to it. Run the program and observe the effect of changing each of the channels in real time.
Conclusion
This tutorial has shown how to use the color balance interface. Particularly, it has shown:
- How to retrieve the list of color available balance channels
with
gst_color_balance_list_channels()
- How to manipulate the current value of each channel using
gst_color_balance_get_value()
andgst_color_balance_set_value()
It has been a pleasure having you here, and see you soon!