[Sink] add examples for sink element
authorjy1210.jung <jy1210.jung@samsung.com>
Thu, 5 Jul 2018 07:42:39 +0000 (16:42 +0900)
committer함명주/동작제어Lab(SR)/Principal Engineer/삼성전자 <myungjoo.ham@samsung.com>
Thu, 5 Jul 2018 10:22:35 +0000 (19:22 +0900)
1. add simple example for data callback from sink
2. add video play example using gst app
3. update cmake (does not install sample app)

**Self evaluation:**
1. Build test: [*]Passed [ ]Failed [ ]Skipped
2. Run test: [*]Passed [ ]Failed [ ]Skipped

Signed-off-by: Jaeyun Jung <jy1210.jung@samsung.com>
nnstreamer_example/CMakeLists.txt
nnstreamer_example/example_sink/CMakeLists.txt [new file with mode: 0644]
nnstreamer_example/example_sink/nnstreamer_sink_example.c [new file with mode: 0644]
nnstreamer_example/example_sink/nnstreamer_sink_example_play.c [new file with mode: 0644]

index 3e2d782..11f297b 100644 (file)
@@ -3,3 +3,4 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
 ADD_SUBDIRECTORY(custom_example_passthrough)
 ADD_SUBDIRECTORY(custom_example_scaler)
 ADD_SUBDIRECTORY(custom_example_average)
