Implement Tizen-CAPI, "start/stop/getstate/destroy" for a nnstreamer pipeline.
Add testcases for them.
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
capi_base_common_dep,
dlog_dep,
tizen_capi_dep,
- gtest_dep
+ gtest_dep,
+ glib_dep
]
unittest_tizen_capi = executable('unittest_tizen_capi',
#include <tizen-api.h>
#include <gtest/gtest.h>
+#include <glib.h>
/**
* @brief Test NNStreamer pipeline construct & destruct
const char *pipeline = "videotestsrc num_buffers=2 ! fakesink";
nns_pipeline_h handle;
int status = nns_pipeline_construct (pipeline, &handle);
-
EXPECT_EQ (status, NNS_ERROR_NONE);
status = nns_pipeline_destroy (handle);
-
EXPECT_EQ (status, NNS_ERROR_NONE);
}
const char *pipeline = "videotestsrc num_buffers=2 ! videoconvert ! videoscale ! video/x-raw,format=RGBx,width=224,height=224 ! tensor_converter ! fakesink";
nns_pipeline_h handle;
int status = nns_pipeline_construct (pipeline, &handle);
-
EXPECT_EQ (status, NNS_ERROR_NONE);
status = nns_pipeline_destroy (handle);
-
EXPECT_EQ (status, NNS_ERROR_NONE);
}
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";
nns_pipeline_h handle;
int status = nns_pipeline_construct (pipeline, &handle);
-
EXPECT_EQ (status, NNS_ERROR_NONE);
status = nns_pipeline_destroy (handle);
+ EXPECT_EQ (status, NNS_ERROR_NONE);
+}
+
+/**
+ * @brief Test NNStreamer pipeline construct & destruct
+ */
+TEST (nnstreamer_capi_playstop, dummy_01)
+{
+ const char *pipeline = "videotestsrc is-live=true num-buffers=30 framerate=60/1 ! videoconvert ! videoscale ! video/x-raw,format=RGBx,width=224,height=224 ! tensor_converter ! valve name=valvex ! valve name=valvey ! input-selector name=is01 ! tensor_sink name=sinkx";
+ nns_pipeline_h handle;
+ nns_pipeline_state state;
+ int status = nns_pipeline_construct (pipeline, &handle);
+ EXPECT_EQ (status, NNS_ERROR_NONE);
+
+ status = nns_pipeline_start (handle);
+ EXPECT_EQ (status, NNS_ERROR_NONE);
+ status = nns_pipeline_getstate(handle, &state);
+ EXPECT_EQ (status, NNS_ERROR_NONE); /* At this moment, it can be READY, PAUSED, or PLAYING */
+ EXPECT_NE (state, NNS_PIPELINE_UNKNOWN);
+ EXPECT_NE (state, NNS_PIPELINE_NULL);
+
+ g_usleep(50000); /* 50ms. Let a few frames flow. */
+ status = nns_pipeline_getstate(handle, &state);
+ EXPECT_EQ (status, NNS_ERROR_NONE);
+ EXPECT_EQ (state, NNS_PIPELINE_PLAYING);
+
+ status = nns_pipeline_stop (handle);
+ EXPECT_EQ (status, NNS_ERROR_NONE);
+ g_usleep(50000); /* 50ms. Let a few frames flow. */
+ status = nns_pipeline_getstate(handle, &state);
+ EXPECT_EQ (status, NNS_ERROR_NONE);
+ EXPECT_EQ (state, NNS_PIPELINE_PAUSED);
+
+ status = nns_pipeline_destroy (handle);
EXPECT_EQ (status, NNS_ERROR_NONE);
}
typedef struct _element {
GstElement *element; /**< The Sink/Src/Valve/Switch element */
nns_pipeline *pipe; /**< The main pipeline */
- const char *name;
+ char *name;
elementType type;
GstPad *src;
GstPad *sink; /**< Unref this at destroy */
NNS_ERROR_NONE = TIZEN_ERROR_NONE, /**< Success! */
NNS_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER, /**< Invalid parameter */
NNS_ERROR_NOT_SUPPORTED = TIZEN_ERROR_NOT_SUPPORTED, /**< The feature is not supported */
- NNS_ERROR_PIPELINE_FAIL = TIZEN_ERROR_STREAMS_PIPE, /**< Cannot create Gstreamer pipeline. */
+ NNS_ERROR_PIPELINE_FAIL = TIZEN_ERROR_STREAMS_PIPE, /**< Cannot create or access Gstreamer pipeline. */
} nns_error_e;
/**
* @param[in] pipe The pipeline to be destroyed.
* @return @c 0 on success. otherwise a negative error value
* @retval #NNS_ERROR_NONE Successful
+ * @retval #NNS_ERROR_PIPELINE_FAIL Fail. Cannot access the pipeline status.
+ * @retval #NNS_ERROR_INVALID_PARAMETER Fail. The parameter is invalid (pipe is NULL?)
*/
int nns_pipeline_destroy (nns_pipeline_h pipe);
* @param[out] state The pipeline state.
* @return @c 0 on success. otherwise a negative error value
* @retval #NNS_ERROR_NONE Successful
+ * @retval #NNS_ERROR_INVALID_PARAMETER Given parameter is invalid. (pipe is NULL?)
+ * @retval #NNS_ERROR_PIPELINE_FAIL Failed to get state from the pipeline.
*/
int nns_pipeline_getstate (nns_pipeline_h pipe, nns_pipeline_state *state);
/**
* @brief Start the pipeline
* @detail The pipeline handle returned by nns_construct_pipeline (pipe) is started.
+ * Note that this is asynchronous function. State might be "pending".
* @since_tizen 5.5
* @param[in] pipe The pipeline to be started.
* @return @c 0 on success. otherwise a negative error value
* @retval #NNS_ERROR_NONE Successful
+ * @retval #NNS_ERROR_PIPELINE_FAIL Failed to start.
*/
int nns_pipeline_start (nns_pipeline_h pipe);
/**
* @brief Stop the pipeline
* @detail The pipeline handle returned by nns_construct_pipeline (pipe) is stopped.
+ * Note that this is asynchronous function. State might be "pending".
* @since_tizen 5.5
* @param[in] pipe The pipeline to be stopped.
* @return @c 0 on success. otherwise a negative error value
* @retval #NNS_ERROR_NONE Successful
+ * @retval #NNS_ERROR_PIPELINE_FAIL Failed to start.
*/
int nns_pipeline_stop (nns_pipeline_h pipe);
{
element *elem = data;
+ /** @todo CRITICAL if the pipeline is being killed, don't proceed! */
+
GstMemory *mem[NNS_TENSOR_SIZE_LIMIT];
GstMapInfo info[NNS_TENSOR_SIZE_LIMIT];
guint i;
}
/**
+ * @brief Private function for nns_pipeline_destroy, cleaning up nodes in namednodes
+ */
+static void
+cleanup_node (gpointer data)
+{
+ element *e = data;
+ g_mutex_lock (&e->lock);
+ g_free (e->name);
+ if (e->src)
+ gst_object_unref (e->src);
+ if (e->sink)
+ gst_object_unref (e->sink);
+
+ /** @todo CRITICAL. Stop the handle callbacks if they are running/ready */
+ if (e->handles)
+ g_list_free_full (e->handles, g_free);
+ e->handles = NULL;
+
+ g_mutex_unlock (&e->lock);
+ g_mutex_clear (&e->lock);
+
+ g_free (e);
+}
+
+/**
* @brief Construct the pipeline (more info in tizen-api.h)
*/
int
g_mutex_init (&pipe_h->lock);
g_mutex_lock (&pipe_h->lock);
- pipe_h->namednodes = g_hash_table_new (g_str_hash, g_str_equal);
+ pipe_h->namednodes =
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free, cleanup_node);
it = gst_bin_iterate_elements (GST_BIN (pipeline));
if (it != NULL) {
}
if (e != NULL)
- g_hash_table_insert (pipe_h->namednodes, e, name);
+ g_hash_table_insert (pipe_h->namednodes, g_strdup (name), e);
}
g_free (name);
}
g_object_unref (outputs);
}
- /** @todo CRITICAL: Prepare the pipeline. Maybe as a forked thread */
-
-
g_mutex_unlock (&pipe_h->lock);
return ret;
}
int
nns_pipeline_destroy (nns_pipeline_h pipe)
{
- /* nns_pipeline *p = pipe; */
-
- /** @todo NYI */
+ nns_pipeline *p = pipe;
+ GstStateChangeReturn scret;
+ GstState state, pending;
+
+ if (p == NULL)
+ return NNS_ERROR_INVALID_PARAMETER;
+
+ g_mutex_lock (&p->lock);
+
+ /* if it's PLAYING, PAUSE it. */
+ scret = gst_element_get_state (p->element, &state, &pending, 10000000UL); /* 10ms */
+ if (scret != GST_STATE_CHANGE_FAILURE && state == GST_STATE_PLAYING) {
+ /* Pause the pipeline if it's Playing */
+ scret = gst_element_set_state (p->element, GST_STATE_PAUSED);
+ if (scret == GST_STATE_CHANGE_FAILURE) {
+ g_mutex_unlock (&p->lock);
+ return NNS_ERROR_PIPELINE_FAIL;
+ }
+ }
- /** @todo Pause the pipeline if it's Playing */
+ /** @todo Ensure all callbacks are gone. (kill'em all!) THIS IS CRITICAL! */
+ g_mutex_unlock (&p->lock);
+ 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);
- /** @todo Ensure all callbacks are gone. (kill'em all!) */
+ /** Destroy registered callback handles */
+ g_hash_table_remove_all (p->namednodes);
- /** @todo Stop (NULL State) the pipeline */
+ /** Stop (NULL State) the pipeline */
+ scret = gst_element_set_state (p->element, GST_STATE_NULL);
+ if (scret != GST_STATE_CHANGE_SUCCESS) {
+ g_mutex_unlock (&p->lock);
+ return NNS_ERROR_PIPELINE_FAIL;
+ }
- /** @todo Destroy Everything */
+ gst_object_unref (p->element);
+ g_mutex_unlock (&p->lock);
+ g_mutex_clear (&p->lock);
return NNS_ERROR_NONE;
}
int
nns_pipeline_getstate (nns_pipeline_h pipe, nns_pipeline_state * state)
{
- /* *state = NNSAPI_UNKNOWN; */
-
- /** @todo NYI */
+ nns_pipeline *p = pipe;
+ GstState _state;
+ GstState pending;
+ GstStateChangeReturn scret;
+ *state = NNS_PIPELINE_UNKNOWN;
+
+ if (p == NULL)
+ return NNS_ERROR_INVALID_PARAMETER;
+
+ g_mutex_lock (&p->lock);
+ scret =
+ gst_element_get_state (p->element, &_state, &pending,
+ GST_CLOCK_TIME_NONE);
+ g_mutex_unlock (&p->lock);
+
+ if (scret == GST_STATE_CHANGE_FAILURE)
+ return NNS_ERROR_PIPELINE_FAIL;
+ *state = _state;
return NNS_ERROR_NONE;
}
int
nns_pipeline_start (nns_pipeline_h pipe)
{
- /** @todo NYI */
+ nns_pipeline *p = pipe;
+ GstStateChangeReturn scret;
+
+ g_mutex_lock (&p->lock);
+ scret = gst_element_set_state (p->element, GST_STATE_PLAYING);
+ g_mutex_unlock (&p->lock);
+
+ if (scret == GST_STATE_CHANGE_FAILURE)
+ return NNS_ERROR_PIPELINE_FAIL;
return NNS_ERROR_NONE;
}
int
nns_pipeline_stop (nns_pipeline_h pipe)
{
- /** @todo NYI */
+ nns_pipeline *p = pipe;
+ GstStateChangeReturn scret;
+
+ g_mutex_lock (&p->lock);
+ scret = gst_element_set_state (p->element, GST_STATE_PAUSED);
+ g_mutex_unlock (&p->lock);
+
+ if (scret == GST_STATE_CHANGE_FAILURE)
+ return NNS_ERROR_PIPELINE_FAIL;
return NNS_ERROR_NONE;
}