diff --git a/tests/examples/meson.build b/tests/examples/meson.build index fa7442f2db..61da627bb1 100644 --- a/tests/examples/meson.build +++ b/tests/examples/meson.build @@ -12,6 +12,7 @@ subdir('opencv', if_found: opencv_dep) subdir('uvch264') subdir('waylandsink') subdir('webrtc') +subdir('msdk') executable('playout', 'playout.c', diff --git a/tests/examples/msdk/meson.build b/tests/examples/msdk/meson.build new file mode 100644 index 0000000000..819f9cf9e2 --- /dev/null +++ b/tests/examples/msdk/meson.build @@ -0,0 +1,7 @@ +if have_msdk + executable('test-roi', 'test-roi.c', + include_directories : [configinc], + dependencies: [gst_dep, gstvideo_dep], + c_args : gst_plugins_bad_args, + install: false) +endif diff --git a/tests/examples/msdk/test-roi.c b/tests/examples/msdk/test-roi.c new file mode 100644 index 0000000000..70c9bf3d56 --- /dev/null +++ b/tests/examples/msdk/test-roi.c @@ -0,0 +1,267 @@ +/* + * test-roi.c - Testsuite for Region of Interest + * + * Copyright (C) 2019 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 +#include +#include +#include + +typedef struct _CustomData +{ + GstElement *pipeline; + GMainLoop *loop; + gboolean roi_enabled; + gboolean is_delta_qp; +} 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 'd': + app->is_delta_qp = !app->is_delta_qp; + gst_println ("Use %s", app->is_delta_qp ? "delta QP" : "Priority"); + 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); + + rmeta = + gst_buffer_add_video_region_of_interest_meta (buf, "test", 0, 0, 320, + 240); + + if (app->is_delta_qp) + s = gst_structure_new ("roi/msdk", "delta-qp", G_TYPE_INT, 20, NULL); + else + s = gst_structure_new ("roi/msdk", "priority", G_TYPE_INT, -3, 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 roi/msdk 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; + data.is_delta_qp = TRUE; + + /* Initialize GStreamer */ + gst_init (&argc, &argv); + + /* Print usage map */ + g_print ("USAGE: 'r' to enable/disable ROI, " + "'d' to change ROI mode && 'q' to quit\n"); + +#define SRC "videotestsrc pattern=snow ! " \ + "video/x-raw, format=NV12, width=320, framerate=5/1" +#define ENCDEC "msdkh265enc rate-control=cqp ! msdkh265dec ! " \ + "msdkvpp ! video/x-raw, width=640" +#define TEXT "textoverlay font-desc=\"Arial Bold 48\" " + + data.pipeline = + gst_parse_launch + ("videomixer name=mix ! msdkvpp ! glimagesink 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; +}