+ADD_SUBDIRECTORY(example_sink)
diff --git a/nnstreamer_example/example_sink/CMakeLists.txt b/nnstreamer_example/example_sink/CMakeLists.txt
new file mode 100644 (file)
index 0000000..74d2f5a
--- /dev/null
@@ -0,0 +1,18 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
+
+# example 1
+ADD_EXECUTABLE(nnstreamer_sink_example nnstreamer_sink_example.c)
+
+TARGET_LINK_LIBRARIES(nnstreamer_sink_example ${pkgs_LIBRARIES})
+TARGET_INCLUDE_DIRECTORIES(nnstreamer_sink_example PUBLIC ${pkgs_INCLUDE_DIRS})
+TARGET_COMPILE_OPTIONS(nnstreamer_sink_example PUBLIC ${pkgs_CFLAGS_OTHER})
+
+# example 2
+ADD_EXECUTABLE(nnstreamer_sink_example_play nnstreamer_sink_example_play.c)
+
+TARGET_LINK_LIBRARIES(nnstreamer_sink_example_play gstapp-1.0 ${pkgs_LIBRARIES})
+TARGET_INCLUDE_DIRECTORIES(nnstreamer_sink_example_play PUBLIC ${pkgs_INCLUDE_DIRS})
+TARGET_COMPILE_OPTIONS(nnstreamer_sink_example_play PUBLIC ${pkgs_CFLAGS_OTHER})
+
+# do not install sample app
+# INSTALL(TARGETS nnstreamer_sink_example nnstreamer_sink_example_play RUNTIME DESTINATION ${EXEC_PREFIX})
diff --git a/nnstreamer_example/example_sink/nnstreamer_sink_example.c b/nnstreamer_example/example_sink/nnstreamer_sink_example.c
new file mode 100644 (file)
index 0000000..2347739
--- /dev/null
@@ -0,0 +1,304 @@
+/**
+ * @file       nnstreamer_sink_example.c
+ * @date       3 July 2018
+ * @brief      Sample code for tensor sink plugin
+ * @see                https://github.sec.samsung.net/STAR/nnstreamer
+ * @author     Jaeyun Jung <jy1210.jung@samsung.com>
+ *
+ * Simple example to init tensor sink element and get data.
+ *
+ * Run example :
+ * ./nnstreamer_sink_example --gst-plugin-path=<nnstreamer plugin path>
+ */
+
+#include <gst/gst.h>
+
+/**
+ * @brief Macro for debug mode.
+ */
+#ifndef DBG
+#define DBG TRUE
+#endif
+
+/**
+ * @brief Macro for debug message.
+ */
+#define _print_log(...) if (DBG) g_message (__VA_ARGS__)
+
+/**
+ * @brief Macro to check error case.
+ */
+#define _check_cond_err(cond) \
+  if (!(cond)) { \
+    _print_log ("app failed! [line : %d]", __LINE__); \
+    goto error; \
+  }
+
+/**
+ * @brief Data structure for app.
+ */
+typedef struct
+{
+  GMainLoop *loop; /**< main event loop */
+  GstElement *pipeline; /**< gst pipeline for test */
+  GstBus *bus; /**< gst bus for test */
+  GstElement *sink; /**< tensor sink element */
+
+  guint received; /**< received buffer count */
+} AppData;
+
+/**
+ * @brief Data for pipeline and result.
+ */
+static AppData g_app_data;
+
+/**
+ * @brief Free resources in app data.
+ */
+static void
+_free_app_data (void)
+{
+  if (g_app_data.loop) {
+    g_main_loop_unref (g_app_data.loop);
+    g_app_data.loop = NULL;
+  }
+
+  if (g_app_data.bus) {
+    gst_bus_remove_signal_watch (g_app_data.bus);
+    gst_object_unref (g_app_data.bus);
+    g_app_data.bus = NULL;
+  }
+
+  if (g_app_data.sink) {
+    gst_object_unref (g_app_data.sink);
+    g_app_data.sink = NULL;
+  }
+
+  if (g_app_data.pipeline) {
+    gst_object_unref (g_app_data.pipeline);
+    g_app_data.pipeline = NULL;
+  }
+}
+
+/**
+ * @brief Function to print error message.
+ */
+static void
+_parse_err_message (GstMessage * message)
+{
+  gchar *debug;
+  GError *error;
+
+  switch (GST_MESSAGE_TYPE (message)) {
+    case GST_MESSAGE_ERROR:
+      gst_message_parse_error (message, &error, &debug);
+      break;
+
+    case GST_MESSAGE_WARNING:
+      gst_message_parse_warning (message, &error, &debug);
+      break;
+
+    default:
+      return;
+  }
+
+  gst_object_default_error (GST_MESSAGE_SRC (message), error, debug);
+  g_error_free (error);
+  g_free (debug);
+}
+
+/**
+ * @brief Callback for message.
+ */
+static void
+_message_cb (GstBus * bus, GstMessage * message, gpointer user_data)
+{
+  switch (GST_MESSAGE_TYPE (message)) {
+    case GST_MESSAGE_EOS:
+      _print_log ("received eos message");
+      g_main_loop_quit (g_app_data.loop);
+      break;
+
+    case GST_MESSAGE_ERROR:
+      _print_log ("received error message");
+      _parse_err_message (message);
+      g_main_loop_quit (g_app_data.loop);
+      break;
+
+    case GST_MESSAGE_WARNING:
+      _print_log ("received warning message");
+      _parse_err_message (message);
+      break;
+
+    case GST_MESSAGE_STREAM_START:
+      _print_log ("received start message");
+      break;
+
+    default:
+      break;
+  }
+}
+
+/**
+ * @brief Callback for signal new-data.
+ */
+static void
+_new_data_cb (GstElement * element, GstBuffer * buffer, gpointer user_data)
+{
+  g_app_data.received++;
+  _print_log ("new data callback [%d]", g_app_data.received);
+
+  /* example to get data */
+  if (DBG) {
+    GstMemory *mem;
+    GstMapInfo info;
+    guint i;
+    guint num_mems;
+
+    num_mems = gst_buffer_n_memory (buffer);
+    for (i = 0; i < num_mems; i++) {
+      mem = gst_buffer_peek_memory (buffer, i);
+
+      if (gst_memory_map (mem, &info, GST_MAP_READ)) {
+        /* check data (info.data, info.size) */
+        _print_log ("received %zd", info.size);
+
+        gst_memory_unmap (mem, &info);
+      }
+    }
+  }
+
+  /* example to get negotiated caps */
+  if (DBG) {
+    GstPad *sink_pad;
+    GstCaps *caps;
+
+    sink_pad = gst_element_get_static_pad (g_app_data.sink, "sink");
+
+    if (sink_pad) {
+      caps = gst_pad_get_current_caps (sink_pad);
+
+      if (caps) {
+        guint caps_size, i;
+
+        caps_size = gst_caps_get_size (caps);
+        _print_log ("caps size is %d", caps_size);
+
+        for (i = 0; i < caps_size; i++) {
+          GstStructure *structure = gst_caps_get_structure (caps, i);
+          gchar *str = gst_structure_to_string (structure);
+
+          _print_log ("[%d] %s", i, str);
+          g_free (str);
+        }
+
+        gst_caps_unref (caps);
+      }
+
+      gst_object_unref (sink_pad);
+    }
+  }
+}
+
+/**
+ * @brief Callback for signal stream-start.
+ */
+static void
+_stream_start_cb (GstElement * element, GstBuffer * buffer, gpointer user_data)
+{
+  _print_log ("stream start callback");
+}
+
+/**
+ * @brief Callback for signal eos.
+ */
+static void
+_eos_cb (GstElement * element, GstBuffer * buffer, gpointer user_data)
+{
+  _print_log ("eos callback");
+}
+
+/**
+ * @brief Main function.
+ */
+int
+main (int argc, char **argv)
+{
+  const guint num_buffers = 100;
+
+  gchar *str_pipeline;
+  gulong handle_id;
+  GstStateChangeReturn state_ret;
+
+  /* init gstreamer */
+  gst_init (&argc, &argv);
+
+  /* init app variable */
+  g_app_data.received = 0;
+
+  /* main loop and pipeline */
+  g_app_data.loop = g_main_loop_new (NULL, FALSE);
+  _check_cond_err (g_app_data.loop != NULL);
+
+  /* 640x480 30fps for test */
+  str_pipeline =
+      g_strdup_printf
+      ("videotestsrc num-buffers=%d ! video/x-raw,width=640,height=480 ! videoconvert ! video/x-raw,format=RGB ! tensor_converter ! tensor_sink name=tensor_sink",
+      num_buffers);
+  g_app_data.pipeline = gst_parse_launch (str_pipeline, NULL);
+  g_free (str_pipeline);
+  _check_cond_err (g_app_data.pipeline != NULL);
+
+  /* message callback */
+  g_app_data.bus = gst_element_get_bus (g_app_data.pipeline);
+  _check_cond_err (g_app_data.bus != NULL);
+
+  gst_bus_add_signal_watch (g_app_data.bus);
+  handle_id = g_signal_connect (g_app_data.bus, "message",
+      (GCallback) _message_cb, NULL);
+  _check_cond_err (handle_id > 0);
+
+  /* get tensor sink element using name */
+  g_app_data.sink =
+      gst_bin_get_by_name (GST_BIN (g_app_data.pipeline), "tensor_sink");
+  _check_cond_err (g_app_data.sink != NULL);
+
+  if (DBG) {
+    /* print logs, default TRUE */
+    g_object_set (g_app_data.sink, "silent", (gboolean) FALSE, NULL);
+  }
+
+  /* enable emit-signal, default TRUE */
+  g_object_set (g_app_data.sink, "emit-signal", (gboolean) TRUE, NULL);
+
+  /* tensor sink signal : new data callback */
+  handle_id = g_signal_connect (g_app_data.sink, "new-data",
+      (GCallback) _new_data_cb, NULL);
+  _check_cond_err (handle_id > 0);
+
+  /* tensor sink signal : stream-start callback, optional */
+  handle_id = g_signal_connect (g_app_data.sink, "stream-start",
+      (GCallback) _stream_start_cb, NULL);
+  _check_cond_err (handle_id > 0);
+
+  /* tensor sink signal : eos callback, optional */
+  handle_id = g_signal_connect (g_app_data.sink, "eos",
+      (GCallback) _eos_cb, NULL);
+  _check_cond_err (handle_id > 0);
+
+  /* start pipeline */
+  state_ret = gst_element_set_state (g_app_data.pipeline, GST_STATE_PLAYING);
+  _check_cond_err (state_ret != GST_STATE_CHANGE_FAILURE);
+
+  g_main_loop_run (g_app_data.loop);
+
+  /* quit when received eos message */
+  state_ret = gst_element_set_state (g_app_data.pipeline, GST_STATE_NULL);
+  _check_cond_err (state_ret != GST_STATE_CHANGE_FAILURE);
+
+  _print_log ("total received %d", g_app_data.received);
+
+error:
+  _free_app_data ();
+  return 0;
+}
diff --git a/nnstreamer_example/example_sink/nnstreamer_sink_example_play.c b/nnstreamer_example/example_sink/nnstreamer_sink_example_play.c
new file mode 100644 (file)
index 0000000..bbaa2bf
--- /dev/null
@@ -0,0 +1,326 @@
+/**
+ * @file       nnstreamer_sink_example_play.c
+ * @date       5 July 2018
+ * @brief      Sample code for tensor sink plugin
+ * @see                https://github.sec.samsung.net/STAR/nnstreamer
+ * @author     Jaeyun Jung <jy1210.jung@samsung.com>
+ *
+ * This sample app shows video frame using two pipelines.
+ *
+ * [1st pipeline : videotestsrc-videoconvert-tensor_converter-tensor_sink]
+ * push buffer to appsrc
+ * [2nd pipeline : appsrc-tensordec-videoconvert-xvimagesink]
+ *
+ * Run example :
+ * ./nnstreamer_sink_example_play --gst-plugin-path=<nnstreamer plugin path>
+ */
+
+#include <gst/gst.h>
+#include <gst/app/gstappsrc.h>
+
+/**
+ * @brief Macro for debug mode.
+ */
+#ifndef DBG
+#define DBG TRUE
+#endif
+
+/**
+ * @brief Macro for debug message.
+ */
+#define _print_log(...) if (DBG) g_message (__VA_ARGS__)
+
+/**
+ * @brief Macro to check error case.
+ */
+#define _check_cond_err(cond) \
+  if (!(cond)) { \
+    _print_log ("app failed! [line : %d]", __LINE__); \
+    goto error; \
+  }
+
+/**
+ * @brief Data structure for app.
+ */
+typedef struct
+{
+  GMainLoop *loop; /**< main event loop */
+  GstElement *data_pipeline; /**< gst pipeline for data stream */
+  GstBus *data_bus; /**< gst bus for data pipeline */
+  GstElement *player_pipeline; /**< gst pipeline for player */
+  GstBus *player_bus; /**< gst bus for player pipeline */
+  GstElement *tensor_sink; /**< tensor sink element */
+  GstElement *player_src; /**< player source element */
+
+  guint received; /**< received buffer count */
+  gboolean set_caps; /**< caps passed to player pipeline */
+} AppData;
+
+/**
+ * @brief Data for pipeline and result.
+ */
+static AppData g_app_data;
+
+/**
+ * @brief Free resources in app data.
+ */
+static void
+_free_app_data (void)
+{
+  if (g_app_data.loop) {
+    g_main_loop_unref (g_app_data.loop);
+    g_app_data.loop = NULL;
+  }
+
+  if (g_app_data.data_bus) {
+    gst_bus_remove_signal_watch (g_app_data.data_bus);
+    gst_object_unref (g_app_data.data_bus);
+    g_app_data.data_bus = NULL;
+  }
+
+  if (g_app_data.player_bus) {
+    gst_bus_remove_signal_watch (g_app_data.player_bus);
+    gst_object_unref (g_app_data.player_bus);
+    g_app_data.player_bus = NULL;
+  }
+
+  if (g_app_data.tensor_sink) {
+    gst_object_unref (g_app_data.tensor_sink);
+    g_app_data.tensor_sink = NULL;
+  }
+
+  if (g_app_data.player_src) {
+    gst_object_unref (g_app_data.player_src);
+    g_app_data.player_src = NULL;
+  }
+
+  if (g_app_data.data_pipeline) {
+    gst_object_unref (g_app_data.data_pipeline);
+    g_app_data.data_pipeline = NULL;
+  }
+
+  if (g_app_data.player_pipeline) {
+    gst_object_unref (g_app_data.player_pipeline);
+    g_app_data.player_pipeline = NULL;
+  }
+}
+
+/**
+ * @brief Function to print error message.
+ */
+static void
+_parse_err_message (GstMessage * message)
+{
+  gchar *debug;
+  GError *error;
+
+  switch (GST_MESSAGE_TYPE (message)) {
+    case GST_MESSAGE_ERROR:
+      gst_message_parse_error (message, &error, &debug);
+      break;
+
+    case GST_MESSAGE_WARNING:
+      gst_message_parse_warning (message, &error, &debug);
+      break;
+
+    default:
+      return;
+  }
+
+  gst_object_default_error (GST_MESSAGE_SRC (message), error, debug);
+  g_error_free (error);
+  g_free (debug);
+}
+
+/**
+ * @brief Callback for message.
+ */
+static void
+_data_message_cb (GstBus * bus, GstMessage * message, gpointer user_data)
+{
+  switch (GST_MESSAGE_TYPE (message)) {
+    case GST_MESSAGE_EOS:
+      _print_log ("[data] received eos message");
+      gst_app_src_end_of_stream (GST_APP_SRC (g_app_data.player_src));
+      break;
+
+    case GST_MESSAGE_ERROR:
+      _print_log ("[data] received error message");
+      _parse_err_message (message);
+      g_main_loop_quit (g_app_data.loop);
+      break;
+
+    case GST_MESSAGE_WARNING:
+      _print_log ("[data] received warning message");
+      _parse_err_message (message);
+      break;
+
+    case GST_MESSAGE_STREAM_START:
+      _print_log ("[data] received start message");
+      break;
+
+    default:
+      break;
+  }
+}
+
+/**
+ * @brief Callback for message.
+ */
+static void
+_player_message_cb (GstBus * bus, GstMessage * message, gpointer user_data)
+{
+  switch (GST_MESSAGE_TYPE (message)) {
+    case GST_MESSAGE_EOS:
+      _print_log ("[player] received eos message");
+      g_main_loop_quit (g_app_data.loop);
+      break;
+
+    case GST_MESSAGE_ERROR:
+      _print_log ("[player] received error message");
+      _parse_err_message (message);
+      g_main_loop_quit (g_app_data.loop);
+      break;
+
+    case GST_MESSAGE_WARNING:
+      _print_log ("[player] received warning message");
+      _parse_err_message (message);
+      break;
+
+    case GST_MESSAGE_STREAM_START:
+      _print_log ("[player] received start message");
+      break;
+
+    default:
+      break;
+  }
+}
+
+/**
+ * @brief Callback for signal new-data.
+ */
+static void
+_new_data_cb (GstElement * element, GstBuffer * buffer, gpointer user_data)
+{
+  g_app_data.received++;
+  _print_log ("new data callback [%d]", g_app_data.received);
+
+  if (!g_app_data.set_caps) {
+    GstPad *sink_pad;
+    GstCaps *caps;
+
+    sink_pad = gst_element_get_static_pad (g_app_data.tensor_sink, "sink");
+
+    if (sink_pad) {
+      caps = gst_pad_get_current_caps (sink_pad);
+
+      if (caps) {
+        gst_app_src_set_caps (GST_APP_SRC (g_app_data.player_src), caps);
+
+        gst_caps_unref (caps);
+        g_app_data.set_caps = TRUE;
+      }
+
+      gst_object_unref (sink_pad);
+    }
+  }
+
+  gst_app_src_push_buffer (GST_APP_SRC (g_app_data.player_src),
+      gst_buffer_copy (buffer));
+}
+
+/**
+ * @brief Main function.
+ */
+int
+main (int argc, char **argv)
+{
+  const guint num_buffers = 200;
+  const guint width = 640;
+  const guint height = 480;
+
+  gchar *str_pipeline;
+  gulong handle_id;
+
+  /* init gstreamer */
+  gst_init (&argc, &argv);
+
+  /* init app variable */
+  g_app_data.received = 0;
+  g_app_data.set_caps = FALSE;
+
+  /* main loop and pipeline */
+  g_app_data.loop = g_main_loop_new (NULL, FALSE);
+  _check_cond_err (g_app_data.loop != NULL);
+
+  str_pipeline =
+      g_strdup_printf
+      ("videotestsrc num-buffers=%d ! video/x-raw,width=%d,height=%d ! videoconvert ! video/x-raw,format=RGB ! tensor_converter ! tensor_sink name=tensor_sink",
+      num_buffers, width, height);
+  g_app_data.data_pipeline = gst_parse_launch (str_pipeline, NULL);
+  g_free (str_pipeline);
+  _check_cond_err (g_app_data.data_pipeline != NULL);
+
+  /* data message callback */
+  g_app_data.data_bus = gst_element_get_bus (g_app_data.data_pipeline);
+  _check_cond_err (g_app_data.data_bus != NULL);
+
+  gst_bus_add_signal_watch (g_app_data.data_bus);
+  handle_id = g_signal_connect (g_app_data.data_bus, "message",
+      (GCallback) _data_message_cb, NULL);
+  _check_cond_err (handle_id > 0);
+
+  /* get tensor sink element using name */
+  g_app_data.tensor_sink =
+      gst_bin_get_by_name (GST_BIN (g_app_data.data_pipeline), "tensor_sink");
+  _check_cond_err (g_app_data.tensor_sink != NULL);
+
+  if (DBG) {
+    /* print logs, default TRUE */
+    g_object_set (g_app_data.tensor_sink, "silent", (gboolean) FALSE, NULL);
+  }
+
+  /* enable emit-signal, default TRUE */
+  g_object_set (g_app_data.tensor_sink, "emit-signal", (gboolean) TRUE, NULL);
+
+  /* tensor sink signal : new data callback */
+  handle_id = g_signal_connect (g_app_data.tensor_sink, "new-data",
+      (GCallback) _new_data_cb, NULL);
+  _check_cond_err (handle_id > 0);
+
+  /* init player pipeline */
+  str_pipeline =
+      g_strdup_printf
+      ("appsrc name=player_src ! tensordec ! videoconvert ! xvimagesink");
+  g_app_data.player_pipeline = gst_parse_launch (str_pipeline, NULL);
+  g_free (str_pipeline);
+  _check_cond_err (g_app_data.player_pipeline != NULL);
+
+  /* player message callback */
+  g_app_data.player_bus = gst_element_get_bus (g_app_data.player_pipeline);
+  _check_cond_err (g_app_data.player_bus != NULL);
+
+  gst_bus_add_signal_watch (g_app_data.player_bus);
+  handle_id = g_signal_connect (g_app_data.player_bus, "message",
+      (GCallback) _player_message_cb, NULL);
+  _check_cond_err (handle_id > 0);
+
+  /* player source element */
+  g_app_data.player_src =
+      gst_bin_get_by_name (GST_BIN (g_app_data.player_pipeline), "player_src");
+  _check_cond_err (g_app_data.player_src != NULL);
+
+  /* start pipeline */
+  gst_element_set_state (g_app_data.data_pipeline, GST_STATE_PLAYING);
+  gst_element_set_state (g_app_data.player_pipeline, GST_STATE_PLAYING);
+
+  g_main_loop_run (g_app_data.loop);
+
+  /* quit when received eos message */
+  gst_element_set_state (g_app_data.data_pipeline, GST_STATE_NULL);
+  gst_element_set_state (g_app_data.player_pipeline, GST_STATE_NULL);
+
+error:
+  _free_app_data ();
+  return 0;
+}