[Android] custom-easy filter
authorJaeyun <jy1210.jung@samsung.com>
Wed, 17 Jun 2020 03:28:15 +0000 (12:28 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Tue, 7 Jul 2020 11:24:02 +0000 (20:24 +0900)
Update custom-filter API using custom-easy.
- register custom-filter with in/out info and implement invoke interface only.

Signed-off-by: Jaeyun <jy1210.jung@samsung.com>
api/android/api/src/androidTest/java/org/nnsuite/nnstreamer/APITestCustomFilter.java
api/android/api/src/main/java/org/nnsuite/nnstreamer/CustomFilter.java
api/android/api/src/main/jni/nnstreamer-native-api.c
api/android/api/src/main/jni/nnstreamer-native-customfilter.c
api/android/api/src/main/jni/nnstreamer-native.h

index d21055b..5424278 100644 (file)
@@ -54,77 +54,68 @@ public class APITestCustomFilter {
 
     private void registerCustomFilters() {
         try {
+            TensorsInfo inputInfo = new TensorsInfo();
+            inputInfo.addTensorInfo(NNStreamer.TensorType.INT32, new int[]{10});
+
+            TensorsInfo outputInfo = inputInfo.clone();
+
             /* register custom-filter (passthrough) */
             mCustomPassthrough = CustomFilter.registerCustomFilter("custom-passthrough",
-                new CustomFilter.CustomFilterCallback() {
-                    @Override
-                    public TensorsInfo getOutputInfo(TensorsInfo in) {
-                        return in;
-                    }
-
-                    @Override
-                    public TensorsData invoke(TensorsData in) {
-                        return in;
-                    }
-                });
+                    inputInfo, outputInfo, new CustomFilter.CustomFilterCallback() {
+                @Override
+                public TensorsData invoke(TensorsData in) {
+                    return in;
+                }
+            });
 
             /* register custom-filter (convert data type to float) */
+            outputInfo.setTensorType(0, NNStreamer.TensorType.FLOAT32);
             mCustomConvert = CustomFilter.registerCustomFilter("custom-convert",
-                new CustomFilter.CustomFilterCallback() {
-                    @Override
-                    public TensorsInfo getOutputInfo(TensorsInfo in) {
-                        in.setTensorType(0, NNStreamer.TensorType.FLOAT32);
-                        return in;
-                    }
-
-                    @Override
-                    public TensorsData invoke(TensorsData in) {
-                        TensorsInfo info = in.getTensorsInfo();
-                        ByteBuffer input = in.getTensorData(0);
+                    inputInfo, outputInfo, new CustomFilter.CustomFilterCallback() {
+                @Override
+                public TensorsData invoke(TensorsData in) {
+                    TensorsInfo info = in.getTensorsInfo();
+                    ByteBuffer input = in.getTensorData(0);
 
-                        info.setTensorType(0, NNStreamer.TensorType.FLOAT32);
+                    info.setTensorType(0, NNStreamer.TensorType.FLOAT32);
 
-                        TensorsData out = info.allocate();
-                        ByteBuffer output = out.getTensorData(0);
+                    TensorsData out = info.allocate();
+                    ByteBuffer output = out.getTensorData(0);
 
-                        for (int i = 0; i < 10; i++) {
-                            float value = (float) input.getInt(i * 4);
-                            output.putFloat(i * 4, value);
-                        }
-
-                        out.setTensorData(0, output);
-                        return out;
+                    for (int i = 0; i < 10; i++) {
+                        float value = (float) input.getInt(i * 4);
+                        output.putFloat(i * 4, value);
                     }
-                });
+
+                    out.setTensorData(0, output);
+                    return out;
+                }
+            });
 
             /* register custom-filter (add constant) */
+            inputInfo.setTensorType(0, NNStreamer.TensorType.FLOAT32);
             mCustomAdd = CustomFilter.registerCustomFilter("custom-add",
-                new CustomFilter.CustomFilterCallback() {
-                    @Override
-                    public TensorsInfo getOutputInfo(TensorsInfo in) {
-                        return in;
-                    }
+                    inputInfo, outputInfo, new CustomFilter.CustomFilterCallback() {
+                @Override
+                public TensorsData invoke(TensorsData in) {
+                    TensorsInfo info = in.getTensorsInfo();
+                    ByteBuffer input = in.getTensorData(0);
 
-                    @Override
-                    public TensorsData invoke(TensorsData in) {
-                        TensorsInfo info = in.getTensorsInfo();
-                        ByteBuffer input = in.getTensorData(0);
+                    TensorsData out = info.allocate();
+                    ByteBuffer output = out.getTensorData(0);
 
-                        TensorsData out = info.allocate();
-                        ByteBuffer output = out.getTensorData(0);
+                    for (int i = 0; i < 10; i++) {
+                        float value = input.getFloat(i * 4);
 
-                        for (int i = 0; i < 10; i++) {
-                            float value = input.getFloat(i * 4);
-
-                            /* add constant */
-                            value += 1.5f;
-                            output.putFloat(i * 4, value);
-                        }
-
-                        out.setTensorData(0, output);
-                        return out;
+                        /* add constant */
+                        value += 1.5f;
+                        output.putFloat(i * 4, value);
                     }
-                });
+
+                    out.setTensorData(0, output);
+                    return out;
+                }
+            });
 
             mRegistered = true;
         } catch (Exception e) {
@@ -169,9 +160,9 @@ public class APITestCustomFilter {
     public void testCustomFilters() {
         String desc = "appsrc name=srcx ! " +
                 "other/tensor,dimension=(string)10:1:1:1,type=(string)int32,framerate=(fraction)0/1 ! " +
-                "tensor_filter framework=" + mCustomPassthrough.getName() + " ! " +
-                "tensor_filter framework=" + mCustomConvert.getName() + " ! " +
-                "tensor_filter framework=" + mCustomAdd.getName() + " ! " +
+                "tensor_filter framework=custom-easy model=" + mCustomPassthrough.getName() + " ! " +
+                "tensor_filter framework=custom-easy model=" + mCustomConvert.getName() + " ! " +
+                "tensor_filter framework=custom-easy model=" + mCustomAdd.getName() + " ! " +
                 "tensor_sink name=sinkx";
 
         try (Pipeline pipe = new Pipeline(desc)) {
@@ -215,16 +206,16 @@ public class APITestCustomFilter {
 
     @Test
     public void testDropBuffer() {
+        TensorsInfo inputInfo = new TensorsInfo();
+        inputInfo.addTensorInfo(NNStreamer.TensorType.INT32, new int[]{10,1,1,1});
+
+        TensorsInfo outputInfo = inputInfo.clone();
+
         CustomFilter customDrop = CustomFilter.registerCustomFilter("custom-drop",
-                new CustomFilter.CustomFilterCallback() {
+                inputInfo, outputInfo, new CustomFilter.CustomFilterCallback() {
             int received = 0;
 
             @Override
-            public TensorsInfo getOutputInfo(TensorsInfo in) {
-                return in;
-            }
-
-            @Override
             public TensorsData invoke(TensorsData in) {
                 received++;
 
@@ -239,10 +230,10 @@ public class APITestCustomFilter {
 
         String desc = "appsrc name=srcx ! " +
                 "other/tensor,dimension=(string)10:1:1:1,type=(string)int32,framerate=(fraction)0/1 ! " +
-                "tensor_filter framework=" + customDrop.getName() + " ! " +
-                "tensor_filter framework=" + mCustomPassthrough.getName() + " ! " +
-                "tensor_filter framework=" + mCustomConvert.getName() + " ! " +
-                "tensor_filter framework=" + mCustomAdd.getName() + " ! " +
+                "tensor_filter framework=custom-easy model=" + customDrop.getName() + " ! " +
+                "tensor_filter framework=custom-easy model=" + mCustomPassthrough.getName() + " ! " +
+                "tensor_filter framework=custom-easy model=" + mCustomConvert.getName() + " ! " +
+                "tensor_filter framework=custom-easy model=" + mCustomAdd.getName() + " ! " +
                 "tensor_sink name=sinkx";
 
         try (Pipeline pipe = new Pipeline(desc)) {
@@ -286,18 +277,58 @@ public class APITestCustomFilter {
 
     @Test
     public void testRegisterNullName_n() {
+        TensorsInfo inputInfo = new TensorsInfo();
+        inputInfo.addTensorInfo(NNStreamer.TensorType.INT32, new int[]{10});
+
+        TensorsInfo outputInfo = inputInfo.clone();
+
         try {
-            CustomFilter.registerCustomFilter(null,
-                new CustomFilter.CustomFilterCallback() {
-                    @Override
-                    public TensorsInfo getOutputInfo(TensorsInfo in) {
-                        return in;
-                    }
+            CustomFilter.registerCustomFilter(null, inputInfo, outputInfo,
+                    new CustomFilter.CustomFilterCallback() {
+                @Override
+                public TensorsData invoke(TensorsData in) {
+                    return in;
+                }
+            });
 
-                    @Override
-                    public TensorsData invoke(TensorsData in) {
-                        return in;
-                    }
+            fail();
+        } catch (Exception e) {
+            /* expected */
+        }
+    }
+
+    @Test
+    public void testRegisterNullInputInfo_n() {
+        TensorsInfo outputInfo = new TensorsInfo();
+        outputInfo.addTensorInfo(NNStreamer.TensorType.INT32, new int[]{10});
+
+        try {
+            CustomFilter.registerCustomFilter("custom-invalid-info", null, outputInfo,
+                    new CustomFilter.CustomFilterCallback() {
+                @Override
+                public TensorsData invoke(TensorsData in) {
+                    return in;
+                }
+            });
+
+            fail();
+        } catch (Exception e) {
+            /* expected */
+        }
+    }
+
+    @Test
+    public void testRegisterNullOutputInfo_n() {
+        TensorsInfo inputInfo = new TensorsInfo();
+        inputInfo.addTensorInfo(NNStreamer.TensorType.INT32, new int[]{10});
+
+        try {
+            CustomFilter.registerCustomFilter("custom-invalid-info", inputInfo, null,
+                    new CustomFilter.CustomFilterCallback() {
+                @Override
+                public TensorsData invoke(TensorsData in) {
+                    return in;
+                }
             });
 
             fail();
@@ -308,8 +339,14 @@ public class APITestCustomFilter {
 
     @Test
     public void testRegisterNullCallback_n() {
+        TensorsInfo inputInfo = new TensorsInfo();
+        inputInfo.addTensorInfo(NNStreamer.TensorType.INT32, new int[]{10});
+
+        TensorsInfo outputInfo = inputInfo.clone();
+
         try {
-            CustomFilter.registerCustomFilter("custom-invalid-cb", null);
+            CustomFilter.registerCustomFilter("custom-invalid-cb", inputInfo, outputInfo, null);
+
             fail();
         } catch (Exception e) {
             /* expected */
@@ -318,19 +355,19 @@ public class APITestCustomFilter {
 
     @Test
     public void testRegisterDuplicatedName_n() {
-        try {
-            CustomFilter.registerCustomFilter(mCustomPassthrough.getName(),
-                new CustomFilter.CustomFilterCallback() {
-                    @Override
-                    public TensorsInfo getOutputInfo(TensorsInfo in) {
-                        return in;
-                    }
+        TensorsInfo inputInfo = new TensorsInfo();
+        inputInfo.addTensorInfo(NNStreamer.TensorType.INT32, new int[]{10});
 
-                    @Override
-                    public TensorsData invoke(TensorsData in) {
-                        return in;
-                    }
-                });
+        TensorsInfo outputInfo = inputInfo.clone();
+
+        try {
+            CustomFilter.registerCustomFilter(mCustomPassthrough.getName(), inputInfo, outputInfo,
+                    new CustomFilter.CustomFilterCallback() {
+                @Override
+                public TensorsData invoke(TensorsData in) {
+                    return in;
+                }
+            });
 
             fail();
         } catch (Exception e) {
@@ -340,19 +377,19 @@ public class APITestCustomFilter {
 
     @Test
     public void testRegisterPreservedName_n() {
-        try {
-            CustomFilter.registerCustomFilter("auto",
-                new CustomFilter.CustomFilterCallback() {
-                    @Override
-                    public TensorsInfo getOutputInfo(TensorsInfo in) {
-                        return in;
-                    }
+        TensorsInfo inputInfo = new TensorsInfo();
+        inputInfo.addTensorInfo(NNStreamer.TensorType.INT32, new int[]{10});
 
-                    @Override
-                    public TensorsData invoke(TensorsData in) {
-                        return in;
-                    }
-                });
+        TensorsInfo outputInfo = inputInfo.clone();
+
+        try {
+            CustomFilter.registerCustomFilter("auto", inputInfo, outputInfo,
+                    new CustomFilter.CustomFilterCallback() {
+                @Override
+                public TensorsData invoke(TensorsData in) {
+                    return in;
+                }
+            });
 
             fail();
         } catch (Exception e) {
index 9337880..1285cac 100644 (file)
@@ -20,7 +20,8 @@ import android.support.annotation.NonNull;
 /**
  * Provides interfaces to create a custom-filter in the pipeline.<br>
  * <br>
- * To register a new custom-filter, an application should call {@link #registerCustomFilter(String, CustomFilterCallback)}
+ * To register a new custom-filter, an application should call
+ * {@link #registerCustomFilter(String, TensorsInfo, TensorsInfo, CustomFilterCallback)}
  * before constructing the pipeline.
  */
 public final class CustomFilter implements AutoCloseable {
@@ -28,30 +29,16 @@ public final class CustomFilter implements AutoCloseable {
     private String mName = null;
     private CustomFilterCallback mCallback = null;
 
-    private native long nativeInitialize(String name);
+    private native long nativeInitialize(String name, TensorsInfo in, TensorsInfo out);
     private native void nativeDestroy(long handle);
 
     /**
      * Interface definition for a callback to be invoked while processing the pipeline.
      *
-     * @see #registerCustomFilter(String, CustomFilterCallback)
+     * @see #registerCustomFilter(String, TensorsInfo, TensorsInfo, CustomFilterCallback)
      */
     public interface CustomFilterCallback {
         /**
-         * Called synchronously when constructing a pipeline.
-         *
-         * NNStreamer filter configures input and output tensors information during the caps negotiation.
-         *
-         * Note that this is not a fixed value and the pipeline may try different values during the caps negotiation.
-         * An application should validate the information of input tensors and return proper output information.
-         *
-         * @param in The input tensors information
-         *
-         * @return The output tensors information
-         */
-        TensorsInfo getOutputInfo(TensorsInfo in);
-
-        /**
          * Called synchronously while processing the pipeline.
          *
          * NNStreamer filter invokes the given custom-filter callback while processing the pipeline.
@@ -65,11 +52,14 @@ public final class CustomFilter implements AutoCloseable {
     }
 
     /**
-     * Registers new custom-filter with name.
+     * Registers new custom-filter with input and output tensors information.
      *
+     * NNStreamer processes the tensors with 'custom-easy' framework which can execute without the model file.
      * Note that if given name is duplicated in the pipeline, the registration will be failed and throw an exception.
      *
      * @param name     The name of custom-filter
+     * @param in       The input tensors information
+     * @param out      The output tensors information
      * @param callback The function to be called while processing the pipeline
      *
      * @return {@link CustomFilter} instance
@@ -77,8 +67,9 @@ public final class CustomFilter implements AutoCloseable {
      * @throws IllegalArgumentException if given param is null
      * @throws IllegalStateException if failed to initialize custom-filter
      */
-    public static CustomFilter registerCustomFilter(@NonNull String name, @NonNull CustomFilterCallback callback) {
-        return new CustomFilter(name, callback);
+    public static CustomFilter registerCustomFilter(@NonNull String name, @NonNull TensorsInfo in,
+            @NonNull TensorsInfo out, @NonNull CustomFilterCallback callback) {
+        return new CustomFilter(name, in, out, callback);
     }
 
     /**
@@ -94,21 +85,27 @@ public final class CustomFilter implements AutoCloseable {
      * Internal constructor to create and register a custom-filter.
      *
      * @param name     The name of custom-filter
+     * @param in       The input tensors information
+     * @param out      The output tensors information
      * @param callback The function to be called while processing the pipeline
      *
      * @throws IllegalArgumentException if given param is null
      * @throws IllegalStateException if failed to initialize custom-filter
      */
-    private CustomFilter(@NonNull String name, @NonNull CustomFilterCallback callback) {
+    private CustomFilter(String name, TensorsInfo in, TensorsInfo out, CustomFilterCallback callback) {
         if (name == null) {
             throw new IllegalArgumentException("Given name is null");
         }
 
+        if (in == null || out == null) {
+            throw new IllegalArgumentException("Given info is null");
+        }
+
         if (callback == null) {
             throw new IllegalArgumentException("Given callback is null");
         }
 
-        mHandle = nativeInitialize(name);
+        mHandle = nativeInitialize(name, in, out);
         if (mHandle == 0) {
             throw new IllegalStateException("Failed to initialize custom-filter " + name);
         }
@@ -118,19 +115,6 @@ public final class CustomFilter implements AutoCloseable {
     }
 
     /**
-     * Internal method called from native during the caps negotiation.
-     */
-    private TensorsInfo getOutputInfo(TensorsInfo in) {
-        TensorsInfo out = null;
-
-        if (mCallback != null) {
-            out = mCallback.getOutputInfo(in);
-        }
-
-        return out;
-    }
-
-    /**
      * Internal method called from native while processing the pipeline.
      */
     private TensorsData invoke(TensorsData in) {
index 7b821a5..f5c0214 100644 (file)
@@ -285,10 +285,7 @@ nns_destroy_pipe_info (pipeline_info_s * pipe_info, JNIEnv * env)
       ml_pipeline_destroy (pipe_info->pipeline_handle);
       break;
     case NNS_PIPE_TYPE_CUSTOM:
-      /**
-       * Do nothing here (no handle to close).
-       * The handle is filter-framework and it will be closed in customfilter-destroy function.
-       */
+      ml_pipeline_custom_easy_filter_unregister (pipe_info->pipeline_handle);
       break;
 #endif
     case NNS_PIPE_TYPE_SINGLE:
@@ -444,7 +441,8 @@ nns_parse_tensors_data (pipeline_info_s * pipe_info, JNIEnv * env,
   g_return_val_if_fail (obj_data, FALSE);
   g_return_val_if_fail (data_h, FALSE);
 
-  if (ml_tensors_data_create_no_alloc (NULL, data_h) != ML_ERROR_NONE) {
+  if (*data_h == NULL &&
+      ml_tensors_data_create_no_alloc (NULL, data_h) != ML_ERROR_NONE) {
     nns_loge ("Failed to create handle for tensors data.");
     return FALSE;
   }
@@ -465,7 +463,8 @@ nns_parse_tensors_data (pipeline_info_s * pipe_info, JNIEnv * env,
       gsize data_size = (gsize) (*env)->GetDirectBufferCapacity (env, tensor);
       gpointer data_ptr = (*env)->GetDirectBufferAddress (env, tensor);
 
-      data->tensors[i].tensor = g_malloc (data_size);
+      if (data->tensors[i].tensor == NULL)
+        data->tensors[i].tensor = g_malloc (data_size);
       if (data->tensors[i].tensor == NULL) {
         nns_loge ("Failed to allocate memory %zd, index %d.", data_size, i);
         (*env)->DeleteLocalRef (env, tensor);
@@ -844,7 +843,7 @@ static JNINativeMethod native_methods_pipeline[] = {
  * @brief List of implemented native methods for CustomFilter class.
  */
 static JNINativeMethod native_methods_customfilter[] = {
-  {"nativeInitialize", "(Ljava/lang/String;)J",
+  {"nativeInitialize", "(Ljava/lang/String;L" NNS_CLS_TINFO ";L" NNS_CLS_TINFO ";)J",
       (void *) nns_native_custom_initialize},
   {"nativeDestroy", "(J)V", (void *) nns_native_custom_destroy}
 };
index 59de9ea..79b1dc7 100644 (file)
 typedef struct
 {
   jmethodID mid_invoke;
-  jmethodID mid_info;
   ml_tensors_info_h in_info;
   jobject in_info_obj;
 } customfilter_priv_data_s;
 
 /**
- * @brief Table to handle custom-filter.
- */
-static GHashTable *g_customfilters = NULL;
-
-/**
  * @brief Release private data in custom filter.
  */
 static void
@@ -87,65 +81,31 @@ nns_customfilter_priv_set_in_info (pipeline_info_s * pipe_info, JNIEnv * env,
 }
 
 /**
- * @brief The mandatory callback for GstTensorFilterFramework.
- * @param prop The property of tensor_filter instance
- * @param private_data Sub-plugin's private data
- * @param[in] input The array of input tensors
- * @param[out] output The array of output tensors
- * @return 0 if OK. Non-zero if error.
+ * @brief The mandatory callback for custom-filter execution.
+ * @return 0 if OK. 1 to drop input buffer. Negative value if error.
  */
 static int
-nns_customfilter_invoke (const GstTensorFilterProperties * prop,
-    void **private_data, const GstTensorMemory * input,
-    GstTensorMemory * output)
+nns_customfilter_invoke (const ml_tensors_data_h in, ml_tensors_data_h out,
+    void *user_data)
 {
   pipeline_info_s *pipe_info = NULL;
   customfilter_priv_data_s *priv;
-  ml_tensors_data_h in_data, out_data;
-  ml_tensors_info_h in_info;
-  ml_tensors_data_s *_data;
   JNIEnv *env;
   jobject obj_in_data, obj_out_data;
-  guint i;
   int ret = -1;
 
   /* get pipe info and init */
-  pipe_info = g_hash_table_lookup (g_customfilters, prop->fwname);
+  pipe_info = (pipeline_info_s *) user_data;
   g_return_val_if_fail (pipe_info, -1);
 
   env = nns_get_jni_env (pipe_info);
   g_return_val_if_fail (env, -1);
 
-  in_data = out_data = NULL;
-  in_info = NULL;
   obj_in_data = obj_out_data = NULL;
   priv = (customfilter_priv_data_s *) pipe_info->priv_data;
 
-  if (ml_tensors_data_create_no_alloc (NULL, &in_data) != ML_ERROR_NONE) {
-    nns_loge ("Failed to create handle for input tensors data.");
-    goto done;
-  }
-
-  if (ml_tensors_info_create (&in_info) != ML_ERROR_NONE) {
-    nns_loge ("Failed to create handle for input tensors info.");
-    goto done;
-  }
-
-  /* convert to c-api data type */
-  _data = (ml_tensors_data_s *) in_data;
-  _data->num_tensors = prop->input_meta.num_tensors;
-  for (i = 0; i < _data->num_tensors; i++) {
-    _data->tensors[i].tensor = input[i].data;
-    _data->tensors[i].size = input[i].size;
-  }
-
-  ml_tensors_info_copy_from_gst (in_info, &prop->input_meta);
-  if (!nns_customfilter_priv_set_in_info (pipe_info, env, in_info)) {
-    goto done;
-  }
-
   /* convert to data object */
-  if (!nns_convert_tensors_data (pipe_info, env, in_data, priv->in_info_obj,
+  if (!nns_convert_tensors_data (pipe_info, env, in, priv->in_info_obj,
           &obj_in_data)) {
     nns_loge ("Failed to convert input data to data-object.");
     goto done;
@@ -167,23 +127,11 @@ nns_customfilter_invoke (const GstTensorFilterProperties * prop,
     goto done;
   }
 
-  if (!nns_parse_tensors_data (pipe_info, env, obj_out_data, &out_data, NULL)) {
+  if (!nns_parse_tensors_data (pipe_info, env, obj_out_data, &out, NULL)) {
     nns_loge ("Failed to parse output data.");
     goto done;
   }
 
-  /* set output data */
-  _data = (ml_tensors_data_s *) out_data;
-  for (i = 0; i < _data->num_tensors; i++) {
-    output[i].data = _data->tensors[i].tensor;
-
-    if (_data->tensors[i].size != output[i].size) {
-      nns_logw ("The result has different buffer size at index %d [%zd:%zd]",
-          i, output[i].size, _data->tensors[i].size);
-      output[i].size = _data->tensors[i].size;
-    }
-  }
-
   /* callback finished */
   ret = 0;
 
@@ -193,161 +141,73 @@ done:
   if (obj_out_data)
     (*env)->DeleteLocalRef (env, obj_out_data);
 
-  g_free (in_data);
-  g_free (out_data);
-  ml_tensors_info_destroy (in_info);
   return ret;
 }
 
 /**
- * @brief The optional callback for GstTensorFilterFramework.
- * @param prop The property of tensor_filter instance
- * @param private_data Sub-plugin's private data
- * @param[in] in_info The dimension and type of input tensors
- * @param[out] out_info The dimension and type of output tensors
- * @return 0 if OK. Non-zero if error.
+ * @brief Native method for custom filter.
  */
-static int
-nns_customfilter_set_dimension (const GstTensorFilterProperties * prop,
-    void **private_data, const GstTensorsInfo * in_info,
-    GstTensorsInfo * out_info)
+jlong
+nns_native_custom_initialize (JNIEnv * env, jobject thiz, jstring name,
+    jobject in, jobject out)
 {
   pipeline_info_s *pipe_info = NULL;
   customfilter_priv_data_s *priv;
-  ml_tensors_info_h in, out;
-  jobject obj_in_info, obj_out_info;
-  JNIEnv *env;
-  int ret = -1;
+  ml_custom_easy_filter_h custom;
+  ml_tensors_info_h in_info, out_info;
+  gboolean is_done = FALSE;
+  int status;
+  const char *model_name = (*env)->GetStringUTFChars (env, name, NULL);
 
-  /* get pipe info and init */
-  pipe_info = g_hash_table_lookup (g_customfilters, prop->fwname);
-  g_return_val_if_fail (pipe_info, -1);
+  nns_logd ("Try to add custom-filter %s.", model_name);
+  in_info = out_info = NULL;
 
-  env = nns_get_jni_env (pipe_info);
-  g_return_val_if_fail (env, -1);
-
-  in = out = NULL;
-  obj_in_info = obj_out_info = NULL;
-  priv = (customfilter_priv_data_s *) pipe_info->priv_data;
-
-  if (ml_tensors_info_create (&in) != ML_ERROR_NONE) {
-    nns_loge ("Failed to create handle for input tensors info.");
+  pipe_info = nns_construct_pipe_info (env, thiz, NULL, NNS_PIPE_TYPE_CUSTOM);
+  if (pipe_info == NULL) {
+    nns_loge ("Failed to create pipe info.");
     goto done;
   }
 
-  /* convert to c-api data type */
-  ml_tensors_info_copy_from_gst (in, in_info);
-
-  if (!nns_convert_tensors_info (pipe_info, env, in, &obj_in_info)) {
-    nns_loge ("Failed to convert input tensors info to data object.");
-    goto done;
-  }
+  priv = g_new0 (customfilter_priv_data_s, 1);
+  priv->mid_invoke = (*env)->GetMethodID (env, pipe_info->cls, "invoke",
+      "(L" NNS_CLS_TDATA ";)L" NNS_CLS_TDATA ";");
+  ml_tensors_info_create (&priv->in_info);
 
-  /* call output info callback */
-  obj_out_info = (*env)->CallObjectMethod (env, pipe_info->instance,
-      priv->mid_info, obj_in_info);
+  nns_set_priv_data (pipe_info, priv, nns_customfilter_priv_free);
 
-  if ((*env)->ExceptionCheck (env)) {
-    nns_loge ("Failed to call the custom-info callback.");
-    (*env)->ExceptionClear (env);
+  if (!nns_parse_tensors_info (pipe_info, env, in, &in_info)) {
+    nns_loge ("Failed to parse input info.");
     goto done;
   }
 
-  if (!nns_parse_tensors_info (pipe_info, env, obj_out_info, &out)) {
+  if (!nns_parse_tensors_info (pipe_info, env, out, &out_info)) {
     nns_loge ("Failed to parse output info.");
     goto done;
   }
 
-  /* set output data */
-  ml_tensors_info_copy_from_ml (out_info, out);
-
-  /* callback finished */
-  ret = 0;
-
-done:
-  if (obj_in_info)
-    (*env)->DeleteLocalRef (env, obj_in_info);
-  if (obj_out_info)
-    (*env)->DeleteLocalRef (env, obj_out_info);
-
-  ml_tensors_info_destroy (in);
-  ml_tensors_info_destroy (out);
-  return ret;
-}
-
-/**
- * @brief Native method for custom filter.
- */
-jlong
-nns_native_custom_initialize (JNIEnv * env, jobject thiz, jstring name)
-{
-  pipeline_info_s *pipe_info = NULL;
-  customfilter_priv_data_s *priv;
-  GstTensorFilterFramework *fw = NULL;
-  const char *filter_name = (*env)->GetStringUTFChars (env, name, NULL);
-
-  nns_logd ("Try to add custom-filter %s.", filter_name);
-
-  if (nnstreamer_filter_find (filter_name)) {
-    nns_logw ("Custom-filter %s already exists.", filter_name);
-    goto done;
-  }
-
-  /* prepare filter-framework */
-  fw = g_new0 (GstTensorFilterFramework, 1);
-  if (fw == NULL) {
-    nns_loge ("Failed to allocate memory for filter framework.");
+  /* update input info */
+  if (!nns_customfilter_priv_set_in_info (pipe_info, env, in_info)) {
     goto done;
   }
 
-  fw->version = GST_TENSOR_FILTER_FRAMEWORK_V0;
-  fw->name = g_strdup (filter_name);
-  fw->allocate_in_invoke = TRUE;
-  fw->run_without_model = TRUE;
-  fw->invoke_NN = nns_customfilter_invoke;
-  fw->setInputDimension = nns_customfilter_set_dimension;
-
-  /* register custom-filter */
-  if (!nnstreamer_filter_probe (fw)) {
-    nns_loge ("Failed to register custom-filter %s.", filter_name);
-    g_free (fw->name);
-    g_free (fw);
+  status = ml_pipeline_custom_easy_filter_register (model_name,
+      in_info, out_info, nns_customfilter_invoke, pipe_info, &custom);
+  if (status != ML_ERROR_NONE) {
+    nns_loge ("Failed to register custom-filter %s.", model_name);
     goto done;
   }
 
-  pipe_info = nns_construct_pipe_info (env, thiz, fw, NNS_PIPE_TYPE_CUSTOM);
-  if (pipe_info == NULL) {
-    nns_loge ("Failed to create pipe info.");
-    nnstreamer_filter_exit (fw->name);
-    g_free (fw->name);
-    g_free (fw);
-    goto done;
-  }
+  pipe_info->pipeline_handle = custom;
+  is_done = TRUE;
 
-  /* add custom-filter handle to the table */
-  g_mutex_lock (&pipe_info->lock);
+done:
+  (*env)->ReleaseStringUTFChars (env, name, model_name);
 
-  if (g_customfilters == NULL) {
-    g_customfilters =
-        g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+  if (!is_done) {
+    nns_destroy_pipe_info (pipe_info, env);
+    pipe_info = NULL;
   }
 
-  g_assert (g_hash_table_insert (g_customfilters, g_strdup (filter_name),
-          pipe_info));
-
-  g_mutex_unlock (&pipe_info->lock);
-
-  priv = g_new0 (customfilter_priv_data_s, 1);
-  priv->mid_invoke = (*env)->GetMethodID (env, pipe_info->cls, "invoke",
-      "(L" NNS_CLS_TDATA ";)L" NNS_CLS_TDATA ";");
-  priv->mid_info = (*env)->GetMethodID (env, pipe_info->cls, "getOutputInfo",
-      "(L" NNS_CLS_TINFO ";)L" NNS_CLS_TINFO ";");
-  ml_tensors_info_create (&priv->in_info);
-
-  nns_set_priv_data (pipe_info, priv, nns_customfilter_priv_free);
-
-done:
-  (*env)->ReleaseStringUTFChars (env, name, filter_name);
   return CAST_TO_LONG (pipe_info);
 }
 
@@ -358,23 +218,7 @@ void
 nns_native_custom_destroy (JNIEnv * env, jobject thiz, jlong handle)
 {
   pipeline_info_s *pipe_info = NULL;
-  GstTensorFilterFramework *fw = NULL;
 
   pipe_info = CAST_TO_TYPE (handle, pipeline_info_s *);
-  g_return_if_fail (pipe_info);
-
-  fw = (GstTensorFilterFramework *) pipe_info->pipeline_handle;
-  nns_logd ("Start to unregister custom-filter %s.", fw->name);
-
-  g_mutex_lock (&pipe_info->lock);
-  if (!g_hash_table_remove (g_customfilters, fw->name)) {
-    nns_logw ("Failed to remove custom-filter %s.", fw->name);
-  }
-  g_mutex_unlock (&pipe_info->lock);
-
-  nnstreamer_filter_exit (fw->name);
-  g_free (fw->name);
-  g_free (fw);
-
   nns_destroy_pipe_info (pipe_info, env);
 }
index bbe7e86..2e3449d 100644 (file)
@@ -249,7 +249,7 @@ extern jboolean
 nns_native_single_set_input_info (JNIEnv * env, jobject thiz, jlong handle, jobject in);
 #if !defined (NNS_SINGLE_ONLY)
 extern jlong
-nns_native_custom_initialize (JNIEnv * env, jobject thiz, jstring name);
+nns_native_custom_initialize (JNIEnv * env, jobject thiz, jstring name, jobject in, jobject out);
 extern void
 nns_native_custom_destroy (JNIEnv * env, jobject thiz, jlong handle);
 extern jlong