examples: nvcodec: Add example for runtime configuration change with nvcodec
authorSeungha Yang <seungha.yang@navercorp.com>
Fri, 6 Sep 2019 03:52:37 +0000 (12:52 +0900)
committerMatthew Waters <matthew@centricular.com>
Wed, 11 Sep 2019 23:15:06 +0000 (23:15 +0000)
Add new example for testing dynamic change of configuration suce as
resolution and properties.

tests/examples/meson.build
tests/examples/nvcodec/meson.build [new file with mode: 0644]
tests/examples/nvcodec/nvcodec-kb.c [new file with mode: 0644]
tests/examples/nvcodec/nvcodec.c [new file with mode: 0644]
tests/examples/nvcodec/nvcodec.h [new file with mode: 0644]

index 6a05682..ce12c65 100644 (file)
@@ -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 (file)
index 0000000..749d302
--- /dev/null
@@ -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 (file)
index 0000000..a4dc97d
--- /dev/null
@@ -0,0 +1,142 @@
+/* GStreamer command line playback testing utility - keyboard handling helpers
+ *
+ * Copyright (C) 2013 Tim-Philipp Müller <tim centricular net>
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef G_OS_UNIX
+#include <unistd.h>
+#include <termios.h>
+#endif
+
+#include <gst/gst.h>
+
+/* 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 (file)
index 0000000..f5c2b8a
--- /dev/null
@@ -0,0 +1,472 @@
+/* GStreamer
+ * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
+ *
+ * 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 <gst/gst.h>
+#include <gst/video/video.h>
+
+#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 (file)
index 0000000..ff6d9e4
--- /dev/null
@@ -0,0 +1,35 @@
+/* GStreamer command line playback testing utility - keyboard handling helpers
+ *
+ * Copyright (C) 2013 Tim-Philipp Müller <tim centricular net>
+ * 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 <glib.h>
+
+#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__ */