[C-Api] callback for pipeline state
authorJaeyun <jy1210.jung@samsung.com>
Thu, 4 Jul 2019 07:54:34 +0000 (16:54 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Mon, 8 Jul 2019 10:22:29 +0000 (19:22 +0900)
Add callback to notify the state change.
If it is required to get the state, add callback when constructing the pipeline.

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

index 58fd984..8f55df9 100644 (file)
@@ -155,6 +155,10 @@ typedef struct _ml_pipeline_element {
  */
 struct _ml_pipeline {
   GstElement *element;    /**< The pipeline itself (GstPipeline) */
+  GstBus *bus;            /**< The bus of the pipeline */
+  gulong signal_msg;      /**< The message signal (connected to bus) */
+  ml_pipeline_state_cb cb;
+  void *pdata;
   GMutex lock;            /**< Lock for pipeline operations */
   GHashTable *namednodes; /**< hash table of "element"s. */
 };
index c278a32..049f110 100644 (file)
@@ -219,6 +219,15 @@ typedef enum {
  */
 typedef void (*ml_pipeline_sink_cb) (const ml_tensors_data_h data, const ml_tensors_info_h info, void *user_data);
 
+/**
+ * @brief Callback for the change of pipeline state.
+ * @details If an application wants to get the change of pipeline state, use this callback. This callback can be registered when constructing the pipeline using ml_pipeline_construct(). Do not spend too much time in the callback.
+ * @since_tizen 5.5
+ * @param[out] state The new state of the pipeline.
+ * @param[out] user_data User application's private data.
+ */
+typedef void (*ml_pipeline_state_cb) (ml_pipeline_state_e state, void *user_data);
+
 /****************************************************
  ** NNStreamer Pipeline Construction (gst-parse)   **
  ****************************************************/
@@ -228,13 +237,15 @@ typedef void (*ml_pipeline_sink_cb) (const ml_tensors_data_h data, const ml_tens
  * @since_tizen 5.5
  * @remarks If the function succeeds, @a pipe handle must be released using ml_pipeline_destroy().
  * @param[in] pipeline_description The pipeline description compatible with GStreamer gst_parse_launch(). Refer to GStreamer manual or NNStreamer (github.com/nnsuite/nnstreamer) documentation for examples and the grammar.
+ * @param[in] cb The function to be called when the pipeline state is changed. You may set NULL if it's not required.
+ * @param[in] user_data Private data for the callback. This value is passed to the callback when it's invoked.
  * @param[out] pipe The NNStreamer pipeline handler from the given description.
  * @return @c 0 on success. Otherwise a negative error value.
  * @retval #ML_ERROR_NONE Successful
  * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid. (pipe is NULL?)
  * @retval #ML_ERROR_STREAMS_PIPE Pipeline construction is failed because of wrong parameter or initialization failure.
  */
-int ml_pipeline_construct (const char *pipeline_description, ml_pipeline_h *pipe);
+int ml_pipeline_construct (const char *pipeline_description, ml_pipeline_state_cb cb, void *user_data, ml_pipeline_h *pipe);
 
 /**
  * @brief Destroys the pipeline.
@@ -265,9 +276,10 @@ int ml_pipeline_get_state (ml_pipeline_h pipe, ml_pipeline_state_e *state);
  ** NNStreamer Pipeline Start/Stop Control         **
  ****************************************************/
 /**
- * @brief Starts the pipeline.
+ * @brief Starts the pipeline, asynchronously.
  * @details The pipeline handle returned by ml_pipeline_construct() is started.
  *          Note that this is asynchronous function. State might be "pending".
+ *          If you need to get the changed state, add a callback while constructing a pipeline with ml_pipeline_construct().
  * @since_tizen 5.5
  * @param[in] pipe The pipeline handle.
  * @return @c 0 on success. Otherwise a negative error value.
@@ -278,9 +290,10 @@ int ml_pipeline_get_state (ml_pipeline_h pipe, ml_pipeline_state_e *state);
 int ml_pipeline_start (ml_pipeline_h pipe);
 
 /**
- * @brief Stops the pipeline.
+ * @brief Stops the pipeline, asynchronously.
  * @details The pipeline handle returned by ml_pipeline_construct() is stopped.
  *          Note that this is asynchronous function. State might be "pending".
+ *          If you need to get the changed state, add a callback while constructing a pipeline with ml_pipeline_construct().
  * @since_tizen 5.5
  * @param[in] pipe The pipeline to be stopped.
  * @return @c 0 on success. Otherwise a negative error value.
index 934b7c3..ba29d9d 100644 (file)
@@ -265,6 +265,40 @@ cb_appsink_new_sample (GstElement * e, gpointer user_data)
 }
 
 /**
+ * @brief Callback for bus message.
+ */
+static void
+cb_bus_sync_message (GstBus * bus, GstMessage * message, gpointer user_data)
+{
+  ml_pipeline *pipe_h;
+
+  pipe_h = (ml_pipeline *) user_data;
+
+  if (pipe_h == NULL)
+    return;
+
+  switch (GST_MESSAGE_TYPE (message)) {
+    case GST_MESSAGE_STATE_CHANGED:
+      if (GST_MESSAGE_SRC (message) == GST_OBJECT_CAST (pipe_h->element)) {
+        GstState old_state, new_state;
+
+        gst_message_parse_state_changed (message, &old_state, &new_state, NULL);
+
+        ml_logd ("The pipeline state changed from %s to %s.",
+            gst_element_state_get_name (old_state),
+            gst_element_state_get_name (new_state));
+
+        if (pipe_h->cb) {
+          pipe_h->cb (new_state, pipe_h->pdata);
+        }
+      }
+      break;
+    default:
+      break;
+  }
+}
+
+/**
  * @brief Private function for ml_pipeline_destroy, cleaning up nodes in namednodes
  */
 static void
@@ -299,7 +333,8 @@ cleanup_node (gpointer data)
  * @brief Construct the pipeline (more info in nnstreamer.h)
  */
 int
-ml_pipeline_construct (const char *pipeline_description, ml_pipeline_h * pipe)
+ml_pipeline_construct (const char *pipeline_description,
+    ml_pipeline_state_cb cb, void *user_data, ml_pipeline_h * pipe)
 {
   GError *err = NULL;
   GstElement *pipeline;
@@ -343,6 +378,18 @@ ml_pipeline_construct (const char *pipeline_description, ml_pipeline_h * pipe)
   g_assert (GST_IS_PIPELINE (pipeline));
   pipe_h->element = pipeline;
   g_mutex_init (&pipe_h->lock);
+
+  /* bus and message callback */
+  pipe_h->bus = gst_element_get_bus (pipeline);
+  g_assert (pipe_h->bus);
+
+  gst_bus_enable_sync_message_emission (pipe_h->bus);
+  pipe_h->signal_msg = g_signal_connect (pipe_h->bus, "sync-message",
+      G_CALLBACK (cb_bus_sync_message), pipe_h);
+
+  pipe_h->cb = cb;
+  pipe_h->pdata = user_data;
+
   g_mutex_lock (&pipe_h->lock);
 
   pipe_h->namednodes =
@@ -459,6 +506,9 @@ ml_pipeline_destroy (ml_pipeline_h pipe)
   g_usleep (50000);             /* do 50ms sleep until we have it implemented. Let them complete. And hope they don't call start(). */
   g_mutex_lock (&p->lock);
 
+  g_signal_handler_disconnect (p->bus, p->signal_msg);
+  gst_object_unref (p->bus);
+
   /** Destroy registered callback handles */
   g_hash_table_remove_all (p->namednodes);
 
index feb0c8c..2ae92b7 100644 (file)
@@ -216,7 +216,7 @@ ml_single_open (ml_single_h * single, const char *model,
       return ML_ERROR_NOT_SUPPORTED;
   }
 
-  status = ml_pipeline_construct (pipeline_desc, &pipe);
+  status = ml_pipeline_construct (pipeline_desc, NULL, NULL, &pipe);
   g_free (pipeline_desc);
   if (status != ML_ERROR_NONE) {
     /* Failed to construct pipeline. */
index 429307e..e048f7b 100644 (file)
 #include <glib/gstdio.h> /* GStatBuf */
 
 /**
+ * @brief Struct to check the pipeline state changes.
+ */
+typedef struct
+{
+  gboolean paused;
+  gboolean playing;
+} TestPipeState;
+
+/**
  * @brief Test NNStreamer pipeline construct & destruct
  */
 TEST (nnstreamer_capi_construct_destruct, dummy_01)
 {
   const char *pipeline = "videotestsrc num_buffers=2 ! fakesink";
   ml_pipeline_h handle;
-  int status = ml_pipeline_construct (pipeline, &handle);
+  int status = ml_pipeline_construct (pipeline, NULL, NULL, &handle);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   status = ml_pipeline_destroy (handle);
@@ -34,7 +43,7 @@ TEST (nnstreamer_capi_construct_destruct, dummy_02)
 {
   const char *pipeline = "videotestsrc num_buffers=2 ! videoconvert ! videoscale ! video/x-raw,format=RGBx,width=224,height=224 ! tensor_converter ! fakesink";
   ml_pipeline_h handle;
-  int status = ml_pipeline_construct (pipeline, &handle);
+  int status = ml_pipeline_construct (pipeline, NULL, NULL, &handle);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   status = ml_pipeline_destroy (handle);
@@ -48,7 +57,7 @@ TEST (nnstreamer_capi_construct_destruct, dummy_03)
 {
   const char *pipeline = "videotestsrc num_buffers=2 ! videoconvert ! videoscale ! video/x-raw,format=RGBx,width=224,height=224 ! tensor_converter ! valve name=valvex ! tensor_sink name=sinkx";
   ml_pipeline_h handle;
-  int status = ml_pipeline_construct (pipeline, &handle);
+  int status = ml_pipeline_construct (pipeline, NULL, NULL, &handle);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   status = ml_pipeline_destroy (handle);
@@ -62,7 +71,7 @@ TEST (nnstreamer_capi_construct_destruct, failed_01)
 {
   const char *pipeline = "nonexistsrc ! fakesink";
   ml_pipeline_h handle;
-  int status = ml_pipeline_construct (pipeline, &handle);
+  int status = ml_pipeline_construct (pipeline, NULL, NULL, &handle);
   EXPECT_EQ (status, ML_ERROR_STREAMS_PIPE);
 }
 
@@ -73,7 +82,7 @@ TEST (nnstreamer_capi_construct_destruct, failed_02)
 {
   const char *pipeline = "videotestsrc num_buffers=2 ! audioconvert ! fakesink";
   ml_pipeline_h handle;
-  int status = ml_pipeline_construct (pipeline, &handle);
+  int status = ml_pipeline_construct (pipeline, NULL, NULL, &handle);
   EXPECT_EQ (status, ML_ERROR_STREAMS_PIPE);
 }
 
@@ -85,7 +94,7 @@ TEST (nnstreamer_capi_playstop, dummy_01)
   const char *pipeline = "videotestsrc is-live=true ! videoconvert ! videoscale ! video/x-raw,format=RGBx,width=224,height=224,framerate=60/1 ! tensor_converter ! valve name=valvex ! valve name=valvey ! input-selector name=is01 ! tensor_sink name=sinkx";
   ml_pipeline_h handle;
   ml_pipeline_state_e state;
-  int status = ml_pipeline_construct (pipeline, &handle);
+  int status = ml_pipeline_construct (pipeline, NULL, NULL, &handle);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   status = ml_pipeline_start (handle);
@@ -121,7 +130,7 @@ TEST (nnstreamer_capi_playstop, dummy_02)
   const char *pipeline = "videotestsrc is-live=true ! videoconvert ! videoscale ! video/x-raw,format=RGBx,width=224,height=224,framerate=60/1 ! tensor_converter ! valve name=valvex ! valve name=valvey ! input-selector name=is01 ! tensor_sink name=sinkx";
   ml_pipeline_h handle;
   ml_pipeline_state_e state;
-  int status = ml_pipeline_construct (pipeline, &handle);
+  int status = ml_pipeline_construct (pipeline, NULL, NULL, &handle);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   status = ml_pipeline_start (handle);
@@ -182,7 +191,7 @@ TEST (nnstreamer_capi_valve, test01)
   ml_pipeline_state_e state;
   ml_pipeline_valve_h valve1;
 
-  int status = ml_pipeline_construct (pipeline, &handle);
+  int status = ml_pipeline_construct (pipeline, NULL, NULL, &handle);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   EXPECT_TRUE (dir != NULL);
@@ -249,7 +258,7 @@ TEST (nnstreamer_capi_valve, failure_01)
 
   pipeline = g_strdup ("videotestsrc num-buffers=3 ! videoconvert ! valve name=valvex ! tensor_converter ! tensor_sink name=sinkx");
 
-  status = ml_pipeline_construct (pipeline, &handle);
+  status = ml_pipeline_construct (pipeline, NULL, NULL, &handle);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   /* invalid param : pipe */
@@ -319,6 +328,28 @@ test_sink_callback_count (const ml_tensors_data_h data,
 }
 
 /**
+ * @brief Pipeline state changed callback
+ */
+static void
+test_pipe_state_callback (ml_pipeline_state_e state, void *user_data)
+{
+  TestPipeState *pipe_state;
+
+  pipe_state = (TestPipeState *) user_data;
+
+  switch (state) {
+    case ML_PIPELINE_STATE_PAUSED:
+      pipe_state->paused = TRUE;
+      break;
+    case ML_PIPELINE_STATE_PLAYING:
+      pipe_state->playing = TRUE;
+      break;
+    default:
+      break;
+  }
+}
+
+/**
  * @brief compare the two files.
  */
 static int
@@ -372,7 +403,7 @@ TEST (nnstreamer_capi_sink, dummy_01)
   ml_pipeline_h handle;
   ml_pipeline_state_e state;
   ml_pipeline_sink_h sinkhandle;
-  int status = ml_pipeline_construct (pipeline, &handle);
+  int status = ml_pipeline_construct (pipeline, NULL, NULL, &handle);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   status = ml_pipeline_sink_register (handle, "sinkx", test_sink_callback_dm01, file2, &sinkhandle);
@@ -421,6 +452,7 @@ TEST (nnstreamer_capi_sink, dummy_02)
   gchar *pipeline;
   int status;
   guint *count_sink;
+  TestPipeState *pipe_state;
 
   /* pipeline with appsink */
   pipeline = g_strdup ("videotestsrc num-buffers=3 ! videoconvert ! tensor_converter ! appsink name=sinkx");
@@ -428,7 +460,9 @@ TEST (nnstreamer_capi_sink, dummy_02)
   count_sink = (guint *) g_malloc (sizeof (guint));
   *count_sink = 0;
 
-  status = ml_pipeline_construct (pipeline, &handle);
+  pipe_state = (TestPipeState *) g_new0 (TestPipeState, 1);
+
+  status = ml_pipeline_construct (pipeline, test_pipe_state_callback, pipe_state, &handle);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   status = ml_pipeline_sink_register (handle, "sinkx", test_sink_callback_count, count_sink, &sinkhandle);
@@ -457,9 +491,12 @@ TEST (nnstreamer_capi_sink, dummy_02)
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   EXPECT_TRUE (*count_sink > 0U);
+  EXPECT_TRUE (pipe_state->paused);
+  EXPECT_TRUE (pipe_state->playing);
 
   g_free (pipeline);
   g_free (count_sink);
+  g_free (pipe_state);
 }
 
 /**
@@ -479,7 +516,7 @@ TEST (nnstreamer_capi_sink, failure_01)
   count_sink = (guint *) g_malloc (sizeof (guint));
   *count_sink = 0;
 
-  status = ml_pipeline_construct (pipeline, &handle);
+  status = ml_pipeline_construct (pipeline, NULL, NULL, &handle);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   /* invalid param : pipe */
@@ -559,7 +596,7 @@ TEST (nnstreamer_capi_src, dummy_01)
   uint8_t *content;
   gsize len;
 
-  status = ml_pipeline_construct (pipeline, &handle);
+  status = ml_pipeline_construct (pipeline, NULL, NULL, &handle);
   EXPECT_EQ (status, ML_ERROR_NONE);
   EXPECT_TRUE (dir != NULL);
   for (i = 0; i < 10; i++) {
@@ -712,7 +749,7 @@ TEST (nnstreamer_capi_src, failure_02)
   ml_pipeline_h handle;
   ml_pipeline_src_h srchandle;
 
-  int status = ml_pipeline_construct (pipeline, &handle);
+  int status = ml_pipeline_construct (pipeline, NULL, NULL, &handle);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   /* invalid param : pipe */
@@ -751,7 +788,7 @@ TEST (nnstreamer_capi_src, failure_03)
   ml_tensors_data_h data;
   ml_tensors_info_h info;
 
-  int status = ml_pipeline_construct (pipeline, &handle);
+  int status = ml_pipeline_construct (pipeline, NULL, NULL, &handle);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   status = ml_pipeline_start (handle);
@@ -794,6 +831,7 @@ TEST (nnstreamer_capi_switch, dummy_01)
   gchar *pipeline;
   int status;
   guint *count_sink;
+  TestPipeState *pipe_state;
   gchar **node_list = NULL;
 
   pipeline = g_strdup ("input-selector name=ins ! tensor_converter ! tensor_sink name=sinkx "
@@ -803,7 +841,9 @@ TEST (nnstreamer_capi_switch, dummy_01)
   count_sink = (guint *) g_malloc (sizeof (guint));
   *count_sink = 0;
 
-  status = ml_pipeline_construct (pipeline, &handle);
+  pipe_state = (TestPipeState *) g_new0 (TestPipeState, 1);
+
+  status = ml_pipeline_construct (pipeline, test_pipe_state_callback, pipe_state, &handle);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   status = ml_pipeline_switch_get_handle (handle, "ins", &type, &switchhandle);
@@ -850,8 +890,12 @@ TEST (nnstreamer_capi_switch, dummy_01)
 
   EXPECT_EQ (*count_sink, 3U);
 
+  EXPECT_TRUE (pipe_state->paused);
+  EXPECT_TRUE (pipe_state->playing);
+
   g_free (pipeline);
   g_free (count_sink);
+  g_free (pipe_state);
 }
 
 /**
@@ -883,7 +927,7 @@ TEST (nnstreamer_capi_switch, dummy_02)
   count_sink1 = (guint *) g_malloc (sizeof (guint));
   *count_sink1 = 0;
 
-  status = ml_pipeline_construct (pipeline, &handle);
+  status = ml_pipeline_construct (pipeline, NULL, NULL, &handle);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   status = ml_pipeline_switch_get_handle (handle, "outs", &type, &switchhandle);
@@ -958,7 +1002,7 @@ TEST (nnstreamer_capi_switch, failure_01)
       "videotestsrc is-live=true ! videoconvert ! ins.sink_0 "
       "videotestsrc num-buffers=3 ! videoconvert ! ins.sink_1");
 
-  status = ml_pipeline_construct (pipeline, &handle);
+  status = ml_pipeline_construct (pipeline, NULL, NULL, &handle);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   /* invalid param : pipe */