*/
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) **
****************************************************/
* @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.
** 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.
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.
}
/**
+ * @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
* @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;
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 =
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);
#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);
{
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);
{
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);
{
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);
}
{
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);
}
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);
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);
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);
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 */
}
/**
+ * @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
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);
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");
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);
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);
}
/**
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 */
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++) {
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 */
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);
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 "
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);
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);
}
/**
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);
"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 */