4 #include <gst/video/video.h>
5 #include <gst/controller/gstinterpolationcontrolsource.h>
6 #include <gst/controller/gstdirectcontrolbinding.h>
7 #include <gst/va/gstvadisplay.h>
9 #define CHANGE_DIR_WITH_EVENT 0
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;
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},
43 GstControlSource *sharpen;
44 gint right, left, top, bottom;
45 gint ldir, rdir, tdir, bdir;
48 static GstBusSyncReply
49 context_handler (GstBus * bus, GstMessage * msg, gpointer data)
51 struct _app *app = data;
52 const gchar *context_type;
54 switch (GST_MESSAGE_TYPE (msg)) {
55 case GST_MESSAGE_HAVE_CONTEXT:{
56 GstContext *context = NULL;
58 gst_message_parse_have_context (msg, &context);
60 context_type = gst_context_get_context_type (context);
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;
67 gst_printerr ("got have context %s from %s: ", context_type,
68 GST_MESSAGE_SRC_NAME (msg));
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);
76 g_mutex_lock (&app->mutex);
77 gst_object_replace (&app->display, display);
78 gst_object_unref (display);
79 g_mutex_unlock (&app->mutex);
84 gst_message_unref (msg);
88 case GST_MESSAGE_NEED_CONTEXT:
89 gst_message_parse_context_type (msg, &context_type);
91 if (g_strcmp0 (context_type, GST_VA_DISPLAY_HANDLE_CONTEXT_TYPE_STR) == 0) {
95 gst_printerr ("got need context %s from %s: ", context_type,
96 GST_MESSAGE_SRC_NAME (msg));
98 g_mutex_lock (&app->mutex);
100 g_mutex_unlock (&app->mutex);
101 gst_printerrln ("no gst display yet");
102 gst_message_unref (msg);
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,
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);
118 gst_message_unref (msg);
130 message_handler (GstBus * bus, GstMessage * msg, gpointer data)
132 struct _app *app = data;
134 switch (GST_MESSAGE_TYPE (msg)) {
135 case GST_MESSAGE_EOS:
136 g_main_loop_quit (app->loop);
138 case GST_MESSAGE_ERROR:{
142 gst_message_parse_error (msg, &err, &debug);
143 gst_printerrln ("GStreamer error: %s\n%s", err->message,
150 g_main_loop_quit (app->loop);
161 config_simple (struct _app *app)
164 GObjectClass *g_class = G_OBJECT_GET_CLASS (app->vpp);
165 const static gchar *props[] = { "brightness", "hue", "saturation",
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);
175 max = ((GParamSpecFloat *) pspec)->maximum;
176 g_object_set (app->vpp, "skin-tone", max, NULL);
182 for (i = 0; i < G_N_ELEMENTS (props); i++) {
183 pspec = g_object_class_find_property (g_class, props[i]);
187 max = ((GParamSpecFloat *) pspec)->maximum;
188 g_object_set (app->vpp, props[i], max, NULL);
193 build_pipeline (struct _app *app)
198 GString *cmd = g_string_new (NULL);
199 const gchar *source = camera ? "v4l2src" : "videotestsrc";
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);
206 app->pipeline = gst_parse_launch (cmd->str, &err);
207 g_string_free (cmd, TRUE);
209 gst_printerrln ("Couldn't create pipeline: %s", err->message);
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);
220 app->vpp = gst_bin_get_by_name (GST_BIN (app->pipeline), "vpp");
221 if (!randomcb && !randomdir && !randomsharpen && !randomcrop)
224 app->crop = gst_bin_get_by_name (GST_BIN (app->pipeline), "crop");
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);
235 change_cb_randomly (gpointer data)
237 struct _app *app = data;
241 if (!GST_COLOR_BALANCE_GET_INTERFACE (app->vpp))
242 return G_SOURCE_REMOVE;
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;
249 g_random_int_range (channel->min_value, channel->max_value + 1);
251 gst_color_balance_set_value (cb, channel, value);
254 return G_SOURCE_CONTINUE;
258 change_dir_randomly (gpointer data)
260 struct _app *app = data;
261 GObjectClass *g_class = G_OBJECT_GET_CLASS (app->vpp);
264 pspec = g_object_class_find_property (g_class, "video-direction");
266 return G_SOURCE_REMOVE;
268 /* choose either sent direction by property or by event */
269 #if !CHANGE_DIR_WITH_EVENT
271 GEnumClass *enumclass;
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;
278 g_object_set (app->vpp, "video-direction", value, NULL);
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",
290 idx = g_random_int_range (0, G_N_ELEMENTS (orientation));
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);
298 return G_SOURCE_CONTINUE;
301 static inline GParamSpec *
302 vpp_has_sharpen (GstElement * vpp)
304 GObjectClass *g_class = G_OBJECT_GET_CLASS (vpp);
305 return g_object_class_find_property (g_class, "sharpen");
309 change_sharpen_randomly (gpointer data)
311 struct _app *app = data;
315 pspec = vpp_has_sharpen (app->vpp);
317 return G_SOURCE_REMOVE;
318 value = g_random_double_range (G_PARAM_SPEC_FLOAT (pspec)->minimum,
319 G_PARAM_SPEC_FLOAT (pspec)->maximum);
321 gst_timed_value_control_source_set (GST_TIMED_VALUE_CONTROL_SOURCE
322 (app->sharpen), GST_SECOND, value);
324 return G_SOURCE_CONTINUE;
328 change_crop_randomly (gpointer data)
330 struct _app *app = data;
332 g_object_set (app->crop, "bottom", app->bottom, "top", app->top, "left",
333 app->left, "right", app->right, NULL);
335 app->top += app->tdir;
338 else if (app->top < 10)
341 app->bottom += app->bdir;
342 if (app->bottom >= 60)
344 else if (app->bottom < 10)
347 app->left += app->ldir;
348 if (app->left >= 100)
350 else if (app->left < 10)
353 app->right += app->rdir;
354 if (app->right >= 80)
356 else if (app->right < 10)
359 return G_SOURCE_CONTINUE;
363 parse_arguments (int *argc, char ***argv)
365 GOptionContext *ctxt;
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 ());
372 if (!g_option_context_parse (ctxt, argc, argv, &err)) {
373 gst_printerrln ("option parsing failed: %s", err->message);
378 g_option_context_free (ctxt);
383 main (int argc, char **argv)
386 struct _app app = { NULL, };
387 int ret = EXIT_FAILURE;
389 if (!parse_arguments (&argc, &argv))
392 g_mutex_init (&app.mutex);
394 app.loop = g_main_loop_new (NULL, TRUE);
396 if (!build_pipeline (&app))
400 g_timeout_add_seconds (1, change_cb_randomly, &app);
403 #if CHANGE_DIR_WITH_EVENT
404 gst_util_set_object_arg (G_OBJECT (app.vpp), "video-direction", "auto");
406 g_timeout_add_seconds (1, change_dir_randomly, &app);
409 if (randomsharpen && vpp_has_sharpen (app.vpp)) {
410 GstControlBinding *bind;
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);
418 change_sharpen_randomly (&app);
419 g_timeout_add_seconds (1, change_sharpen_randomly, &app);
423 app.bdir = app.ldir = app.rdir = app.tdir = 10;
424 g_timeout_add (150, change_crop_randomly, &app);
427 gst_element_set_state (app.pipeline, GST_STATE_PLAYING);
429 g_main_loop_run (app.loop);
431 gst_element_set_state (app.pipeline, GST_STATE_NULL);
433 bus = gst_pipeline_get_bus (GST_PIPELINE (app.pipeline));
434 gst_bus_remove_watch (bus);
435 gst_object_unref (bus);
437 gst_clear_object (&app.display);
441 gst_clear_object (&app.vpp);
442 gst_clear_object (&app.pipeline);
443 gst_clear_object (&app.sharpen);
444 gst_clear_object (&app.crop);
447 g_mutex_clear (&app.mutex);
448 g_main_loop_unref (app.loop);