summaryrefslogtreecommitdiff
path: root/tests/examples
diff options
context:
space:
mode:
authorU. Artie Eoff <ullysses.a.eoff@intel.com>2019-10-08 12:19:06 -0700
committerU. Artie Eoff <ullysses.a.eoff@intel.com>2019-10-16 12:48:24 -0700
commit97aabe8784c057a4bee33d6f541ab7d75ca3708e (patch)
treeba1cf34ee77bd61bc9bf8844379302fb0d609ebe /tests/examples
parent8af5ef8a0be0e5a6d97b6f8e9c942331d065e30a (diff)
tests: move examples and tests to subfolders
This makes way for adding unit (check) tests.
Diffstat (limited to 'tests/examples')
-rw-r--r--tests/examples/meson.build28
-rw-r--r--tests/examples/test-roi.c261
-rw-r--r--tests/examples/test-vaapicontext.c354
-rw-r--r--tests/examples/test-vaapipostproc.c157
-rw-r--r--tests/examples/test-vaapisink.c189
5 files changed, 989 insertions, 0 deletions
diff --git a/tests/examples/meson.build b/tests/examples/meson.build
new file mode 100644
index 00000000..0a61f7d0
--- /dev/null
+++ b/tests/examples/meson.build
@@ -0,0 +1,28 @@
+examples = [
+ 'test-vaapisink',
+ 'test-vaapipostproc',
+ 'test-roi',
+]
+
+foreach example : examples
+ executable(example, '@0@.c'.format(example),
+ c_args : gstreamer_vaapi_args,
+ include_directories: [configinc, libsinc],
+ dependencies : [gst_dep, gstvideo_dep],
+ install: false)
+endforeach
+
+if USE_X11
+if gtk_dep.found()
+ executable('test-vaapicontext', 'test-vaapicontext.c',
+ c_args : gstreamer_vaapi_args,
+ include_directories: [configinc, libsinc],
+ dependencies : [ gst_dep,
+ gstvideo_dep,
+ libva_dep,
+ x11_dep,
+ gtk_dep,
+ libva_x11_dep ],
+ install: false)
+endif
+endif
diff --git a/tests/examples/test-roi.c b/tests/examples/test-roi.c
new file mode 100644
index 00000000..2ce614ef
--- /dev/null
+++ b/tests/examples/test-roi.c
@@ -0,0 +1,261 @@
+/*
+ * test-roi.c - Testsuite for Region of Interest
+ *
+ * Copyright (C) 2017 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <gst/gst.h>
+#include <gst/video/navigation.h>
+#include <gst/video/gstvideometa.h>
+
+typedef struct _CustomData
+{
+ GstElement *pipeline;
+ GMainLoop *loop;
+ gboolean roi_enabled;
+} AppData;
+
+static void
+send_eos_event (AppData * data)
+{
+ gst_element_send_event (data->pipeline, gst_event_new_eos ());
+}
+
+static void
+dispatch_keystroke (AppData * app, const gchar * str)
+{
+ switch (g_ascii_tolower (str[0])) {
+ case 'r':
+ app->roi_enabled = !app->roi_enabled;
+ gst_println ("ROI %s", app->roi_enabled ? "enabled" : "disabled");
+ break;
+ case 'q':
+ send_eos_event (app);
+ break;
+ default:
+ break;
+ }
+
+ return;
+}
+
+static void
+cb_msg (GstBus * bus, GstMessage * msg, gpointer data)
+{
+ AppData *app = data;
+ GstNavigationMessageType mtype = gst_navigation_message_get_type (msg);
+ GstEvent *ev = NULL;
+ GstNavigationEventType type;
+ const gchar *key;
+
+ if (mtype != GST_NAVIGATION_MESSAGE_EVENT)
+ return;
+ if (!gst_navigation_message_parse_event (msg, &ev))
+ goto bail;
+
+ type = gst_navigation_event_get_type (ev);
+ if (type != GST_NAVIGATION_EVENT_KEY_PRESS)
+ goto bail;
+ if (!gst_navigation_event_parse_key_event (ev, &key))
+ goto bail;
+
+ dispatch_keystroke (app, key);
+
+bail:
+ if (ev)
+ gst_event_unref (ev);
+}
+
+static void
+cb_msg_eos (GstBus * bus, GstMessage * msg, gpointer data)
+{
+ AppData *app = data;
+ g_main_loop_quit (app->loop);
+}
+
+
+static void
+cb_msg_error (GstBus * bus, GstMessage * msg, gpointer data)
+{
+ AppData *app = data;
+ gchar *debug = NULL;
+ GError *err = NULL;
+
+ gst_message_parse_error (msg, &err, &debug);
+
+ g_print ("Error: %s\n", err->message);
+ g_error_free (err);
+
+ if (debug) {
+ g_print ("Debug details: %s\n", debug);
+ g_free (debug);
+ }
+
+ g_main_loop_quit (app->loop);
+}
+
+static GstPadProbeReturn
+cb_add_roi (GstPad * pad, GstPadProbeInfo * info, gpointer data)
+{
+ AppData *app = data;
+ GstVideoRegionOfInterestMeta *rmeta;
+ GstBuffer *buf = GST_PAD_PROBE_INFO_BUFFER (info);
+ GstStructure *s;
+
+ if (!app->roi_enabled)
+ return GST_PAD_PROBE_OK;
+
+ buf = gst_buffer_make_writable (buf);
+ if (!buf)
+ return GST_PAD_PROBE_OK;
+
+ rmeta =
+ gst_buffer_add_video_region_of_interest_meta (buf, "test", 0, 0, 320,
+ 240);
+ if (!rmeta)
+ return GST_PAD_PROBE_OK;
+
+ s = gst_structure_new ("roi/vaapi", "delta-qp", G_TYPE_INT, -10, NULL);
+ gst_video_region_of_interest_meta_add_param (rmeta, s);
+
+ GST_PAD_PROBE_INFO_DATA (info) = buf;
+ return GST_PAD_PROBE_OK;
+}
+
+/* Process keyboard input */
+static gboolean
+handle_keyboard (GIOChannel * source, GIOCondition cond, gpointer data)
+{
+ AppData *app = data;
+ gchar *str = NULL;
+
+ if (g_io_channel_read_line (source, &str, NULL, NULL,
+ NULL) != G_IO_STATUS_NORMAL) {
+ return TRUE;
+ }
+
+ dispatch_keystroke (app, str);
+
+ g_free (str);
+ return TRUE;
+}
+
+/*
+ * This is an example pipeline to recognize difference between ROI and non-ROI.
+ * 1. Produce snow pattern with 320p
+ * 2. Encode and decode the raw data with 2 pipelines at same time.
+ * 2.1. Insert GstVideoRegionOfInterestMeta to the 2nd pipeline buffers to enable ROI.
+ * 3. Mix both streams in videomixer.
+ * 5. Output the result in one window.
+ *
+ * Note that the higher definition of original raw data, the easier we
+ * recognize. So you can replace videotestsrc with your
+ * high-definition camera or other src elements.
+ */
+
+/*
+.----------. .---. .--------. .---. .---. .---. .--------. .----------. .-----.
+| videosrc |->|tee|->Q->|txtovrly|->|enc|->|dec|->|vpp|->|videobox|->|videomixer|->|vsink|
+'----------' '---' '--------' '---' '---' '---' '--------' '----------' '-----'
+ ^ ^
+ | |
+ | .--------. .---. .---. .---. .--------. |
+ '--->Q->|txtovrly|->|enc|->|dec|->|vpp|->|videobox|->'
+ ^ '--------' '---' '---' '---' '--------'
+ |
+ '-- Insert GstVideoRegionOfInterestMeta width roit/vaapi params on buffers
+*/
+
+int
+main (int argc, char *argv[])
+{
+ AppData data = { 0, };
+ GstStateChangeReturn ret;
+ GstElement *el;
+ GstPad *pad;
+ GError *err = NULL;
+ GIOChannel *io_stdin;
+ GstBus *bus;
+
+ data.roi_enabled = TRUE;
+
+ /* Initialize GStreamer */
+ gst_init (&argc, &argv);
+
+ /* Print usage map */
+ g_print ("USAGE: 'r' to enable/disable ROI && 'q' to quit\n");
+
+#define SRC "videotestsrc pattern=snow ! " \
+ "video/x-raw, format=NV12, width=320, framerate=5/1"
+#define ENCDEC "vaapih265enc rate-control=cbr bitrate=2000 ! vaapih265dec ! " \
+ "vaapipostproc ! video/x-raw, width=640"
+#define TEXT "textoverlay font-desc=\"Arial Bold 48\" "
+
+ data.pipeline =
+ gst_parse_launch
+ ("videomixer name=mix ! vaapipostproc ! vaapisink sync=false "
+ SRC " ! tee name=t ! queue ! " TEXT " text=\"non-ROI\" ! " ENCDEC
+ " ! videobox left=-640 ! mix. "
+ " t. ! queue name=roi ! " TEXT " text=\"ROI\" ! " ENCDEC
+ " ! videobox ! mix.", &err);
+
+ if (err) {
+ g_printerr ("failed to parse pipeline: %s\n", err->message);
+ g_error_free (err);
+ return -1;
+ }
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (data.pipeline));
+ gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
+ gst_bus_enable_sync_message_emission (bus);
+ g_signal_connect (bus, "message::error", G_CALLBACK (cb_msg_error), &data);
+ g_signal_connect (bus, "message::eos", G_CALLBACK (cb_msg_eos), &data);
+ g_signal_connect (bus, "message::element", G_CALLBACK (cb_msg), &data);
+ gst_object_unref (bus);
+
+ el = gst_bin_get_by_name (GST_BIN (data.pipeline), "roi");
+ pad = gst_element_get_static_pad (el, "src");
+ gst_object_unref (el);
+ gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER, cb_add_roi, &data, NULL);
+ gst_object_unref (pad);
+
+ /* Add a keyboard watch so we get notified of keystrokes */
+ io_stdin = g_io_channel_unix_new (fileno (stdin));
+ g_io_add_watch (io_stdin, G_IO_IN, handle_keyboard, &data);
+
+ /* Start playing */
+ ret = gst_element_set_state (data.pipeline, GST_STATE_PLAYING);
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+ g_printerr ("Unable to set the pipeline to the playing state.\n");
+ gst_object_unref (data.pipeline);
+ return -1;
+ }
+
+ /* Create a GLib Main Loop and set it to run */
+ data.loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (data.loop);
+
+ /* Free resources */
+ g_main_loop_unref (data.loop);
+ gst_element_set_state (data.pipeline, GST_STATE_NULL);
+ gst_object_unref (data.pipeline);
+ g_io_channel_unref (io_stdin);
+
+ return 0;
+}
diff --git a/tests/examples/test-vaapicontext.c b/tests/examples/test-vaapicontext.c
new file mode 100644
index 00000000..0085ca96
--- /dev/null
+++ b/tests/examples/test-vaapicontext.c
@@ -0,0 +1,354 @@
+/*
+ * test-vaapicontext.c - Testsuite for VAAPI app context
+ *
+ * Copyright (C) 2017 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#include <gst/gst.h>
+#include <gst/video/videooverlay.h>
+
+#include <va/va.h>
+#include <gtk/gtk.h>
+
+#include <X11/Xlib.h>
+#include <va/va_x11.h>
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#else
+#error "X11 is not supported in GTK+"
+#endif
+
+static gboolean g_multisink;
+static gchar *g_filepath;
+
+static GOptionEntry g_options[] = {
+ {"multi", 'm', 0, G_OPTION_ARG_NONE, &g_multisink, "test multiple vaapisink",
+ NULL},
+ {"file", 'f', 0, G_OPTION_ARG_STRING, &g_filepath,
+ "file path to play (only mp4/h264)", NULL},
+ {NULL,}
+};
+
+typedef struct _CustomData
+{
+ GtkWidget *main_window;
+ VADisplay va_display;
+ GstElement *pipeline;
+ guintptr videoarea_handle[2];
+ GstObject *gstvaapidisplay;
+} AppData;
+
+static void
+delete_event_cb (GtkWidget * widget, GdkEvent * event, gpointer data)
+{
+ AppData *app = data;
+
+ gst_element_set_state (app->pipeline, GST_STATE_NULL);
+ gtk_main_quit ();
+}
+
+static void
+button_rotate_cb (GtkWidget * widget, GstElement * elem)
+{
+ static gint counter = 0;
+ const static gint tags[] = { 90, 180, 270, 0 };
+
+ g_object_set (elem, "rotation", tags[counter++ % G_N_ELEMENTS (tags)], NULL);
+}
+
+static Display *
+get_x11_window_display (AppData * app)
+{
+#if defined(GDK_WINDOWING_X11)
+ GdkDisplay *gdk_display;
+ Display *x11_display;
+
+ gdk_display = gtk_widget_get_display (app->main_window);
+ x11_display = gdk_x11_display_get_xdisplay (gdk_display);
+ return x11_display;
+#endif
+ g_error ("Running in a non-X11 environment");
+}
+
+static VADisplay
+ensure_va_display (AppData * app)
+{
+ if (app->va_display)
+ return app->va_display;
+ app->va_display = vaGetDisplay (get_x11_window_display (app));
+ /* There's no need to call vaInitialize() since element does it
+ * internally */
+ return app->va_display;
+}
+
+static GstContext *
+create_vaapi_app_display_context (AppData * app, gboolean new_va_display)
+{
+ GstContext *context;
+ GstStructure *s;
+ VADisplay va_display;
+ Display *x11_display;
+
+ x11_display = get_x11_window_display (app);
+
+ if (new_va_display)
+ va_display = vaGetDisplay (x11_display);
+ else
+ va_display = ensure_va_display (app);
+
+ context = gst_context_new ("gst.vaapi.app.Display", FALSE);
+ s = gst_context_writable_structure (context);
+ gst_structure_set (s, "va-display", G_TYPE_POINTER, va_display, NULL);
+ gst_structure_set (s, "x11-display", G_TYPE_POINTER, x11_display, NULL);
+
+ return context;
+}
+
+static GstBusSyncReply
+bus_sync_handler (GstBus * bus, GstMessage * msg, gpointer data)
+{
+ AppData *app = data;
+
+ switch (GST_MESSAGE_TYPE (msg)) {
+ case GST_MESSAGE_NEED_CONTEXT:{
+ const gchar *context_type;
+ gboolean new_va_disp;
+ GstContext *context;
+
+ gst_message_parse_context_type (msg, &context_type);
+ gst_println ("Got need context %s from %s", context_type,
+ GST_MESSAGE_SRC_NAME (msg));
+
+ if (g_strcmp0 (context_type, "gst.vaapi.Display") == 0) {
+ if (app->gstvaapidisplay) {
+ GstStructure *s;
+
+ context = gst_context_new ("gst.vaapi.Display", FALSE);
+ s = gst_context_writable_structure (context);
+ gst_structure_set (s, "gst.vaapi.Display",
+ GST_TYPE_OBJECT, app->gstvaapidisplay, NULL);
+ }
+ break;
+ }
+
+ if (g_strcmp0 (context_type, "gst.vaapi.app.Display") != 0)
+ break;
+
+ /* create a new VA display *only* for the second video sink */
+ new_va_disp = (g_strcmp0 (GST_MESSAGE_SRC_NAME (msg), "sink2") == 0);
+
+ context = create_vaapi_app_display_context (app, new_va_disp);
+ gst_element_set_context (GST_ELEMENT (GST_MESSAGE_SRC (msg)), context);
+ gst_context_unref (context);
+ break;
+ }
+ case GST_MESSAGE_ELEMENT:{
+ if (!gst_is_video_overlay_prepare_window_handle_message (msg))
+ break;
+
+ if (g_strcmp0 (GST_MESSAGE_SRC_NAME (msg), "sink2") == 0)
+ gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY
+ (GST_MESSAGE_SRC (msg)), app->videoarea_handle[1]);
+ else
+ gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY
+ (GST_MESSAGE_SRC (msg)), app->videoarea_handle[0]);
+ break;
+ }
+ case GST_MESSAGE_HAVE_CONTEXT:{
+ const gchar *context_type;
+ const GstStructure *s;
+ GstContext *context = NULL;
+ const GValue *value;
+
+ gst_message_parse_have_context (msg, &context);
+ if (!context)
+ break;
+
+ context_type = gst_context_get_context_type (context);
+ gst_println ("Got have context %s from %s", context_type,
+ GST_MESSAGE_SRC_NAME (msg));
+
+ if (g_strcmp0 (context_type, "gst.vaapi.Display") != 0)
+ break;
+ s = gst_context_get_structure (context);
+ if (!s)
+ break;
+ value = gst_structure_get_value (s, "gst.vaapi.Display");
+ if (!value)
+ break;
+ app->gstvaapidisplay = g_value_dup_object (value);
+ gst_println ("found display %s", GST_OBJECT_NAME (app->gstvaapidisplay));
+ break;
+ }
+ case GST_MESSAGE_EOS:
+ gtk_main_quit ();
+ break;
+ default:
+ break;
+ }
+
+ return GST_BUS_PASS;
+}
+
+static void
+realize_cb (GtkWidget * widget, gpointer data)
+{
+ AppData *app = data;
+ GdkWindow *window;
+ static guint counter = 0;
+
+#if defined(GDK_WINDOWING_X11)
+ window = gtk_widget_get_window (widget);
+
+ if (!gdk_window_ensure_native (window))
+ g_error ("Couldn't create native window needed for GstXOverlay!");
+
+ app->videoarea_handle[counter++ % 2] = GDK_WINDOW_XID (window);
+#endif
+}
+
+static GtkWidget *
+create_video_box (AppData * app)
+{
+ GtkWidget *video_area;
+
+ video_area = gtk_drawing_area_new ();
+ gtk_widget_set_size_request (video_area, 640, 480);
+ g_signal_connect (video_area, "realize", G_CALLBACK (realize_cb), app);
+ return video_area;
+}
+
+static GtkWidget *
+create_rotate_button (AppData * app, const gchar * name)
+{
+ GtkWidget *rotate;
+ GstElement *sink;
+
+ sink = gst_bin_get_by_name (GST_BIN (app->pipeline), name);
+ g_assert (sink);
+
+ rotate = gtk_button_new_with_label ("Rotate");
+ g_signal_connect (rotate, "clicked", G_CALLBACK (button_rotate_cb), sink);
+ gst_object_unref (sink);
+
+ return rotate;
+}
+
+static void
+build_ui (AppData * app)
+{
+ GtkWidget *mainwin, *vbox, *pane, *bbox;
+
+ mainwin = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (mainwin), "VAAPI display context test");
+ gtk_window_set_resizable (GTK_WINDOW (mainwin), FALSE);
+ g_signal_connect (mainwin, "delete-event", G_CALLBACK (delete_event_cb), app);
+ app->main_window = mainwin;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (mainwin), vbox);
+
+ pane = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_box_pack_start (GTK_BOX (vbox), pane, TRUE, TRUE, 0);
+
+ /* first video box */
+ gtk_paned_pack1 (GTK_PANED (pane), create_video_box (app), TRUE, TRUE);
+
+ /* rotate buttons */
+ bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_SPREAD);
+ gtk_box_pack_end (GTK_BOX (vbox), bbox, TRUE, TRUE, 0);
+
+ gtk_box_pack_start (GTK_BOX (bbox), create_rotate_button (app, "sink1"), TRUE,
+ TRUE, 0);
+
+ if (g_multisink) {
+ /* second video box */
+ gtk_paned_pack2 (GTK_PANED (pane), create_video_box (app), TRUE, TRUE);
+
+ gtk_box_pack_start (GTK_BOX (bbox), create_rotate_button (app, "sink2"),
+ TRUE, TRUE, 0);
+ }
+
+ gtk_widget_show_all (mainwin);
+}
+
+int
+main (gint argc, gchar ** argv)
+{
+ AppData app = { 0, };
+ GstBus *bus;
+ GOptionContext *ctx;
+ GError *error = NULL;
+
+ XInitThreads ();
+
+ ctx = g_option_context_new ("- test options");
+ if (!ctx)
+ return -1;
+
+ g_option_context_add_group (ctx, gtk_get_option_group (TRUE));
+ g_option_context_add_group (ctx, gst_init_get_option_group ());
+ g_option_context_add_main_entries (ctx, g_options, NULL);
+ if (!g_option_context_parse (ctx, &argc, &argv, NULL))
+ return -1;
+ g_option_context_free (ctx);
+
+ if (g_multisink) {
+ app.pipeline = gst_parse_launch ("videotestsrc ! tee name=t ! queue ! "
+ "vaapisink name=sink1 t. ! queue ! vaapisink name=sink2", &error);
+ } else if (!g_filepath) {
+ app.pipeline = gst_parse_launch ("videotestsrc ! vaapih264enc ! "
+ "vaapidecodebin ! vaapisink name=sink1", &error);
+ } else {
+ app.pipeline = gst_parse_launch ("filesrc name=src ! qtdemux ! h264parse ! "
+ "vaapidecodebin ! vaapisink name=sink1", &error);
+ }
+
+ if (error) {
+ gst_printerrln ("failed to parse pipeline: %s", error->message);
+ g_error_free (error);
+ return -1;
+ }
+
+ if (!g_multisink && g_filepath) {
+ GstElement *src;
+
+ src = gst_bin_get_by_name (GST_BIN (app.pipeline), "src");
+ g_assert (src);
+ g_object_set (src, "location", g_filepath, NULL);
+ gst_object_unref (src);
+ }
+
+ build_ui (&app);
+
+ bus = gst_element_get_bus (app.pipeline);
+ gst_bus_set_sync_handler (bus, bus_sync_handler, (gpointer) & app, NULL);
+ gst_object_unref (bus);
+
+ gst_element_set_state (app.pipeline, GST_STATE_PLAYING);
+ gst_println ("Now playing…");
+
+ gtk_main ();
+
+ gst_object_unref (app.pipeline);
+ gst_object_unref (app.gstvaapidisplay);
+ /* there is no need to call vaTerminate() because it is done by the
+ * vaapi elements */
+ return 0;
+}
diff --git a/tests/examples/test-vaapipostproc.c b/tests/examples/test-vaapipostproc.c
new file mode 100644
index 00000000..87d13539
--- /dev/null
+++ b/tests/examples/test-vaapipostproc.c
@@ -0,0 +1,157 @@
+/*
+ * test-vaapipostproc.c - Testsuite for VAAPI Postprocessor
+ *
+ * Copyright (C) 2017 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <gst/gst.h>
+#include <gst/base/gstbasetransform.h>
+
+typedef struct _CustomData
+{
+ GstElement *pipeline;
+ GstElement *postproc;
+ GMainLoop *loop;
+} AppData;
+
+static gboolean
+_check_passthrough_mode (gpointer user_data)
+{
+ gboolean ret;
+ AppData *data = (AppData *) user_data;
+
+ ret = gst_base_transform_is_passthrough (GST_BASE_TRANSFORM (data->postproc));
+
+ if (ret)
+ gst_println ("Now this pipeline is on passthrough mode");
+ else
+ gst_println ("Now this pipeline is NOT on passthrough mode");
+
+ return FALSE;
+}
+
+static void
+set_contrast (AppData * data)
+{
+ static gfloat value = 1.0;
+
+ value = value == 1.0 ? 0.5 : 1.0;
+ g_object_set (data->postproc, "contrast", value, NULL);
+ gst_println ("contrast value is changed to %f", value);
+
+ g_timeout_add (300, _check_passthrough_mode, data);
+}
+
+static void
+change_size (AppData * data)
+{
+ static gint i = 0;
+ if (i == 0) {
+ g_object_set (data->postproc, "width", 1280, "height", 720, NULL);
+ gst_println ("frame size is changed to 1280x720");
+ i++;
+ } else {
+ g_object_set (data->postproc, "width", 0, "height", 0, NULL);
+ gst_println ("frame size is changed to default");
+ i = 0;
+ }
+
+ g_timeout_add (300, _check_passthrough_mode, data);
+}
+
+/* Process keyboard input */
+static gboolean
+handle_keyboard (GIOChannel * source, GIOCondition cond, AppData * data)
+{
+ gchar *str = NULL;
+
+ if (g_io_channel_read_line (source, &str, NULL, NULL,
+ NULL) != G_IO_STATUS_NORMAL) {
+ return TRUE;
+ }
+
+ switch (g_ascii_tolower (str[0])) {
+ case 's':{
+ set_contrast (data);
+ break;
+ }
+ case 'c':{
+ change_size (data);
+ break;
+ }
+ case 'q':
+ g_main_loop_quit (data->loop);
+ break;
+ default:
+ break;
+ }
+
+ g_free (str);
+
+ return TRUE;
+}
+
+int
+main (int argc, char *argv[])
+{
+ AppData data = { 0, };
+ GstStateChangeReturn ret;
+ GIOChannel *io_stdin;
+
+ /* Initialize GStreamer */
+ gst_init (&argc, &argv);
+
+ /* Print usage map */
+ gst_println ("USAGE: Choose one of the following options, then press enter:\n"
+ " 's' to set contrast\n" " 'c' to change size\n" " 'q' to quit\n");
+
+ data.pipeline =
+ gst_parse_launch
+ ("videotestsrc name=src ! vaapih264enc ! vaapih264dec ! vaapipostproc name=postproc ! vaapisink",
+ NULL);
+ data.postproc = gst_bin_get_by_name (GST_BIN (data.pipeline), "postproc");
+
+ /* Add a keyboard watch so we get notified of keystrokes */
+ io_stdin = g_io_channel_unix_new (fileno (stdin));
+ g_io_add_watch (io_stdin, G_IO_IN, (GIOFunc) handle_keyboard, &data);
+
+ /* Start playing */
+ ret = gst_element_set_state (data.pipeline, GST_STATE_PLAYING);
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+ g_printerr ("Unable to set the pipeline to the playing state.\n");
+ gst_object_unref (data.pipeline);
+ return -1;
+ }
+
+ g_timeout_add (300, _check_passthrough_mode, &data);
+
+ /* Create a GLib Main Loop and set it to run */
+ data.loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (data.loop);
+
+ /* Free resources */
+ g_main_loop_unref (data.loop);
+ g_io_channel_unref (io_stdin);
+ gst_element_set_state (data.pipeline, GST_STATE_NULL);
+
+ gst_object_unref (data.postproc);
+ gst_object_unref (data.pipeline);
+
+ return 0;
+}
diff --git a/tests/examples/test-vaapisink.c b/tests/examples/test-vaapisink.c
new file mode 100644
index 00000000..41f3fc3e
--- /dev/null
+++ b/tests/examples/test-vaapisink.c
@@ -0,0 +1,189 @@
+#include <stdio.h>
+#include <string.h>
+#include <gst/gst.h>
+#include <gst/video/video.h>
+
+static gboolean use_postproc;
+static GOptionEntry g_options[] = {
+ {"postproc", 'p', 0, G_OPTION_ARG_NONE, &use_postproc,
+ "use vaapipostproc to rotate rather than vaapisink", NULL},
+ {NULL,}
+};
+
+typedef struct _CustomData
+{
+ GstElement *pipeline;
+ GstElement *rotator;
+ GMainLoop *loop;
+ gboolean orient_automatic;
+} AppData;
+
+static void
+send_rotate_event (AppData * data)
+{
+ gboolean res = FALSE;
+ GstEvent *event;
+ static gint counter = 0;
+ const static gchar *tags[] = { "rotate-90", "rotate-180", "rotate-270",
+ "rotate-0", "flip-rotate-0", "flip-rotate-90", "flip-rotate-180",
+ "flip-rotate-270",
+ };
+
+ event = gst_event_new_tag (gst_tag_list_new (GST_TAG_IMAGE_ORIENTATION,
+ tags[counter++ % G_N_ELEMENTS (tags)], NULL));
+
+ /* Send the event */
+ g_print ("Sending event %" GST_PTR_FORMAT ": ", event);
+ res = gst_element_send_event (data->pipeline, event);
+ g_print ("%s\n", res ? "ok" : "failed");
+
+}
+
+static void
+keyboard_cb (const gchar * key, AppData * data)
+{
+ switch (g_ascii_tolower (key[0])) {
+ case 'r':
+ send_rotate_event (data);
+ break;
+ case 's':{
+ if (use_postproc) {
+ g_object_set (G_OBJECT (data->rotator), "video-direction",
+ GST_VIDEO_ORIENTATION_AUTO, NULL);
+ } else {
+ /* rotation=360 means auto for vaapisnk */
+ g_object_set (G_OBJECT (data->rotator), "rotation", 360, NULL);
+ }
+ break;
+ }
+ case 'q':
+ g_main_loop_quit (data->loop);
+ break;
+ default:
+ break;
+ }
+}
+
+static gboolean
+bus_msg (GstBus * bus, GstMessage * msg, gpointer user_data)
+{
+ AppData *data = user_data;
+
+ switch (GST_MESSAGE_TYPE (msg)) {
+ case GST_MESSAGE_ELEMENT:
+ {
+ GstNavigationMessageType mtype = gst_navigation_message_get_type (msg);
+ if (mtype == GST_NAVIGATION_MESSAGE_EVENT) {
+ GstEvent *ev = NULL;
+
+ if (gst_navigation_message_parse_event (msg, &ev)) {
+ GstNavigationEventType type = gst_navigation_event_get_type (ev);
+ if (type == GST_NAVIGATION_EVENT_KEY_PRESS) {
+ const gchar *key;
+
+ if (gst_navigation_event_parse_key_event (ev, &key))
+ keyboard_cb (key, data);
+ }
+ }
+ if (ev)
+ gst_event_unref (ev);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+/* Process keyboard input */
+static gboolean
+handle_keyboard (GIOChannel * source, GIOCondition cond, AppData * data)
+{
+ gchar *str = NULL;
+
+ if (g_io_channel_read_line (source, &str, NULL, NULL,
+ NULL) != G_IO_STATUS_NORMAL) {
+ return TRUE;
+ }
+
+ keyboard_cb (str, data);
+ g_free (str);
+
+ return TRUE;
+}
+
+int
+main (int argc, char *argv[])
+{
+ AppData data;
+ GstStateChangeReturn ret;
+ GIOChannel *io_stdin;
+ GOptionContext *ctx;
+ GError *err = NULL;
+ guint srcid;
+
+ /* Initialize GStreamer */
+ ctx = g_option_context_new ("- test options");
+ if (!ctx)
+ return -1;
+ g_option_context_add_group (ctx, gst_init_get_option_group ());
+ g_option_context_add_main_entries (ctx, g_options, NULL);
+ if (!g_option_context_parse (ctx, &argc, &argv, NULL))
+ return -1;
+ g_option_context_free (ctx);
+
+ /* Print usage map */
+ g_print ("USAGE: Choose one of the following options, then press enter:\n"
+ " 'r' to send image-orientation tag event\n"
+ " 's' to set orient-automatic\n" " 'Q' to quit\n");
+
+ if (use_postproc) {
+ data.pipeline =
+ gst_parse_launch ("videotestsrc ! vaapipostproc name=pp ! xvimagesink",
+ &err);
+ } else {
+ data.pipeline =
+ gst_parse_launch ("videotestsrc ! vaapisink name=sink", &err);
+ }
+ if (err) {
+ g_printerr ("failed to create pipeline: %s\n", err->message);
+ g_error_free (err);
+ return -1;
+ }
+
+ if (use_postproc)
+ data.rotator = gst_bin_get_by_name (GST_BIN (data.pipeline), "pp");
+ else
+ data.rotator = gst_bin_get_by_name (GST_BIN (data.pipeline), "sink");
+ srcid = gst_bus_add_watch (GST_ELEMENT_BUS (data.pipeline), bus_msg, &data);
+
+ /* Add a keyboard watch so we get notified of keystrokes */
+ io_stdin = g_io_channel_unix_new (fileno (stdin));
+ g_io_add_watch (io_stdin, G_IO_IN, (GIOFunc) handle_keyboard, &data);
+
+ /* Start playing */
+ ret = gst_element_set_state (data.pipeline, GST_STATE_PLAYING);
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+ g_printerr ("Unable to set the pipeline to the playing state.\n");
+ goto bail;
+ }
+
+ /* Create a GLib Main Loop and set it to run */
+ data.loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (data.loop);
+
+ gst_element_set_state (data.pipeline, GST_STATE_NULL);
+
+bail:
+ /* Free resources */
+ g_source_remove (srcid);
+ g_main_loop_unref (data.loop);
+ g_io_channel_unref (io_stdin);
+
+ gst_object_unref (data.rotator);
+ gst_object_unref (data.pipeline);
+
+ return 0;
+}