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