[C-API] Add element-wise control functions
authorSangjung Woo <sangjung.woo@samsung.com>
Tue, 16 Jun 2020 08:10:12 +0000 (17:10 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Thu, 2 Jul 2020 07:29:20 +0000 (16:29 +0900)
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 <sangjung.woo@samsung.com>
api/capi/include/nnstreamer-capi-private.h
api/capi/include/nnstreamer.h
api/capi/src/nnstreamer-capi-pipeline.c

index 22f83b8..08704c4 100644 (file)
@@ -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.
index 89a02ce..5c2cda0 100644 (file)
@@ -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                           **
  ****************************************************/
index 899c390..81560a8 100644 (file)
@@ -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).
  */