example: va: Add skin tone enhancement.
[platform/upstream/gstreamer.git] / tests / examples / va / multiple-vpp.c
1 #include <stdlib.h>
2
3 #include <gst/gst.h>
4 #include <gst/video/video.h>
5 #include <gst/controller/gstinterpolationcontrolsource.h>
6 #include <gst/controller/gstdirectcontrolbinding.h>
7 #include <gst/va/gstvadisplay.h>
8
9 #define CHANGE_DIR_WITH_EVENT 0
10
11 static gint num_buffers = 50;
12 static gboolean camera = FALSE;
13 static gboolean randomcb = FALSE;
14 static gboolean randomdir = FALSE;
15 static gboolean randomsharpen = FALSE;
16 static gboolean randomcrop = FALSE;
17
18 static GOptionEntry entries[] = {
19   {"num-buffers", 'n', 0, G_OPTION_ARG_INT, &num_buffers,
20       "Number of buffers (<= 0 : forever)", "N"},
21   {"camera", 'c', 0, G_OPTION_ARG_NONE, &camera,
22       "Use default v4l2src as video source", NULL},
23   {"random-cb", 'r', 0, G_OPTION_ARG_NONE, &randomcb,
24       "Change colorbalance randomly every second (if supported)", NULL},
25   {"random-dir", 'd', 0, G_OPTION_ARG_NONE, &randomdir,
26       "Change video direction randomly every second (if supported)", NULL},
27   {"random-sharpen", 's', 0, G_OPTION_ARG_NONE, &randomsharpen,
28       "Change sharpen filter randomly every second (if supported)", NULL},
29   {"random-crop", 'p', 0, G_OPTION_ARG_NONE, &randomcrop,
30       "Change cropping randomly every 150 miliseconds", NULL},
31   {NULL},
32 };
33
34 struct _app
35 {
36   GMainLoop *loop;
37   GstObject *display;
38   GstElement *pipeline;
39   GstElement *vpp;
40   GstElement *crop;
41   GMutex mutex;
42
43   GstControlSource *sharpen;
44   gint right, left, top, bottom;
45   gint ldir, rdir, tdir, bdir;
46 };
47
48 static GstBusSyncReply
49 context_handler (GstBus * bus, GstMessage * msg, gpointer data)
50 {
51   struct _app *app = data;
52   const gchar *context_type;
53
54   switch (GST_MESSAGE_TYPE (msg)) {
55     case GST_MESSAGE_HAVE_CONTEXT:{
56       GstContext *context = NULL;
57
58       gst_message_parse_have_context (msg, &context);
59       if (context) {
60         context_type = gst_context_get_context_type (context);
61
62         if (g_strcmp0 (context_type,
63                 GST_VA_DISPLAY_HANDLE_CONTEXT_TYPE_STR) == 0) {
64           const GstStructure *s = gst_context_get_structure (context);
65           GstObject *display = NULL;
66
67           gst_printerr ("got have context %s from %s: ", context_type,
68               GST_MESSAGE_SRC_NAME (msg));
69
70           gst_structure_get (s, "gst-display", GST_TYPE_OBJECT, &display, NULL);
71           gst_printerrln ("%s", display ?
72               GST_OBJECT_NAME (display) : "no gst display");
73           gst_context_unref (context);
74
75           if (display) {
76             g_mutex_lock (&app->mutex);
77             gst_object_replace (&app->display, display);
78             gst_object_unref (display);
79             g_mutex_unlock (&app->mutex);
80           }
81         }
82       }
83
84       gst_message_unref (msg);
85
86       return GST_BUS_DROP;
87     }
88     case GST_MESSAGE_NEED_CONTEXT:
89       gst_message_parse_context_type (msg, &context_type);
90
91       if (g_strcmp0 (context_type, GST_VA_DISPLAY_HANDLE_CONTEXT_TYPE_STR) == 0) {
92         GstContext *context;
93         GstStructure *s;
94
95         gst_printerr ("got need context %s from %s: ", context_type,
96             GST_MESSAGE_SRC_NAME (msg));
97
98         g_mutex_lock (&app->mutex);
99         if (!app->display) {
100           g_mutex_unlock (&app->mutex);
101           gst_printerrln ("no gst display yet");
102           gst_message_unref (msg);
103           return GST_BUS_DROP;
104         }
105
106         context =
107             gst_context_new (GST_VA_DISPLAY_HANDLE_CONTEXT_TYPE_STR, TRUE);
108         s = gst_context_writable_structure (context);
109         gst_structure_set (s, "gst-display", GST_TYPE_OBJECT, app->display,
110             NULL);
111         gst_printerrln ("%s", GST_OBJECT_NAME (app->display));
112         gst_element_set_context (GST_ELEMENT (GST_MESSAGE_SRC (msg)), context);
113         gst_context_unref (context);
114         g_mutex_unlock (&app->mutex);
115
116       }
117
118       gst_message_unref (msg);
119
120       return GST_BUS_DROP;
121
122     default:
123       break;
124   }
125
126   return GST_BUS_PASS;
127 }
128
129 static gboolean
130 message_handler (GstBus * bus, GstMessage * msg, gpointer data)
131 {
132   struct _app *app = data;
133
134   switch (GST_MESSAGE_TYPE (msg)) {
135     case GST_MESSAGE_EOS:
136       g_main_loop_quit (app->loop);
137       break;
138     case GST_MESSAGE_ERROR:{
139       gchar *debug = NULL;
140       GError *err = NULL;
141
142       gst_message_parse_error (msg, &err, &debug);
143       gst_printerrln ("GStreamer error: %s\n%s", err->message,
144           debug ? debug : "");
145       if (debug)
146         g_free (debug);
147       if (err)
148         g_error_free (err);
149
150       g_main_loop_quit (app->loop);
151       break;
152     }
153     default:
154       break;
155   }
156
157   return TRUE;
158 }
159
160 static void
161 config_simple (struct _app *app)
162 {
163   GParamSpec *pspec;
164   GObjectClass *g_class = G_OBJECT_GET_CLASS (app->vpp);
165   const static gchar *props[] = { "brightness", "hue", "saturation",
166     "contrast"
167   };
168   gfloat max;
169   guint i;
170
171   if (camera && (pspec = g_object_class_find_property (g_class, "skin-tone"))) {
172     if (G_PARAM_SPEC_TYPE (pspec) == G_TYPE_BOOLEAN) {
173       g_object_set (app->vpp, "skin-tone", TRUE, NULL);
174     } else {
175       max = ((GParamSpecFloat *) pspec)->maximum;
176       g_object_set (app->vpp, "skin-tone", max, NULL);
177     }
178
179     return;
180   }
181
182   for (i = 0; i < G_N_ELEMENTS (props); i++) {
183     pspec = g_object_class_find_property (g_class, props[i]);
184     if (!pspec)
185       continue;
186
187     max = ((GParamSpecFloat *) pspec)->maximum;
188     g_object_set (app->vpp, props[i], max, NULL);
189   }
190 }
191
192 static gboolean
193 build_pipeline (struct _app *app)
194 {
195   GstElement *src;
196   GstBus *bus;
197   GError *err = NULL;
198   GString *cmd = g_string_new (NULL);
199   const gchar *source = camera ? "v4l2src" : "videotestsrc";
200
201   g_string_printf (cmd, "%s name=src ! tee name=t "
202       "t. ! queue ! videocrop name=crop ! vapostproc name=vpp ! "
203       "fpsdisplaysink video-sink=autovideosink "
204       "t. ! queue ! vapostproc ! timeoverlay ! autovideosink", source);
205
206   app->pipeline = gst_parse_launch (cmd->str, &err);
207   g_string_free (cmd, TRUE);
208   if (err) {
209     gst_printerrln ("Couldn't create pipeline: %s", err->message);
210     g_error_free (err);
211     return FALSE;
212   }
213
214   if (num_buffers > 0) {
215     src = gst_bin_get_by_name (GST_BIN (app->pipeline), "src");
216     g_object_set (src, "num-buffers", num_buffers, NULL);
217     gst_object_unref (src);
218   }
219
220   app->vpp = gst_bin_get_by_name (GST_BIN (app->pipeline), "vpp");
221   if (!randomcb && !randomdir && !randomsharpen && !randomcrop)
222     config_simple (app);
223
224   app->crop = gst_bin_get_by_name (GST_BIN (app->pipeline), "crop");
225
226   bus = gst_pipeline_get_bus (GST_PIPELINE (app->pipeline));
227   gst_bus_set_sync_handler (bus, context_handler, app, NULL);
228   gst_bus_add_watch (bus, message_handler, app);
229   gst_object_unref (bus);
230
231   return TRUE;
232 }
233
234 static gboolean
235 change_cb_randomly (gpointer data)
236 {
237   struct _app *app = data;
238   GstColorBalance *cb;
239   GList *channels;
240
241   if (!GST_COLOR_BALANCE_GET_INTERFACE (app->vpp))
242     return G_SOURCE_REMOVE;
243
244   cb = GST_COLOR_BALANCE (app->vpp);
245   channels = (GList *) gst_color_balance_list_channels (cb);
246   for (; channels && channels->data; channels = channels->next) {
247     GstColorBalanceChannel *channel = channels->data;
248     gint value =
249         g_random_int_range (channel->min_value, channel->max_value + 1);
250
251     gst_color_balance_set_value (cb, channel, value);
252   }
253
254   return G_SOURCE_CONTINUE;
255 }
256
257 static gboolean
258 change_dir_randomly (gpointer data)
259 {
260   struct _app *app = data;
261   GObjectClass *g_class = G_OBJECT_GET_CLASS (app->vpp);
262   GParamSpec *pspec;
263
264   pspec = g_object_class_find_property (g_class, "video-direction");
265   if (!pspec)
266     return G_SOURCE_REMOVE;
267
268   /* choose either sent direction by property or by event */
269 #if !CHANGE_DIR_WITH_EVENT
270   {
271     GEnumClass *enumclass;
272     guint idx, value;
273
274     enumclass = G_PARAM_SPEC_ENUM (pspec)->enum_class;
275     idx = g_random_int_range (0, enumclass->n_values);
276     value = enumclass->values[idx].value;
277
278     g_object_set (app->vpp, "video-direction", value, NULL);
279   }
280 #else
281   {
282     GstEvent *event;
283     guint idx;
284     static const gchar *orientation[] = {
285       "rotate-0", "rotate-90", "rotate-180", "rotate-270",
286       "flip-rotate-0", "flip-rotate-90", "flip-rotate-180", "flip-rotate-270",
287       "undefined",
288     };
289
290     idx = g_random_int_range (0, G_N_ELEMENTS (orientation));
291
292     event = gst_event_new_tag (gst_tag_list_new (GST_TAG_IMAGE_ORIENTATION,
293             orientation[idx], NULL));
294     gst_element_send_event (app->pipeline, event);
295   }
296 #endif
297
298   return G_SOURCE_CONTINUE;
299 }
300
301 static inline GParamSpec *
302 vpp_has_sharpen (GstElement * vpp)
303 {
304   GObjectClass *g_class = G_OBJECT_GET_CLASS (vpp);
305   return g_object_class_find_property (g_class, "sharpen");
306 }
307
308 static gboolean
309 change_sharpen_randomly (gpointer data)
310 {
311   struct _app *app = data;
312   GParamSpec *pspec;
313   gdouble value;
314
315   pspec = vpp_has_sharpen (app->vpp);
316   if (!pspec)
317     return G_SOURCE_REMOVE;
318   value = g_random_double_range (G_PARAM_SPEC_FLOAT (pspec)->minimum,
319       G_PARAM_SPEC_FLOAT (pspec)->maximum);
320
321   gst_timed_value_control_source_set (GST_TIMED_VALUE_CONTROL_SOURCE
322       (app->sharpen), GST_SECOND, value);
323
324   return G_SOURCE_CONTINUE;
325 }
326
327 static gboolean
328 change_crop_randomly (gpointer data)
329 {
330   struct _app *app = data;
331
332   g_object_set (app->crop, "bottom", app->bottom, "top", app->top, "left",
333       app->left, "right", app->right, NULL);
334
335   app->top += app->tdir;
336   if (app->top >= 80)
337     app->tdir = -10;
338   else if (app->top < 10)
339     app->tdir = 10;
340
341   app->bottom += app->bdir;
342   if (app->bottom >= 60)
343     app->bdir = -10;
344   else if (app->bottom < 10)
345     app->bdir = 10;
346
347   app->left += app->ldir;
348   if (app->left >= 100)
349     app->ldir = -10;
350   else if (app->left < 10)
351     app->ldir = 10;
352
353   app->right += app->rdir;
354   if (app->right >= 80)
355     app->rdir = -10;
356   else if (app->right < 10)
357     app->rdir = 10;
358
359   return G_SOURCE_CONTINUE;
360 }
361
362 static gboolean
363 parse_arguments (int *argc, char ***argv)
364 {
365   GOptionContext *ctxt;
366   GError *err = NULL;
367
368   ctxt = g_option_context_new ("— Multiple VA postprocessors");
369   g_option_context_add_main_entries (ctxt, entries, NULL);
370   g_option_context_add_group (ctxt, gst_init_get_option_group ());
371
372   if (!g_option_context_parse (ctxt, argc, argv, &err)) {
373     gst_printerrln ("option parsing failed: %s", err->message);
374     g_error_free (err);
375     return FALSE;
376   }
377
378   g_option_context_free (ctxt);
379   return TRUE;
380 }
381
382 int
383 main (int argc, char **argv)
384 {
385   GstBus *bus;
386   struct _app app = { NULL, };
387   int ret = EXIT_FAILURE;
388
389   if (!parse_arguments (&argc, &argv))
390     return EXIT_FAILURE;
391
392   g_mutex_init (&app.mutex);
393
394   app.loop = g_main_loop_new (NULL, TRUE);
395
396   if (!build_pipeline (&app))
397     goto gst_failed;
398
399   if (randomcb)
400     g_timeout_add_seconds (1, change_cb_randomly, &app);
401
402   if (randomdir) {
403 #if CHANGE_DIR_WITH_EVENT
404     gst_util_set_object_arg (G_OBJECT (app.vpp), "video-direction", "auto");
405 #endif
406     g_timeout_add_seconds (1, change_dir_randomly, &app);
407   }
408
409   if (randomsharpen && vpp_has_sharpen (app.vpp)) {
410     GstControlBinding *bind;
411
412     app.sharpen = gst_interpolation_control_source_new ();
413     bind = gst_direct_control_binding_new_absolute (GST_OBJECT (app.vpp),
414         "sharpen", app.sharpen);
415     gst_object_add_control_binding (GST_OBJECT (app.vpp), bind);
416     g_object_set (app.sharpen, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL);
417
418     change_sharpen_randomly (&app);
419     g_timeout_add_seconds (1, change_sharpen_randomly, &app);
420   }
421
422   if (randomcrop) {
423     app.bdir = app.ldir = app.rdir = app.tdir = 10;
424     g_timeout_add (150, change_crop_randomly, &app);
425   }
426
427   gst_element_set_state (app.pipeline, GST_STATE_PLAYING);
428
429   g_main_loop_run (app.loop);
430
431   gst_element_set_state (app.pipeline, GST_STATE_NULL);
432
433   bus = gst_pipeline_get_bus (GST_PIPELINE (app.pipeline));
434   gst_bus_remove_watch (bus);
435   gst_object_unref (bus);
436
437   gst_clear_object (&app.display);
438
439   ret = EXIT_SUCCESS;
440
441   gst_clear_object (&app.vpp);
442   gst_clear_object (&app.pipeline);
443   gst_clear_object (&app.sharpen);
444   gst_clear_object (&app.crop);
445
446 gst_failed:
447   g_mutex_clear (&app.mutex);
448   g_main_loop_unref (app.loop);
449
450   gst_deinit ();
451
452   return ret;
453 }