[Filter] Support dynamic invoke
authorgichan2-jang <gichan2.jang@samsung.com>
Mon, 12 Jun 2023 05:50:08 +0000 (14:50 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Tue, 13 Jun 2023 01:38:18 +0000 (10:38 +0900)
 - Support dynamic invoke
 - dynamic invoke function is added for custom-easy filter.
 - New custom callback and register function is added.

Signed-off-by: gichan2-jang <gichan2.jang@samsung.com>
gst/nnstreamer/include/tensor_filter_custom_easy.h
gst/nnstreamer/tensor_filter/tensor_filter.c
gst/nnstreamer/tensor_filter/tensor_filter_common.c
gst/nnstreamer/tensor_filter/tensor_filter_custom_easy.c
tests/nnstreamer_filter_custom/unittest_filter_custom.cc

index 01c46a9..23951d9 100644 (file)
@@ -64,6 +64,31 @@ extern int NNS_custom_easy_register (const char * modelname,
     const GstTensorsInfo * in_info, const GstTensorsInfo * out_info);
 
 /**
+ * @brief Invoke the "main function" with flexible input and output. Output tensor memory should be allocated.
+ * @param[in/out] private_data A subplugin may save its internal private data here. The subplugin is responsible for alloc/free of this pointer.
+ * @param[in] info structure of input tensors info
+ * @param[out] info structure of output tensors info. The subplugin should fill this info.
+ * @param[in] input The array of input tensors. Allocated and filled by tensor_filter/main
+ * @param[out] output The array of output tensors. The subplugin should allocate the memory block for output tensor. (data in GstTensorMemory)
+ * @note rank limit (NNS_TENSOR_RANK_LIMIT) and typesize (tensor_element_size) defined in tensor_typedef.h
+ * @return 0 if success
+ */
+typedef int (*NNS_custom_invoke_dynamic) (void *private_data, const GstTensorsInfo * in_info, GstTensorsInfo * out_info,
+      const GstTensorMemory * input, GstTensorMemory * output);
+
+/**
+ * @brief Register the custom-easy tensor function for dynamic invoke.
+ * @param[in] modelname The name of custom-easy tensor function.
+ * @param[in] func The tensor function body
+ * @param[in/out] private_data The internal data for the function
+ * @param[in] in_info Input tensor metadata.
+ * @note NNS_custom_invoke_dynamic defined in tensor_filter_custom.h
+ *       Output buffers should be allocated in the invoke function.
+ */
+extern int NNS_custom_easy_dynamic_register (const char * modelname,
+    NNS_custom_invoke_dynamic func, void *data, const GstTensorsInfo * in_info);
+
+/**
  * @brief Unregister the custom-easy tensor function.
  * @param[in] modelname The registered name of custom-easy tensor function.
  * @return 0 if success, non-zero if error
index 12a898e..a32ee4f 100644 (file)
@@ -677,6 +677,12 @@ gst_tensor_filter_transform (GstBaseTransform * trans,
   out_flexible =
       gst_tensor_pad_caps_is_flexible (GST_BASE_TRANSFORM_SRC_PAD (trans));
 
+  if (priv->prop.invoke_dynamic && !out_flexible) {
+    ml_loge
+        ("Dynamic Invoke of tensor filter is activated but the output of tensor filter is static tensors. Currently, only flexible tensors is supported as output of dynamic invoke. If you don't want to dynamic invoke, remove the invoke-dynamic option of tensor filter.");
+    return GST_FLOW_ERROR;
+  }
+
   /* 1. Get all input tensors from inbuf. */
   /* Internal Logic Error or GST Bug (sinkcap changed!) */
   num_tensors = gst_buffer_n_tensor (inbuf);
@@ -694,6 +700,7 @@ gst_tensor_filter_transform (GstBaseTransform * trans,
     if (in_flexible) {
       gst_tensor_meta_info_parse_header (&in_meta[i], in_info[i].data);
       hsize = gst_tensor_meta_info_get_header_size (&in_meta[i]);
+      gst_tensor_meta_info_convert (&in_meta[i], &prop->input_meta.info[i]);
     }
 
     in_tensors[i].data = in_info[i].data + hsize;
@@ -751,7 +758,7 @@ gst_tensor_filter_transform (GstBaseTransform * trans,
     out_tensors[i].size = gst_tensor_filter_get_tensor_size (self, i, FALSE);
 
     hsize = 0;
-    if (out_flexible) {
+    if (out_flexible && !priv->prop.invoke_dynamic) {
       gst_tensor_info_convert_to_meta (&prop->output_meta.info[i],
           &out_meta[i]);
       hsize = gst_tensor_meta_info_get_header_size (&out_meta[i]);
@@ -871,7 +878,22 @@ gst_tensor_filter_transform (GstBaseTransform * trans,
       }
     }
 
-    if (allocate_in_invoke) {
+    if (priv->prop.invoke_dynamic) {
+      GstTensorMetaInfo meta;
+      GstMemory *flex_mem;
+
+      /* Convert to flexible tensors */
+      gst_tensor_info_convert_to_meta (&prop->output_meta.info[i], &meta);
+      meta.media_type = _NNS_TENSOR;
+      meta.format = _NNS_TENSOR_FORMAT_FLEXIBLE;
+
+      flex_mem = gst_memory_new_wrapped (0,
+          out_tensors[i].data, out_tensors[i].size, 0,
+          out_tensors[i].size, out_tensors[i].data, g_free);
+
+      out_mem[i] = gst_tensor_meta_info_append_header (&meta, flex_mem);
+      gst_memory_unref (flex_mem);
+    } else if (allocate_in_invoke) {
       /* prepare memory block if successfully done */
       out_mem[i] = mem = gst_tensor_filter_get_wrapped_mem (self,
           out_tensors[i].data, out_tensors[i].size);
@@ -927,7 +949,6 @@ gst_tensor_filter_configure_tensor (GstTensorFilter * self,
   gboolean flexible;
 
   g_return_val_if_fail (incaps != NULL, FALSE);
-
   priv = &self->priv;
   prop = &priv->prop;
   gst_tensors_config_init (&in_config);
@@ -1084,8 +1105,10 @@ gst_tensor_filter_configure_tensor (GstTensorFilter * self,
 
   if (priv->configured) {
     /** already configured, compare to old. */
-    g_assert (gst_tensors_config_is_equal (&priv->in_config, &in_config));
-    g_assert (gst_tensors_config_is_equal (&priv->out_config, &out_config));
+    if (!priv->prop.invoke_dynamic) {
+      g_assert (gst_tensors_config_is_equal (&priv->in_config, &in_config));
+      g_assert (gst_tensors_config_is_equal (&priv->out_config, &out_config));
+    }
   } else {
     gst_tensors_config_copy (&priv->in_config, &in_config);
     gst_tensors_config_copy (&priv->out_config, &out_config);
@@ -1098,6 +1121,7 @@ done:
   gst_tensors_config_free (&out_config);
   gst_tensors_info_free (&in_info);
   gst_tensors_info_free (&out_info);
+
   return priv->configured;
 }
 
@@ -1295,7 +1319,8 @@ gst_tensor_filter_set_caps (GstBaseTransform * trans,
     return FALSE;
   }
 
-  if (!gst_tensors_config_validate (&priv->out_config)) {
+  if (!priv->prop.invoke_dynamic &&
+      !gst_tensors_config_validate (&priv->out_config)) {
     GST_ELEMENT_ERROR_BTRACE (self, STREAM, WRONG_TYPE,
         ("Failed to validate output tensor configuration. Please refer to the error log of gst_tensors_config_validate(): %s",
             GST_STR_NULL (_nnstreamer_error ())));
@@ -1516,6 +1541,7 @@ gst_tensor_filter_start (GstBaseTransform * trans)
   if (priv->fw == NULL)
     return FALSE;
   gst_tensor_filter_common_open_fw (priv);
+
   return priv->prop.fw_opened;
 }
 
index 8061e5f..e4003e0 100644 (file)
@@ -129,6 +129,7 @@ enum
   PROP_OUTPUTCOMBINATION,
   PROP_SHARED_TENSOR_FILTER_KEY,
   PROP_LATENCY_REPORT,
+  PROP_INVOKE_DYNAMIC,
 };
 
 /**
@@ -781,6 +782,9 @@ gst_tensor_filter_allocate_in_invoke (GstTensorFilterPrivate * priv)
 {
   int allocate_in_invoke = 0;
 
+  if (priv->prop.invoke_dynamic)
+    return TRUE;
+
   if (GST_TF_FW_V0 (priv->fw)) {
     allocate_in_invoke = priv->fw->allocate_in_invoke;
     if (allocate_in_invoke == TRUE && priv->fw->allocateInInvoke) {
@@ -1013,6 +1017,12 @@ gst_tensor_filter_install_properties (GObjectClass * gobject_class)
       g_param_spec_boolean ("latency-report", "Latency report",
           "Report to the pipeline the estimated tensor-filter element latency.",
           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (gobject_class, PROP_INVOKE_DYNAMIC,
+      g_param_spec_boolean ("invoke-dynamic", "Enable dynamic invoke",
+          "Flexible tensors whose memory size changes can be used as"
+          "input and output of the tensor filter. "
+          "With this option, the output caps is always in the format of flexible tensors.",
+          FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 }
 
 /**
@@ -1034,6 +1044,7 @@ gst_tensor_filter_common_init_property (GstTensorFilterPrivate * priv)
 
   /* init internal properties */
   priv->silent = TRUE;
+  priv->prop.invoke_dynamic = FALSE;
   gst_tensors_config_init (&priv->in_config);
   gst_tensors_config_init (&priv->out_config);
 }
@@ -1906,6 +1917,19 @@ _gtfc_setprop_SHARED_TENSOR_FILTER_KEY (GstTensorFilterProperties * prop,
 }
 
 /**
+ * @brief Handle "PROP_INVOKE_DYNAMIC" for set-property
+ */
+static gint
+_gtfc_setprop_PROP_INVOKE_DYNAMIC (GstTensorFilterPrivate * priv,
+    const GValue * value)
+{
+  priv->prop.invoke_dynamic = g_value_get_boolean (value);
+  priv->info.allocate_in_invoke = TRUE;
+
+  return 0;
+}
+
+/**
  * @brief Set the properties for tensor_filter
  * @param[in] priv Struct containing the properties of the object
  * @param[in] prop_id Id for the property
@@ -1988,6 +2012,9 @@ gst_tensor_filter_common_set_property (GstTensorFilterPrivate * priv,
     case PROP_LATENCY_REPORT:
       priv->latency_reporting = g_value_get_boolean (value);
       break;
+    case PROP_INVOKE_DYNAMIC:
+      status = _gtfc_setprop_PROP_INVOKE_DYNAMIC (priv, value);
+      break;
     default:
       return FALSE;
   }
@@ -2193,6 +2220,9 @@ gst_tensor_filter_common_get_property (GstTensorFilterPrivate * priv,
     case PROP_LATENCY_REPORT:
       g_value_set_boolean (value, priv->latency_reporting);
       break;
+    case PROP_INVOKE_DYNAMIC:
+      g_value_set_boolean (value, prop->invoke_dynamic);
+      break;
     default:
       /* unknown property */
       return FALSE;
@@ -2388,8 +2418,11 @@ gst_tensor_filter_load_tensor_info (GstTensorFilterPrivate * priv)
     silent_debug_info (&in_info, "input tensor");
   }
 
-  /* supposed fixed out-tensor info if getOutputDimension was success. */
-  if (!prop->output_configured && res_out == 0) {
+  /** In case of dynamic invoke, output tensors info is determined after invoke. */
+  if (prop->invoke_dynamic) {
+    prop->output_configured = TRUE;
+  } else if (!prop->output_configured && res_out == 0) {
+    /* supposed fixed out-tensor info if getOutputDimension was success. */
     g_assert (out_info.num_tensors > 0);
 
     /** if set-property called and already has info, verify it! */
index acf60c7..9300e04 100644 (file)
@@ -44,6 +44,7 @@ typedef struct _internal_data
   GstTensorsInfo in_info;
   GstTensorsInfo out_info;
   void *data; /**< The easy-filter writer's data */
+  NNS_custom_invoke_dynamic func_dynamic;
 } internal_data;
 
 /**
@@ -102,6 +103,39 @@ NNS_custom_easy_register (const char *modelname,
   return -EINVAL;
 }
 
+
+/**
+ * @brief Register the custom-easy tensor function. More info in .h
+ * @return 0 if success. -ERRNO if error.
+ */
+int
+NNS_custom_easy_dynamic_register (const char *modelname,
+    NNS_custom_invoke_dynamic func, void *data, const GstTensorsInfo * in_info)
+{
+  internal_data *ptr;
+
+  if (!func || !in_info)
+    return -EINVAL;
+
+  if (!gst_tensors_info_validate (in_info))
+    return -EINVAL;
+
+  ptr = g_new0 (internal_data, 1);
+
+  if (!ptr)
+    return -ENOMEM;
+
+  ptr->func_dynamic = func;
+  ptr->data = data;
+  gst_tensors_info_copy (&ptr->in_info, in_info);
+
+  if (register_subplugin (NNS_EASY_CUSTOM_FILTER, modelname, ptr))
+    return 0;
+
+  custom_free_internal_data (ptr);
+  return -EINVAL;
+}
+
 /**
  * @brief Unregister the custom-easy tensor function.
  * @return 0 if success. -EINVAL if invalid model name.
@@ -144,19 +178,38 @@ custom_open (const GstTensorFilterProperties * prop, void **private_data)
     goto errorreturn;
   }
 
-  if (NULL == rd->model->func) {
+  if (NULL == rd->model->func && NULL == rd->model->func_dynamic) {
     ml_logf
         ("A custom-easy filter, \"%s\", should provide invoke function body, 'func'. A null-ptr is supplied instead.\n",
         prop->model_files[0]);
     goto errorreturn;
   }
+
+  if (!prop->invoke_dynamic && rd->model->func_dynamic) {
+    ml_loge
+        ("Not matched easy-custom model, \"%s\". "
+        "Dynamic invoke option is disabled but you registered dynamic invoke function. "
+        "You should register model using NNS_custom_easy_register.",
+        prop->model_files[0]);
+    goto errorreturn;
+  }
+
+  if (prop->invoke_dynamic && rd->model->func) {
+    ml_loge
+        ("Not matched easy-custom model, \"%s\". "
+        "If want to use dynamic invoke, register model using NNS_custom_easy_dynamic_register.",
+        prop->model_files[0]);
+    goto errorreturn;
+  }
+
   if (!gst_tensors_info_validate (&rd->model->in_info)) {
     ml_logf
         ("A custom-easy filter, \"%s\", should provide input stream metadata, 'in_info'.\n",
         prop->model_files[0]);
     goto errorreturn;
   }
-  if (!gst_tensors_info_validate (&rd->model->out_info)) {
+
+  if (rd->model->func && !gst_tensors_info_validate (&rd->model->out_info)) {
     ml_logf
         ("A custom-easy filter, \"%s\", should provide output stream metadata, 'out_info'.\n",
         prop->model_files[0]);
@@ -191,13 +244,31 @@ custom_invoke (const GstTensorFilterFramework * self,
     GstTensorFilterProperties * prop, void *private_data,
     const GstTensorMemory * input, GstTensorMemory * output)
 {
+  int ret = 0;
   runtime_data *rd = (runtime_data *) private_data;
   UNUSED (self);
 
   /* Internal Logic Error */
-  g_assert (rd && rd->model && rd->model->func);
+  g_assert (rd && rd->model);
+
+  if (!prop->invoke_dynamic) {
+    if (!rd->model->func) {
+      ml_loge
+          ("Custom filter function is not registered. Register the function using `NNS_custom_easy_register`.");
+      return -1;
+    }
+    return rd->model->func (rd->model->data, prop, input, output);
+  } else {
+    if (!rd->model->func_dynamic) {
+      ml_loge
+          ("Dynamic invoke is enabled but dynamic custom filter function is not registered. Register the function using `NNS_custom_easy_dynamic_register`.");
+      return -1;
+    }
+    return rd->model->func_dynamic (rd->model->data, &prop->input_meta,
+        &prop->output_meta, input, output);
+  }
 
-  return rd->model->func (rd->model->data, prop, input, output);
+  return ret;
 }
 
 /**
index ec7f009..14e5ad2 100644 (file)
 #include <gtest/gtest.h>
 #include <glib/gstdio.h>
 #include <gst/gst.h>
-#include <stdlib.h>
-#include <unittest_util.h>
-#include <tensor_filter_custom_easy.h>
-#include <nnstreamer_util.h>
 #include <nnstreamer_plugin_api.h>
 #include <nnstreamer_plugin_api_util.h>
+#include <nnstreamer_util.h>
+#include <stdlib.h>
+#include <tensor_filter_custom_easy.h>
+#include <unittest_util.h>
 
 static guint filter_received;
 static guint sink_received;
 
-#define NNS_custom_easy_dynamic_register(...) 0
 /**
  * @brief In-Code Test Function for custom-easy filter
  */
-// static int
-// _custom_easy_filter_dynamic (void *data, const GstTensorsInfo * in_info,
-//     GstTensorsInfo * out_info, const GstTensorMemory * input,
-//     GstTensorMemory * output)
-// {
-//   gchar *dim_str;
-//   guint i;
-
-//  /* Fill output tensors info */
-//   gst_tensors_info_init (out_info);
-//   out_info->info[0].type = _NNS_UINT32;
-//   dim_str = g_strdup_printf ("%u:1:1:1", ++filter_received);
-//   gst_tensor_parse_dimension (dim_str, out_info->info[0].dimension);
-//   out_info->num_tensors = 1;
-//   out_info->format = _NNS_TENSOR_FORMAT_FLEXIBLE;
-
-//   /* Allocate and fill output memory */
-//   output[0].size = sizeof (guint) * filter_received;
-//   output[0].data = g_malloc0 (output[0].size);
-
-//   for (i = 0; i < filter_received; i++) {
-//     ((guint *) output[0].data)[i] = i;
-//   }
-
-//   return 0;
-// }
+static int
+_custom_easy_filter_dynamic (void *data, const GstTensorsInfo *in_info,
+    GstTensorsInfo *out_info, const GstTensorMemory *input, GstTensorMemory *output)
+{
+  gchar *dim_str;
+  guint i;
+
+  /* Fill output tensors info */
+  gst_tensors_info_init (out_info);
+  out_info->info[0].type = _NNS_UINT32;
+  dim_str = g_strdup_printf ("%u:1:1:1", ++filter_received);
+  gst_tensor_parse_dimension (dim_str, out_info->info[0].dimension);
+  out_info->num_tensors = 1;
+  out_info->format = _NNS_TENSOR_FORMAT_FLEXIBLE;
+
+  /* Allocate and fill output memory */
+  output[0].size = sizeof (guint) * filter_received;
+  output[0].data = g_malloc0 (output[0].size);
+
+  for (i = 0; i < filter_received; i++) {
+    ((guint *) output[0].data)[i] = i;
+  }
+
+  return 0;
+}
 
 /**
  * @brief Callback for tensor sink signal.
@@ -94,7 +92,7 @@ new_data_cb (GstElement *element, GstBuffer *buffer, gpointer user_data)
  * @brief Test custom-easy filter with flexible tensor input/output.
  * @todo Enable the test after development is done.
  */
-TEST (tensorFilterCustom, DISABLED_flexibleInvoke_p)
+TEST (tensorFilterCustom, flexibleInvoke_p)
 {
   gchar *pipeline;
   GstElement *gstpipe;
@@ -114,10 +112,10 @@ TEST (tensorFilterCustom, DISABLED_flexibleInvoke_p)
 
   /* create a nnstreamer pipeline */
   pipeline = g_strdup_printf (
-    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=224,height=224,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_0 "
-    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=320,height=240,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_1 "
-    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=640,height=480,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_2 "
-    "join name=j ! other/tensors,format=flexible ! tensor_filter framework=custom-easy invoke-dynamic=TRUE model=flexbible_filter ! other/tensors,format=flexible ! tensor_sink name=sinkx sync=true");
+      "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=224,height=224,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_0 "
+      "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=320,height=240,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_1 "
+      "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=640,height=480,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_2 "
+      "join name=j ! other/tensors,format=flexible ! tensor_filter framework=custom-easy invoke-dynamic=TRUE model=flexbible_filter ! other/tensors,format=flexible ! tensor_sink name=sinkx sync=true");
 
   gstpipe = gst_parse_launch (pipeline, &err);
   ASSERT_TRUE (gstpipe != nullptr);
@@ -148,7 +146,7 @@ TEST (tensorFilterCustom, DISABLED_flexibleInvoke_p)
  * @brief Test custom-easy filter with static input, flexible output.
  * @todo Enable the test after development is done.
  */
-TEST (tensorFilterCustom, DISABLED_staticFlexibleInvoke_p)
+TEST (tensorFilterCustom, staticFlexibleInvoke_p)
 {
   gchar *pipeline;
   GstElement *gstpipe;
@@ -168,10 +166,10 @@ TEST (tensorFilterCustom, DISABLED_staticFlexibleInvoke_p)
 
   /* create a nnstreamer pipeline */
   pipeline = g_strdup_printf (
-    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=224,height=224,framerate=10/1 ! tensor_converter ! j.sink_0 "
-    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=320,height=240,framerate=10/1 ! tensor_converter ! j.sink_1 "
-    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=640,height=480,framerate=10/1 ! tensor_converter ! j.sink_2 "
-    "join name=j ! other/tensors,format=flexible ! tensor_filter framework=custom-easy invoke-dynamic=TRUE model=flexbible_filter ! other/tensors,format=flexible ! tensor_sink name=sinkx sync=true");
+      "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=224,height=224,framerate=10/1 ! tensor_converter ! j.sink_0 "
+      "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=320,height=240,framerate=10/1 ! tensor_converter ! j.sink_1 "
+      "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=640,height=480,framerate=10/1 ! tensor_converter ! j.sink_2 "
+      "join name=j ! other/tensors,format=flexible ! tensor_filter framework=custom-easy invoke-dynamic=TRUE model=flexbible_filter ! other/tensors,format=flexible ! tensor_sink name=sinkx sync=true");
 
   gstpipe = gst_parse_launch (pipeline, &err);
   ASSERT_TRUE (gstpipe != nullptr);
@@ -202,7 +200,7 @@ TEST (tensorFilterCustom, DISABLED_staticFlexibleInvoke_p)
  * @brief Test dynamic invoke with invalid prop..
  * @todo Enable the test after development is done.
  */
-TEST (tensorFilterCustom, DISABLED_flexibleInvokeInvalidProp_n)
+TEST (tensorFilterCustom, flexibleInvokeInvalidProp_n)
 {
   gchar *pipeline;
   GstElement *gstpipe;
@@ -222,10 +220,10 @@ TEST (tensorFilterCustom, DISABLED_flexibleInvokeInvalidProp_n)
 
   /* create a nnstreamer pipeline */
   pipeline = g_strdup_printf (
-    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=224,height=224,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_0 "
-    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=320,height=240,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_1 "
-    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=640,height=480,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_2 "
-    "join name=j ! other/tensors,format=flexible ! tensor_filter framework=custom-easy invoke-dynamic=FALSE model=flexbible_filter ! other/tensors,format=flexible ! tensor_sink name=sinkx sync=true");
+      "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=224,height=224,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_0 "
+      "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=320,height=240,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_1 "
+      "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=640,height=480,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_2 "
+      "join name=j ! other/tensors,format=flexible ! tensor_filter framework=custom-easy invoke-dynamic=FALSE model=flexbible_filter ! other/tensors,format=flexible ! tensor_sink name=sinkx sync=true");
 
   gstpipe = gst_parse_launch (pipeline, &err);
   ASSERT_TRUE (gstpipe != nullptr);
@@ -272,7 +270,7 @@ _custom_easy_filter (void *data, const GstTensorFilterProperties *prop,
  * @brief Test custom-easy statc invoke with flexible tensor input/output.
  * @todo Enable the test after development is done.
  */
-TEST (tensorFilterCustom, DISABLED_staticInvoke_n)
+TEST (tensorFilterCustom, staticInvoke_n)
 {
   gchar *pipeline;
   GstElement *gstpipe;
@@ -298,10 +296,10 @@ TEST (tensorFilterCustom, DISABLED_staticInvoke_n)
 
   /* create a nnstreamer pipeline */
   pipeline = g_strdup_printf (
-    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=224,height=224,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_0 "
-    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=320,height=240,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_1 "
-    "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=640,height=480,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_2 "
-    "join name=j ! other/tensors,format=flexible ! tensor_filter framework=custom-easy model=normal_filter ! other/tensors,format=flexible ! tensor_sink name=sinkx sync=true");
+      "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=224,height=224,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_0 "
+      "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=320,height=240,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_1 "
+      "videotestsrc num-buffers=3 ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=640,height=480,framerate=10/1 ! tensor_converter ! other/tensors,format=flexible ! j.sink_2 "
+      "join name=j ! other/tensors,format=flexible ! tensor_filter framework=custom-easy model=normal_filter ! other/tensors,format=flexible ! tensor_sink name=sinkx sync=true");
 
   gstpipe = gst_parse_launch (pipeline, &err);
   ASSERT_TRUE (gstpipe != nullptr);
@@ -327,7 +325,7 @@ TEST (tensorFilterCustom, DISABLED_staticInvoke_n)
 /**
  * @brief Test custom-easy filter with flexible tensor input/output without register custom easy model.
  */
-TEST (tensorFilterCustom, DISABLED_notRegisterFlexibleInvoke_n)
+TEST (tensorFilterCustom, notRegisterFlexibleInvoke_n)
 {
   gchar *pipeline;
   GstElement *gstpipe;
@@ -342,8 +340,9 @@ TEST (tensorFilterCustom, DISABLED_notRegisterFlexibleInvoke_n)
 
   /* create a nnstreamer pipeline */
   pipeline = g_strdup_printf (
-    "videotestsrc num-buffers=3 ! videoconvert ! video/x-raw,width=160,height=120,format=RGB,framerate=10/1 ! "
-        "tensor_converter ! tensor_filter name=test_filter framework=custom invoke-dynamic=TRUE model=%s ! tensor_sink sync=true", model_file);
+      "videotestsrc num-buffers=3 ! videoconvert ! video/x-raw,width=160,height=120,format=RGB,framerate=10/1 ! "
+      "tensor_converter ! tensor_filter name=test_filter framework=custom invoke-dynamic=TRUE model=%s ! tensor_sink sync=true",
+      model_file);
 
   gstpipe = gst_parse_launch (pipeline, &err);
   ASSERT_TRUE (gstpipe != nullptr);
@@ -360,7 +359,7 @@ TEST (tensorFilterCustom, DISABLED_notRegisterFlexibleInvoke_n)
  * @brief Test dynamic invoke with invalid param.
  * @todo Enable the test after development is done.
  */
-TEST (tensorFilterCustom, DISABLED_dynamicRegisterInvalidParam_n)
+TEST (tensorFilterCustom, dynamicRegisterInvalidParam_n)
 {
   GstTensorsInfo info_in;
   int ret;
@@ -370,12 +369,10 @@ TEST (tensorFilterCustom, DISABLED_dynamicRegisterInvalidParam_n)
   info_in.info[0].name = NULL;
   info_in.format = _NNS_TENSOR_FORMAT_FLEXIBLE;
 
-  ret = NNS_custom_easy_dynamic_register (
-      NULL, _custom_easy_filter_dynamic, NULL, &info_in);
+  ret = NNS_custom_easy_dynamic_register (NULL, _custom_easy_filter_dynamic, NULL, &info_in);
   EXPECT_NE (0, ret);
 
-  ret = NNS_custom_easy_dynamic_register (
-      "temp_name", NULL, NULL, &info_in);
+  ret = NNS_custom_easy_dynamic_register ("temp_name", NULL, NULL, &info_in);
   EXPECT_NE (0, ret);
 
   ret = NNS_custom_easy_dynamic_register (