va: add multiple-vpp example
authorVíctor Manuel Jáquez Leal <vjaquez@igalia.com>
Sat, 19 Sep 2020 19:39:06 +0000 (21:39 +0200)
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Wed, 17 Feb 2021 13:18:15 +0000 (13:18 +0000)
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/2015>

tests/examples/va/meson.build
tests/examples/va/multiple-vpp.c [new file with mode: 0644]

index 1909956..c716eb4 100644 (file)
@@ -16,3 +16,11 @@ if have_va and gtk_dep.found() and gtk_x11_dep.found() and x11_dep.found() and l
     c_args : gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API'],
   )
 endif
+
+executable('multiple-vpp',
+  'multiple-vpp.c',
+  install: false,
+  include_directories : [configinc],
+  dependencies : [gst_dep],
+  c_args : gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API'],
+)
diff --git a/tests/examples/va/multiple-vpp.c b/tests/examples/va/multiple-vpp.c
new file mode 100644 (file)
index 0000000..c761f0b
--- /dev/null
@@ -0,0 +1,258 @@
+#include <stdlib.h>
+
+#include <gst/gst.h>
+
+static gint num_buffers = 50;
+static gboolean camera = 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 v4l2src as video source",
+      NULL},
+  {NULL},
+};
+
+struct _app
+{
+  GMainLoop *loop;
+  GstObject *display;
+  GstElement *pipeline;
+  GstElement *caps;
+  GMutex mutex;
+};
+
+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") == 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") == 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", 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 *vpp, *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 ! vapostproc name=vpp ! capsfilter name=caps ! 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);
+  }
+
+  vpp = gst_bin_get_by_name (GST_BIN (app->pipeline), "vpp");
+  config_vpp (vpp);
+  gst_object_unref (vpp);
+
+  app->caps = gst_bin_get_by_name (GST_BIN (app->pipeline), "caps");
+
+  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
+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;
+
+  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.caps);
+  gst_clear_object (&app.pipeline);
+
+gst_failed:
+  g_mutex_clear (&app.mutex);
+  g_main_loop_unref (app.loop);
+
+  gst_deinit ();
+
+  return ret;
+}