[Tizen/Api] add appsink to handle sink event
authorJaeyun <jy1210.jung@samsung.com>
Mon, 3 Jun 2019 11:16:56 +0000 (20:16 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Tue, 4 Jun 2019 07:39:11 +0000 (03:39 -0400)
1. Register the appsink as a sink element. User can register appsink and tensor-sink as a sink element.
2. Move logging macro to private header.
3. Add testcases to test appsink element.

Signed-off-by: Jaeyun Jung <jy1210.jung@samsung.com>
tests/tizen_capi/unittest_tizen_capi.cpp
tizen-api/include/nnstreamer.h
tizen-api/include/tizen-api-private.h
tizen-api/src/tizen-api-pipeline.c

index 8cd488e..fca8ab5 100644 (file)
@@ -404,6 +404,58 @@ TEST (nnstreamer_capi_sink, dummy_01)
 
 /**
  * @brief Test NNStreamer pipeline sink
+ */
+TEST (nnstreamer_capi_sink, dummy_02)
+{
+  nns_pipeline_h handle;
+  nns_pipeline_state_e state;
+  nns_sink_h sinkhandle;
+  gchar *pipeline;
+  int status;
+  guint *count_sink;
+
+  /* pipeline with appsink */
+  pipeline = g_strdup ("videotestsrc num-buffers=3 ! videoconvert ! valve name=valvex ! tensor_converter ! appsink name=sinkx");
+
+  count_sink = (guint *) g_malloc (sizeof (guint));
+  *count_sink = 0;
+
+  status = nns_pipeline_construct (pipeline, &handle);
+  EXPECT_EQ (status, NNS_ERROR_NONE);
+
+  status = nns_pipeline_sink_register (handle, "sinkx", nns_sink_callback_count, &sinkhandle, count_sink);
+  EXPECT_EQ (status, NNS_ERROR_NONE);
+
+  status = nns_pipeline_start (handle);
+  EXPECT_EQ (status, NNS_ERROR_NONE);
+
+  g_usleep (100000); /* 100ms. Let a few frames flow. */
+  status = nns_pipeline_get_state (handle, &state);
+  EXPECT_EQ (status, NNS_ERROR_NONE);
+  EXPECT_EQ (state, NNS_PIPELINE_STATE_PLAYING);
+
+  status = nns_pipeline_stop (handle);
+  EXPECT_EQ (status, NNS_ERROR_NONE);
+  g_usleep (10000); /* 10ms. Wait a bit. */
+
+  status = nns_pipeline_get_state (handle, &state);
+  EXPECT_EQ (status, NNS_ERROR_NONE);
+  EXPECT_EQ (state, NNS_PIPELINE_STATE_PAUSED);
+
+  status = nns_pipeline_sink_unregister (sinkhandle);
+  EXPECT_EQ (status, NNS_ERROR_NONE);
+
+  status = nns_pipeline_destroy (handle);
+  EXPECT_EQ (status, NNS_ERROR_NONE);
+
+  EXPECT_TRUE (*count_sink > 0U);
+
+  g_free (pipeline);
+  g_free (count_sink);
+}
+
+/**
+ * @brief Test NNStreamer pipeline sink
  * @detail Failure case to register callback with invalid param.
  */
 TEST (nnstreamer_capi_sink, failure_01)
index d45ae34..e6582f7 100644 (file)
@@ -279,6 +279,7 @@ int nns_pipeline_stop (nns_pipeline_h pipe);
  * @param[in] pdata Private data for the callback. This value is passed to the callback when it's invoked.
  * @return @c 0 on success. otherwise a negative error value
  * @retval #NNS_ERROR_NONE Successful
+ * @retval #NNS_ERROR_STREAMS_PIPE Failed to connect a signal to sink element.
  * @retval #NNS_ERROR_INVALID_PARAMETER Given parameter is invalid. (pipe is NULL, sink_name is not found, or sink_name has an invalid type.)
  */
 int nns_pipeline_sink_register (nns_pipeline_h pipe, const char *sink_name, nns_sink_cb cb, nns_sink_h *h, void *pdata);
index 420e055..8f0e3ea 100644 (file)
 #include <gst/gst.h>
 #include "nnstreamer.h"
 #include <tizen_error.h>
+#include <dlog.h>
 #include <nnstreamer/tensor_typedef.h>
 #include <gst/app/gstappsrc.h>
 
+#define DLOG_TAG "nnstreamer-capi-pipeline"
+
+#define dlogi(...) \
+    dlog_print (DLOG_INFO, DLOG_TAG, __VA_ARGS__)
+
+#define dlogw(...) \
+    dlog_print (DLOG_WARN, DLOG_TAG, __VA_ARGS__)
+
+#define dloge(...) \
+    dlog_print (DLOG_ERROR, DLOG_TAG, __VA_ARGS__)
+
 #ifdef __cplusplus
 extern "C" {
 #endif /* __cplusplus */
@@ -42,8 +54,9 @@ extern "C" {
 typedef enum {
   NNSAPI_UNKNOWN = 0x0,
   NNSAPI_SINK = 0x1,
-  NNSAPI_SRC = 0x2,
-  NNSAPI_VALVE = 0x3,
+  NNSAPI_APP_SRC = 0x2,
+  NNSAPI_APP_SINK = 0x3,
+  NNSAPI_VALVE = 0x4,
   NNSAPI_SWITCH_INPUT = 0x8,
   NNSAPI_SWITCH_OUTPUT = 0x9,
 } elementType;
@@ -68,6 +81,7 @@ typedef struct _element {
 
   GList *handles;
   int maxid; /**< to allocate id for each handle */
+  gulong handle_id;
 
   GMutex lock; /**< Lock for internal values */
 } element;
index fc876ab..b805b2c 100644 (file)
 #include <glib-object.h>        /* Get GType from GObject Instances */
 #include <gmodule.h>
 #include <tizen-api-private.h>
-#include <dlog.h>
+
 #include <nnstreamer/tensor_typedef.h>
 #include <nnstreamer/nnstreamer_plugin_api.h>
 
 #include <gst/gstbuffer.h>
 #include <gst/app/app.h>        /* To push data to pipeline */
 
-#define DLOG_TAG "nnstreamer-capi-pipeline"
-
-#define dlogi(...) \
-    dlog_print (DLOG_INFO, DLOG_TAG, __VA_ARGS__)
-
-#define dlogw(...) \
-    dlog_print (DLOG_WARN, DLOG_TAG, __VA_ARGS__)
-
-#define dloge(...) \
-    dlog_print (DLOG_ERROR, DLOG_TAG, __VA_ARGS__)
-
 #define handle_init(type, name, h) \
   nns_##type *name= (h); \
   nns_pipeline *p; \
@@ -93,6 +82,7 @@ construct_element (GstElement * e, nns_pipeline * p, const char *name,
   gst_tensors_info_init (&ret->tensorsinfo);
   ret->size = 0;
   ret->maxid = 0;
+  ret->handle_id = 0;
   g_mutex_init (&ret->lock);
   return ret;
 }
@@ -257,6 +247,25 @@ cb_sink_event (GstElement * e, GstBuffer * b, gpointer data)
 }
 
 /**
+ * @brief Handle a appsink element for registered nns_sink_cb
+ */
+static GstFlowReturn
+cb_appsink_new_sample (GstElement * e, gpointer user_data)
+{
+  GstSample *sample;
+  GstBuffer *buffer;
+
+  /* get the sample from appsink */
+  sample = gst_app_sink_pull_sample (GST_APP_SINK (e));
+  buffer = gst_sample_get_buffer (sample);
+
+  cb_sink_event (e, buffer, user_data);
+
+  gst_sample_unref (sample);
+  return GST_FLOW_OK;
+}
+
+/**
  * @brief Private function for nns_pipeline_destroy, cleaning up nodes in namednodes
  */
 static void
@@ -360,11 +369,10 @@ nns_pipeline_construct (const char *pipeline_description, nns_pipeline_h * pipe)
               if (G_TYPE_CHECK_INSTANCE_TYPE (elem,
                       gst_element_factory_get_element_type (tensor_sink))) {
                 e = construct_element (elem, pipe_h, name, NNSAPI_SINK);
-                g_object_set (elem, "emit-signal", (gboolean) TRUE, NULL);
-                g_signal_connect (elem, "new-data", (GCallback) cb_sink_event,
-                    e);
               } else if (G_TYPE_CHECK_INSTANCE_TYPE (elem, GST_TYPE_APP_SRC)) {
-                e = construct_element (elem, pipe_h, name, NNSAPI_SRC);
+                e = construct_element (elem, pipe_h, name, NNSAPI_APP_SRC);
+              } else if (G_TYPE_CHECK_INSTANCE_TYPE (elem, GST_TYPE_APP_SINK)) {
+                e = construct_element (elem, pipe_h, name, NNSAPI_APP_SINK);
               } else if (G_TYPE_CHECK_INSTANCE_TYPE (elem,
                       gst_element_factory_get_element_type (valve))) {
                 e = construct_element (elem, pipe_h, name, NNSAPI_VALVE);
@@ -578,12 +586,42 @@ nns_pipeline_sink_register (nns_pipeline_h pipe, const char *sink_name,
     goto unlock_return;
   }
 
-  if (elem->type != NNSAPI_SINK) {
-    dloge ("The element [%s] in the pipeline is not a tensor_sink.", sink_name);
+  if (elem->type != NNSAPI_SINK && elem->type != NNSAPI_APP_SINK) {
+    dloge ("The element [%s] in the pipeline is not a sink element.",
+        sink_name);
     ret = NNS_ERROR_INVALID_PARAMETER;
     goto unlock_return;
   }
 
+  if (elem->handle_id > 0) {
+    dlogw ("Sink callback is already registered.");
+    ret = NNS_ERROR_NONE;
+    goto unlock_return;
+  }
+
+  /* set callback for new data */
+  if (elem->type == NNSAPI_SINK) {
+    /* tensor_sink */
+    g_object_set (G_OBJECT (elem->element), "emit-signal", (gboolean) TRUE,
+        NULL);
+    elem->handle_id =
+        g_signal_connect (elem->element, "new-data", G_CALLBACK (cb_sink_event),
+        elem);
+  } else {
+    /* appsink */
+    g_object_set (G_OBJECT (elem->element), "emit-signals", (gboolean) TRUE,
+        NULL);
+    elem->handle_id =
+        g_signal_connect (elem->element, "new-sample",
+        G_CALLBACK (cb_appsink_new_sample), elem);
+  }
+
+  if (elem->handle_id == 0) {
+    dloge ("Failed to connect a signal to the element [%s].", sink_name);
+    ret = NNS_ERROR_STREAMS_PIPE;
+    goto unlock_return;
+  }
+
   *h = g_new0 (nns_sink, 1);
   sink = *h;
 
@@ -613,6 +651,11 @@ nns_pipeline_sink_unregister (nns_sink_h h)
 {
   handle_init (sink, sink, h);
 
+  if (elem->handle_id > 0) {
+    g_signal_handler_disconnect (elem->element, elem->handle_id);
+    elem->handle_id = 0;
+  }
+
   elem->handles = g_list_remove (elem->handles, sink);
 
   handle_exit (h);
@@ -686,8 +729,9 @@ nns_pipeline_src_get_handle (nns_pipeline_h pipe, const char *src_name,
     goto unlock_return;
   }
 
-  if (elem->type != NNSAPI_SRC) {
-    dloge ("The element [%s] in the pipeline is not a tensor_src.", src_name);
+  if (elem->type != NNSAPI_APP_SRC) {
+    dloge ("The element [%s] in the pipeline is not a source element.",
+        src_name);
     ret = NNS_ERROR_INVALID_PARAMETER;
     goto unlock_return;
   }