gstreamer/tests/examples/va/multiple-vpp.c
2021-08-18 09:00:55 +00:00

443 lines
11 KiB
C

#include <stdlib.h>
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/controller/gstinterpolationcontrolsource.h>
#include <gst/controller/gstdirectcontrolbinding.h>
#include <gst/va/gstvadisplay.h>
#define CHANGE_DIR_WITH_EVENT 0
static gint num_buffers = 50;
static gboolean camera = FALSE;
static gboolean randomcb = FALSE;
static gboolean randomdir = FALSE;
static gboolean randomsharpen = FALSE;
static gboolean randomcrop = FALSE;
static GOptionEntry entries[] = {
{"num-buffers", 'n', 0, G_OPTION_ARG_INT, &num_buffers,
"Number of buffers (<= 0 : forever)", "N"},
{"camera", 'c', 0, G_OPTION_ARG_NONE, &camera,
"Use default v4l2src as video source", NULL},
{"random-cb", 'r', 0, G_OPTION_ARG_NONE, &randomcb,
"Change colorbalance randomly every second (if supported)", NULL},
{"random-dir", 'd', 0, G_OPTION_ARG_NONE, &randomdir,
"Change video direction randomly every second (if supported)", NULL},
{"random-sharpen", 's', 0, G_OPTION_ARG_NONE, &randomsharpen,
"Change sharpen filter randomly every second (if supported)", NULL},
{"random-crop", 'p', 0, G_OPTION_ARG_NONE, &randomcrop,
"Change cropping randomly every 150 miliseconds", NULL},
{NULL},
};
struct _app
{
GMainLoop *loop;
GstObject *display;
GstElement *pipeline;
GstElement *vpp;
GstElement *crop;
GMutex mutex;
GstControlSource *sharpen;
gint right, left, top, bottom;
gint ldir, rdir, tdir, bdir;
};
static GstBusSyncReply
context_handler (GstBus * bus, GstMessage * msg, gpointer data)
{
struct _app *app = data;
const gchar *context_type;
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_HAVE_CONTEXT:{
GstContext *context = NULL;
gst_message_parse_have_context (msg, &context);
if (context) {
context_type = gst_context_get_context_type (context);
if (g_strcmp0 (context_type,
GST_VA_DISPLAY_HANDLE_CONTEXT_TYPE_STR) == 0) {
const GstStructure *s = gst_context_get_structure (context);
GstObject *display = NULL;
gst_printerr ("got have context %s from %s: ", context_type,
GST_MESSAGE_SRC_NAME (msg));
gst_structure_get (s, "gst-display", GST_TYPE_OBJECT, &display, NULL);
gst_printerrln ("%s", display ?
GST_OBJECT_NAME (display) : "no gst display");
gst_context_unref (context);
if (display) {
g_mutex_lock (&app->mutex);
gst_object_replace (&app->display, display);
gst_object_unref (display);
g_mutex_unlock (&app->mutex);
}
}
}
gst_message_unref (msg);
return GST_BUS_DROP;
}
case GST_MESSAGE_NEED_CONTEXT:
gst_message_parse_context_type (msg, &context_type);
if (g_strcmp0 (context_type, GST_VA_DISPLAY_HANDLE_CONTEXT_TYPE_STR) == 0) {
GstContext *context;
GstStructure *s;
gst_printerr ("got need context %s from %s: ", context_type,
GST_MESSAGE_SRC_NAME (msg));
g_mutex_lock (&app->mutex);
if (!app->display) {
g_mutex_unlock (&app->mutex);
gst_printerrln ("no gst display yet");
gst_message_unref (msg);
return GST_BUS_DROP;
}
context =
gst_context_new (GST_VA_DISPLAY_HANDLE_CONTEXT_TYPE_STR, TRUE);
s = gst_context_writable_structure (context);
gst_structure_set (s, "gst-display", GST_TYPE_OBJECT, app->display,
NULL);
gst_printerrln ("%s", GST_OBJECT_NAME (app->display));
gst_element_set_context (GST_ELEMENT (GST_MESSAGE_SRC (msg)), context);
gst_context_unref (context);
g_mutex_unlock (&app->mutex);
}
gst_message_unref (msg);
return GST_BUS_DROP;
default:
break;
}
return GST_BUS_PASS;
}
static gboolean
message_handler (GstBus * bus, GstMessage * msg, gpointer data)
{
struct _app *app = data;
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_EOS:
g_main_loop_quit (app->loop);
break;
case GST_MESSAGE_ERROR:{
gchar *debug = NULL;
GError *err = NULL;
gst_message_parse_error (msg, &err, &debug);
gst_printerrln ("GStreamer error: %s\n%s", err->message,
debug ? debug : "");
if (debug)
g_free (debug);
if (err)
g_error_free (err);
g_main_loop_quit (app->loop);
break;
}
default:
break;
}
return TRUE;
}
static void
config_vpp (GstElement * vpp)
{
GParamSpec *pspec;
GObjectClass *g_class = G_OBJECT_GET_CLASS (vpp);
const static gchar *props[] = { "brightness", "hue", "saturation",
"contrast"
};
gfloat max;
guint i;
for (i = 0; i < G_N_ELEMENTS (props); i++) {
pspec = g_object_class_find_property (g_class, props[i]);
if (!pspec)
continue;
max = ((GParamSpecFloat *) pspec)->maximum;
g_object_set (vpp, props[i], max, NULL);
}
}
static gboolean
build_pipeline (struct _app *app)
{
GstElement *src;
GstBus *bus;
GError *err = NULL;
GString *cmd = g_string_new (NULL);
const gchar *source = camera ? "v4l2src" : "videotestsrc";
g_string_printf (cmd, "%s name=src ! tee name=t "
"t. ! queue ! videocrop name=crop ! vapostproc name=vpp ! "
"fpsdisplaysink video-sink=autovideosink "
"t. ! queue ! vapostproc ! timeoverlay ! autovideosink", source);
app->pipeline = gst_parse_launch (cmd->str, &err);
g_string_free (cmd, TRUE);
if (err) {
gst_printerrln ("Couldn't create pipeline: %s", err->message);
g_error_free (err);
return FALSE;
}
if (num_buffers > 0) {
src = gst_bin_get_by_name (GST_BIN (app->pipeline), "src");
g_object_set (src, "num-buffers", num_buffers, NULL);
gst_object_unref (src);
}
app->vpp = gst_bin_get_by_name (GST_BIN (app->pipeline), "vpp");
if (!randomcb && !randomdir && !randomsharpen && !randomcrop)
config_vpp (app->vpp);
app->crop = gst_bin_get_by_name (GST_BIN (app->pipeline), "crop");
bus = gst_pipeline_get_bus (GST_PIPELINE (app->pipeline));
gst_bus_set_sync_handler (bus, context_handler, app, NULL);
gst_bus_add_watch (bus, message_handler, app);
gst_object_unref (bus);
return TRUE;
}
static gboolean
change_cb_randomly (gpointer data)
{
struct _app *app = data;
GstColorBalance *cb;
GList *channels;
if (!GST_COLOR_BALANCE_GET_INTERFACE (app->vpp))
return G_SOURCE_REMOVE;
cb = GST_COLOR_BALANCE (app->vpp);
channels = (GList *) gst_color_balance_list_channels (cb);
for (; channels && channels->data; channels = channels->next) {
GstColorBalanceChannel *channel = channels->data;
gint value =
g_random_int_range (channel->min_value, channel->max_value + 1);
gst_color_balance_set_value (cb, channel, value);
}
return G_SOURCE_CONTINUE;
}
static gboolean
change_dir_randomly (gpointer data)
{
struct _app *app = data;
GObjectClass *g_class = G_OBJECT_GET_CLASS (app->vpp);
GParamSpec *pspec;
pspec = g_object_class_find_property (g_class, "video-direction");
if (!pspec)
return G_SOURCE_REMOVE;
/* choose either sent direction by property or by event */
#if !CHANGE_DIR_WITH_EVENT
{
GEnumClass *enumclass;
guint idx, value;
enumclass = G_PARAM_SPEC_ENUM (pspec)->enum_class;
idx = g_random_int_range (0, enumclass->n_values);
value = enumclass->values[idx].value;
g_object_set (app->vpp, "video-direction", value, NULL);
}
#else
{
GstEvent *event;
guint idx;
static const gchar *orientation[] = {
"rotate-0", "rotate-90", "rotate-180", "rotate-270",
"flip-rotate-0", "flip-rotate-90", "flip-rotate-180", "flip-rotate-270",
"undefined",
};
idx = g_random_int_range (0, G_N_ELEMENTS (orientation));
event = gst_event_new_tag (gst_tag_list_new (GST_TAG_IMAGE_ORIENTATION,
orientation[idx], NULL));
gst_element_send_event (app->pipeline, event);
}
#endif
return G_SOURCE_CONTINUE;
}
static inline GParamSpec *
vpp_has_sharpen (GstElement * vpp)
{
GObjectClass *g_class = G_OBJECT_GET_CLASS (vpp);
return g_object_class_find_property (g_class, "sharpen");
}
static gboolean
change_sharpen_randomly (gpointer data)
{
struct _app *app = data;
GParamSpec *pspec;
gdouble value;
pspec = vpp_has_sharpen (app->vpp);
if (!pspec)
return G_SOURCE_REMOVE;
value = g_random_double_range (G_PARAM_SPEC_FLOAT (pspec)->minimum,
G_PARAM_SPEC_FLOAT (pspec)->maximum);
gst_timed_value_control_source_set (GST_TIMED_VALUE_CONTROL_SOURCE
(app->sharpen), GST_SECOND, value);
return G_SOURCE_CONTINUE;
}
static gboolean
change_crop_randomly (gpointer data)
{
struct _app *app = data;
g_object_set (app->crop, "bottom", app->bottom, "top", app->top, "left",
app->left, "right", app->right, NULL);
app->top += app->tdir;
if (app->top >= 80)
app->tdir = -10;
else if (app->top < 10)
app->tdir = 10;
app->bottom += app->bdir;
if (app->bottom >= 60)
app->bdir = -10;
else if (app->bottom < 10)
app->bdir = 10;
app->left += app->ldir;
if (app->left >= 100)
app->ldir = -10;
else if (app->left < 10)
app->ldir = 10;
app->right += app->rdir;
if (app->right >= 80)
app->rdir = -10;
else if (app->right < 10)
app->rdir = 10;
return G_SOURCE_CONTINUE;
}
static gboolean
parse_arguments (int *argc, char ***argv)
{
GOptionContext *ctxt;
GError *err = NULL;
ctxt = g_option_context_new ("— Multiple VA postprocessors");
g_option_context_add_main_entries (ctxt, entries, NULL);
g_option_context_add_group (ctxt, gst_init_get_option_group ());
if (!g_option_context_parse (ctxt, argc, argv, &err)) {
gst_printerrln ("option parsing failed: %s", err->message);
g_error_free (err);
return FALSE;
}
g_option_context_free (ctxt);
return TRUE;
}
int
main (int argc, char **argv)
{
GstBus *bus;
struct _app app = { NULL, };
int ret = EXIT_FAILURE;
if (!parse_arguments (&argc, &argv))
return EXIT_FAILURE;
g_mutex_init (&app.mutex);
app.loop = g_main_loop_new (NULL, TRUE);
if (!build_pipeline (&app))
goto gst_failed;
if (randomcb)
g_timeout_add_seconds (1, change_cb_randomly, &app);
if (randomdir) {
#if CHANGE_DIR_WITH_EVENT
gst_util_set_object_arg (G_OBJECT (app.vpp), "video-direction", "auto");
#endif
g_timeout_add_seconds (1, change_dir_randomly, &app);
}
if (randomsharpen && vpp_has_sharpen (app.vpp)) {
GstControlBinding *bind;
app.sharpen = gst_interpolation_control_source_new ();
bind = gst_direct_control_binding_new_absolute (GST_OBJECT (app.vpp),
"sharpen", app.sharpen);
gst_object_add_control_binding (GST_OBJECT (app.vpp), bind);
g_object_set (app.sharpen, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL);
change_sharpen_randomly (&app);
g_timeout_add_seconds (1, change_sharpen_randomly, &app);
}
if (randomcrop) {
app.bdir = app.ldir = app.rdir = app.tdir = 10;
g_timeout_add (150, change_crop_randomly, &app);
}
gst_element_set_state (app.pipeline, GST_STATE_PLAYING);
g_main_loop_run (app.loop);
gst_element_set_state (app.pipeline, GST_STATE_NULL);
bus = gst_pipeline_get_bus (GST_PIPELINE (app.pipeline));
gst_bus_remove_watch (bus);
gst_object_unref (bus);
gst_clear_object (&app.display);
ret = EXIT_SUCCESS;
gst_clear_object (&app.vpp);
gst_clear_object (&app.pipeline);
gst_clear_object (&app.sharpen);
gst_clear_object (&app.crop);
gst_failed:
g_mutex_clear (&app.mutex);
g_main_loop_unref (app.loop);
gst_deinit ();
return ret;
}