From: Seungha Yang Date: Fri, 6 Sep 2019 03:52:37 +0000 (+0900) Subject: examples: nvcodec: Add example for runtime configuration change with nvcodec X-Git-Tag: 1.19.3~507^2~2853 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1da684643412e3542c1c0629a065ebca28a24f67;p=platform%2Fupstream%2Fgstreamer.git examples: nvcodec: Add example for runtime configuration change with nvcodec Add new example for testing dynamic change of configuration suce as resolution and properties. --- diff --git a/tests/examples/meson.build b/tests/examples/meson.build index 6a05682..ce12c65 100644 --- a/tests/examples/meson.build +++ b/tests/examples/meson.build @@ -6,6 +6,7 @@ subdir('directfb') subdir('ipcpipeline') subdir('mpegts') subdir('mxf') +subdir('nvcodec') subdir('opencv', if_found: opencv_dep) subdir('uvch264') subdir('waylandsink') diff --git a/tests/examples/nvcodec/meson.build b/tests/examples/nvcodec/meson.build new file mode 100644 index 0000000..749d302 --- /dev/null +++ b/tests/examples/nvcodec/meson.build @@ -0,0 +1,6 @@ +executable('nvcodec', + ['nvcodec.c', 'nvcodec-kb.c'], + include_directories : [configinc], + dependencies: [gst_dep, gstbase_dep, gstvideo_dep], + c_args : gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API'], + install: false) diff --git a/tests/examples/nvcodec/nvcodec-kb.c b/tests/examples/nvcodec/nvcodec-kb.c new file mode 100644 index 0000000..a4dc97d --- /dev/null +++ b/tests/examples/nvcodec/nvcodec-kb.c @@ -0,0 +1,142 @@ +/* GStreamer command line playback testing utility - keyboard handling helpers + * + * Copyright (C) 2013 Tim-Philipp Müller + * Copyright (C) 2013 Centricular Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "nvcodec.h" + +#include +#include +#include + +#ifdef G_OS_UNIX +#include +#include +#endif + +#include + +/* This is all not thread-safe, but doesn't have to be really */ + +#ifdef G_OS_UNIX + +static struct termios term_settings; +static gboolean term_settings_saved = FALSE; +static GstNvCodecPlayKbFunc kb_callback; +static gpointer kb_callback_data; +static gulong io_watch_id; + +static gboolean +gst_nvcodec_kb_io_cb (GIOChannel * ioc, GIOCondition cond, gpointer user_data) +{ + GIOStatus status; + + if (cond & G_IO_IN) { + gchar buf[16] = { 0, }; + gsize read; + + status = g_io_channel_read_chars (ioc, buf, sizeof (buf) - 1, &read, NULL); + if (status == G_IO_STATUS_ERROR) + return G_SOURCE_REMOVE; + if (status == G_IO_STATUS_NORMAL) { + if (kb_callback) + kb_callback (buf, kb_callback_data); + } + } + + return G_SOURCE_CONTINUE; +} + +gboolean +gst_nvcodec_kb_set_key_handler (GstNvCodecPlayKbFunc kb_func, + gpointer user_data) +{ + GIOChannel *ioc; + + if (!isatty (STDIN_FILENO)) { + GST_INFO ("stdin is not connected to a terminal"); + return FALSE; + } + + if (io_watch_id > 0) { + g_source_remove (io_watch_id); + io_watch_id = 0; + } + + if (kb_func == NULL && term_settings_saved) { + /* restore terminal settings */ + if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &term_settings) == 0) + term_settings_saved = FALSE; + else + g_warning ("could not restore terminal attributes"); + + setvbuf (stdin, NULL, _IOLBF, 0); + } + + if (kb_func != NULL) { + struct termios new_settings; + + if (!term_settings_saved) { + if (tcgetattr (STDIN_FILENO, &term_settings) != 0) { + g_warning ("could not save terminal attributes"); + return FALSE; + } + term_settings_saved = TRUE; + + /* Echo off, canonical mode off, extended input processing off */ + new_settings = term_settings; + new_settings.c_lflag &= ~(ECHO | ICANON | IEXTEN); + new_settings.c_cc[VMIN] = 0; + new_settings.c_cc[VTIME] = 0; + + if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &new_settings) != 0) { + g_warning ("Could not set terminal state"); + return FALSE; + } + setvbuf (stdin, NULL, _IONBF, 0); + } + } + + ioc = g_io_channel_unix_new (STDIN_FILENO); + + io_watch_id = g_io_add_watch_full (ioc, G_PRIORITY_DEFAULT, G_IO_IN, + (GIOFunc) gst_nvcodec_kb_io_cb, user_data, NULL); + g_io_channel_unref (ioc); + + kb_callback = kb_func; + kb_callback_data = user_data; + + return TRUE; +} + +#else /* !G_OS_UNIX */ + +gboolean +gst_nvcodec_kb_set_key_handler (GstNvCodecPlayKbFunc key_func, + gpointer user_data) +{ + GST_FIXME ("Keyboard handling for this OS needs to be implemented"); + return FALSE; +} + +#endif /* !G_OS_UNIX */ diff --git a/tests/examples/nvcodec/nvcodec.c b/tests/examples/nvcodec/nvcodec.c new file mode 100644 index 0000000..f5c2b8a --- /dev/null +++ b/tests/examples/nvcodec/nvcodec.c @@ -0,0 +1,472 @@ +/* GStreamer + * Copyright (C) 2019 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "nvcodec.h" + +#define DEFAULT_VIDEO_SINK "autovideosink" + +static GMainLoop *loop = NULL; +static gint width = 320; +static gint height = 240; +static gint bitrate = 2000; + +typedef struct +{ + GstElement *pipeline; + GstElement *capsfilter; + GstElement *nvenc; + gulong probe_id; + + gint prev_width; + gint prev_height; +} TestCallbackData; + +static void +restore_terminal (void) +{ + gst_nvcodec_kb_set_key_handler (NULL, NULL); +} + +static void +print_keyboard_help (void) +{ + static struct + { + const gchar *key_desc; + const gchar *key_help; + } key_controls[] = { + { + "q or ESC", "Quit"}, { + "right arrow", "Increase Width"}, { + "left arrow", "Decrease Width"}, { + "up arrow", "Increase Height"}, { + "down arrow", "Decrease Height"}, { + ">", "Increase encoding bitrate by 100 kbit/sec"}, { + "<", "Decrease encoding bitrate by 100 kbit/sec"}, { + "k", "show keyboard shortcuts"} + }; + guint i, chars_to_pad, desc_len, max_desc_len = 0; + + g_print ("\n\n%s\n\n", "Keyboard controls:"); + + for (i = 0; i < G_N_ELEMENTS (key_controls); ++i) { + desc_len = g_utf8_strlen (key_controls[i].key_desc, -1); + max_desc_len = MAX (max_desc_len, desc_len); + } + ++max_desc_len; + + for (i = 0; i < G_N_ELEMENTS (key_controls); ++i) { + chars_to_pad = max_desc_len - g_utf8_strlen (key_controls[i].key_desc, -1); + g_print ("\t%s", key_controls[i].key_desc); + g_print ("%-*s: ", chars_to_pad, ""); + g_print ("%s\n", key_controls[i].key_help); + } + g_print ("\n"); +} + +static void +keyboard_cb (const gchar * key_input, gpointer user_data) +{ + TestCallbackData *data = (TestCallbackData *) user_data; + gchar key = '\0'; + + /* only want to switch/case on single char, not first char of string */ + if (key_input[0] != '\0' && key_input[1] == '\0') + key = g_ascii_tolower (key_input[0]); + + switch (key) { + case 'k': + print_keyboard_help (); + break; + case 'q': + case 'Q': + gst_element_send_event (data->pipeline, gst_event_new_eos ()); + g_main_loop_quit (loop); + break; + case 27: /* ESC */ + if (key_input[1] == '\0') { + gst_element_send_event (data->pipeline, gst_event_new_eos ()); + g_main_loop_quit (loop); + } + break; + case '>': + bitrate += 100; + bitrate = MIN (bitrate, 2048000); + g_print ("Increase encoding bitrate to %d\n", bitrate); + g_object_set (G_OBJECT (data->nvenc), "bitrate", bitrate, NULL); + break; + case '<': + bitrate -= 100; + bitrate = MAX (bitrate, 100); + g_print ("Decrease encoding bitrate to %d\n", bitrate); + g_object_set (G_OBJECT (data->nvenc), "bitrate", bitrate, NULL); + break; + default:{ + if (strcmp (key_input, GST_NVCODEC_KB_ARROW_RIGHT) == 0) { + g_print ("Increase width to %d\n", ++width); + } else if (strcmp (key_input, GST_NVCODEC_KB_ARROW_LEFT) == 0) { + g_print ("Decrease width to %d\n", --width); + } else if (strcmp (key_input, GST_NVCODEC_KB_ARROW_UP) == 0) { + g_print ("Increase height to %d\n", ++height); + } else if (strcmp (key_input, GST_NVCODEC_KB_ARROW_DOWN) == 0) { + g_print ("Decrease height to %d\n", --height); + } + + break; + } + } +} + +static gboolean +bus_msg (GstBus * bus, GstMessage * msg, gpointer user_data) +{ + TestCallbackData *data = (TestCallbackData *) user_data; + GstElement *pipeline = data->pipeline; + + switch (GST_MESSAGE_TYPE (msg)) { + case GST_MESSAGE_STATE_CHANGED: + if (GST_MESSAGE_SRC (msg) == GST_OBJECT_CAST (pipeline)) { + gchar *state_transition_name; + GstState old, new, pending; + + gst_message_parse_state_changed (msg, &old, &new, &pending); + + state_transition_name = g_strdup_printf ("%s_%s", + gst_element_state_get_name (old), gst_element_state_get_name (new)); + + /* dump graph for (some) pipeline state changes */ + { + gchar *dump_name = g_strconcat ("nvcodec.", state_transition_name, + NULL); + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), + GST_DEBUG_GRAPH_SHOW_ALL, dump_name); + g_free (dump_name); + } + g_free (state_transition_name); + } + break; + case GST_MESSAGE_ERROR:{ + GError *err; + gchar *dbg; + + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), + GST_DEBUG_GRAPH_SHOW_ALL, "nvcodec.error"); + + gst_message_parse_error (msg, &err, &dbg); + g_printerr ("ERROR %s \n", err->message); + if (dbg != NULL) + g_printerr ("ERROR debug information: %s\n", dbg); + g_clear_error (&err); + g_free (dbg); + + g_main_loop_quit (loop); + break; + } + 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 e_type = gst_navigation_event_get_type (ev); + if (e_type == GST_NAVIGATION_EVENT_KEY_PRESS) { + const gchar *key; + + if (gst_navigation_event_parse_key_event (ev, &key)) { + GST_INFO ("Key press: %s", key); + + if (strcmp (key, "Left") == 0) + key = GST_NVCODEC_KB_ARROW_LEFT; + else if (strcmp (key, "Right") == 0) + key = GST_NVCODEC_KB_ARROW_RIGHT; + else if (strcmp (key, "Up") == 0) + key = GST_NVCODEC_KB_ARROW_UP; + else if (strcmp (key, "Down") == 0) + key = GST_NVCODEC_KB_ARROW_DOWN; + else if (strlen (key) > 1) + break; + + keyboard_cb (key, user_data); + } + } + } + if (ev) + gst_event_unref (ev); + } + break; + } + default: + break; + } + + return TRUE; +} + +static gboolean +check_nvcodec_available (void) +{ + gboolean ret = TRUE; + GstElement *elem; + + elem = gst_element_factory_make ("nvh264enc", NULL); + if (!elem) { + GST_WARNING ("nvh264enc is not available, possibly driver load failure"); + return FALSE; + } + + /* GST_STATE_READY is meaning that driver could be loaded */ + if (gst_element_set_state (elem, + GST_STATE_PAUSED) != GST_STATE_CHANGE_SUCCESS) { + GST_WARNING ("cannot open device"); + ret = FALSE; + } + + gst_element_set_state (elem, GST_STATE_NULL); + gst_object_unref (elem); + + if (ret) { + elem = gst_element_factory_make ("nvh264dec", NULL); + if (!elem) { + GST_WARNING ("nvh264dec is not available, possibly driver load failure"); + return FALSE; + } + + /* GST_STATE_READY is meaning that driver could be loaded */ + if (gst_element_set_state (elem, + GST_STATE_PAUSED) != GST_STATE_CHANGE_SUCCESS) { + GST_WARNING ("cannot open device"); + ret = FALSE; + } + + gst_element_set_state (elem, GST_STATE_NULL); + gst_object_unref (elem); + } + + return ret; +} + +static GstPadProbeReturn +resolution_change_probe (GstPad * pad, GstPadProbeInfo * info, + gpointer user_data) +{ + GstPadProbeReturn ret = GST_PAD_PROBE_OK; + TestCallbackData *data = (TestCallbackData *) user_data; + + if (GST_IS_BUFFER (GST_PAD_PROBE_INFO_DATA (info))) { + GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER (info); + GstPad *peer = gst_pad_get_peer (pad); + GstFlowReturn flow_ret = GST_FLOW_OK; + + ret = GST_PAD_PROBE_HANDLED; + + if (peer) { + flow_ret = gst_pad_chain (peer, buffer); + + if (flow_ret != GST_FLOW_OK) { + gst_pad_remove_probe (pad, data->probe_id); + data->probe_id = 0; + } else { + if (data->prev_width != width || data->prev_height != height) { + GstCaps *caps = NULL; + gint next_width, next_height; + + next_width = width; + next_height = height; + + g_object_get (data->capsfilter, "caps", &caps, NULL); + caps = gst_caps_make_writable (caps); + gst_caps_set_simple (caps, + "width", G_TYPE_INT, next_width, "height", G_TYPE_INT, + next_height, NULL); + g_object_set (data->capsfilter, "caps", caps, NULL); + gst_caps_unref (caps); + + data->prev_width = next_width; + data->prev_height = next_height; + } + } + } + } + + return ret; +} + +gint +main (gint argc, gchar ** argv) +{ + GstElement *pipeline, *src, *convert, *capsfilter, *queue, *sink, *parse; + GstElement *enc, *dec; + GstStateChangeReturn sret; + GError *error = NULL; + gboolean use_gl = FALSE; + gint exitcode = 1; + GOptionContext *option_ctx; + GstCaps *caps; + TestCallbackData data = { 0, }; + GstPad *pad; + + GOptionEntry options[] = { + {"use-gl", 0, 0, G_OPTION_ARG_NONE, &use_gl, + "Use OpenGL memory as input to the nvenc", NULL} + , + {NULL} + }; + + option_ctx = g_option_context_new ("nvcodec dynamic reconfigure example"); + g_option_context_add_main_entries (option_ctx, options, NULL); + g_option_context_set_help_enabled (option_ctx, TRUE); + if (!g_option_context_parse (option_ctx, &argc, &argv, &error)) { + g_printerr ("option parsing failed: %s\n", error->message); + g_clear_error (&error); + exit (1); + } + + g_option_context_free (option_ctx); + gst_init (NULL, NULL); + + if (!check_nvcodec_available ()) { + g_printerr ("Cannot load nvcodec plugin"); + exit (1); + } + + /* prepare the pipeline */ + loop = g_main_loop_new (NULL, FALSE); + + pipeline = gst_pipeline_new ("nvcodec-example"); + + if (use_gl) + src = gst_element_factory_make ("gltestsrc", NULL); + else + src = gst_element_factory_make ("videotestsrc", NULL); + + if (!src) { + g_printerr ("%s element is not available\n", + use_gl ? "gltestsrc" : "videotestsrc"); + goto terminate; + } + + gst_bin_add (GST_BIN (pipeline), src); + + if (use_gl) + convert = gst_element_factory_make ("glcolorconvert", NULL); + else + convert = gst_element_factory_make ("videoconvert", NULL); + + if (!convert) { + g_printerr ("%s element is not available\n", + use_gl ? "glcolorconvert" : "videoconvert"); + goto terminate; + } + + gst_bin_add (GST_BIN (pipeline), convert); + + if (use_gl) { + sink = gst_element_factory_make ("glimagesink", NULL); + } else { + sink = gst_element_factory_make (DEFAULT_VIDEO_SINK, NULL); + } + + if (!sink) { + g_printerr ("%s element is not available\n", + use_gl ? "glimagesink" : DEFAULT_VIDEO_SINK); + goto terminate; + } + + gst_bin_add (GST_BIN (pipeline), sink); + + capsfilter = gst_element_factory_make ("capsfilter", NULL); + queue = gst_element_factory_make ("queue", NULL); + enc = gst_element_factory_make ("nvh264enc", NULL); + parse = gst_element_factory_make ("h264parse", NULL); + + /* vbr with target bitrate */ + g_object_set (G_OBJECT (enc), "rc-mode", 4, "bitrate", bitrate, NULL); + + dec = gst_element_factory_make ("nvh264dec", NULL); + + gst_bin_add_many (GST_BIN (pipeline), capsfilter, queue, enc, parse, dec, + NULL); + + if (!use_gl) { + GstElement *sink_convert = gst_element_factory_make ("videoconvert", NULL); + gst_bin_add (GST_BIN (pipeline), sink_convert); + + gst_element_link_many (src, + convert, capsfilter, enc, parse, dec, queue, sink_convert, sink, NULL); + } else { + gst_element_link_many (src, + convert, capsfilter, enc, parse, dec, queue, sink, NULL); + } + + caps = gst_caps_from_string ("video/x-raw,format=NV12"); + + if (use_gl) { + gst_caps_set_features_simple (caps, + gst_caps_features_from_string ("memory:GLMemory")); + } + + g_object_set (G_OBJECT (capsfilter), "caps", caps, NULL); + gst_caps_unref (caps); + + data.pipeline = pipeline; + data.capsfilter = capsfilter; + data.nvenc = enc; + + pad = gst_element_get_static_pad (convert, "src"); + data.probe_id = gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER, + (GstPadProbeCallback) resolution_change_probe, &data, NULL); + gst_object_unref (pad); + data.prev_width = width; + data.prev_height = height; + + gst_bus_add_watch (GST_ELEMENT_BUS (pipeline), bus_msg, &data); + + if (gst_nvcodec_kb_set_key_handler (keyboard_cb, &data)) { + g_print ("Press 'k' to see a list of keyboard shortcuts.\n"); + atexit (restore_terminal); + } + + /* run the pipeline */ + sret = gst_element_set_state (pipeline, GST_STATE_PLAYING); + if (sret == GST_STATE_CHANGE_FAILURE) { + g_printerr ("Pipeline doesn't want to playing\n"); + } else { + g_main_loop_run (loop); + } + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_bus_remove_watch (GST_ELEMENT_BUS (pipeline)); + + exitcode = 0; + +terminate: + + gst_object_unref (pipeline); + g_main_loop_unref (loop); + + return exitcode; +} diff --git a/tests/examples/nvcodec/nvcodec.h b/tests/examples/nvcodec/nvcodec.h new file mode 100644 index 0000000..ff6d9e4 --- /dev/null +++ b/tests/examples/nvcodec/nvcodec.h @@ -0,0 +1,35 @@ +/* GStreamer command line playback testing utility - keyboard handling helpers + * + * Copyright (C) 2013 Tim-Philipp Müller + * Copyright (C) 2013 Centricular Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef __GST_NV_CODEC_KB_H__ +#define __GST_NV_CODEC_KB_H__ + +#include + +#define GST_NVCODEC_KB_ARROW_UP "\033[A" +#define GST_NVCODEC_KB_ARROW_DOWN "\033[B" +#define GST_NVCODEC_KB_ARROW_RIGHT "\033[C" +#define GST_NVCODEC_KB_ARROW_LEFT "\033[D" + +typedef void (*GstNvCodecPlayKbFunc) (const gchar * kb_input, gpointer user_data); + +gboolean gst_nvcodec_kb_set_key_handler (GstNvCodecPlayKbFunc kb_func, gpointer user_data); + +#endif /* __GST_NV_CODEC_EXAMPLE_KB_H__ */