From 3994490fc0863e69e56285b3d2749d1ae2f8945b Mon Sep 17 00:00:00 2001 From: Sangjung Woo Date: Tue, 16 Jun 2020 17:10:12 +0900 Subject: [PATCH] [C-API] Add element-wise control functions This patch newly adds element-wise control functions to get and set property values of the target element in NNStreamer pipeline. Signed-off-by: Sangjung Woo --- api/capi/include/nnstreamer-capi-private.h | 27 +--- api/capi/include/nnstreamer.h | 163 ++++++++++++++++++++++ api/capi/src/nnstreamer-capi-pipeline.c | 214 +++++++++++++++++++++++++++-- 3 files changed, 367 insertions(+), 37 deletions(-) diff --git a/api/capi/include/nnstreamer-capi-private.h b/api/capi/include/nnstreamer-capi-private.h index 22f83b8..08704c4 100644 --- a/api/capi/include/nnstreamer-capi-private.h +++ b/api/capi/include/nnstreamer-capi-private.h @@ -123,6 +123,7 @@ typedef enum { ML_PIPELINE_ELEMENT_VALVE = 0x4, ML_PIPELINE_ELEMENT_SWITCH_INPUT = 0x8, ML_PIPELINE_ELEMENT_SWITCH_OUTPUT = 0x9, + ML_PIPELINE_ELEMENT_COMMON = 0xB, } ml_pipeline_element_e; /** @@ -194,34 +195,14 @@ typedef struct _ml_pipeline_sink { } ml_pipeline_sink; /** - * @brief Internal private representation of src handle of GstAppSrc + * @brief Internal private representation of common element handle (All GstElement except AppSink and TensorSink) * @details This represents a single instance of registration. This should not be exposed to applications. */ -typedef struct _ml_pipeline_src { +typedef struct _ml_pipeline_common_elem { ml_pipeline *pipe; ml_pipeline_element *element; guint32 id; -} ml_pipeline_src; - -/** - * @brief Internal private representation of switch handle (GstInputSelector, GstOutputSelector) - * @details This represents a single instance of registration. This should not be exposed to applications. - */ -typedef struct _ml_pipeline_switch { - ml_pipeline *pipe; - ml_pipeline_element *element; - guint32 id; -} ml_pipeline_switch; - -/** - * @brief Internal private representation of valve handle (GstValve) - * @details This represents a single instance of registration. This should not be exposed to applications. - */ -typedef struct _ml_pipeline_valve { - ml_pipeline *pipe; - ml_pipeline_element *element; - guint32 id; -} ml_pipeline_valve; +} ml_pipeline_common_elem; /** * @brief An information to create single-shot instance. diff --git a/api/capi/include/nnstreamer.h b/api/capi/include/nnstreamer.h index 89a02ce..5c2cda0 100644 --- a/api/capi/include/nnstreamer.h +++ b/api/capi/include/nnstreamer.h @@ -113,6 +113,11 @@ typedef void *ml_pipeline_switch_h; typedef void *ml_pipeline_valve_h; /** + * @brief A handle of a common element (i.e. All GstElement except AppSrc, AppSink, TensorSink, Selector and Valve) of an NNStreamer pipeline + */ +typedef void *ml_pipeline_element_h; + +/** * @brief Types of NNFWs. * @details To check if a nnfw-type is supported in a system, an application may call the API, ml_check_nnfw_availability(). * @since_tizen 5.5 @@ -569,6 +574,164 @@ int ml_pipeline_valve_release_handle (ml_pipeline_valve_h valve_handle); */ int ml_pipeline_valve_set_open (ml_pipeline_valve_h valve_handle, bool open); +/******************************************************** + ** NNStreamer Element Property Control in Pipeline ** + ********************************************************/ + +/** + * @brief Gets an element handle in NNStreamer pipelines to control its properties. + * @since_tizen 6.0 + * @remarks If the function succeeds, @a elm_h handle must be released using ml_pipeline_element_release_handle(). + * @param[in] pipe The pipeline to be managed. + * @param[in] element_name The name of element to control. + * @param[out] elm_h The element handle. + * @return @c 0 on success. Otherwise a negative error value. + * @retval #ML_ERROR_NONE Successful + * @retval #ML_ERROR_NOT_SUPPORTED Not supported. + * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid. + * @retval #ML_ERROR_OUT_OF_MEMORY Failed to allocate required memory. + */ +int ml_pipeline_element_get_handle (ml_pipeline_h pipe, const char *element_name, ml_pipeline_element_h *elm_h); + +/** + * @brief Releases the given element handle. + * @since_tizen 6.0 + * @param[in] elm_h The handle to be released. + * @return @c 0 on success. Otherwise a negative error value. + * @retval #ML_ERROR_NONE Successful + * @retval #ML_ERROR_NOT_SUPPORTED Not supported. + * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid. + */ +int ml_pipeline_element_release_handle (ml_pipeline_element_h elm_h); + +/** + * @brief Sets element properties in NNStreamer pipelines. + * @since_tizen 6.0 + * @remarks This function supports a varying number of name/value pairs, finished by NULL. + * @remarks If one of given property name does not exist, all properties are not set. + * @param[in] elm_h The target element handle. + * @param[in] first_property_name The name of first property to set. + * @param[in] ... A varying number of name/value pairs, finished by NULL. + * @return @c 0 on success. Otherwise a negative error value. + * @retval #ML_ERROR_NONE Successful + * @retval #ML_ERROR_NOT_SUPPORTED Not supported. + * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid or given property name does not exist. + * + * Here is an example of the usage: + * @code + * ml_pipeline_h handle; + * ml_pipeline_element_h elem_h; + * gchar *pipeline; + * gchar *ret_mode = NULL; + * gboolean ret_silent; + * int status; + * + * // Pipeline description + * pipeline = g_strdup("videotestsrc ! video/x-raw,format=RGB,width=640,height=480 ! videorate max-rate=1 ! " \ + * "tensor_converter ! tensor_mux name=mux ! tensor_demux ! tensor_sink"); + * + * status = ml_pipeline_construct (pipeline, NULL, NULL, &handle); + * if (status != ML_ERROR_NONE) { + * // handle error case + * goto error; + * } + * + * // Get the handle of target element + * status = ml_pipeline_element_get_handle (handle, "mux", &elem_h); + * if (status != ML_ERROR_NONE) { + * // handle error case + * goto error; + * } + * + * // Set single property of target element + * status = ml_pipeline_element_set_property(elem_h, "sync_mode", "slowest", NULL); + * if (status != ML_ERROR_NONE) { + * // handle error case + * goto error; + * } + * + * // Set multiple properties of target element + * status = ml_pipeline_element_set_property(elem_h, "sync_mode", "nosync", + * "silent", TRUE, NULL); + * if (status != ML_ERROR_NONE) { + * // handle error case + * goto error; + * } + * + * error: + * ml_pipeline_element_release_handle (elem_h); + * ml_pipeline_destroy (handle); + * g_free(pipeline); + * @endcode + */ +int ml_pipeline_element_set_property (ml_pipeline_element_h elm_h, const char *first_property_name, ...); + +/** + * @brief Gets element properties in NNStreamer pipelines. + * @since_tizen 6.0 + * @remarks This function supports a varying number of name/return location pairs, finished by NULL. + * @remarks If one of given property name does not exist, error code returns without fetching any properties. + * @remarks The callers is responsible for freeing the allocated memory by calling g_free(). + * @param[in] elm_h The target element handle. + * @param[in] first_property_name The name of first property to get. + * @param[in] ... A varying number of name/return location pairs, finished by NULL. + * @return @c 0 on success. Otherwise a negative error value. + * @retval #ML_ERROR_NONE Successful + * @retval #ML_ERROR_NOT_SUPPORTED Not supported. + * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid or given property name does not exist. + * + * Here is an example of the usage: + * @code + * ml_pipeline_h handle; + * ml_pipeline_element_h elem_h; + * gchar *pipeline; + * gchar *ret_mode = NULL; + * gboolean ret_silent; + * int status; + * + * // Pipeline description + * pipeline = g_strdup("videotestsrc ! video/x-raw,format=RGB,width=640,height=480 ! videorate max-rate=1 ! " \ + * "tensor_converter ! tensor_mux name=mux ! tensor_demux ! tensor_sink"); + * + * status = ml_pipeline_construct (pipeline, NULL, NULL, &handle); + * if (status != ML_ERROR_NONE) { + * // handle error case + * goto error; + * } + * + * // Get the handle of target element + * status = ml_pipeline_element_get_handle (handle, "mux", &elem_h); + * if (status != ML_ERROR_NONE) { + * // handle error case + * goto error; + * } + * + * // Get single property of target element + * status = ml_pipeline_element_get_property (elem_h, "sync_mode", &ret_mode, NULL); + * if (status != ML_ERROR_NONE) { + * // handle error case + * goto error; + * } + * g_assert_true (g_strcmp0 ("slowest", ret_mode) == 0); + * g_free (ret_mode); + * + * // Get multiple properties of target element + * status = ml_pipeline_element_get_property (elem_h, "sync_mode", &ret_mode, + * "silent", &ret_silent, NULL); + * if (status != ML_ERROR_NONE) { + * // handle error case + * goto error; + * } + * g_free (ret_mode); + * + * error: + * ml_pipeline_element_release_handle (elem_h); + * ml_pipeline_destroy (handle); + * g_free(pipeline); + * @endcode + */ +int ml_pipeline_element_get_property (ml_pipeline_element_h elm_h, const char *first_property_name, ...); + /**************************************************** ** NNStreamer Utilities ** ****************************************************/ diff --git a/api/capi/src/nnstreamer-capi-pipeline.c b/api/capi/src/nnstreamer-capi-pipeline.c index 899c390..81560a8 100644 --- a/api/capi/src/nnstreamer-capi-pipeline.c +++ b/api/capi/src/nnstreamer-capi-pipeline.c @@ -992,7 +992,7 @@ ml_pipeline_src_get_handle (ml_pipeline_h pipe, const char *src_name, { ml_pipeline *p = pipe; ml_pipeline_element *elem; - ml_pipeline_src *src; + ml_pipeline_common_elem *src; int ret = ML_ERROR_NONE; check_feature_state (); @@ -1032,7 +1032,7 @@ ml_pipeline_src_get_handle (ml_pipeline_h pipe, const char *src_name, goto unlock_return; } - src = *h = g_new0 (ml_pipeline_src, 1); + src = *h = g_new0 (ml_pipeline_common_elem, 1); if (src == NULL) { ml_loge ("Failed to allocate the src handle for %s.", src_name); ret = ML_ERROR_OUT_OF_MEMORY; @@ -1063,7 +1063,7 @@ unlock_return: int ml_pipeline_src_release_handle (ml_pipeline_src_h h) { - handle_init (src, src, h); + handle_init (common_elem, src, h); elem->handles = g_list_remove (elem->handles, src); g_free (src); @@ -1086,7 +1086,7 @@ ml_pipeline_src_input_data (ml_pipeline_src_h h, ml_tensors_data_h data, ml_tensors_data_s *_data; unsigned int i; - handle_init (src, src, h); + handle_init (common_elem, src, h); _data = (ml_tensors_data_s *) data; if (!_data) { @@ -1186,7 +1186,7 @@ destroy_data: int ml_pipeline_src_get_tensors_info (ml_pipeline_src_h h, ml_tensors_info_h * info) { - handle_init (src, src, h); + handle_init (common_elem, src, h); if (info == NULL) { ret = ML_ERROR_INVALID_PARAMETER; @@ -1216,7 +1216,7 @@ ml_pipeline_switch_get_handle (ml_pipeline_h pipe, const char *switch_name, { ml_pipeline_element *elem; ml_pipeline *p = pipe; - ml_pipeline_switch *swtc; + ml_pipeline_common_elem *swtc; int ret = ML_ERROR_NONE; check_feature_state (); @@ -1264,7 +1264,7 @@ ml_pipeline_switch_get_handle (ml_pipeline_h pipe, const char *switch_name, goto unlock_return; } - swtc = *h = g_new0 (ml_pipeline_switch, 1); + swtc = *h = g_new0 (ml_pipeline_common_elem, 1); if (swtc == NULL) { ml_loge ("Failed to allocate the switch handle for %s.", switch_name); ret = ML_ERROR_OUT_OF_MEMORY; @@ -1293,7 +1293,7 @@ unlock_return: int ml_pipeline_switch_release_handle (ml_pipeline_switch_h h) { - handle_init (switch, swtc, h); + handle_init (common_elem, swtc, h); elem->handles = g_list_remove (elem->handles, swtc); g_free (swtc); @@ -1310,7 +1310,7 @@ ml_pipeline_switch_select (ml_pipeline_switch_h h, const char *pad_name) GstPad *active_pad, *new_pad; gchar *active_name; - handle_init (switch, swtc, h); + handle_init (common_elem, swtc, h); if (pad_name == NULL) { ml_loge ("The second argument, pad name, is not valid."); @@ -1364,7 +1364,7 @@ ml_pipeline_switch_get_pad_list (ml_pipeline_switch_h h, char ***list) GstPad *pad; int counter = 0; - handle_init (switch, swtc, h); + handle_init (common_elem, swtc, h); if (list == NULL) { ml_loge ("The second argument, list, is not valid."); @@ -1457,7 +1457,7 @@ ml_pipeline_valve_get_handle (ml_pipeline_h pipe, const char *valve_name, { ml_pipeline_element *elem; ml_pipeline *p = pipe; - ml_pipeline_valve *valve; + ml_pipeline_common_elem *valve; int ret = ML_ERROR_NONE; check_feature_state (); @@ -1498,7 +1498,7 @@ ml_pipeline_valve_get_handle (ml_pipeline_h pipe, const char *valve_name, goto unlock_return; } - valve = *h = g_new0 (ml_pipeline_valve, 1); + valve = *h = g_new0 (ml_pipeline_common_elem, 1); if (valve == NULL) { ml_loge ("Failed to allocate the valve handle for %s.", valve_name); ret = ML_ERROR_OUT_OF_MEMORY; @@ -1527,7 +1527,7 @@ unlock_return: int ml_pipeline_valve_release_handle (ml_pipeline_valve_h h) { - handle_init (valve, valve, h); + handle_init (common_elem, valve, h); elem->handles = g_list_remove (elem->handles, valve); g_free (valve); @@ -1542,7 +1542,7 @@ int ml_pipeline_valve_set_open (ml_pipeline_valve_h h, bool open) { gboolean drop = FALSE; - handle_init (valve, valve, h); + handle_init (common_elem, valve, h); g_object_get (G_OBJECT (elem->element), "drop", &drop, NULL); @@ -1558,6 +1558,192 @@ ml_pipeline_valve_set_open (ml_pipeline_valve_h h, bool open) handle_exit (h); } +/******************************************************** + ** NNStreamer Element Property Control in Pipeline ** + ********************************************************/ +/** + * @brief Gets an element handle in NNStreamer pipelines to control its properties. + */ +int +ml_pipeline_element_get_handle (ml_pipeline_h pipe, const char *element_name, + ml_pipeline_element_h *elm_h) +{ + int ret = ML_ERROR_NONE; + ml_pipeline_element *elem; + ml_pipeline_common_elem *common_elem; + ml_pipeline *p = pipe; + + /* Check input parameter */ + if (pipe == NULL) { + ml_loge ("The first argument, pipeline handle, is not valid."); + return ML_ERROR_INVALID_PARAMETER; + } + + if (element_name == NULL) { + ml_loge ("The second argument, element name, is not valid."); + return ML_ERROR_INVALID_PARAMETER; + } + + if (elm_h == NULL) { + ml_loge ("The argument element handle is not valid."); + return ML_ERROR_INVALID_PARAMETER; + } + *elm_h = NULL; + + g_mutex_lock (&p->lock); + + /* 1. Search element in lookup table first */ + elem = g_hash_table_lookup (p->namednodes, element_name); + if (elem == NULL) { + /* 2. Search element in pipeline itself */ + GstElement *gst_elem = gst_bin_get_by_name (GST_BIN (p->element), element_name); + if (gst_elem == NULL) { + ml_loge ("The element named [%s] is not found in the pipeline", element_name); + ret = ML_ERROR_INVALID_PARAMETER; + goto unlock_return; + } + + /* Caching for next search */ + elem = construct_element (gst_elem, pipe, element_name, ML_PIPELINE_ELEMENT_COMMON); + if (elem == NULL) { + ml_loge ("Failed to allocate the internal memory"); + ret = ML_ERROR_OUT_OF_MEMORY; + goto unlock_return; + } + g_hash_table_insert (p->namednodes, g_strdup (element_name), elem); + } + + /* Type checking */ + if (elem->type == ML_PIPELINE_ELEMENT_UNKNOWN) { + ml_loge ("There is an element named [%s] in the pipeline, but it is unknown type.", element_name); + ret = ML_ERROR_INVALID_PARAMETER; + goto unlock_return; + } + + common_elem = *elm_h = g_new0(ml_pipeline_common_elem, 1); + if (common_elem == NULL) { + ml_loge ("Failed to allocate the internal handler for %s.", element_name); + ret = ML_ERROR_OUT_OF_MEMORY; + goto unlock_return; + } + + common_elem->pipe = p; + common_elem->element = elem; + + g_mutex_lock (&elem->lock); + elem->maxid++; + common_elem->id = elem->maxid; + elem->handles = g_list_append (elem->handles, common_elem); + g_mutex_unlock (&elem->lock); + +unlock_return: + g_mutex_unlock (&p->lock); + return ret; +} + +/** + * @brief Releases the given element handle. + */ +int +ml_pipeline_element_release_handle (ml_pipeline_element_h elm_h) +{ + handle_init (common_elem, common_elem, elm_h); + + elem->handles = g_list_remove (elem->handles, common_elem); + g_free (common_elem); + + handle_exit (elm_h); +} + +/** + * @brief Sets element properties in NNStreamer pipelines. + */ +int ml_pipeline_element_set_property (ml_pipeline_element_h elm_h, + const char *first_property_name, ...) +{ + va_list args_check; + va_list args; + const char *name; + + handle_init (common_elem, common_elem, elm_h); + + /* Check input parameter */ + if (first_property_name == NULL) { + ml_loge ("The second argument, first property name is not valid."); + ret = ML_ERROR_INVALID_PARAMETER; + goto unlock_return; + } + + /* Check property existence */ + va_start (args_check, first_property_name); + va_copy (args, args_check); + + name = first_property_name; + while (name) { + if (g_object_class_find_property (G_OBJECT_GET_CLASS (elem->element), name) == NULL) { + ml_loge ("The property name [%s] does not exist.", name); + ret = ML_ERROR_INVALID_PARAMETER; + va_end (args_check); + va_end (args); + goto unlock_return; + } + /* The value: skipped */ + va_arg (args_check, gchar*); + name = va_arg (args_check, gchar*); + } + va_end (args_check); + + g_object_set_valist (G_OBJECT (elem->element), first_property_name, args); + va_end (args); + + handle_exit (elm_h); +} + +/** + * @brief Gets element properties in NNStreamer pipelines. + */ +int ml_pipeline_element_get_property (ml_pipeline_element_h elm_h, + const char *first_property_name, ...) +{ + va_list args_check; + va_list args; + const char *name; + + handle_init (common_elem, common_elem, elm_h); + + /* Check input parameter */ + if (first_property_name == NULL) { + ml_loge ("The second argument, first property name is not valid."); + ret = ML_ERROR_INVALID_PARAMETER; + goto unlock_return; + } + + /* Check property existence */ + va_start (args_check, first_property_name); + va_copy (args, args_check); + + name = first_property_name; + while (name) { + if (g_object_class_find_property (G_OBJECT_GET_CLASS (elem->element), name) == NULL) { + ml_loge ("The property name [%s] does not exist.", name); + ret = ML_ERROR_INVALID_PARAMETER; + va_end (args_check); + va_end (args); + goto unlock_return; + } + /* The value location: skiped */ + va_arg (args_check, gchar*); + name = va_arg (args_check, gchar*); + } + va_end (args_check); + + /* Get property */ + g_object_get_valist (G_OBJECT (elem->element), first_property_name, args); + va_end (args); + + handle_exit (elm_h); +} + /** * @brief Gets the element of pipeline itself (GstElement). */ -- 2.7.4