Update theme submodule
[platform/upstream/gstreamer.git] / markdown / tutorials / playback / color-balance.md
1 # Playback tutorial 5: Color Balance
2
3 ## Goal
4
5 Brightness, Contrast, Hue and Saturation are common video adjustments,
6 which are collectively known as Color Balance settings in GStreamer.
7 This tutorial shows:
8
9   - How to find out the available color balance channels
10   - How to change them
11
12 ## Introduction
13 [](tutorials/basic/toolkit-integration.md) has
14 already explained the concept of GObject interfaces: applications use
15 them to find out if certain functionality is available, regardless of
16 the actual element which implements it.
17
18 `playbin` implements the Color Balance interface (`GstColorBalance`),
19 which allows access to the color balance settings. If any of the
20 elements in the `playbin` pipeline support this interface,
21 `playbin` simply forwards it to the application, otherwise, a
22 colorbalance element is inserted in the pipeline.
23
24 This interface allows querying for the available color balance channels
25 (`GstColorBalanceChannel`), along with their name and valid range of
26 values, and then modify the current value of any of them.
27
28 ## Color balance example
29
30 Copy this code into a text file named `playback-tutorial-5.c`.
31
32 **playback-tutorial-5.c**
33
34 ``` c
35 #include <string.h>
36 #include <stdio.h>
37 #include <gst/gst.h>
38 #include <gst/video/colorbalance.h>
39
40 typedef struct _CustomData {
41   GstElement *pipeline;
42   GMainLoop *loop;
43 } CustomData;
44
45 /* Process a color balance command */
46 static void update_color_channel (const gchar *channel_name, gboolean increase, GstColorBalance *cb) {
47   gdouble step;
48   gint value;
49   GstColorBalanceChannel *channel = NULL;
50   const GList *channels, *l;
51
52   /* Retrieve the list of channels and locate the requested one */
53   channels = gst_color_balance_list_channels (cb);
54   for (l = channels; l != NULL; l = l->next) {
55     GstColorBalanceChannel *tmp = (GstColorBalanceChannel *)l->data;
56
57     if (g_strrstr (tmp->label, channel_name)) {
58       channel = tmp;
59       break;
60     }
61   }
62   if (!channel)
63     return;
64
65   /* Change the channel's value */
66   step = 0.1 * (channel->max_value - channel->min_value);
67   value = gst_color_balance_get_value (cb, channel);
68   if (increase) {
69     value = (gint)(value + step);
70     if (value > channel->max_value)
71       value = channel->max_value;
72   } else {
73     value = (gint)(value - step);
74     if (value < channel->min_value)
75       value = channel->min_value;
76   }
77   gst_color_balance_set_value (cb, channel, value);
78 }
79
80 /* Output the current values of all Color Balance channels */
81 static void print_current_values (GstElement *pipeline) {
82   const GList *channels, *l;
83
84   /* Output Color Balance values */
85   channels = gst_color_balance_list_channels (GST_COLOR_BALANCE (pipeline));
86   for (l = channels; l != NULL; l = l->next) {
87     GstColorBalanceChannel *channel = (GstColorBalanceChannel *)l->data;
88     gint value = gst_color_balance_get_value (GST_COLOR_BALANCE (pipeline), channel);
89     g_print ("%s: %3d%% ", channel->label,
90         100 * (value - channel->min_value) / (channel->max_value - channel->min_value));
91   }
92   g_print ("\n");
93 }
94
95 /* Process keyboard input */
96 static gboolean handle_keyboard (GIOChannel *source, GIOCondition cond, CustomData *data) {
97   gchar *str = NULL;
98
99   if (g_io_channel_read_line (source, &str, NULL, NULL, NULL) != G_IO_STATUS_NORMAL) {
100     return TRUE;
101   }
102
103   switch (g_ascii_tolower (str[0])) {
104   case 'c':
105     update_color_channel ("CONTRAST", g_ascii_isupper (str[0]), GST_COLOR_BALANCE (data->pipeline));
106     break;
107   case 'b':
108     update_color_channel ("BRIGHTNESS", g_ascii_isupper (str[0]), GST_COLOR_BALANCE (data->pipeline));
109     break;
110   case 'h':
111     update_color_channel ("HUE", g_ascii_isupper (str[0]), GST_COLOR_BALANCE (data->pipeline));
112     break;
113   case 's':
114     update_color_channel ("SATURATION", g_ascii_isupper (str[0]), GST_COLOR_BALANCE (data->pipeline));
115     break;
116   case 'q':
117     g_main_loop_quit (data->loop);
118     break;
119   default:
120     break;
121   }
122
123   g_free (str);
124
125   print_current_values (data->pipeline);
126
127   return TRUE;
128 }
129
130 int main(int argc, char *argv[]) {
131   CustomData data;
132   GstStateChangeReturn ret;
133   GIOChannel *io_stdin;
134
135   /* Initialize GStreamer */
136   gst_init (&argc, &argv);
137
138   /* Initialize our data structure */
139   memset (&data, 0, sizeof (data));
140
141   /* Print usage map */
142   g_print (
143     "USAGE: Choose one of the following options, then press enter:\n"
144     " 'C' to increase contrast, 'c' to decrease contrast\n"
145     " 'B' to increase brightness, 'b' to decrease brightness\n"
146     " 'H' to increase hue, 'h' to decrease hue\n"
147     " 'S' to increase saturation, 's' to decrease saturation\n"
148     " 'Q' to quit\n");
149
150   /* Build the pipeline */
151   data.pipeline = gst_parse_launch ("playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm", NULL);
152
153   /* Add a keyboard watch so we get notified of keystrokes */
154 #ifdef G_OS_WIN32
155   io_stdin = g_io_channel_win32_new_fd (fileno (stdin));
156 #else
157   io_stdin = g_io_channel_unix_new (fileno (stdin));
158 #endif
159   g_io_add_watch (io_stdin, G_IO_IN, (GIOFunc)handle_keyboard, &data);
160
161   /* Start playing */
162   ret = gst_element_set_state (data.pipeline, GST_STATE_PLAYING);
163   if (ret == GST_STATE_CHANGE_FAILURE) {
164     g_printerr ("Unable to set the pipeline to the playing state.\n");
165     gst_object_unref (data.pipeline);
166     return -1;
167   }
168   print_current_values (data.pipeline);
169
170   /* Create a GLib Main Loop and set it to run */
171   data.loop = g_main_loop_new (NULL, FALSE);
172   g_main_loop_run (data.loop);
173
174   /* Free resources */
175   g_main_loop_unref (data.loop);
176   g_io_channel_unref (io_stdin);
177   gst_element_set_state (data.pipeline, GST_STATE_NULL);
178   gst_object_unref (data.pipeline);
179   return 0;
180 }
181 ```
182
183 > ![information] If you need help to compile this code, refer to the
184 > **Building the tutorials** section for your platform: [Mac] or
185 > [Windows] or use this specific command on Linux:
186 >
187 > `` gcc playback-tutorial-5.c -o playback-tutorial-5 `pkg-config --cflags --libs gstreamer-1.0 gstreamer-video-1.0` ``
188 >
189 > If you need help to run this code, refer to the **Running the
190 > tutorials** section for your platform: [Mac OS X], [Windows][1], for
191 > [iOS] or for [android].
192 >
193 > 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.
194 >
195 >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.
196 >
197 > Required libraries: `gstreamer-1.0 gstreamer-video-1.0`
198
199 ## Walkthrough
200
201 The `main()` function is fairly simple. A `playbin` pipeline is
202 instantiated and set to run, and a keyboard watch is installed so
203 keystrokes can be monitored.
204
205 ``` c
206 /* Output the current values of all Color Balance channels */
207 static void print_current_values (GstElement *pipeline) {
208   const GList *channels, *l;
209
210   /* Output Color Balance values */
211   channels = gst_color_balance_list_channels (GST_COLOR_BALANCE (pipeline));
212   for (l = channels; l != NULL; l = l->next) {
213     GstColorBalanceChannel *channel = (GstColorBalanceChannel *)l->data;
214     gint value = gst_color_balance_get_value (GST_COLOR_BALANCE (pipeline), channel);
215     g_print ("%s: %3d%% ", channel->label,
216         100 * (value - channel->min_value) / (channel->max_value - channel->min_value));
217   }
218   g_print ("\n");
219 }
220 ```
221
222 This method prints the current value for all channels, and exemplifies
223 how to retrieve the list of channels. This is accomplished through the
224 `gst_color_balance_list_channels()` method. It returns a `GList` which
225 needs to be traversed.
226
227 Each element in the list is a `GstColorBalanceChannel` structure,
228 informing of the channel’s name, minimum value and maximum value.
229 `gst_color_balance_get_value()` can then be called on each channel to
230 retrieve the current value.
231
232 In this example, the minimum and maximum values are used to output the
233 current value as a percentage.
234
235 ``` c
236 /* Process a color balance command */
237 static void update_color_channel (const gchar *channel_name, gboolean increase, GstColorBalance *cb) {
238   gdouble step;
239   gint value;
240   GstColorBalanceChannel *channel = NULL;
241   const GList *channels, *l;
242
243   /* Retrieve the list of channels and locate the requested one */
244   channels = gst_color_balance_list_channels (cb);
245   for (l = channels; l != NULL; l = l->next) {
246     GstColorBalanceChannel *tmp = (GstColorBalanceChannel *)l->data;
247
248     if (g_strrstr (tmp->label, channel_name)) {
249       channel = tmp;
250       break;
251     }
252   }
253   if (!channel)
254     return;
255 ```
256
257 This method locates the specified channel by name and increases or
258 decreases it as requested. Again, the list of channels is retrieved and
259 parsed looking for the channel with the specified name. Obviously, this
260 list could be parsed only once and the pointers to the channels be
261 stored and indexed by something more efficient than a string.
262
263 ``` c
264   /* Change the channel's value */
265   step = 0.1 * (channel->max_value - channel->min_value);
266   value = gst_color_balance_get_value (cb, channel);
267   if (increase) {
268     value = (gint)(value + step);
269     if (value > channel->max_value)
270       value = channel->max_value;
271   } else {
272     value = (gint)(value - step);
273     if (value < channel->min_value)
274       value = channel->min_value;
275   }
276   gst_color_balance_set_value (cb, channel, value);
277 }
278 ```
279
280 The current value for the channel is then retrieved, changed (the
281 increment is proportional to its dynamic range), clamped (to avoid
282 out-of-range values) and set using `gst_color_balance_set_value()`.
283
284 And there is not much more to it. Run the program and observe the effect
285 of changing each of the channels in real time.
286
287 ## Conclusion
288
289 This tutorial has shown how to use the color balance interface.
290 Particularly, it has shown:
291
292   - How to retrieve the list of color available balance channels
293     with `gst_color_balance_list_channels()`
294   - How to manipulate the current value of each channel using
295     `gst_color_balance_get_value()` and `gst_color_balance_set_value()`
296
297 It has been a pleasure having you here, and see you soon\!
298
299
300   [information]: images/icons/emoticons/information.png
301   [Mac]: installing/on-mac-osx.md
302   [Windows]: installing/on-windows.md
303   [Mac OS X]: installing/on-mac-osx.md#building-the-tutorials
304   [1]: installing/on-windows.md#running-the-tutorials
305   [iOS]: installing/for-ios-development.md#building-the-tutorials
306   [android]: installing/for-android-development.md#building-the-tutorials
307   [warning]: images/icons/emoticons/warning.png