[Tizen/Api] add common utilities and change name prefix
authorJaeyun <jy1210.jung@samsung.com>
Mon, 10 Jun 2019 06:55:16 +0000 (15:55 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Wed, 12 Jun 2019 12:44:13 +0000 (21:44 +0900)
1. Define new functions to handle tensor data and refactor api functions.
2. Update api description.
3. Add testcases using custom filter.

Signed-off-by: Jaeyun Jung <jy1210.jung@samsung.com>
tests/tizen_capi/unittest_tizen_capi.cpp
tizen-api/include/nnstreamer-single.h
tizen-api/include/nnstreamer.h
tizen-api/include/tizen-api-private.h
tizen-api/meson.build
tizen-api/src/nnstreamer-single.c
tizen-api/src/tizen-api-pipeline.c
tizen-api/src/tizen-api-util.c [new file with mode: 0644]

index 90fc3c8..129fb50 100644 (file)
@@ -783,7 +783,7 @@ TEST (nnstreamer_capi_switch, dummy_01)
   ml_pipeline_h handle;
   ml_pipeline_switch_h switchhandle;
   ml_pipeline_sink_h sinkhandle;
-  ml_pipeline_switch_type_e type;
+  ml_pipeline_switch_e type;
   gchar *pipeline;
   int status;
   guint *count_sink;
@@ -855,7 +855,7 @@ TEST (nnstreamer_capi_switch, dummy_02)
   ml_pipeline_h handle;
   ml_pipeline_switch_h switchhandle;
   ml_pipeline_sink_h sinkhandle0, sinkhandle1;
-  ml_pipeline_switch_type_e type;
+  ml_pipeline_switch_e type;
   gchar *pipeline;
   int status;
   guint *count_sink0, *count_sink1;
@@ -943,7 +943,7 @@ TEST (nnstreamer_capi_switch, failure_01)
 {
   ml_pipeline_h handle;
   ml_pipeline_switch_h switchhandle;
-  ml_pipeline_switch_type_e type;
+  ml_pipeline_switch_e type;
   gchar *pipeline;
   int status;
 
@@ -1000,11 +1000,11 @@ TEST (nnstreamer_capi_switch, failure_01)
 }
 
 /**
- * @brief Test NNStreamer single shot
+ * @brief Test NNStreamer single shot (tensorflow-lite)
  */
 TEST (nnstreamer_capi_singleshot, invoke_01)
 {
-  ml_simpleshot_model_h model;
+  ml_single_h single;
   ml_tensors_info_s in_info, out_info;
   ml_tensors_info_s in_res, out_res;
   ml_tensors_data_s *input, *output1, *output2;
@@ -1013,10 +1013,10 @@ TEST (nnstreamer_capi_singleshot, invoke_01)
   const gchar *root_path = g_getenv ("NNSTREAMER_BUILD_ROOT_PATH");
   gchar *test_model;
 
-  memset (&in_info, 0, sizeof (ml_tensors_info_s));
-  memset (&out_info, 0, sizeof (ml_tensors_info_s));
-  memset (&in_res, 0, sizeof (ml_tensors_info_s));
-  memset (&out_res, 0, sizeof (ml_tensors_info_s));
+  ml_util_initialize_tensors_info (&in_info);
+  ml_util_initialize_tensors_info (&out_info);
+  ml_util_initialize_tensors_info (&in_res);
+  ml_util_initialize_tensors_info (&out_res);
 
   ASSERT_TRUE (root_path != NULL);
   test_model = g_build_filename (root_path, "tests", "test_models", "models",
@@ -1036,12 +1036,12 @@ TEST (nnstreamer_capi_singleshot, invoke_01)
   out_info.info[0].dimension[2] = 1;
   out_info.info[0].dimension[3] = 1;
 
-  status = ml_model_open (test_model, &model, &in_info, &out_info,
+  status = ml_single_open (&single, test_model, &in_info, &out_info,
       ML_NNFW_TENSORFLOW_LITE, ML_NNFW_HW_DO_NOT_CARE);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   /* input tensor in filter */
-  status = ml_model_get_input_type (model, &in_res);
+  status = ml_single_get_input_info (single, &in_res);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   EXPECT_TRUE (in_info.num_tensors == in_res.num_tensors);
@@ -1054,7 +1054,7 @@ TEST (nnstreamer_capi_singleshot, invoke_01)
   }
 
   /* output tensor in filter */
-  status = ml_model_get_output_type (model, &out_res);
+  status = ml_single_get_output_info (single, &out_res);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   EXPECT_TRUE (out_info.num_tensors == out_res.num_tensors);
@@ -1067,36 +1067,51 @@ TEST (nnstreamer_capi_singleshot, invoke_01)
   }
 
   /* generate dummy data */
-  input = ml_model_allocate_tensors_data (&in_info);
+  input = ml_util_allocate_tensors_data (&in_info);
   EXPECT_TRUE (input != NULL);
 
-  output1 = ml_model_inference (model, input, NULL);
+  status = ml_util_get_last_error ();
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  output1 = ml_single_inference (single, input, NULL);
   EXPECT_TRUE (output1 != NULL);
-  ml_model_free_tensors_data (output1);
 
-  output2 = ml_model_allocate_tensors_data (&out_info);
+  status = ml_util_get_last_error ();
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  ml_util_free_tensors_data (&output1);
+
+  output2 = ml_util_allocate_tensors_data (&out_info);
   EXPECT_TRUE (output2 != NULL);
 
-  output1 = ml_model_inference (model, input, output2);
+  status = ml_util_get_last_error ();
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  output1 = ml_single_inference (single, input, output2);
   EXPECT_TRUE (output1 != NULL);
   EXPECT_TRUE (output1 == output2);
-  ml_model_free_tensors_data (output2);
 
-  ml_model_free_tensors_data (input);
+  status = ml_util_get_last_error ();
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  ml_util_free_tensors_data (&output2);
+  ml_util_free_tensors_data (&input);
 
-  status = ml_model_close (model);
+  status = ml_single_close (single);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   g_free (test_model);
+  ml_util_free_tensors_info (&in_res);
+  ml_util_free_tensors_info (&out_res);
 }
 
 /**
- * @brief Test NNStreamer single shot
+ * @brief Test NNStreamer single shot (tensorflow-lite)
  * @detail Start pipeline without tensor info
  */
 TEST (nnstreamer_capi_singleshot, invoke_02)
 {
-  ml_simpleshot_model_h model;
+  ml_single_h single;
   ml_tensors_info_s in_info, out_info;
   ml_tensors_data_s *input, *output1, *output2;
   int status;
@@ -1104,6 +1119,9 @@ TEST (nnstreamer_capi_singleshot, invoke_02)
   const gchar *root_path = g_getenv ("NNSTREAMER_BUILD_ROOT_PATH");
   gchar *test_model;
 
+  ml_util_initialize_tensors_info (&in_info);
+  ml_util_initialize_tensors_info (&out_info);
+
   ASSERT_TRUE (root_path != NULL);
   test_model = g_build_filename (root_path, "tests", "test_models", "models",
       "mobilenet_v1_1.0_224_quant.tflite", NULL);
@@ -1122,71 +1140,191 @@ TEST (nnstreamer_capi_singleshot, invoke_02)
   out_info.info[0].dimension[2] = 1;
   out_info.info[0].dimension[3] = 1;
 
-  status = ml_model_open (test_model, &model, NULL, NULL,
+  status = ml_single_open (&single, test_model, NULL, NULL,
       ML_NNFW_TENSORFLOW_LITE, ML_NNFW_HW_DO_NOT_CARE);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   /* generate dummy data */
-  input = ml_model_allocate_tensors_data (&in_info);
+  input = ml_util_allocate_tensors_data (&in_info);
   EXPECT_TRUE (input != NULL);
 
-  output1 = ml_model_inference (model, input, NULL);
+  status = ml_util_get_last_error ();
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  output1 = ml_single_inference (single, input, NULL);
+  EXPECT_TRUE (output1 != NULL);
+
+  status = ml_util_get_last_error ();
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  ml_util_free_tensors_data (&output1);
+
+  output2 = ml_util_allocate_tensors_data (&out_info);
+  EXPECT_TRUE (output2 != NULL);
+
+  status = ml_util_get_last_error ();
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  output1 = ml_single_inference (single, input, output2);
+  EXPECT_TRUE (output1 != NULL);
+  EXPECT_TRUE (output1 == output2);
+
+  status = ml_util_get_last_error ();
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  ml_util_free_tensors_data (&output2);
+  ml_util_free_tensors_data (&input);
+
+  status = ml_single_close (single);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  g_free (test_model);
+}
+
+/**
+ * @brief Test NNStreamer single shot (custom filter)
+ * @detail Run pipeline with custom filter, handle multi tensors.
+ */
+TEST (nnstreamer_capi_singleshot, invoke_03)
+{
+  ml_single_h single;
+  ml_tensors_info_s in_info, out_info;
+  ml_tensors_data_s *input, *output1, *output2;
+  int i, status;
+
+  const gchar *root_path = g_getenv ("NNSTREAMER_BUILD_ROOT_PATH");
+  gchar *test_model;
+
+  ml_util_initialize_tensors_info (&in_info);
+  ml_util_initialize_tensors_info (&out_info);
+
+  ASSERT_TRUE (root_path != NULL);
+  test_model = g_build_filename (root_path, "build", "nnstreamer_example", "custom_example_passthrough",
+      "libnnstreamer_customfilter_passthrough_variable.so", NULL);
+
+  in_info.num_tensors = 2;
+  in_info.info[0].type = ML_TENSOR_TYPE_INT16;
+  in_info.info[0].dimension[0] = 10;
+  in_info.info[0].dimension[1] = 1;
+  in_info.info[0].dimension[2] = 1;
+  in_info.info[0].dimension[3] = 1;
+  in_info.info[1].type = ML_TENSOR_TYPE_FLOAT32;
+  in_info.info[1].dimension[0] = 10;
+  in_info.info[1].dimension[1] = 1;
+  in_info.info[1].dimension[2] = 1;
+  in_info.info[1].dimension[3] = 1;
+
+  ml_util_copy_tensors_info (&out_info, &in_info);
+
+  status = ml_single_open (&single, test_model, &in_info, &out_info,
+      ML_NNFW_CUSTOM_FILTER, ML_NNFW_HW_DO_NOT_CARE);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  /* generate input data */
+  input = ml_util_allocate_tensors_data (&in_info);
+  ASSERT_TRUE (input != NULL);
+  EXPECT_TRUE (input->num_tensors == 2U);
+
+  for (i = 0; i < 10; i++) {
+    int16_t i16 = (int16_t) (i + 1);
+    float f32 = (float) (i + .1);
+
+    ((int16_t *) input->tensors[0].tensor)[i] = i16;
+    ((float *) input->tensors[1].tensor)[i] = f32;
+  }
+
+  status = ml_util_get_last_error ();
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  output1 = ml_single_inference (single, input, NULL);
   EXPECT_TRUE (output1 != NULL);
-  ml_model_free_tensors_data (output1);
 
-  output2 = ml_model_allocate_tensors_data (&out_info);
+  status = ml_util_get_last_error ();
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  EXPECT_TRUE (output1->tensors[0].size == ml_util_get_tensor_size (&in_info.info[0]));
+  EXPECT_TRUE (output1->tensors[1].size == ml_util_get_tensor_size (&in_info.info[1]));
+
+  for (i = 0; i < 10; i++) {
+    int16_t i16 = (int16_t) (i + 1);
+    float f32 = (float) (i + .1);
+
+    EXPECT_EQ (((int16_t *) output1->tensors[0].tensor)[i], i16);
+    EXPECT_FLOAT_EQ (((float *) output1->tensors[1].tensor)[i], f32);
+  }
+
+  ml_util_free_tensors_data (&output1);
+
+  output2 = ml_util_allocate_tensors_data (&out_info);
   EXPECT_TRUE (output2 != NULL);
 
-  output1 = ml_model_inference (model, input, output2);
+  status = ml_util_get_last_error ();
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  output1 = ml_single_inference (single, input, output2);
   EXPECT_TRUE (output1 != NULL);
   EXPECT_TRUE (output1 == output2);
-  ml_model_free_tensors_data (output2);
 
-  ml_model_free_tensors_data (input);
+  status = ml_util_get_last_error ();
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  EXPECT_TRUE (output1->tensors[0].size == ml_util_get_tensor_size (&in_info.info[0]));
+  EXPECT_TRUE (output1->tensors[1].size == ml_util_get_tensor_size (&in_info.info[1]));
+
+  for (i = 0; i < 10; i++) {
+    int16_t i16 = (int16_t) (i + 1);
+    float f32 = (float) (i + .1);
+
+    EXPECT_EQ (((int16_t *) output1->tensors[0].tensor)[i], i16);
+    EXPECT_FLOAT_EQ (((float *) output1->tensors[1].tensor)[i], f32);
+  }
+
+  ml_util_free_tensors_data (&output2);
+  ml_util_free_tensors_data (&input);
 
-  status = ml_model_close (model);
+  status = ml_single_close (single);
   EXPECT_EQ (status, ML_ERROR_NONE);
 
   g_free (test_model);
 }
 
 /**
- * @brief Test NNStreamer single shot
+ * @brief Test NNStreamer single shot (tensorflow-lite)
  * @detail Failure case with invalid param.
  */
 TEST (nnstreamer_capi_singleshot, failure_01)
 {
-  ml_simpleshot_model_h model;
+  ml_single_h single;
   ml_tensors_info_s in_info, out_info;
   int status;
 
   const gchar *root_path = g_getenv ("NNSTREAMER_BUILD_ROOT_PATH");
   gchar *test_model;
 
-  memset (&in_info, 0, sizeof (ml_tensors_info_s));
-  memset (&out_info, 0, sizeof (ml_tensors_info_s));
+  ml_util_initialize_tensors_info (&in_info);
+  ml_util_initialize_tensors_info (&out_info);
 
   ASSERT_TRUE (root_path != NULL);
   test_model = g_build_filename (root_path, "tests", "test_models", "models",
       "mobilenet_v1_1.0_224_quant.tflite", NULL);
 
   /* invalid file path */
-  status = ml_model_open ("wrong_file_name", &model, &in_info, &out_info,
+  status = ml_single_open (&single, "wrong_file_name", &in_info, &out_info,
       ML_NNFW_TENSORFLOW_LITE, ML_NNFW_HW_DO_NOT_CARE);
   EXPECT_EQ (status, ML_ERROR_INVALID_PARAMETER);
 
   /* null file path */
-  status = ml_model_open (NULL, &model, &in_info, &out_info,
+  status = ml_single_open (&single, NULL, &in_info, &out_info,
       ML_NNFW_TENSORFLOW_LITE, ML_NNFW_HW_DO_NOT_CARE);
   EXPECT_EQ (status, ML_ERROR_INVALID_PARAMETER);
 
   /* invalid handle */
-  status = ml_model_open (test_model, NULL, &in_info, &out_info,
+  status = ml_single_open (NULL, test_model, &in_info, &out_info,
       ML_NNFW_TENSORFLOW_LITE, ML_NNFW_HW_DO_NOT_CARE);
   EXPECT_EQ (status, ML_ERROR_INVALID_PARAMETER);
 
   /* invalid input tensor info */
-  status = ml_model_open (test_model, &model, &in_info, &out_info,
+  status = ml_single_open (&single, test_model, &in_info, &out_info,
       ML_NNFW_TENSORFLOW_LITE, ML_NNFW_HW_DO_NOT_CARE);
   EXPECT_EQ (status, ML_ERROR_INVALID_PARAMETER);
 
@@ -1198,7 +1336,7 @@ TEST (nnstreamer_capi_singleshot, failure_01)
   in_info.info[0].dimension[3] = 1;
 
   /* invalid output tensor info */
-  status = ml_model_open (test_model, &model, &in_info, &out_info,
+  status = ml_single_open (&single, test_model, &in_info, &out_info,
       ML_NNFW_TENSORFLOW_LITE, ML_NNFW_HW_DO_NOT_CARE);
   EXPECT_EQ (status, ML_ERROR_INVALID_PARAMETER);
 
@@ -1210,12 +1348,12 @@ TEST (nnstreamer_capi_singleshot, failure_01)
   out_info.info[0].dimension[3] = 1;
 
   /* unknown fw type */
-  status = ml_model_open (test_model, &model, &in_info, &out_info,
+  status = ml_single_open (&single, test_model, &in_info, &out_info,
       ML_NNFW_UNKNOWN, ML_NNFW_HW_DO_NOT_CARE);
   EXPECT_EQ (status, ML_ERROR_NOT_SUPPORTED);
 
   /* invalid handle */
-  status = ml_model_close (model);
+  status = ml_single_close (single);
   EXPECT_EQ (status, ML_ERROR_INVALID_PARAMETER);
 
   g_free (test_model);
index e2c34e4..70e230d 100644 (file)
@@ -16,7 +16,7 @@
 /**
  * @file nnstreamer-single.h
  * @date 29 March 2019
- * @brief Tizen NNStreamer single shot invocation C-API Header.
+ * @brief Tizen NNStreamer single-shot invocation C-API Header.
  *        This allows to invoke a neural network model directly.
  * @see        https://github.com/nnsuite/nnstreamer
  * @author MyungJoo Ham <myungjoo.ham@samsung.com>
@@ -41,51 +41,28 @@ extern "C" {
  */
 
 /**
- * @brief A handle of a simpleshot instance
+ * @brief A handle of a single-shot instance
  * @since_tizen 5.5
  */
-typedef void *ml_simpleshot_model_h;
-
-/**
- * @brief Types of NNFWs.
- * @since_tizen 5.5
- */
-typedef enum {
-  ML_NNFW_UNKNOWN = 0, /**< it is unknown or we do not care this value. */
-  ML_NNFW_CUSTOM_FILTER, /**< custom filter (independent shared object). */
-  ML_NNFW_TENSORFLOW_LITE, /**< tensorflow-lite (.tflite). */
-  ML_NNFW_TENSORFLOW, /**< tensorflow (.pb). */
-} ml_model_nnfw;
-
-/**
- * @brief Types of NNFWs. Note that if the affinity (nnn) is not supported by the driver or hardware, it is ignored.
- * @since_tizen 5.5
- */
-typedef enum {
-  ML_NNFW_HW_DO_NOT_CARE = 0, /**< Hardware resource is not specified. */
-  ML_NNFW_HW_AUTO = 1, /**< Try to schedule and optimize if possible. */
-  ML_NNFW_HW_CPU = 0x1000, /**< 0x1000: any CPU. 0x1nnn: CPU # nnn-1. */
-  ML_NNFW_HW_GPU = 0x2000, /**< 0x2000: any GPU. 0x2nnn: GPU # nnn-1. */
-  ML_NNFW_HW_NPU = 0x3000, /**< 0x3000: any NPU. 0x3nnn: NPU # nnn-1. */
-} ml_model_hw;
+typedef void *ml_single_h;
 
 /*************
  * MAIN FUNC *
  *************/
 /**
- * @brief Open an ML model and return the model as a handle.
+ * @brief Opens an ML model and returns the instance as a handle.
  * @since_tizen 5.5
+ * @param[out] single This is the model handle opened. Users are required to close
+ *                   the given instance with ml_single_close().
  * @param[in] model_path This is the path to the neural network model file.
- * @param[out] model This is the model opened. Users are required to close
- *                   the given model with ml_model_close().
- * @param[in] input_type This is required if the given model has flexible input
+ * @param[in] input_info This is required if the given model has flexible input
  *                      dimension, where the input dimension MUST be given
  *                      before executing the model.
  *                      However, once it's given, the input dimension cannot
  *                      be changed for the given model handle.
- *                      It is required by some custom filters of nnstreamer.
+ *                      It is required by some custom filters of NNStreamer.
  *                      You may set NULL if it's not required.
- * @param[in] output_type This is required if the given model has flexible output dimension.
+ * @param[in] output_info This is required if the given model has flexible output dimension.
  * @param[in] nnfw The nerual network framework used to open the given
  *                 @model_path. Set ML_NNFW_UNKNOWN to let it auto-detect.
  * @param[in] hw Tell the corresponding @nnfw to use a specific hardware.
@@ -97,128 +74,72 @@ typedef enum {
  *         input data frames of an instance of a model should share the
  *         same dimension.
  */
-int ml_model_open (const char *model_path, ml_simpleshot_model_h *model,
-    const ml_tensors_info_s *input_type, const ml_tensors_info_s *output_type,
-    ml_model_nnfw nnfw, ml_model_hw hw);
+int ml_single_open (ml_single_h *single, const char *model_path, const ml_tensors_info_s *input_info, const ml_tensors_info_s *output_info, ml_nnfw_e nnfw, ml_nnfw_hw_e hw);
 
 /**
- * @brief Close the opened model handle.
+ * @brief Closes the opened model handle.
  * @since_tizen 5.5
- * @param[in] model The model handle to be closed.
+ * @param[in] single The model handle to be closed.
  * @return @c 0 on success. otherwise a negative error value
  * @retval #ML_ERROR_NONE Successful
  * @retval #ML_ERROR_INVALID_PARAMETER Fail. The parameter is invalid (pipe is NULL?)
  */
-int ml_model_close (ml_simpleshot_model_h model);
+int ml_single_close (ml_single_h single);
 
 /**
- * @brief Invoke the model with the given input data.
+ * @brief Invokes the model with the given input data.
  * @since_tizen 5.5
- * @param[in] model The model to be inferred.
+ * @param[in] single The model handle to be inferred.
  * @param[in] input The input data to be inferred.
  * @param[out] output The output buffer. Set NULL if you want to let
  *                    this function to allocate a new output buffer.
  * @return @c The output buffer. If @output is NULL, this is a newly
  *         allocated buffer; thus, the user needs to free it.
- *         If there is an error, this is set NULL. Check get_last_result()
+ *         If there is an error, this is set NULL. Check ml_util_get_last_error()
  *         of tizen_error.h in such cases.
  *
  * @detail Even if the model has flexible input data dimensions,
  *         input data frames of an instance of a model should share the
  *         same dimension.
  */
-ml_tensors_data_s * ml_model_inference (ml_simpleshot_model_h model,
-    const ml_tensors_data_s *input, ml_tensors_data_s *output);
+ml_tensors_data_s * ml_single_inference (ml_single_h single, const ml_tensors_data_s *input, ml_tensors_data_s *output);
 
 /*************
  * UTILITIES *
  *************/
 
 /**
- * @brief Get type (tensor dimension, type, name and so on) of required input
- *        data for the given model.
+ * @brief Gets the type (tensor dimension, type, name and so on) of required input
+ *        data for the given handle.
  * @detail Note that a model may not have such
  *         information if its input type is flexible.
  *         Besides, names of tensors may be not available while dimensions and
  *         types are available.
  * @since_tizen 5.5
- * @param[in] model The model to be investigated
- * @param[out] input_type The type of input tensor.
- * @return @c 0 on success. otherwise a negative error value
+ * @param[in] single The model handle to be investigated.
+ * @param[out] input_info The struct of input tensors info. Caller is responsible to free the information with ml_util_free_tensors_info().
+ * @return @c 0 on success. otherwise a negative error value.
  * @retval #ML_ERROR_NONE Successful
+ * @retval #ML_ERROR_INVALID_PARAMETER Fail. The parameter is invalid.
  */
-int ml_model_get_input_type (ml_simpleshot_model_h model,
-    ml_tensors_info_s *input_type);
+int ml_single_get_input_info (ml_single_h single, ml_tensors_info_s *input_info);
 
 /**
- * @brief Get type (tensor dimension, type, name and so on) of output
- *        data of the given model.
+ * @brief Gets the type (tensor dimension, type, name and so on) of output
+ *        data for the given handle.
  * @detail Note that a model may not have such
  *         information if its input type is flexible and output type is
  *         not determined statically.
  *         Besides, names of tensors may be not available while dimensions and
  *         types are available.
  * @since_tizen 5.5
- * @param[in] model The model to be investigated
- * @param[out] output_type The type of output tensor.
- * @return @c 0 on success. otherwise a negative error value
+ * @param[in] single The model handle to be investigated.
+ * @param[out] output_info The struct of output tensors info. Caller is responsible to free the returned with ml_util_free_tensors_info().
+ * @return @c 0 on success. otherwise a negative error value.
  * @retval #ML_ERROR_NONE Successful
+ * @retval #ML_ERROR_INVALID_PARAMETER Fail. The parameter is invalid.
  */
-int ml_model_get_output_type (ml_simpleshot_model_h model,
-    ml_tensors_info_s *output_type);
-
-/**
- * @brief Get the byte size of the given tensor type.
- * @since_tizen 5.5
- * @param[in] info The tensor information to be investigated.
- * @return @c >= 0 on success with byte size. otherwise a negative error value
- */
-size_t ml_util_get_tensor_size (const ml_tensor_info_s *info);
-
-/**
- * @brief Get the byte size of the given tensors type.
- * @since_tizen 5.5
- * @param[in] info The tensors information to be investigated.
- * @return @c >= 0 on success with byte size. otherwise a negative error value
- */
-size_t ml_util_get_tensors_size (const ml_tensors_info_s *info);
-
-/**
- * @brief Free the tensors type pointer.
- * @since_tizen 5.5
- * @param[in] type the tensors type pointer to be freed.
- */
-void ml_model_free_tensors_info (ml_tensors_info_s *type);
-
-/**
- * @brief Free the tensors data pointer.
- * @since_tizen 5.5
- * @param[in] data The tensors data pointer to be freed.
- */
-void ml_model_free_tensors_data (ml_tensors_data_s *data);
-
-/**
- * @brief Allocate a tensor data frame with the given tensors type.
- * @since_tizen 5.5
- * @param[in] info The tensors information for the allocation
- * @return @c Tensors data pointer allocated. Null if error.
- * @retval NULL there is an error. call ml_get_last_result() to get specific error numbers.
- */
-ml_tensors_data_s *ml_model_allocate_tensors_data (const ml_tensors_info_s *info);
-
-/**
- * @brief Check the availability of the given execution environments.
- * @since_tizen 5.5
- * @param[in] nnfw Check if the nnfw is available in the system.
- *                 Set ML_NNFW_UNKNOWN to skip checking nnfw.
- * @param[in] hw Check if the hardware is available in the system.
- *               Set ML_NNFW_HW_DO_NOT_CARE to skip checking hardware.
- * @return @c 0 if it's available. 1 if it's not available.
- *            negative value if there is an error.
- * @retval #ML_ERROR_NONE Successful and the environments are available.
- * @retval 1 Successful but the environments are not available.
- */
-int ml_model_check_nnfw (ml_model_nnfw nnfw, ml_model_hw hw);
+int ml_single_get_output_info (ml_single_h single, ml_tensors_info_s *output_info);
 
 /**
  * @}
index d5bce53..b54ca52 100644 (file)
@@ -84,6 +84,29 @@ typedef void *ml_pipeline_switch_h;
 typedef void *ml_pipeline_valve_h;
 
 /**
+ * @brief Types of NNFWs.
+ * @since_tizen 5.5
+ */
+typedef enum {
+  ML_NNFW_UNKNOWN = 0, /**< it is unknown or we do not care this value. */
+  ML_NNFW_CUSTOM_FILTER, /**< custom filter (independent shared object). */
+  ML_NNFW_TENSORFLOW_LITE, /**< tensorflow-lite (.tflite). */
+  ML_NNFW_TENSORFLOW, /**< tensorflow (.pb). */
+} ml_nnfw_e;
+
+/**
+ * @brief Types of NNFWs. Note that if the affinity (nnn) is not supported by the driver or hardware, it is ignored.
+ * @since_tizen 5.5
+ */
+typedef enum {
+  ML_NNFW_HW_DO_NOT_CARE = 0, /**< Hardware resource is not specified. */
+  ML_NNFW_HW_AUTO = 1, /**< Try to schedule and optimize if possible. */
+  ML_NNFW_HW_CPU = 0x1000, /**< 0x1000: any CPU. 0x1nnn: CPU # nnn-1. */
+  ML_NNFW_HW_GPU = 0x2000, /**< 0x2000: any GPU. 0x2nnn: GPU # nnn-1. */
+  ML_NNFW_HW_NPU = 0x3000, /**< 0x3000: any NPU. 0x3nnn: NPU # nnn-1. */
+} ml_nnfw_hw_e;
+
+/**
  * @brief Possible data element types of Tensor in NNStreamer.
  * @since_tizen 5.5
  */
@@ -162,10 +185,10 @@ typedef enum {
 typedef enum {
   ML_PIPELINE_SWITCH_OUTPUT_SELECTOR                   = 0, /**< GstOutputSelector */
   ML_PIPELINE_SWITCH_INPUT_SELECTOR                    = 1, /**< GstInputSelector */
-} ml_pipeline_switch_type_e;
+} ml_pipeline_switch_e;
 
 /**
- * @brief Data structure for Tensor Information.
+ * @brief Data structure for tensor information.
  * @since_tizen 5.5
  */
 typedef struct {
@@ -175,7 +198,7 @@ typedef struct {
 } ml_tensor_info_s;
 
 /**
- * @brief Data structure for Tensors Information, which contains multiple tensors.
+ * @brief Data structure for tensors information, which contains multiple tensors.
  * @since_tizen 5.5
  */
 typedef struct {
@@ -202,8 +225,8 @@ typedef struct {
 } ml_tensors_data_s;
 
 /**
- * @brief Callback for sink element of nnstreamer pipelines (pipeline's output)
- * @detail If an application wants to accept data outputs of an nnstreamer stream, use this callback to get data from the stream. Note that the buffer may be deallocated after the return and this is synchronously called. Thus, if you need the data afterwards, copy the data to another buffer and return fast. Do not hold too much time in the callback. It is recommended to use very small tensors at sinks.
+ * @brief Callback for sink element of NNStreamer pipelines (pipeline's output)
+ * @detail If an application wants to accept data outputs of an NNStreamer stream, use this callback to get data from the stream. Note that the buffer may be deallocated after the return and this is synchronously called. Thus, if you need the data afterwards, copy the data to another buffer and return fast. Do not hold too much time in the callback. It is recommended to use very small tensors at sinks.
  * @since_tizen 5.5
  * @param[in] data The contents of the tensor output (a single frame. tensor/tensors). Number of tensors is determined by data->num_tensors. Note that max num_tensors is 16 (ML_TENSOR_SIZE_LIMIT).
  * @param[in] info The cardinality, dimension, and type of given tensor/tensors.
@@ -220,8 +243,8 @@ typedef void (*ml_pipeline_sink_cb) (const ml_tensors_data_s *data, const ml_ten
  * @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[out] pipe The nnstreamer pipeline handler from the given description
- * @return @c 0 on success. otherwise a negative error value
+ * @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_STREAMS_PIPE Pipeline construction is failed because of wrong parameter or initialization failure.
  * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid. (pipe is NULL?)
@@ -230,10 +253,10 @@ int ml_pipeline_construct (const char *pipeline_description, ml_pipeline_h *pipe
 
 /**
  * @brief Destroys the pipeline
- * @detail Uses this function to destroy the pipeline constructed with nns_construct_pipeline.
+ * @detail Uses this function to destroy the pipeline constructed with ml_pipeline_construct().
  * @since_tizen 5.5
  * @param[in] pipe The pipeline to be destroyed.
- * @return @c 0 on success. otherwise a negative error value
+ * @return @c 0 on success. Otherwise a negative error value.
  * @retval #ML_ERROR_NONE Successful
  * @retval #ML_ERROR_STREAMS_PIPE Cannot access the pipeline status.
  * @retval #ML_ERROR_INVALID_PARAMETER The parameter is invalid (pipe is NULL?)
@@ -242,11 +265,11 @@ int ml_pipeline_destroy (ml_pipeline_h pipe);
 
 /**
  * @brief Gets the state of pipeline
- * @detail Gets the state of the pipeline handle returned by nns_construct_pipeline (pipe).
+ * @detail Gets the state of the pipeline handle returned by ml_pipeline_construct().
  * @since_tizen 5.5
  * @param[in] pipe The pipeline to be monitored.
  * @param[out] state The pipeline state.
- * @return @c 0 on success. otherwise a negative error value
+ * @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 Failed to get state from the pipeline.
@@ -258,11 +281,11 @@ int ml_pipeline_get_state (ml_pipeline_h pipe, ml_pipeline_state_e *state);
  ****************************************************/
 /**
  * @brief Starts the pipeline
- * @detail The pipeline handle returned by nns_construct_pipeline (pipe) is started.
+ * @detail The pipeline handle returned by ml_pipeline_construct() 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
+ * @return @c 0 on success. Otherwise a negative error value.
  * @retval #ML_ERROR_NONE Successful
  * @retval #ML_ERROR_STREAMS_PIPE Failed to start.
  * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid. (pipe is NULL?)
@@ -271,11 +294,11 @@ int ml_pipeline_start (ml_pipeline_h pipe);
 
 /**
  * @brief Stops the pipeline
- * @detail The pipeline handle returned by nns_construct_pipeline (pipe) is stopped.
+ * @detail The pipeline handle returned by ml_pipeline_construct() 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
+ * @return @c 0 on success. Otherwise a negative error value.
  * @retval #ML_ERROR_NONE Successful
  * @retval #ML_ERROR_STREAMS_PIPE Failed to start.
  * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid. (pipe is NULL?)
@@ -286,7 +309,7 @@ int ml_pipeline_stop (ml_pipeline_h pipe);
  ** NNStreamer Pipeline Sink/Src Control           **
  ****************************************************/
 /**
- * @brief Registers a callback for sink (tensor_sink) of nnstreamer pipelines.
+ * @brief Registers a callback for sink (tensor_sink) of NNStreamer pipelines.
  * @since_tizen 5.5
  * @remarks If the function succeeds, @a h handle must be unregistered using ml_pipeline_sink_unregister.
  * @param[in] pipe The pipeline to be attached with a sink node.
@@ -294,7 +317,7 @@ int ml_pipeline_stop (ml_pipeline_h pipe);
  * @param[in] cb The function to be called by the sink node.
  * @param[out] h The sink handle.
  * @param[in] pdata Private data for the callback. This value is passed to the callback when it's invoked.
- * @return @c 0 on success. otherwise a negative error value
+ * @return @c 0 on success. Otherwise a negative error value.
  * @retval #ML_ERROR_NONE Successful
  * @retval #ML_ERROR_STREAMS_PIPE Failed to connect a signal to sink element.
  * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid. (pipe is NULL, sink_name is not found, or sink_name has an invalid type.)
@@ -302,35 +325,36 @@ int ml_pipeline_stop (ml_pipeline_h pipe);
 int ml_pipeline_sink_register (ml_pipeline_h pipe, const char *sink_name, ml_pipeline_sink_cb cb, ml_pipeline_sink_h *h, void *pdata);
 
 /**
- * @brief Unregisters a callback for sink (tensor_sink) of nnstreamer pipelines.
+ * @brief Unregisters a callback for sink (tensor_sink) of NNStreamer pipelines.
  * @since_tizen 5.5
  * @param[in] h The sink handle to be unregistered (destroyed)
- * @return @c 0 on success. otherwise a negative error value
+ * @return @c 0 on success. Otherwise a negative error value.
  * @retval #ML_ERROR_NONE Successful
  * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid.
  */
 int ml_pipeline_sink_unregister (ml_pipeline_sink_h h);
 
 /**
- * @brief Gets a handle to operate as a src node of nnstreamer pipelines.
+ * @brief Gets a handle to operate as a src node of NNStreamer pipelines.
  * @since_tizen 5.5
  * @remarks If the function succeeds, @a h handle must be released using ml_pipeline_src_put_handle().
  * @param[in] pipe The pipeline to be attached with a src node.
  * @param[in] src_name The name of src node, described with ml_pipeline_construct().
  * @param[out] tensors_info The cardinality, dimension, and type of given tensor/tensors.
  * @param[out] h The src handle.
- * @return 0 on success (buf is filled). otherwise a negative error value.
+ * @return 0 on success. Otherwise a negative error value.
  * @retval #ML_ERROR_NONE Successful
  * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid.
  * @retval #ML_ERROR_STREAMS_PIPE Fail to get SRC element.
+ * @retval #ML_ERROR_TRY_AGAIN The pipeline is not ready yet.
  */
 int ml_pipeline_src_get_handle (ml_pipeline_h pipe, const char *src_name, ml_tensors_info_s *tensors_info, ml_pipeline_src_h *h);
 
 /**
- * @brief Closes the given handle of a src node of nnstreamer pipelines.
+ * @brief Closes the given handle of a src node of NNStreamer pipelines.
  * @since_tizen 5.5
  * @param[in] h The src handle to be put (closed).
- * @return 0 on success (buf is filled). otherwise a negative error value.
+ * @return 0 on success. Otherwise a negative error value.
  * @retval #ML_ERROR_NONE Successful
  * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid.
  */
@@ -341,7 +365,7 @@ int ml_pipeline_src_put_handle (ml_pipeline_src_h h);
  * @param[in] h The source handle returned by ml_pipeline_src_get_handle().
  * @param[in] data The input tensors, in the format of tensors info given by ml_pipeline_src_get_handle().
  * @param[in] policy The policy of buf deallocation.
- * @return 0 on success (buf is filled). otherwise a negative error value.
+ * @return 0 on success. Otherwise a negative error value.
  * @retval #ML_ERROR_NONE Successful
  * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid.
  * @retval #ML_ERROR_STREAMS_PIPE The pipeline has inconsistent padcaps. Not negotiated?
@@ -354,7 +378,7 @@ int ml_pipeline_src_input_data (ml_pipeline_src_h h, const ml_tensors_data_s *da
  ****************************************************/
 
 /**
- * @brief Gets a handle to operate a "GstInputSelector / GstOutputSelector" node of nnstreamer pipelines.
+ * @brief Gets a handle to operate a "GstInputSelector / GstOutputSelector" node of NNStreamer pipelines.
  * @detail Refer to https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-plugins/html/gstreamer-plugins-input-selector.html for input selectors.
  *         Refer to https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-plugins/html/gstreamer-plugins-output-selector.html for output selectors.
  * @remarks If the function succeeds, @a h handle must be released using ml_pipeline_switch_put_handle().
@@ -362,16 +386,16 @@ int ml_pipeline_src_input_data (ml_pipeline_src_h h, const ml_tensors_data_s *da
  * @param[in] switch_name The name of switch (InputSelector/OutputSelector)
  * @param[out] type The type of the switch. If NULL, it is ignored.
  * @param[out] h The switch handle.
- * @return 0 on success (buf is filled). otherwise a negative error value.
+ * @return 0 on success. Otherwise a negative error value.
  * @retval #ML_ERROR_NONE Successful
  * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid.
  */
-int ml_pipeline_switch_get_handle (ml_pipeline_h pipe, const char *switch_name, ml_pipeline_switch_type_e *type, ml_pipeline_switch_h *h);
+int ml_pipeline_switch_get_handle (ml_pipeline_h pipe, const char *switch_name, ml_pipeline_switch_e *type, ml_pipeline_switch_h *h);
 
 /**
  * @brief Closes the given switch handle.
  * @param[in] h The handle to be closed.
- * @return @c 0 on success. otherwise a negative error value
+ * @return @c 0 on success. Otherwise a negative error value.
  * @retval #ML_ERROR_NONE Successful
  * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid.
  */
@@ -381,7 +405,7 @@ int ml_pipeline_switch_put_handle (ml_pipeline_switch_h h);
  * @brief Controls the switch with the given handle to select input/output nodes(pads).
  * @param[in] h The switch handle returned by ml_pipeline_switch_get_handle()
  * @param[in] pad_name The name of the chosen pad to be activated. Use ml_pipeline_switch_nodelist to list the available pad names.
- * @return @c 0 on success. otherwise a negative error value
+ * @return @c 0 on success. Otherwise a negative error value.
  * @retval #ML_ERROR_NONE Successful
  * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid.
  */
@@ -391,7 +415,7 @@ int ml_pipeline_switch_select (ml_pipeline_switch_h h, const char *pad_name);
  * @brief Gets the pad names of a switch.
  * @param[in] h The switch handle returned by ml_pipeline_switch_get_handle()
  * @param[out] list NULL terminated array of char*. The caller must free each string (char*) in the list and free the list itself.
- * @return @c 0 on success. otherwise a negative error value
+ * @return @c 0 on success. Otherwise a negative error value.
  * @retval #ML_ERROR_NONE Successful
  * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid.
  * @retval #ML_ERROR_STREAMS_PIPE The element is not both input and output switch (Internal data inconsistency).
@@ -399,13 +423,13 @@ int ml_pipeline_switch_select (ml_pipeline_switch_h h, const char *pad_name);
 int ml_pipeline_switch_nodelist (ml_pipeline_switch_h h, char *** list);
 
 /**
- * @brief Gets a handle to operate a "GstValve" node of nnstreamer pipelines.
+ * @brief Gets a handle to operate a "GstValve" node of NNStreamer pipelines.
  * @detail Refer to https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-plugins/html/gstreamer-plugins-valve.html for more info.
  * @remarks If the function succeeds, @a h handle must be released using ml_pipeline_valve_put_handle().
  * @param[in] pipe The pipeline to be managed.
  * @param[in] valve_name The name of valve (Valve)
  * @param[out] h The valve handle.
- * @return @c 0 on success. otherwise a negative error value
+ * @return @c 0 on success. Otherwise a negative error value.
  * @retval #ML_ERROR_NONE Successful
  * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid.
  */
@@ -414,7 +438,7 @@ int ml_pipeline_valve_get_handle (ml_pipeline_h pipe, const char *valve_name, ml
 /**
  * @brief Closes the given valve handle.
  * @param[in] h The handle to be closed.
- * @return @c 0 on success. otherwise a negative error value
+ * @return @c 0 on success. Otherwise a negative error value.
  * @retval #ML_ERROR_NONE Successful
  * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid.
  */
@@ -424,12 +448,111 @@ int ml_pipeline_valve_put_handle (ml_pipeline_valve_h h);
  * @brief Controls the valve with the given handle.
  * @param[in] h The valve handle returned by ml_pipeline_valve_get_handle()
  * @param[in] drop 1 to close (drop & stop the flow). 0 to open (let the flow pass)
- * @return @c 0 on success. otherwise a negative error value
+ * @return @c 0 on success. Otherwise a negative error value.
  * @retval #ML_ERROR_NONE Successful
  * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid.
  */
 int ml_pipeline_valve_control (ml_pipeline_valve_h h, int drop);
 
+/****************************************************
+ ** NNStreamer Utilities                           **
+ ****************************************************/
+/**
+ * @brief Initializes the tensors info.
+ * @since_tizen 5.5
+ * @param[in] info The tensors information to be initialized.
+ */
+void ml_util_initialize_tensors_info (ml_tensors_info_s *info);
+
+/**
+ * @brief Validates the given tensor info is valid.
+ * @since_tizen 5.5
+ * @param[in] info The tensor information to be validated.
+ * @return @c 0 on success. Otherwise a negative error value.
+ * @retval #ML_ERROR_NONE Successful
+ * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid.
+ */
+int ml_util_validate_tensor_info (const ml_tensor_info_s *info);
+
+/**
+ * @brief Validates the given tensors info is valid.
+ * @since_tizen 5.5
+ * @param[in] info The tensors information to be validated.
+ * @return @c 0 on success. Otherwise a negative error value.
+ * @retval #ML_ERROR_NONE Successful
+ * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid.
+ */
+int ml_util_validate_tensors_info (const ml_tensors_info_s *info);
+
+/**
+ * @brief Copies tensor meta info.
+ * @since_tizen 5.5
+ * @param[out] dest Newly allocated tensors information.
+ * @param[in] src The tensors information to be copied.
+ */
+void ml_util_copy_tensors_info (ml_tensors_info_s *dest, const ml_tensors_info_s *src);
+
+/**
+ * @brief Gets the byte size of the given tensor type.
+ * @since_tizen 5.5
+ * @param[in] info The tensor information to be investigated.
+ * @return @c >= 0 on success with byte size.
+ */
+size_t ml_util_get_tensor_size (const ml_tensor_info_s *info);
+
+/**
+ * @brief Gets the byte size of the given tensors type.
+ * @since_tizen 5.5
+ * @param[in] info The tensors information to be investigated.
+ * @return @c >= 0 on success with byte size.
+ */
+size_t ml_util_get_tensors_size (const ml_tensors_info_s *info);
+
+/**
+ * @brief Frees the tensors info pointer.
+ * @since_tizen 5.5
+ * @param[in] info The tensors info pointer to be freed.
+ */
+void ml_util_free_tensors_info (ml_tensors_info_s *info);
+
+/**
+ * @brief Frees the tensors data pointer.
+ * @since_tizen 5.5
+ * @param[in] data The tensors data pointer to be freed.
+ */
+void ml_util_free_tensors_data (ml_tensors_data_s **data);
+
+/**
+ * @brief Allocates a tensor data frame with the given tensors type.
+ * @since_tizen 5.5
+ * @param[in] info The tensors information for the allocation.
+ * @return @c Tensors data pointer allocated. Null if error. Caller is responsible to free the allocated data with ml_util_free_tensors_data().
+ * @retval NULL There is an error. Call ml_util_get_last_error() to get specific error code.
+ */
+ml_tensors_data_s *ml_util_allocate_tensors_data (const ml_tensors_info_s *info);
+
+/**
+ * @brief Checks the availability of the given execution environments.
+ * @since_tizen 5.5
+ * @param[in] nnfw Check if the nnfw is available in the system.
+ *                 Set ML_NNFW_UNKNOWN to skip checking nnfw.
+ * @param[in] hw Check if the hardware is available in the system.
+ *               Set ML_NNFW_HW_DO_NOT_CARE to skip checking hardware.
+ * @return @c 0 if it's available. 1 if it's not available.
+ *            negative value if there is an error.
+ * @retval #ML_ERROR_NONE Successful and the environments are available.
+ * @retval #ML_ERROR_NOT_SUPPORTED The given option is not available.
+ * @retval 1 Successful but the environments are not available.
+ */
+int ml_util_check_nnfw (ml_nnfw_e nnfw, ml_nnfw_hw_e hw);
+
+/**
+ * @brief Gets the last error code.
+ * @since_tizen 5.5
+ * @return @c 0 on success. Otherwise a negative error value.
+ */
+int ml_util_get_last_error (void);
+
 /**
  * @}
  */
index c026555..c5365dc 100644 (file)
 #include <tizen_error.h>
 #include <dlog.h>
 
-#include "nnstreamer.h"
 #include <nnstreamer/tensor_typedef.h>
 
+#include "nnstreamer.h"
+
 #define DLOG_TAG "nnstreamer-capi"
 
 #define dlogi(...) \
@@ -61,7 +62,7 @@ typedef enum {
   ML_PIPELINE_ELEMENT_VALVE = 0x4,
   ML_PIPELINE_ELEMENT_SWITCH_INPUT = 0x8,
   ML_PIPELINE_ELEMENT_SWITCH_OUTPUT = 0x9,
-} ml_pipeline_element_type_e;
+} ml_pipeline_element_e;
 
 /**
  * @brief Internal private representation of pipeline handle.
@@ -75,10 +76,10 @@ typedef struct _ml_pipeline_element {
   GstElement *element; /**< The Sink/Src/Valve/Switch element */
   ml_pipeline *pipe; /**< The main pipeline */
   char *name;
-  ml_pipeline_element_type_e type;
+  ml_pipeline_element_e type;
   GstPad *src;
   GstPad *sink; /**< Unref this at destroy */
-  GstTensorsInfo tensorsinfo;
+  ml_tensors_info_s tensors_info;
   size_t size;
 
   GList *handles;
@@ -140,6 +141,21 @@ typedef struct _ml_pipeline_valve {
   guint32 id;
 } ml_pipeline_valve;
 
+/**
+ * @brief Sets the last error code.
+ */
+void ml_util_set_error (int error_code);
+
+/**
+ * @brief Copies tensor metadata from gst tensors info.
+ */
+void ml_util_copy_tensors_info_from_gst (ml_tensors_info_s *ml_info, const GstTensorsInfo *gst_info);
+
+/**
+ * @brief Copies tensor metadata from ml tensors info.
+ */
+void ml_util_copy_tensors_info_from_ml (GstTensorsInfo *gst_info, const ml_tensors_info_s *ml_info);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
index 5a556b6..ce152aa 100644 (file)
@@ -20,6 +20,7 @@ endif
 
 capi_main = []
 capi_main += join_paths(meson.current_source_dir(), 'src', 'tizen-api-pipeline.c')
+capi_main += join_paths(meson.current_source_dir(), 'src', 'tizen-api-util.c')
 capi_main += join_paths(meson.current_source_dir(), 'src', 'nnstreamer-single.c')
 
 capi_devel_main = []
index c0715fc..8590efc 100644 (file)
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 /**
- * @file nnstreamer-simple.c
+ * @file nnstreamer-single.c
  * @date 08 May 2019
- * @brief Tizen NNStreamer/Simple C-API Wrapper.
+ * @brief Tizen NNStreamer/Single C-API Wrapper.
  *        This allows to invoke individual input frame with NNStreamer.
  * @see        https://github.com/nnsuite/nnstreamer
  * @author MyungJoo Ham <myungjoo.ham@samsung.com>
@@ -37,56 +37,16 @@ typedef struct
   GstElement *src;
   GstElement *sink;
   GstElement *filter;
-} ml_simpleshot_model;
 
-/**
- * @brief Check the given tensor info is valid.
- * @todo move this function to common
- */
-static int
-ml_util_validate_tensor_info (const ml_tensor_info_s * info)
-{
-  guint i;
-
-  if (!info)
-    return FALSE;
-
-  if (info->type < 0 || info->type >= ML_TENSOR_TYPE_UNKNOWN)
-    return FALSE;
-
-  for (i = 0; i < ML_TENSOR_RANK_LIMIT; i++) {
-    if (info->dimension[i] == 0)
-      return FALSE;
-  }
-
-  return TRUE;
-}
-
-/**
- * @brief Check the given tensors info is valid.
- * @todo move this function to common
- */
-static int
-ml_util_validate_tensors_info (const ml_tensors_info_s * info)
-{
-  guint i;
-
-  if (!info || info->num_tensors < 1)
-    return FALSE;
-
-  for (i = 0; i < info->num_tensors; i++) {
-    if (!ml_util_validate_tensor_info (&info->info[i]))
-      return FALSE;
-  }
-
-  return TRUE;
-}
+  ml_tensors_info_s in_info;
+  ml_tensors_info_s out_info;
+} ml_single;
 
 /**
- * @brief Get caps from tensors info.
+ * @brief Gets caps from tensors info.
  */
 static GstCaps *
-ml_model_get_caps_from_tensors_info (const ml_tensors_info_s * info)
+ml_single_get_caps_from_tensors_info (const ml_tensors_info_s * info)
 {
   GstCaps *caps;
   GstTensorsConfig config;
@@ -94,8 +54,7 @@ ml_model_get_caps_from_tensors_info (const ml_tensors_info_s * info)
   if (!info)
     return NULL;
 
-  /** @todo Make common structure for tensor config */
-  memcpy (&config.info, info, sizeof (GstTensorsInfo));
+  ml_util_copy_tensors_info_from_ml (&config.info, info);
 
   /* set framerate 0/1 */
   config.rate_n = 0;
@@ -115,33 +74,34 @@ ml_model_get_caps_from_tensors_info (const ml_tensors_info_s * info)
     caps = gst_tensors_caps_from_config (&config);
   }
 
+  gst_tensors_info_free (&config.info);
   return caps;
 }
 
 /**
- * @brief Open an ML model and return the model as a handle. (more info in nnstreamer-single.h)
+ * @brief Opens an ML model and returns the instance as a handle.
  */
 int
-ml_model_open (const char *model_path, ml_simpleshot_model_h * model,
-    const ml_tensors_info_s * input_type, const ml_tensors_info_s * output_type,
-    ml_model_nnfw nnfw, ml_model_hw hw)
+ml_single_open (ml_single_h * single, const char *model_path,
+    const ml_tensors_info_s * input_info, const ml_tensors_info_s * output_info,
+    ml_nnfw_e nnfw, ml_nnfw_hw_e hw)
 {
-  ml_simpleshot_model *model_h;
+  ml_single *single_h;
   ml_pipeline_h pipe;
   ml_pipeline *pipe_h;
   GstElement *appsrc, *appsink, *filter;
   GstCaps *caps;
-  int ret = ML_ERROR_NONE;
+  int status = ML_ERROR_NONE;
   gchar *pipeline_desc = NULL;
 
   /* Validate the params */
-  if (!model) {
-    dloge ("The given param, model is invalid.");
+  if (!single) {
+    dloge ("The given param, single is invalid.");
     return ML_ERROR_INVALID_PARAMETER;
   }
 
   /* init null */
-  *model = NULL;
+  *single = NULL;
 
   if (!g_file_test (model_path, G_FILE_TEST_IS_REGULAR)) {
     dloge ("The given param, model path [%s] is invalid.",
@@ -149,23 +109,31 @@ ml_model_open (const char *model_path, ml_simpleshot_model_h * model,
     return ML_ERROR_INVALID_PARAMETER;
   }
 
-  if (input_type && !ml_util_validate_tensors_info (input_type)) {
+  if (input_info &&
+      ml_util_validate_tensors_info (input_info) != ML_ERROR_NONE) {
     dloge ("The given param, input tensor info is invalid.");
     return ML_ERROR_INVALID_PARAMETER;
   }
 
-  if (output_type && !ml_util_validate_tensors_info (output_type)) {
+  if (output_info &&
+      ml_util_validate_tensors_info (output_info) != ML_ERROR_NONE) {
     dloge ("The given param, output tensor info is invalid.");
     return ML_ERROR_INVALID_PARAMETER;
   }
 
+  status = ml_util_check_nnfw (nnfw, hw);
+  if (status < 0) {
+    dloge ("The given nnfw is not available.");
+    return status;
+  }
+
   /* 1. Determine nnfw */
   /** @todo Check nnfw with file extention. */
   switch (nnfw) {
     case ML_NNFW_CUSTOM_FILTER:
       pipeline_desc =
           g_strdup_printf
-          ("appsrc name=srcx ! tensor_filter name=filterx framework=custom model=%s ! appsink name=sinkx async=false sync=false",
+          ("appsrc name=srcx ! tensor_filter name=filterx framework=custom model=%s ! appsink name=sinkx sync=false",
           model_path);
       break;
     case ML_NNFW_TENSORFLOW_LITE:
@@ -176,7 +144,7 @@ ml_model_open (const char *model_path, ml_simpleshot_model_h * model,
 
       pipeline_desc =
           g_strdup_printf
-          ("appsrc name=srcx ! tensor_filter name=filterx framework=tensorflow-lite model=%s ! appsink name=sinkx async=false sync=false",
+          ("appsrc name=srcx ! tensor_filter name=filterx framework=tensorflow-lite model=%s ! appsink name=sinkx sync=false",
           model_path);
       break;
     default:
@@ -189,11 +157,11 @@ ml_model_open (const char *model_path, ml_simpleshot_model_h * model,
   /** @todo Now the param hw is ignored. (Supposed CPU only) Support others later. */
 
   /* 3. Construct a pipeline */
-  ret = ml_pipeline_construct (pipeline_desc, &pipe);
+  status = ml_pipeline_construct (pipeline_desc, &pipe);
   g_free (pipeline_desc);
-  if (ret != ML_ERROR_NONE) {
+  if (status != ML_ERROR_NONE) {
     /* Failed to construct pipeline. */
-    return ret;
+    return status;
   }
 
   /* 4. Allocate */
@@ -202,140 +170,146 @@ ml_model_open (const char *model_path, ml_simpleshot_model_h * model,
   appsink = gst_bin_get_by_name (GST_BIN (pipe_h->element), "sinkx");
   filter = gst_bin_get_by_name (GST_BIN (pipe_h->element), "filterx");
 
-  model_h = g_new0 (ml_simpleshot_model, 1);
-  *model = model_h;
+  single_h = g_new0 (ml_single, 1);
+  g_assert (single_h);
 
-  model_h->pipe = pipe;
-  model_h->src = appsrc;
-  model_h->sink = appsink;
-  model_h->filter = filter;
+  single_h->pipe = pipe;
+  single_h->src = appsrc;
+  single_h->sink = appsink;
+  single_h->filter = filter;
+  ml_util_initialize_tensors_info (&single_h->in_info);
+  ml_util_initialize_tensors_info (&single_h->out_info);
 
-  /* 5. Set in/out caps */
-  if (input_type) {
-    caps = ml_model_get_caps_from_tensors_info (input_type);
+  /* 5. Set in/out caps and metadata */
+  if (input_info) {
+    caps = ml_single_get_caps_from_tensors_info (input_info);
+    ml_util_copy_tensors_info (&single_h->in_info, input_info);
   } else {
-    ml_tensors_info_s in_info;
+    ml_single_get_input_info (single_h, &single_h->in_info);
 
-    ml_model_get_input_type (model_h, &in_info);
-    if (!ml_util_validate_tensors_info (&in_info)) {
+    status = ml_util_validate_tensors_info (&single_h->in_info);
+    if (status != ML_ERROR_NONE) {
       dloge ("Failed to get the input tensor info.");
       goto error;
     }
 
-    caps = ml_model_get_caps_from_tensors_info (&in_info);
+    caps = ml_single_get_caps_from_tensors_info (&single_h->in_info);
   }
 
   gst_app_src_set_caps (GST_APP_SRC (appsrc), caps);
   gst_caps_unref (caps);
 
-  if (output_type) {
-    caps = ml_model_get_caps_from_tensors_info (output_type);
+  if (output_info) {
+    caps = ml_single_get_caps_from_tensors_info (output_info);
+    ml_util_copy_tensors_info (&single_h->out_info, output_info);
   } else {
-    ml_tensors_info_s out_info;
+    ml_single_get_output_info (single_h, &single_h->out_info);
 
-    ml_model_get_output_type (model_h, &out_info);
-    if (!ml_util_validate_tensors_info (&out_info)) {
+    status = ml_util_validate_tensors_info (&single_h->out_info);
+    if (status != ML_ERROR_NONE) {
       dloge ("Failed to get the output tensor info.");
       goto error;
     }
 
-    caps = ml_model_get_caps_from_tensors_info (&out_info);
+    caps = ml_single_get_caps_from_tensors_info (&single_h->out_info);
   }
 
   gst_app_sink_set_caps (GST_APP_SINK (appsink), caps);
   gst_caps_unref (caps);
 
-  /* 5. Start pipeline */
-  ret = ml_pipeline_start (pipe);
-  if (ret != ML_ERROR_NONE) {
+  /* 6. Start pipeline */
+  status = ml_pipeline_start (pipe);
+  if (status != ML_ERROR_NONE) {
     /* Failed to construct pipeline. */
     goto error;
   }
 
+  *single = single_h;
   return ML_ERROR_NONE;
 
 error:
-  ml_model_close (pipe);
-  return ret;
+  ml_single_close (single_h);
+  return status;
 }
 
 /**
- * @brief Close the opened model handle. (more info in nnstreamer-single.h)
+ * @brief Closes the opened model handle.
  */
 int
-ml_model_close (ml_simpleshot_model_h model)
+ml_single_close (ml_single_h single)
 {
-  ml_simpleshot_model *model_h;
+  ml_single *single_h;
   int ret;
 
-  if (!model) {
-    dloge ("The given param, model is invalid.");
+  if (!single) {
+    dloge ("The given param, single is invalid.");
     return ML_ERROR_INVALID_PARAMETER;
   }
 
-  model_h = (ml_simpleshot_model *) model;
+  single_h = (ml_single *) single;
 
-  if (model_h->src) {
-    gst_object_unref (model_h->src);
-    model_h->src = NULL;
+  if (single_h->src) {
+    gst_object_unref (single_h->src);
+    single_h->src = NULL;
   }
 
-  if (model_h->sink) {
-    gst_object_unref (model_h->sink);
-    model_h->sink = NULL;
+  if (single_h->sink) {
+    gst_object_unref (single_h->sink);
+    single_h->sink = NULL;
   }
 
-  if (model_h->filter) {
-    gst_object_unref (model_h->filter);
-    model_h->filter = NULL;
+  if (single_h->filter) {
+    gst_object_unref (single_h->filter);
+    single_h->filter = NULL;
   }
 
-  ret = ml_pipeline_destroy (model_h->pipe);
-  g_free (model_h);
+  ml_util_free_tensors_info (&single_h->in_info);
+  ml_util_free_tensors_info (&single_h->out_info);
+
+  ret = ml_pipeline_destroy (single_h->pipe);
+  g_free (single_h);
   return ret;
 }
 
 /**
- * @brief Invoke the model with the given input data. (more info in nnstreamer-single.h)
+ * @brief Invokes the model with the given input data.
  */
 ml_tensors_data_s *
-ml_model_inference (ml_simpleshot_model_h model,
+ml_single_inference (ml_single_h single,
     const ml_tensors_data_s * input, ml_tensors_data_s * output)
 {
-  ml_simpleshot_model *model_h;
-  ml_tensors_info_s out_info;
-  ml_tensors_data_s *result;
+  ml_single *single_h;
+  ml_tensors_data_s *result = NULL;
   GstSample *sample;
   GstBuffer *buffer;
   GstMemory *mem;
   GstMapInfo mem_info;
   GstFlowReturn ret;
-  int i, status;
+  int i, status = ML_ERROR_NONE;
 
-  if (!model || !input) {
+  if (!single || !input) {
     dloge ("The given param is invalid.");
-    return NULL;
+    status = ML_ERROR_INVALID_PARAMETER;
+    goto error;
   }
 
-  model_h = (ml_simpleshot_model *) model;
-
-  status = ml_model_get_output_type (model, &out_info);
-  if (status != ML_ERROR_NONE)
-    return NULL;
+  single_h = (ml_single *) single;
 
   /* Validate output memory and size */
   if (output) {
-    if (output->num_tensors != out_info.num_tensors) {
+    if (output->num_tensors != single_h->out_info.num_tensors) {
       dloge ("Invalid output data, the number of output is different.");
-      return NULL;
+      status = ML_ERROR_INVALID_PARAMETER;
+      goto error;
     }
 
     for (i = 0; i < output->num_tensors; i++) {
       if (!output->tensors[i].tensor ||
           output->tensors[i].size !=
-          ml_util_get_tensor_size (&out_info.info[i])) {
+          ml_util_get_tensor_size (&single_h->out_info.info[i])) {
         dloge ("Invalid output data, the size of output is different.");
-        return NULL;
+        status = ML_ERROR_INVALID_PARAMETER;
+        goto error;
       }
     }
   }
@@ -349,29 +323,32 @@ ml_model_inference (ml_simpleshot_model_h model,
     gst_buffer_append_memory (buffer, mem);
   }
 
-  ret = gst_app_src_push_buffer (GST_APP_SRC (model_h->src), buffer);
+  ret = gst_app_src_push_buffer (GST_APP_SRC (single_h->src), buffer);
   if (ret != GST_FLOW_OK) {
     dloge ("Cannot push a buffer into source element.");
-    return NULL;
+    status = ML_ERROR_STREAMS_PIPE;
+    goto error;
   }
 
   /* Try to get the result */
   sample =
-      gst_app_sink_try_pull_sample (GST_APP_SINK (model_h->sink), GST_SECOND);
+      gst_app_sink_try_pull_sample (GST_APP_SINK (single_h->sink), GST_SECOND);
   if (!sample) {
     dloge ("Failed to get the result from sink element.");
-    return NULL;
+    status = ML_ERROR_STREAMS_PIPE;
+    goto error;
   }
 
   if (output) {
     result = output;
   } else {
-    result = ml_model_allocate_tensors_data (&out_info);
-  }
+    result = ml_util_allocate_tensors_data (&single_h->out_info);
 
-  if (!result) {
-    dloge ("Failed to allocate the memory block.");
-    return NULL;
+    if (!result) {
+      dloge ("Failed to allocate the memory block.");
+      status = ml_util_get_last_error ();
+      goto error;
+    }
   }
 
   /* Copy the result */
@@ -386,36 +363,40 @@ ml_model_inference (ml_simpleshot_model_h model,
   }
 
   gst_sample_unref (sample);
+  status = ML_ERROR_NONE;
+
+error:
+  ml_util_set_error (status);
   return result;
 }
 
 /**
- * @brief Get type (tensor dimension, type, name and so on) of required input data for the given model. (more info in nnstreamer-single.h)
+ * @brief Gets the type (tensor dimension, type, name and so on) of required input data for the given handle.
  */
 int
-ml_model_get_input_type (ml_simpleshot_model_h model,
-    ml_tensors_info_s * input_type)
+ml_single_get_input_info (ml_single_h single,
+    ml_tensors_info_s * input_info)
 {
-  ml_simpleshot_model *model_h;
+  ml_single *single_h;
   GstTensorsInfo info;
   gchar *val;
   guint rank;
 
-  if (!model || !input_type)
+  if (!single || !input_info)
     return ML_ERROR_INVALID_PARAMETER;
 
-  model_h = (ml_simpleshot_model *) model;
+  single_h = (ml_single *) single;
 
   gst_tensors_info_init (&info);
 
-  g_object_get (model_h->filter, "input", &val, NULL);
+  g_object_get (single_h->filter, "input", &val, NULL);
   rank = gst_tensors_info_parse_dimensions_string (&info, val);
   g_free (val);
 
   /* set the number of tensors */
   info.num_tensors = rank;
 
-  g_object_get (model_h->filter, "inputtype", &val, NULL);
+  g_object_get (single_h->filter, "inputtype", &val, NULL);
   rank = gst_tensors_info_parse_types_string (&info, val);
   g_free (val);
 
@@ -423,45 +404,46 @@ ml_model_get_input_type (ml_simpleshot_model_h model,
     dlogw ("Invalid state, input tensor type is mismatched in filter.");
   }
 
-  g_object_get (model_h->filter, "inputname", &val, NULL);
+  g_object_get (single_h->filter, "inputname", &val, NULL);
   rank = gst_tensors_info_parse_names_string (&info, val);
   g_free (val);
 
   if (info.num_tensors != rank) {
     dlogw ("Invalid state, input tensor name is mismatched in filter.");
   }
-  /** @todo Make common structure for tensor config */
-  memcpy (input_type, &info, sizeof (GstTensorsInfo));
+
+  ml_util_copy_tensors_info_from_gst (input_info, &info);
+  gst_tensors_info_free (&info);
   return ML_ERROR_NONE;
 }
 
 /**
- * @brief Get type (tensor dimension, type, name and so on) of output data of the given model. (more info in nnstreamer-single.h)
+ * @brief Gets the type (tensor dimension, type, name and so on) of output data for the given handle.
  */
 int
-ml_model_get_output_type (ml_simpleshot_model_h model,
-    ml_tensors_info_s * output_type)
+ml_single_get_output_info (ml_single_h single,
+    ml_tensors_info_s * output_info)
 {
-  ml_simpleshot_model *model_h;
+  ml_single *single_h;
   GstTensorsInfo info;
   gchar *val;
   guint rank;
 
-  if (!model || !output_type)
+  if (!single || !output_info)
     return ML_ERROR_INVALID_PARAMETER;
 
-  model_h = (ml_simpleshot_model *) model;
+  single_h = (ml_single *) single;
 
   gst_tensors_info_init (&info);
 
-  g_object_get (model_h->filter, "output", &val, NULL);
+  g_object_get (single_h->filter, "output", &val, NULL);
   rank = gst_tensors_info_parse_dimensions_string (&info, val);
   g_free (val);
 
   /* set the number of tensors */
   info.num_tensors = rank;
 
-  g_object_get (model_h->filter, "outputtype", &val, NULL);
+  g_object_get (single_h->filter, "outputtype", &val, NULL);
   rank = gst_tensors_info_parse_types_string (&info, val);
   g_free (val);
 
@@ -469,149 +451,15 @@ ml_model_get_output_type (ml_simpleshot_model_h model,
     dlogw ("Invalid state, output tensor type is mismatched in filter.");
   }
 
-  g_object_get (model_h->filter, "outputname", &val, NULL);
+  g_object_get (single_h->filter, "outputname", &val, NULL);
   gst_tensors_info_parse_names_string (&info, val);
   g_free (val);
 
   if (info.num_tensors != rank) {
     dlogw ("Invalid state, output tensor name is mismatched in filter.");
   }
-  /** @todo Make common structure for tensor config */
-  memcpy (output_type, &info, sizeof (GstTensorsInfo));
-  return ML_ERROR_NONE;
-}
-
-/**
- * @brief Get the byte size of the given tensor type. (more info in nnstreamer-single.h)
- */
-size_t
-ml_util_get_tensor_size (const ml_tensor_info_s * info)
-{
-  size_t tensor_size;
-  gint i;
-
-  if (!info) {
-    dloge ("The given param tensor info is invalid.");
-    return 0;
-  }
-
-  switch (info->type) {
-  case ML_TENSOR_TYPE_INT8:
-  case ML_TENSOR_TYPE_UINT8:
-    tensor_size = 1;
-    break;
-  case ML_TENSOR_TYPE_INT16:
-  case ML_TENSOR_TYPE_UINT16:
-    tensor_size = 2;
-    break;
-  case ML_TENSOR_TYPE_INT32:
-  case ML_TENSOR_TYPE_UINT32:
-  case ML_TENSOR_TYPE_FLOAT32:
-    tensor_size = 4;
-    break;
-  case ML_TENSOR_TYPE_FLOAT64:
-  case ML_TENSOR_TYPE_INT64:
-  case ML_TENSOR_TYPE_UINT64:
-    tensor_size = 8;
-    break;
-  default:
-    dloge ("In the given param, tensor type is invalid.");
-    return 0;
-  }
-
-  for (i = 0; i < ML_TENSOR_RANK_LIMIT; i++) {
-    tensor_size *= info->dimension[i];
-  }
-
-  return tensor_size;
-}
-
-/**
- * @brief Get the byte size of the given tensors info. (more info in nnstreamer-single.h)
- */
-size_t
-ml_util_get_tensors_size (const ml_tensors_info_s * info)
-{
-  size_t tensor_size;
-  gint i;
-
-  tensor_size = 0;
-  for (i = 0; i < info->num_tensors; i++) {
-    tensor_size += ml_util_get_tensor_size (&info->info[i]);
-  }
-
-  return tensor_size;
-}
-
-/**
- * @brief Free the tensors type pointer. (more info in nnstreamer-single.h)
- */
-void
-ml_model_free_tensors_info (ml_tensors_info_s * type)
-{
-  /** @todo Make common structure for tensor config and use gst_tensors_info_free () */
-}
-
-/**
- * @brief Free the tensors data pointer. (more info in nnstreamer-single.h)
- */
-void
-ml_model_free_tensors_data (ml_tensors_data_s * data)
-{
-  gint i;
-
-  if (!data) {
-    dloge ("The given param data is invalid.");
-    return;
-  }
-
-  for (i = 0; i < data->num_tensors; i++) {
-    if (data->tensors[i].tensor) {
-      g_free (data->tensors[i].tensor);
-      data->tensors[i].tensor = NULL;
-    }
-
-    data->tensors[i].size = 0;
-  }
-
-  data->num_tensors = 0;
-}
-
-/**
- * @brief Allocate a tensor data frame with the given tensors type. (more info in nnstreamer-single.h)
- */
-ml_tensors_data_s *
-ml_model_allocate_tensors_data (const ml_tensors_info_s * info)
-{
-  ml_tensors_data_s *data;
-  gint i;
-
-  if (!info) {
-    dloge ("The given param info is invalid.");
-    return NULL;
-  }
 
-  data = g_new0 (ml_tensors_data_s, 1);
-  if (!data) {
-    dloge ("Failed to allocate the memory block.");
-    return NULL;
-  }
-
-  data->num_tensors = info->num_tensors;
-  for (i = 0; i < data->num_tensors; i++) {
-    data->tensors[i].size = ml_util_get_tensor_size (&info->info[i]);
-    data->tensors[i].tensor = g_malloc0 (data->tensors[i].size);
-  }
-
-  return data;
-}
-
-/**
- * @brief Check the availability of the given execution environments. (more info in nnstreamer-single.h)
- */
-int
-ml_model_check_nnfw (ml_model_nnfw nnfw, ml_model_hw hw)
-{
-  /** @todo fill this function */
-  return 0;
+  ml_util_copy_tensors_info_from_gst (output_info, &info);
+  gst_tensors_info_free (&info);
+  return ML_ERROR_NONE;
 }
index 90be088..c2c63ba 100644 (file)
@@ -69,7 +69,7 @@ unlock_return: \
  */
 static ml_pipeline_element *
 construct_element (GstElement * e, ml_pipeline * p, const char *name,
-    ml_pipeline_element_type_e t)
+    ml_pipeline_element_e t)
 {
   ml_pipeline_element *ret = g_new0 (ml_pipeline_element, 1);
   ret->element = e;
@@ -79,7 +79,7 @@ construct_element (GstElement * e, ml_pipeline * p, const char *name,
   ret->handles = NULL;
   ret->src = NULL;
   ret->sink = NULL;
-  gst_tensors_info_init (&ret->tensorsinfo);
+  ml_util_initialize_tensors_info (&ret->tensors_info);
   ret->size = 0;
   ret->maxid = 0;
   ret->handle_id = 0;
@@ -88,27 +88,30 @@ construct_element (GstElement * e, ml_pipeline * p, const char *name,
 }
 
 /**
- * @brief Internal function to convert GstTensorsInfo into ml_tensors_info_s structure.
+ * @brief Internal function to get the tensors info from the element caps.
  */
-static int
-get_tensors_info_from_GstTensorsInfo (GstTensorsInfo * gst_tensorsinfo,
-    ml_tensors_info_s * tensors_info)
+static gboolean
+get_tensors_info_from_caps (GstCaps * caps, ml_tensors_info_s * info)
 {
-  if (!gst_tensorsinfo) {
-    dloge ("GstTensorsInfo should not be NULL!");
-    return ML_ERROR_INVALID_PARAMETER;
-  }
+  GstStructure *s;
+  GstTensorsConfig config;
+  guint i, n_caps;
+  gboolean found = FALSE;
 
-  if (!tensors_info) {
-    dloge ("The param ml_tensors_info_s should not be NULL!");
-    return ML_ERROR_INVALID_PARAMETER;
-  }
+  ml_util_initialize_tensors_info (info);
+  n_caps = gst_caps_get_size (caps);
 
-  /** Currently, the data structures of GstTensorsInfo are
-   * completely same as that of ml_tensors_info_s. */
-  memcpy (tensors_info, gst_tensorsinfo, sizeof (GstTensorsInfo));
+  for (i = 0; i < n_caps; i++) {
+    s = gst_caps_get_structure (caps, i);
+    found = gst_tensors_config_from_structure (&config, s);
 
-  return ML_ERROR_NONE;
+    if (found) {
+      ml_util_copy_tensors_info_from_gst (info, &config.info);
+      break;
+    }
+  }
+
+  return found;
 }
 
 /**
@@ -163,25 +166,15 @@ cb_sink_event (GstElement * e, GstBuffer * b, gpointer user_data)
       GstCaps *caps = gst_pad_get_current_caps (elem->sink);
 
       if (caps) {
-        guint n_caps = gst_caps_get_size (caps);
-        GstTensorsConfig tconfig;
-        gboolean found = FALSE;
-
-        for (i = 0; i < n_caps; i++) {
-          GstStructure *s = gst_caps_get_structure (caps, i);
-
-          found = gst_tensors_config_from_structure (&tconfig, s);
-          if (found)
-            break;
-        }
+        gboolean found;
 
+        found = get_tensors_info_from_caps (caps, &elem->tensors_info);
         gst_caps_unref (caps);
 
         if (found) {
-          memcpy (&elem->tensorsinfo, &tconfig.info, sizeof (GstTensorsInfo));
           elem->size = 0;
 
-          if (elem->tensorsinfo.num_tensors != num_mems) {
+          if (elem->tensors_info.num_tensors != num_mems) {
             dloge
                 ("The sink event of [%s] cannot be handled because the number of tensors mismatches.",
                 elem->name);
@@ -191,8 +184,8 @@ cb_sink_event (GstElement * e, GstBuffer * b, gpointer user_data)
             goto error;
           }
 
-          for (i = 0; i < elem->tensorsinfo.num_tensors; i++) {
-            size_t sz = gst_tensor_info_get_size (&elem->tensorsinfo.info[i]);
+          for (i = 0; i < elem->tensors_info.num_tensors; i++) {
+            size_t sz = ml_util_get_tensor_size (&elem->tensors_info.info[i]);
 
             if (sz != tensors_data.tensors[i].size) {
               dloge
@@ -209,6 +202,7 @@ cb_sink_event (GstElement * e, GstBuffer * b, gpointer user_data)
         } else {
           gst_object_unref (elem->sink);
           elem->sink = NULL;    /* It is not valid */
+          goto error;
           /** @todo What if it keeps being "NULL"? Exception handling at 2nd frame? */
         }
       }
@@ -226,13 +220,10 @@ cb_sink_event (GstElement * e, GstBuffer * b, gpointer user_data)
 
   /* Iterate e->handles, pass the data to them */
   for (l = elem->handles; l != NULL; l = l->next) {
-    ml_tensors_info_s tensors_info;
     ml_pipeline_sink *sink = l->data;
     ml_pipeline_sink_cb callback = sink->cb;
 
-    get_tensors_info_from_GstTensorsInfo (&elem->tensorsinfo, &tensors_info);
-
-    callback (&tensors_data, &tensors_info, sink->pdata);
+    callback (&tensors_data, &elem->tensors_info, sink->pdata);
 
     /** @todo Measure time. Warn if it takes long. Kill if it takes too long. */
   }
@@ -282,14 +273,19 @@ cleanup_node (gpointer data)
     gst_object_unref (e->sink);
 
   /** @todo CRITICAL. Stop the handle callbacks if they are running/ready */
+  if (e->handle_id > 0) {
+    g_signal_handler_disconnect (e->element, e->handle_id);
+    e->handle_id = 0;
+  }
+
   if (e->handles)
     g_list_free_full (e->handles, g_free);
   e->handles = NULL;
 
+  ml_util_free_tensors_info (&e->tensors_info);
+
   g_mutex_unlock (&e->lock);
   g_mutex_clear (&e->lock);
-
-  g_free (e);
 }
 
 /**
@@ -364,6 +360,7 @@ ml_pipeline_construct (const char *pipeline_description, ml_pipeline_h * pipe)
 
           if (GST_IS_ELEMENT (obj)) {
             GstElement *elem = GST_ELEMENT (obj);
+
             name = gst_element_get_name (elem);
             if (name != NULL) {
               ml_pipeline_element *e = NULL;
@@ -396,11 +393,12 @@ ml_pipeline_construct (const char *pipeline_description, ml_pipeline_h * pipe)
 
               if (e != NULL)
                 g_hash_table_insert (pipe_h->namednodes, g_strdup (name), e);
+
+              g_free (name);
             }
-            g_free (name);
           }
-          g_value_reset (&item);
 
+          g_value_reset (&item);
           break;
         case GST_ITERATOR_RESYNC:
         case GST_ITERATOR_ERROR:
@@ -729,6 +727,11 @@ ml_pipeline_src_get_handle (ml_pipeline_h pipe, const char *src_name,
     return ML_ERROR_INVALID_PARAMETER;
   }
 
+  if (tensors_info == NULL) {
+    dloge ("The 3rd argument, tensors info is not valid.");
+    return ML_ERROR_INVALID_PARAMETER;
+  }
+
   g_mutex_lock (&p->lock);
 
   elem = g_hash_table_lookup (p->namednodes, src_name);
@@ -746,50 +749,44 @@ ml_pipeline_src_get_handle (ml_pipeline_h pipe, const char *src_name,
     goto unlock_return;
   }
 
-  if (elem->src == NULL)
+  if (elem->src == NULL) {
     elem->src = gst_element_get_static_pad (elem->element, "src");
 
-  if (elem->src != NULL) {
-    /** @todo : refactor this along with ml_pipeline_src_input_data */
-    GstCaps *caps = gst_pad_get_allowed_caps (elem->src);
-
-    /** @todo caps may be NULL for prerolling */
-    if (caps == NULL) {
-      dlogw
-          ("Cannot find caps. The pipeline is not yet negotiated for tensor_src, [%s].",
-          src_name);
-    } else {
-      guint n_caps = gst_caps_get_size (caps);
-      GstTensorsConfig tconfig;
+    if (elem->src != NULL) {
+      /** @todo : refactor this along with ml_pipeline_src_input_data */
+      GstCaps *caps = gst_pad_get_allowed_caps (elem->src);
       gboolean found = FALSE;
 
-      for (i = 0; i < n_caps; i++) {
-        GstStructure *s = gst_caps_get_structure (caps, i);
-
-        found = gst_tensors_config_from_structure (&tconfig, s);
-        if (found)
-          break;
+      /** @todo caps may be NULL for prerolling */
+      if (caps) {
+        found = get_tensors_info_from_caps (caps, &elem->tensors_info);
+        gst_caps_unref (caps);
       }
 
-      gst_caps_unref (caps);
-
       if (found) {
-        memcpy (&elem->tensorsinfo, &tconfig.info, sizeof (GstTensorsInfo));
         elem->size = 0;
-        for (i = 0; i < elem->tensorsinfo.num_tensors; i++) {
-          size_t sz = gst_tensor_info_get_size (&elem->tensorsinfo.info[i]);
+
+        for (i = 0; i < elem->tensors_info.num_tensors; i++) {
+          size_t sz = ml_util_get_tensor_size (&elem->tensors_info.info[i]);
           elem->size += sz;
         }
+      } else {
+        dlogw
+            ("Cannot find caps. The pipeline is not yet negotiated for tensor_src, [%s].",
+            src_name);
+        gst_object_unref (elem->src);
+        elem->src = NULL;
+
+        ret = ML_ERROR_TRY_AGAIN;
+        goto unlock_return;
       }
+    } else {
+      ret = ML_ERROR_STREAMS_PIPE;
+      goto unlock_return;
     }
-  } else {
-    ret = ML_ERROR_STREAMS_PIPE;
-    goto unlock_return;
   }
 
-  ret = get_tensors_info_from_GstTensorsInfo (&elem->tensorsinfo, tensors_info);
-  if (ret != ML_ERROR_NONE)
-    goto unlock_return;
+  ml_util_copy_tensors_info (tensors_info, &elem->tensors_info);
 
   *h = g_new (ml_pipeline_src, 1);
   src = *h;
@@ -845,7 +842,7 @@ ml_pipeline_src_input_data (ml_pipeline_src_h h, const ml_tensors_data_s * data,
   }
 
   if (data->num_tensors < 1 || data->num_tensors > ML_TENSOR_SIZE_LIMIT) {
-    dloge ("The tensor size if invalid. It should be 1 ~ %u; where it is %u",
+    dloge ("The tensor size is invalid. It should be 1 ~ %u; where it is %u",
         ML_TENSOR_SIZE_LIMIT, data->num_tensors);
     ret = ML_ERROR_INVALID_PARAMETER;
     goto unlock_return;
@@ -862,28 +859,18 @@ ml_pipeline_src_input_data (ml_pipeline_src_h h, const ml_tensors_data_s * data,
     GstCaps *caps = gst_pad_get_allowed_caps (elem->src);
 
     if (caps) {
-      guint n_caps = gst_caps_get_size (caps);
-      GstTensorsConfig tconfig;
-      gboolean found = FALSE;
-
-      for (i = 0; i < n_caps; i++) {
-        GstStructure *s = gst_caps_get_structure (caps, i);
-
-        found = gst_tensors_config_from_structure (&tconfig, s);
-        if (found)
-          break;
-      }
+      gboolean found;
 
+      found = get_tensors_info_from_caps (caps, &elem->tensors_info);
       gst_caps_unref (caps);
 
       if (found) {
-        memcpy (&elem->tensorsinfo, &tconfig.info, sizeof (GstTensorsInfo));
         elem->size = 0;
 
-        if (elem->tensorsinfo.num_tensors != data->num_tensors) {
+        if (elem->tensors_info.num_tensors != data->num_tensors) {
           dloge
               ("The src push of [%s] cannot be handled because the number of tensors in a frame mismatches. %u != %u",
-              elem->name, elem->tensorsinfo.num_tensors, data->num_tensors);
+              elem->name, elem->tensors_info.num_tensors, data->num_tensors);
 
           gst_object_unref (elem->src);
           elem->src = NULL;
@@ -891,8 +878,8 @@ ml_pipeline_src_input_data (ml_pipeline_src_h h, const ml_tensors_data_s * data,
           goto unlock_return;
         }
 
-        for (i = 0; i < elem->tensorsinfo.num_tensors; i++) {
-          size_t sz = gst_tensor_info_get_size (&elem->tensorsinfo.info[i]);
+        for (i = 0; i < elem->tensors_info.num_tensors; i++) {
+          size_t sz = ml_util_get_tensor_size (&elem->tensors_info.info[i]);
 
           if (sz != data->tensors[i].size) {
             dloge
@@ -910,6 +897,8 @@ ml_pipeline_src_input_data (ml_pipeline_src_h h, const ml_tensors_data_s * data,
       } else {
         gst_object_unref (elem->src);
         elem->src = NULL;       /* invalid! */
+        ret = ML_ERROR_STREAMS_PIPE;
+        goto unlock_return;
         /** @todo What if it keeps being "NULL"? */
       }
     }
@@ -956,7 +945,7 @@ ml_pipeline_src_input_data (ml_pipeline_src_h h, const ml_tensors_data_s * data,
  */
 int
 ml_pipeline_switch_get_handle (ml_pipeline_h pipe, const char *switch_name,
-    ml_pipeline_switch_type_e * type, ml_pipeline_switch_h * h)
+    ml_pipeline_switch_e * type, ml_pipeline_switch_h * h)
 {
   ml_pipeline_element *elem;
   ml_pipeline *p = pipe;
diff --git a/tizen-api/src/tizen-api-util.c b/tizen-api/src/tizen-api-util.c
new file mode 100644 (file)
index 0000000..7b37770
--- /dev/null
@@ -0,0 +1,428 @@
+/**
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ */
+/**
+ * @file tizen-api-util.c
+ * @date 10 June 2019
+ * @brief Tizen NNStreamer/Utilities C-API Wrapper.
+ * @see        https://github.com/nnsuite/nnstreamer
+ * @author MyungJoo Ham <myungjoo.ham@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include <nnstreamer/nnstreamer_plugin_api.h>
+
+#include "nnstreamer.h"
+#include "tizen-api-private.h"
+
+static int ml_internal_error_code = ML_ERROR_NONE;
+
+/**
+ * @brief Gets the last error code.
+ */
+int
+ml_util_get_last_error (void)
+{
+  return ml_internal_error_code;
+}
+
+/**
+ * @brief Sets the last error code.
+ */
+void
+ml_util_set_error (int error_code)
+{
+  ml_internal_error_code = error_code;
+}
+
+/**
+ * @brief Initializes the tensors info.
+ */
+void
+ml_util_initialize_tensors_info (ml_tensors_info_s * info)
+{
+  guint i, j;
+
+  if (!info)
+    return;
+
+  info->num_tensors = 0;
+
+  for (i = 0; i < ML_TENSOR_SIZE_LIMIT; i++) {
+    info->info[i].name = NULL;
+    info->info[i].type = ML_TENSOR_TYPE_UNKNOWN;
+
+    for (j = 0; j < ML_TENSOR_RANK_LIMIT; j++) {
+      info->info[i].dimension[j] = 0;
+    }
+  }
+}
+
+/**
+ * @brief Validates the given tensor info is valid.
+ */
+int
+ml_util_validate_tensor_info (const ml_tensor_info_s * info)
+{
+  guint i;
+
+  if (!info)
+    return ML_ERROR_INVALID_PARAMETER;
+
+  if (info->type < 0 || info->type >= ML_TENSOR_TYPE_UNKNOWN)
+    return ML_ERROR_INVALID_PARAMETER;
+
+  for (i = 0; i < ML_TENSOR_RANK_LIMIT; i++) {
+    if (info->dimension[i] == 0)
+      return ML_ERROR_INVALID_PARAMETER;
+  }
+
+  return ML_ERROR_NONE;
+}
+
+/**
+ * @brief Validates the given tensors info is valid.
+ */
+int
+ml_util_validate_tensors_info (const ml_tensors_info_s * info)
+{
+  guint i;
+
+  if (!info || info->num_tensors < 1)
+    return ML_ERROR_INVALID_PARAMETER;
+
+  for (i = 0; i < info->num_tensors; i++) {
+    /* Failed if returned value is not 0 (ML_ERROR_NONE) */
+    if (ml_util_validate_tensor_info (&info->info[i]) != ML_ERROR_NONE)
+      return ML_ERROR_INVALID_PARAMETER;
+  }
+
+  return ML_ERROR_NONE;
+}
+
+/**
+ * @brief Gets the byte size of the given tensor info.
+ */
+size_t
+ml_util_get_tensor_size (const ml_tensor_info_s * info)
+{
+  size_t tensor_size;
+  gint i;
+
+  if (!info)
+    return 0;
+
+  switch (info->type) {
+    case ML_TENSOR_TYPE_INT8:
+    case ML_TENSOR_TYPE_UINT8:
+      tensor_size = 1;
+      break;
+    case ML_TENSOR_TYPE_INT16:
+    case ML_TENSOR_TYPE_UINT16:
+      tensor_size = 2;
+      break;
+    case ML_TENSOR_TYPE_INT32:
+    case ML_TENSOR_TYPE_UINT32:
+    case ML_TENSOR_TYPE_FLOAT32:
+      tensor_size = 4;
+      break;
+    case ML_TENSOR_TYPE_FLOAT64:
+    case ML_TENSOR_TYPE_INT64:
+    case ML_TENSOR_TYPE_UINT64:
+      tensor_size = 8;
+      break;
+    default:
+      dloge ("In the given param, tensor type is invalid.");
+      return 0;
+  }
+
+  for (i = 0; i < ML_TENSOR_RANK_LIMIT; i++) {
+    tensor_size *= info->dimension[i];
+  }
+
+  return tensor_size;
+}
+
+/**
+ * @brief Gets the byte size of the given tensors info.
+ */
+size_t
+ml_util_get_tensors_size (const ml_tensors_info_s * info)
+{
+  size_t tensor_size;
+  gint i;
+
+  if (!info)
+    return 0;
+
+  tensor_size = 0;
+  for (i = 0; i < info->num_tensors; i++) {
+    tensor_size += ml_util_get_tensor_size (&info->info[i]);
+  }
+
+  return tensor_size;
+}
+
+/**
+ * @brief Frees the tensors info pointer.
+ */
+void
+ml_util_free_tensors_info (ml_tensors_info_s * info)
+{
+  gint i;
+
+  if (!info)
+    return;
+
+  for (i = 0; i < info->num_tensors; i++) {
+    if (info->info[i].name) {
+      g_free (info->info[i].name);
+      info->info[i].name = NULL;
+    }
+  }
+
+  ml_util_initialize_tensors_info (info);
+}
+
+/**
+ * @brief Frees the tensors data pointer.
+ */
+void
+ml_util_free_tensors_data (ml_tensors_data_s ** data)
+{
+  gint i;
+
+  if (data == NULL || (*data) == NULL)
+    return;
+
+  for (i = 0; i < (*data)->num_tensors; i++) {
+    if ((*data)->tensors[i].tensor) {
+      g_free ((*data)->tensors[i].tensor);
+      (*data)->tensors[i].tensor = NULL;
+    }
+  }
+
+  g_free (*data);
+  *data = NULL;
+}
+
+/**
+ * @brief Allocates a tensor data frame with the given tensors info. (more info in nnstreamer.h)
+ */
+ml_tensors_data_s *
+ml_util_allocate_tensors_data (const ml_tensors_info_s * info)
+{
+  ml_tensors_data_s *data;
+  gint i;
+
+  if (!info) {
+    ml_util_set_error (ML_ERROR_INVALID_PARAMETER);
+    return NULL;
+  }
+
+  data = g_new0 (ml_tensors_data_s, 1);
+  if (!data) {
+    dloge ("Failed to allocate the memory block.");
+    ml_util_set_error (ML_ERROR_STREAMS_PIPE);
+    return NULL;
+  }
+
+  data->num_tensors = info->num_tensors;
+  for (i = 0; i < data->num_tensors; i++) {
+    data->tensors[i].size = ml_util_get_tensor_size (&info->info[i]);
+    data->tensors[i].tensor = g_malloc0 (data->tensors[i].size);
+  }
+
+  ml_util_set_error (ML_ERROR_NONE);
+  return data;
+}
+
+/**
+ * @brief Copies tensor meta info.
+ */
+void
+ml_util_copy_tensors_info (ml_tensors_info_s * dest,
+    const ml_tensors_info_s * src)
+{
+  guint i, j;
+
+  if (!dest || !src)
+    return;
+
+  ml_util_initialize_tensors_info (dest);
+
+  dest->num_tensors = src->num_tensors;
+
+  for (i = 0; i < dest->num_tensors; i++) {
+    dest->info[i].name =
+        (src->info[i].name) ? g_strdup (src->info[i].name) : NULL;
+    dest->info[i].type = src->info[i].type;
+
+    for (j = 0; j < ML_TENSOR_RANK_LIMIT; j++)
+      dest->info[i].dimension[j] = src->info[i].dimension[j];
+  }
+}
+
+/**
+ * @brief Copies tensor meta info from gst tensots info.
+ */
+void
+ml_util_copy_tensors_info_from_gst (ml_tensors_info_s * ml_info,
+    const GstTensorsInfo * gst_info)
+{
+  guint i, j;
+  guint max_dim;
+
+  if (!ml_info || !gst_info)
+    return;
+
+  ml_util_initialize_tensors_info (ml_info);
+  max_dim = MIN (ML_TENSOR_RANK_LIMIT, NNS_TENSOR_RANK_LIMIT);
+
+  ml_info->num_tensors = gst_info->num_tensors;
+
+  for (i = 0; i < gst_info->num_tensors; i++) {
+    /* Copy name string */
+    if (gst_info->info[i].name) {
+      ml_info->info[i].name = g_strdup (gst_info->info[i].name);
+    }
+
+    /* Set tensor type */
+    switch (gst_info->info[i].type) {
+      case _NNS_INT32:
+        ml_info->info[i].type = ML_TENSOR_TYPE_INT32;
+        break;
+      case _NNS_UINT32:
+        ml_info->info[i].type = ML_TENSOR_TYPE_UINT32;
+        break;
+      case _NNS_INT16:
+        ml_info->info[i].type = ML_TENSOR_TYPE_INT16;
+        break;
+      case _NNS_UINT16:
+        ml_info->info[i].type = ML_TENSOR_TYPE_UINT16;
+        break;
+      case _NNS_INT8:
+        ml_info->info[i].type = ML_TENSOR_TYPE_INT8;
+        break;
+      case _NNS_UINT8:
+        ml_info->info[i].type = ML_TENSOR_TYPE_UINT8;
+        break;
+      case _NNS_FLOAT64:
+        ml_info->info[i].type = ML_TENSOR_TYPE_FLOAT64;
+        break;
+      case _NNS_FLOAT32:
+        ml_info->info[i].type = ML_TENSOR_TYPE_FLOAT32;
+        break;
+      case _NNS_INT64:
+        ml_info->info[i].type = ML_TENSOR_TYPE_INT64;
+        break;
+      case _NNS_UINT64:
+        ml_info->info[i].type = ML_TENSOR_TYPE_UINT64;
+        break;
+      default:
+        ml_info->info[i].type = ML_TENSOR_TYPE_UNKNOWN;
+        break;
+    }
+
+    /* Set dimension */
+    for (j = 0; j < max_dim; j++) {
+      ml_info->info[i].dimension[j] = gst_info->info[i].dimension[j];
+    }
+
+    for ( ; j < ML_TENSOR_RANK_LIMIT; j++) {
+      ml_info->info[i].dimension[j] = 1;
+    }
+  }
+}
+
+/**
+ * @brief Copies tensor meta info from gst tensots info.
+ */
+void
+ml_util_copy_tensors_info_from_ml (GstTensorsInfo * gst_info,
+  const ml_tensors_info_s * ml_info)
+{
+  guint i, j;
+  guint max_dim;
+
+  if (!gst_info || !ml_info)
+    return;
+
+  gst_tensors_info_init (gst_info);
+  max_dim = MIN (ML_TENSOR_RANK_LIMIT, NNS_TENSOR_RANK_LIMIT);
+
+  gst_info->num_tensors = ml_info->num_tensors;
+
+  for (i = 0; i < ml_info->num_tensors; i++) {
+    /* Copy name string */
+    if (ml_info->info[i].name) {
+      gst_info->info[i].name = g_strdup (ml_info->info[i].name);
+    }
+
+    /* Set tensor type */
+    switch (ml_info->info[i].type) {
+      case ML_TENSOR_TYPE_INT32:
+        gst_info->info[i].type = _NNS_INT32;
+        break;
+      case ML_TENSOR_TYPE_UINT32:
+        gst_info->info[i].type = _NNS_UINT32;
+        break;
+      case ML_TENSOR_TYPE_INT16:
+        gst_info->info[i].type = _NNS_INT16;
+        break;
+      case ML_TENSOR_TYPE_UINT16:
+        gst_info->info[i].type = _NNS_UINT16;
+        break;
+      case ML_TENSOR_TYPE_INT8:
+        gst_info->info[i].type = _NNS_INT8;
+        break;
+      case ML_TENSOR_TYPE_UINT8:
+        gst_info->info[i].type = _NNS_UINT8;
+        break;
+      case ML_TENSOR_TYPE_FLOAT64:
+        gst_info->info[i].type = _NNS_FLOAT64;
+        break;
+      case ML_TENSOR_TYPE_FLOAT32:
+        gst_info->info[i].type = _NNS_FLOAT32;
+        break;
+      case ML_TENSOR_TYPE_INT64:
+        gst_info->info[i].type = _NNS_INT64;
+        break;
+      case ML_TENSOR_TYPE_UINT64:
+        gst_info->info[i].type = _NNS_UINT64;
+        break;
+      default:
+        gst_info->info[i].type = _NNS_END;
+        break;
+    }
+
+    /* Set dimension */
+    for (j = 0; j < max_dim; j++) {
+      gst_info->info[i].dimension[j] = ml_info->info[i].dimension[j];
+    }
+
+    for ( ; j < NNS_TENSOR_RANK_LIMIT; j++) {
+      gst_info->info[i].dimension[j] = 1;
+    }
+  }
+}
+
+/**
+ * @brief Checks the availability of the given execution environments.
+ */
+int
+ml_util_check_nnfw (ml_nnfw_e nnfw, ml_nnfw_hw_e hw)
+{
+  /** @todo fill this function */
+  return ML_ERROR_NONE;
+}