[Api/Single] replace single implementation
authorJaeyun <jy1210.jung@samsung.com>
Fri, 25 Oct 2019 09:41:36 +0000 (18:41 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Wed, 6 Nov 2019 04:02:38 +0000 (13:02 +0900)
Replace the single api without pipeline, remove old version and clear build script.

Signed-off-by: Jaeyun Jung <jy1210.jung@samsung.com>
api/capi/capi-nnstreamer-single-new.pc.in [deleted file]
api/capi/meson.build
api/capi/src/README.md
api/capi/src/nnstreamer-capi-single-new.c [deleted file]
api/capi/src/nnstreamer-capi-single.c
jni/nnstreamer.mk
packaging/nnstreamer.spec
tests/tizen_capi/meson.build

diff --git a/api/capi/capi-nnstreamer-single-new.pc.in b/api/capi/capi-nnstreamer-single-new.pc.in
deleted file mode 100644 (file)
index 626238b..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-
-# Package Information for pkg-config
-
-prefix=@PREFIX@
-exec_prefix=@PREFIX@
-libdir=@LIB_INSTALL_DIR@
-includedir=@INCLUDE_INSTALL_DIR@
-
-Name: tizen-capi-nnstreamer-single-new
-Description: NNStreamer New-Single CAPI for Tizen
-Version: @VERSION@
-Requires:
-Libs: -L${libdir} -lcapi-nnstreamer-single-new
-Cflags: -I${includedir}/nnstreamer
index 0be5746..7c63bac 100644 (file)
@@ -30,6 +30,7 @@ capi_main = []
 capi_main += join_paths(meson.current_source_dir(), 'src', 'nnstreamer-capi-pipeline.c')
 capi_main += join_paths(meson.current_source_dir(), 'src', 'nnstreamer-capi-util.c')
 capi_main += join_paths(meson.current_source_dir(), 'src', 'nnstreamer-capi-single.c')
+capi_main += join_paths(meson.current_source_dir(), 'src', 'tensor_filter_single.c')
 
 if get_option('enable-tizen')
   capi_main += join_paths('src', 'nnstreamer-capi-tizen.c')
@@ -86,48 +87,11 @@ nnstreamer_capi_dep = declare_dependency(link_with: nnstreamer_capi_lib,
   include_directories: capi_inc,
 )
 
-# New single-shot c-api
-capi_single_new_main = []
-capi_single_new_main += join_paths(meson.current_source_dir(), 'src', 'nnstreamer-capi-pipeline.c')
-capi_single_new_main += join_paths(meson.current_source_dir(), 'src', 'nnstreamer-capi-util.c')
-capi_single_new_main += join_paths(meson.current_source_dir(), 'src', 'nnstreamer-capi-single-new.c')
-capi_single_new_main += join_paths(meson.current_source_dir(), 'src', 'tensor_filter_single.c')
-
-if get_option('enable-tizen')
-  capi_single_new_main += join_paths('src', 'nnstreamer-capi-tizen.c')
-endif
-
-shared_library ('capi-nnstreamer-single-new',
-  capi_single_new_main,
-  dependencies: capi_deps,
-  include_directories: capi_inc,
-  install: true,
-  install_dir: nnstreamer_libdir,
-)
-nnstreamer_capi_single_new_lib = static_library ('capi-nnstreamer-single-new',
-  capi_single_new_main,
-  dependencies: capi_deps,
-  include_directories: capi_inc,
-  install: true,
-  install_dir: nnstreamer_libdir,
-)
-
-nnstreamer_capi_single_new_dep = declare_dependency(
-  link_with: nnstreamer_capi_single_new_lib,
-  dependencies: capi_deps,
-  include_directories: capi_inc,
-)
-
 configure_file(input: 'capi-nnstreamer.pc.in', output: 'capi-nnstreamer.pc',
   install_dir: join_paths(nnstreamer_libdir, 'pkgconfig'),
   configuration: nnstreamer_conf
 )
 
-configure_file(input: 'capi-nnstreamer-single-new.pc.in', output: 'capi-nnstreamer-single-new.pc',
-  install_dir: join_paths(nnstreamer_libdir, 'pkgconfig'),
-  configuration: nnstreamer_conf
-)
-
 install_headers(capi_devel_main,
   subdir: 'nnstreamer'
 )
index 63ee626..3562712 100644 (file)
@@ -1,16 +1,17 @@
-# NNStreamer single-shot APIs
+# NNStreamer APIs
 
 ## Files
 - nnstreamer-capi-pipeline.c - API to make pipeline with NNStreamer
-- nnstreamer-capi-single.c - API to run just a single model with NNStreamer
-- nnstreamer-capi-single-new.c - single API independent of GStreamer
+- nnstreamer-capi-single.c - API to run a single model with NNStreamer, independent of GStreamer
 - nnstreamer-capi-util.c - Utility functions for capi
 - tensor\_filter\_single.c - Tensor\_filter independent of GStreamer
 
-## Latency & Running-time
-Below shows the comparison of existing vs Gst-less singleshot api - showing reduction in latency and running-time for the API (tested with tensorflow-lite).
+## Comparison of Single API
 
-These values averaged over 10 continuous runs
+### Latency & Running-time
+Below shows the comparison of old pipeline-based vs Gst-less Single API - showing reduction in latency and running-time for the API (tested with tensorflow-lite).
+
+These values averaged over 10 continuous runs.
 
 |  | Open (us)      | Invoke (ms)           | Close (us)  |
 | --- |:-------------:|:-------------:|:-----:|
@@ -20,19 +21,19 @@ These values averaged over 10 continuous runs
 | Old (no warmup) | 7201 | 5225  | 5299  |
 
 
-These values are just for the first run
+These values are just for the first run.
 
 | | Open (us)      | Invoke (ms)           | Close (us)  |
 | --- |:-------------:|:-------------:|:-----:|
 | New  |  12326   | 5231 | 2347 |
 | Old |  58772 | 5250   | 52611 |
 
-## Memory consumption
+### Memory consumption
 
 Comparison of the maximum memory consumption between the two API implementations. Both the examples below run the same test case. The difference is that the test case executable has been linked with different API shared library. [mprof](https://github.com/pythonprofilers/memory_profiler) has been used to measure the memory consumption with `--interval 0.01 --include-children` as configuration parameters. The unit test `nnstreamer_capi_singleshot.benchmark_time` was used to compare the memory consumption.
 
-Existing C-API implementation
-![unittest_tizen_capi](https://user-images.githubusercontent.com/6565775/64155170-3e179200-ce6d-11e9-94d5-b3a4138533c7.png)
+- Old pipeline-based implementation
+![unittest_tizen_capi_single_old](https://user-images.githubusercontent.com/6565775/64155170-3e179200-ce6d-11e9-94d5-b3a4138533c7.png)
 
-Gst-Less C-API implementation
+- Gst-less implementation
 ![unittest_tizen_capi_single_new](https://user-images.githubusercontent.com/6565775/64155182-4374dc80-ce6d-11e9-9d74-660d6261ceb6.png)
diff --git a/api/capi/src/nnstreamer-capi-single-new.c b/api/capi/src/nnstreamer-capi-single-new.c
deleted file mode 100644 (file)
index 9f4d636..0000000
+++ /dev/null
@@ -1,787 +0,0 @@
-/**
- * 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 nnstreamer-capi-single-new.c
- * @date 29 Aug 2019
- * @brief 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>
- * @author Parichay Kapoor <pk.kapoor@samsung.com>
- * @bug No known bugs except for NYI items
- */
-
-#include <nnstreamer-single.h>
-#include <nnstreamer-capi-private.h>
-#include <nnstreamer_plugin_api.h>
-
-#include "tensor_filter_single.h"
-
-#define ML_SINGLE_MAGIC 0xfeedfeed
-
-/**
- * @brief Default time to wait for an output in appsink (3 seconds).
- */
-#define SINGLE_DEFAULT_TIMEOUT 3000
-
-/**
- * @brief Global lock for single shot API
- * @detail This lock ensures that ml_single_close is thread safe. All other API
- *         functions use the mutex from the single handle. However for close,
- *         single handle mutex cannot be used as single handle is destroyed at
- *         close
- * @note This mutex is automatically initialized as it is statically declared
- */
-G_LOCK_DEFINE_STATIC (magic);
-
-/**
- * @brief Get valid handle after magic verification
- * @note handle's mutex (single_h->mutex) is acquired after this
- * @param[out] single_h The handle properly casted: (ml_single *).
- * @param[in] single The handle to be validated: (void *).
- * @param[in] reset Set TRUE if the handle is to be reset (magic = 0).
- */
-#define ML_SINGLE_GET_VALID_HANDLE_LOCKED(single_h, single, reset) do { \
-  G_LOCK (magic); \
-  single_h = (ml_single *) single; \
-  if (single_h->magic != ML_SINGLE_MAGIC) { \
-    ml_loge ("The given param, single is invalid."); \
-    G_UNLOCK (magic); \
-    return ML_ERROR_INVALID_PARAMETER; \
-  } \
-  if (reset) \
-    single_h->magic = 0; \
-  g_mutex_lock (&single_h->mutex); \
-  G_UNLOCK (magic); \
-} while (0)
-
-/**
- * @brief This is for the symmetricity with ML_SINGLE_GET_VALID_HANDLE_LOCKED
- * @param[in] single_h The casted handle (ml_single *).
- */
-#define ML_SINGLE_HANDLE_UNLOCK(single_h) g_mutex_unlock (&single_h->mutex);
-
-/** define string names for input/output */
-#define INPUT_STR "input"
-#define OUTPUT_STR "output"
-#define TYPE_STR "type"
-#define NAME_STR "name"
-
-/** concat string from #define */
-#define CONCAT_MACRO_STR(STR1,STR2) STR1 STR2
-
-/** States for invoke thread */
-typedef enum {
-  IDLE = 0,           /**< ready to accept next input */
-  RUNNING,            /**< running an input, cannot accept more input */
-  JOIN_REQUESTED,     /**< should join the thread, will exit soon */
-  ERROR               /**< error on thread, will exit soon */
-} thread_state;
-
-/** ML single api data structure for handle */
-typedef struct
-{
-  GTensorFilterSingle *filter;  /**< tensor filter element */
-  ml_tensors_info_s in_info;    /**< info about input */
-  ml_tensors_info_s out_info;   /**< info about output */
-  ml_nnfw_type_e nnfw;          /**< nnfw type for this filter */
-  guint magic;                  /**< code to verify valid handle */
-
-  GThread *thread;              /**< thread for invoking */
-  GMutex mutex;                 /**< mutex for synchronization */
-  GCond cond;                   /**< condition for synchronization */
-  ml_tensors_data_h input;      /**< input received from user */
-  ml_tensors_data_h *output;    /**< output to be sent back to user */
-  guint timeout;                /**< timeout for invoking */
-  thread_state state;           /**< current state of the thread */
-  gboolean ignore_output;       /**< ignore and free the output */
-  int status;                   /**< status of processing */
-} ml_single;
-
-/**
- * @brief thread to execute calls to invoke
- */
-static void *
-invoke_thread (void *arg)
-{
-  ml_single *single_h;
-  GTensorFilterSingleClass *klass;
-  GstTensorMemory in_tensors[NNS_TENSOR_SIZE_LIMIT];
-  GstTensorMemory out_tensors[NNS_TENSOR_SIZE_LIMIT];
-  ml_tensors_data_s *in_data, *out_data;
-  int i, status = ML_ERROR_NONE;
-
-  single_h = (ml_single *) arg;
-
-  g_mutex_lock (&single_h->mutex);
-
-  /** get the tensor_filter element */
-  klass = g_type_class_peek (G_TYPE_TENSOR_FILTER_SINGLE);
-  if (!klass) {
-    single_h->state = ERROR;
-    goto exit;
-  }
-
-  while (single_h->state <= RUNNING) {
-
-    /** wait for data */
-    while (single_h->state != RUNNING) {
-      g_cond_wait (&single_h->cond, &single_h->mutex);
-      if (single_h->state >= JOIN_REQUESTED)
-        goto exit;
-    }
-
-    in_data = (ml_tensors_data_s *) single_h->input;
-
-    /** Setup input buffer */
-    for (i = 0; i < in_data->num_tensors; i++) {
-      in_tensors[i].data = in_data->tensors[i].tensor;
-      in_tensors[i].size = in_data->tensors[i].size;
-      in_tensors[i].type = (tensor_type) single_h->in_info.info[i].type;
-    }
-
-    /** Setup output buffer */
-    for (i = 0; i < single_h->out_info.num_tensors; i++) {
-      /** memory will be allocated by tensor_filter_single */
-      out_tensors[i].data = NULL;
-      out_tensors[i].size =
-          ml_tensor_info_get_size (&single_h->out_info.info[i]);
-      out_tensors[i].type = (tensor_type) single_h->out_info.info[i].type;
-    }
-    g_mutex_unlock (&single_h->mutex);
-
-    /** invoke the thread */
-    if (klass->invoke (single_h->filter, in_tensors, out_tensors) == FALSE) {
-      status = ML_ERROR_INVALID_PARAMETER;
-      g_mutex_lock (&single_h->mutex);
-      goto wait_for_next;
-    }
-
-    g_mutex_lock (&single_h->mutex);
-    /** Allocate output buffer */
-    if (single_h->ignore_output == FALSE) {
-      status = ml_tensors_data_create_no_alloc (&single_h->out_info,
-          single_h->output);
-      if (status != ML_ERROR_NONE) {
-        ml_loge ("Failed to allocate the memory block.");
-        (*single_h->output) = NULL;
-        goto wait_for_next;
-      }
-
-      /** set the result */
-      out_data = (ml_tensors_data_s *) (*single_h->output);
-      for (i = 0; i < single_h->out_info.num_tensors; i++) {
-        out_data->tensors[i].tensor = out_tensors[i].data;
-      }
-    } else {
-      /**
-       * Caller of the invoke thread has returned back with timeout
-       * so, free the memory allocated by the invoke as their is no receiver
-       */
-      for (i = 0; i < single_h->out_info.num_tensors; i++)
-        g_free (out_tensors[i].data);
-    }
-
-    /** loop over to wait for the next element */
-  wait_for_next:
-    single_h->status = status;
-    if (single_h->state == RUNNING)
-      single_h->state = IDLE;
-    g_cond_broadcast (&single_h->cond);
-  }
-
-exit:
-  if (single_h->state != ERROR)
-    single_h->state = IDLE;
-  g_mutex_unlock (&single_h->mutex);
-  return NULL;
-}
-
-/**
- * @brief Gets the tensors info from tensor-filter.
- */
-static void
-ml_single_get_tensors_info_from_filter (GTensorFilterSingle * filter, gboolean is_input,
-    ml_tensors_info_h * info)
-{
-  ml_tensors_info_s *input_info;
-  GstTensorsInfo gst_info;
-  gchar *val;
-  guint rank;
-  const gchar *prop_prefix, *prop_name, *prop_type;
-
-  if (is_input) {
-    prop_prefix = INPUT_STR;
-    prop_type = CONCAT_MACRO_STR (INPUT_STR, TYPE_STR);
-    prop_name = CONCAT_MACRO_STR (INPUT_STR, NAME_STR);
-  } else {
-    prop_prefix = OUTPUT_STR;
-    prop_type = CONCAT_MACRO_STR (OUTPUT_STR, TYPE_STR);
-    prop_name = CONCAT_MACRO_STR (OUTPUT_STR, NAME_STR);
-  }
-
-  /* allocate handle for tensors info */
-  ml_tensors_info_create (info);
-  input_info = (ml_tensors_info_s *) (*info);
-
-  ml_tensors_info_initialize (input_info);
-  gst_tensors_info_init (&gst_info);
-
-  /* get dimensions */
-  g_object_get (filter, prop_prefix, &val, NULL);
-  rank = gst_tensors_info_parse_dimensions_string (&gst_info, val);
-  g_free (val);
-
-  /* set the number of tensors */
-  gst_info.num_tensors = rank;
-
-  /* get types */
-  g_object_get (filter, prop_type, &val, NULL);
-  rank = gst_tensors_info_parse_types_string (&gst_info, val);
-  g_free (val);
-
-  if (gst_info.num_tensors != rank) {
-    ml_logw ("Invalid state, tensor type is mismatched in filter.");
-  }
-
-  /* get names */
-  g_object_get (filter, prop_name, &val, NULL);
-  rank = gst_tensors_info_parse_names_string (&gst_info, val);
-  g_free (val);
-
-  if (gst_info.num_tensors != rank) {
-    ml_logw ("Invalid state, tensor name is mismatched in filter.");
-  }
-
-  ml_tensors_info_copy_from_gst (input_info, &gst_info);
-  gst_tensors_info_free (&gst_info);
-}
-
-/**
- * @brief Set the info for input/output tensors
- */
-static int
-ml_single_set_inout_tensors_info (GObject * object,
-    const gboolean is_input, ml_tensors_info_s * tensors_info)
-{
-  int status = ML_ERROR_NONE;
-  GstTensorsInfo info;
-  gchar *str_dim, *str_type, *str_name;
-  const gchar *str_type_name, *str_name_name;
-  const gchar *prefix;
-
-  if (is_input) {
-    prefix = INPUT_STR;
-    str_type_name = CONCAT_MACRO_STR (INPUT_STR, TYPE_STR);
-    str_name_name = CONCAT_MACRO_STR (INPUT_STR, NAME_STR);
-  } else {
-    prefix = OUTPUT_STR;
-    str_type_name = CONCAT_MACRO_STR (OUTPUT_STR, TYPE_STR);
-    str_name_name = CONCAT_MACRO_STR (OUTPUT_STR, NAME_STR);
-  }
-
-  ml_tensors_info_copy_from_ml (&info, tensors_info);
-
-  /* Set input option */
-  str_dim = gst_tensors_info_get_dimensions_string (&info);
-  str_type = gst_tensors_info_get_types_string (&info);
-  str_name = gst_tensors_info_get_names_string (&info);
-
-  if (!str_dim || !str_type || !str_name || !str_type_name || !str_name_name) {
-    status = ML_ERROR_INVALID_PARAMETER;
-  } else {
-    g_object_set (object, prefix, str_dim, str_type_name, str_type,
-        str_name_name, str_name, NULL);
-  }
-
-  g_free (str_dim);
-  g_free (str_type);
-  g_free (str_name);
-
-  gst_tensors_info_free (&info);
-
-  return status;
-}
-
-/**
- * @brief Internal static function to set tensors info in the handle.
- */
-static gboolean
-ml_single_set_info_in_handle (ml_single_h single, gboolean is_input,
-    ml_tensors_info_s * tensors_info)
-{
-  ml_single *single_h;
-  ml_tensors_info_s *dest;
-  bool valid = false;
-  gboolean configured = false;
-  GTensorFilterSingleClass *klass;
-  GObject * filter_obj;
-
-  single_h = (ml_single *) single;
-  filter_obj = G_OBJECT (single_h->filter);
-  klass = g_type_class_peek (G_TYPE_TENSOR_FILTER_SINGLE);
-  if (!klass) {
-    return FALSE;
-  }
-
-  if (is_input) {
-    dest = &single_h->in_info;
-    configured = klass->input_configured (single_h->filter);
-  } else {
-    dest = &single_h->out_info;
-    configured = klass->output_configured (single_h->filter);
-  }
-
-  if (tensors_info) {
-    if (!configured)
-      ml_single_set_inout_tensors_info (filter_obj, is_input, tensors_info);
-    ml_tensors_info_clone (dest, tensors_info);
-  } else {
-    ml_tensors_info_h info;
-
-    ml_single_get_tensors_info_from_filter (single_h->filter, is_input, &info);
-    ml_tensors_info_clone (dest, info);
-    ml_tensors_info_destroy (info);
-  }
-
-  if (!ml_tensors_info_is_valid (dest, valid)) {
-    /* invalid tensors info */
-    return FALSE;
-  }
-
-  return TRUE;
-}
-
-/**
- * @brief Opens an ML model and returns the instance as a handle.
- */
-int
-ml_single_open (ml_single_h * single, const char *model,
-    const ml_tensors_info_h input_info, const ml_tensors_info_h output_info,
-    ml_nnfw_type_e nnfw, ml_nnfw_hw_e hw)
-{
-  ml_single *single_h;
-  GObject *filter_obj;
-  int status = ML_ERROR_NONE;
-  GTensorFilterSingleClass *klass;
-  ml_tensors_info_s *in_tensors_info, *out_tensors_info;
-  bool available = false;
-  bool valid = false;
-  GError * error;
-
-  check_feature_state ();
-
-  /* Validate the params */
-  if (!single) {
-    ml_loge ("The given param, single is invalid.");
-    return ML_ERROR_INVALID_PARAMETER;
-  }
-
-  /* init null */
-  *single = NULL;
-
-  in_tensors_info = (ml_tensors_info_s *) input_info;
-  out_tensors_info = (ml_tensors_info_s *) output_info;
-
-  /* Validate input tensor info. */
-  if (input_info && !ml_tensors_info_is_valid (input_info, valid)) {
-    ml_loge ("The given param, input tensor info is invalid.");
-    return ML_ERROR_INVALID_PARAMETER;
-  }
-
-  /* Validate output tensor info. */
-  if (output_info && !ml_tensors_info_is_valid (output_info, valid)) {
-    ml_loge ("The given param, output tensor info is invalid.");
-    return ML_ERROR_INVALID_PARAMETER;
-  }
-
-  /**
-   * 1. Determine nnfw
-   */
-  if ((status = ml_validate_model_file (model, &nnfw)) != ML_ERROR_NONE)
-    return status;
-
-  /**
-   * 2. Determine hw
-   * @todo Now the param hw is ignored.
-   * (Supposed CPU only) Support others later.
-   */
-  status = ml_check_nnfw_availability (nnfw, hw, &available);
-  if (status != ML_ERROR_NONE)
-    return status;
-
-  if (!available) {
-    ml_loge ("The given nnfw is not available.");
-    return ML_ERROR_NOT_SUPPORTED;
-  }
-
-  /** Create ml_single object */
-  single_h = g_new0 (ml_single, 1);
-  if (single_h == NULL) {
-    ml_loge ("Failed to allocate the single handle.");
-    return ML_ERROR_OUT_OF_MEMORY;
-  }
-
-  single_h->magic = ML_SINGLE_MAGIC;
-
-  single_h->filter = g_object_new (G_TYPE_TENSOR_FILTER_SINGLE, NULL);
-  single_h->timeout = SINGLE_DEFAULT_TIMEOUT;
-  if (single_h->filter == NULL) {
-    status = ML_ERROR_INVALID_PARAMETER;
-    goto error;
-  }
-  filter_obj = G_OBJECT (single_h->filter);
-
-  /**
-   * 3. Construct a pipeline
-   * Set the pipeline desc with nnfw.
-   */
-  single_h->nnfw = nnfw;
-  switch (nnfw) {
-    case ML_NNFW_TYPE_CUSTOM_FILTER:
-      g_object_set (filter_obj, "framework", "custom", "model", model, NULL);
-      break;
-    case ML_NNFW_TYPE_TENSORFLOW_LITE:
-      /* We can get the tensor meta from tf-lite model. */
-      g_object_set (filter_obj, "framework", "tensorflow-lite",
-          "model", model, NULL);
-      break;
-    case ML_NNFW_TYPE_TENSORFLOW:
-      if (in_tensors_info && out_tensors_info) {
-        status = ml_single_set_inout_tensors_info (filter_obj, TRUE,
-            in_tensors_info);
-        if (status != ML_ERROR_NONE)
-          goto error;
-
-        status = ml_single_set_inout_tensors_info (filter_obj, FALSE,
-            out_tensors_info);
-        if (status != ML_ERROR_NONE)
-          goto error;
-
-        g_object_set (filter_obj, "framework", "tensorflow",
-            "model", model, NULL);
-      } else {
-        ml_loge ("To run the pipeline with tensorflow model, \
-            input and output information should be initialized.");
-        status = ML_ERROR_INVALID_PARAMETER;
-        goto error;
-      }
-      break;
-    default:
-      /** @todo Add other fw later. */
-      ml_loge ("The given nnfw is not supported.");
-      status = ML_ERROR_NOT_SUPPORTED;
-      goto error;
-  }
-
-  /* 4. Allocate */
-  ml_tensors_info_initialize (&single_h->in_info);
-  ml_tensors_info_initialize (&single_h->out_info);
-
-  /* 5. Start the nnfw to get inout configurations if needed */
-  klass = g_type_class_peek (G_TYPE_TENSOR_FILTER_SINGLE);
-  if (!klass) {
-    status = ML_ERROR_INVALID_PARAMETER;
-    goto error;
-  }
-  if (klass->start (single_h->filter) == FALSE) {
-    status = ML_ERROR_INVALID_PARAMETER;
-    goto error;
-  }
-
-  /* 6. Set in/out configs and metadata */
-  if (!ml_single_set_info_in_handle (single_h, TRUE, in_tensors_info)) {
-    ml_loge ("The input tensor info is invalid.");
-    status = ML_ERROR_INVALID_PARAMETER;
-    goto error;
-  }
-
-  if (!ml_single_set_info_in_handle (single_h, FALSE, out_tensors_info)) {
-    ml_loge ("The output tensor info is invalid.");
-    status = ML_ERROR_INVALID_PARAMETER;
-    goto error;
-  }
-
-  g_mutex_init (&single_h->mutex);
-  g_cond_init (&single_h->cond);
-  single_h->state = IDLE;
-  single_h->ignore_output = FALSE;
-
-  single_h->thread = g_thread_try_new (NULL, invoke_thread, (gpointer) single_h,
-      &error);
-  if (single_h->thread == NULL) {
-    ml_loge ("Failed to create the invoke thread, error: %s.", error->message);
-    g_clear_error (&error);
-    status = ML_ERROR_UNKNOWN;
-    goto error;
-  }
-
-  *single = single_h;
-  return ML_ERROR_NONE;
-
-error:
-  ml_single_close (single_h);
-  return status;
-}
-
-/**
- * @brief Closes the opened model handle.
- */
-int
-ml_single_close (ml_single_h single)
-{
-  ml_single *single_h;
-
-  check_feature_state ();
-
-  if (!single) {
-    ml_loge ("The given param, single is invalid.");
-    return ML_ERROR_INVALID_PARAMETER;
-  }
-
-  ML_SINGLE_GET_VALID_HANDLE_LOCKED (single_h, single, 1);
-
-  single_h->state = JOIN_REQUESTED;
-  g_cond_broadcast (&single_h->cond);
-
-  ML_SINGLE_HANDLE_UNLOCK (single_h);
-  g_thread_join (single_h->thread);
-
-  /** locking ensures correctness with parallel calls on close */
-  if (single_h->filter) {
-    GTensorFilterSingleClass *klass;
-    klass = g_type_class_peek (G_TYPE_TENSOR_FILTER_SINGLE);
-    if (klass)
-      klass->stop (single_h->filter);
-    gst_object_unref (single_h->filter);
-    single_h->filter = NULL;
-  }
-
-  ml_tensors_info_free (&single_h->in_info);
-  ml_tensors_info_free (&single_h->out_info);
-
-  g_cond_clear (&single_h->cond);
-  g_mutex_clear (&single_h->mutex);
-
-  g_free (single_h);
-  return ML_ERROR_NONE;
-}
-
-/**
- * @brief Invokes the model with the given input data.
- */
-int
-ml_single_invoke (ml_single_h single,
-    const ml_tensors_data_h input, ml_tensors_data_h * output)
-{
-  ml_single *single_h;
-  ml_tensors_data_s *in_data;
-  gint64 end_time;
-  int i, status = ML_ERROR_NONE;
-
-  check_feature_state ();
-
-  if (!single || !input || !output) {
-    ml_loge ("The given param is invalid.");
-    return ML_ERROR_INVALID_PARAMETER;
-  }
-
-  ML_SINGLE_GET_VALID_HANDLE_LOCKED (single_h, single, 0);
-
-  in_data = (ml_tensors_data_s *) input;
-  *output = NULL;
-
-  if (!single_h->filter || single_h->state >= JOIN_REQUESTED) {
-    ml_loge ("The given param is invalid, model is missing.");
-    status = ML_ERROR_INVALID_PARAMETER;
-    goto exit;
-  }
-
-  /* Validate input data */
-  if (in_data->num_tensors != single_h->in_info.num_tensors) {
-    ml_loge ("The given param input is invalid, \
-        different number of memory blocks.");
-    status = ML_ERROR_INVALID_PARAMETER;
-    goto exit;
-  }
-
-  for (i = 0; i < in_data->num_tensors; i++) {
-    size_t raw_size = ml_tensor_info_get_size (&single_h->in_info.info[i]);
-
-    if (!in_data->tensors[i].tensor || in_data->tensors[i].size != raw_size) {
-      ml_loge ("The given param input is invalid, \
-          different size of memory block.");
-      status = ML_ERROR_INVALID_PARAMETER;
-      goto exit;
-    }
-  }
-
-  if (single_h->state != IDLE) {
-    status = ML_ERROR_TRY_AGAIN;
-    goto exit;
-  }
-
-  single_h->input = input;
-  single_h->output = output;
-  single_h->state = RUNNING;
-  single_h->ignore_output = FALSE;
-
-  end_time = g_get_monotonic_time () +
-    single_h->timeout * G_TIME_SPAN_MILLISECOND;
-
-  g_cond_broadcast (&single_h->cond);
-  if (g_cond_wait_until (&single_h->cond, &single_h->mutex, end_time)) {
-    status = single_h->status;
-  } else {
-    ml_logw ("Wait for invoke has timed out");
-    status = ML_ERROR_TIMED_OUT;
-    /** This is set to notify invoke_thread to not process if timedout */
-    single_h->ignore_output = TRUE;
-
-    /** Free if any output memory was allocated */
-    if (*single_h->output != NULL) {
-      ml_tensors_data_destroy ((ml_tensors_data_h) *single_h->output);
-      *single_h->output = NULL;
-    }
-  }
-
-exit:
-  ML_SINGLE_HANDLE_UNLOCK (single_h);
-  return status;
-}
-
-/**
- * @brief Gets the tensors info for the given handle.
- */
-static int
-ml_single_get_tensors_info (ml_single_h single, gboolean is_input,
-    ml_tensors_info_h * info)
-{
-  ml_single *single_h;
-  int status = ML_ERROR_NONE;
-  ml_tensors_info_s *input_info;
-
-  check_feature_state ();
-
-  if (!single || !info)
-    return ML_ERROR_INVALID_PARAMETER;
-
-  ML_SINGLE_GET_VALID_HANDLE_LOCKED (single_h, single, 0);
-
-  /* allocate handle for tensors info */
-  status = ml_tensors_info_create (info);
-  if (status != ML_ERROR_NONE)
-    goto exit;
-
-  input_info = (ml_tensors_info_s *) (*info);
-
-  if (is_input)
-    status = ml_tensors_info_clone (input_info, &single_h->in_info);
-  else
-    status = ml_tensors_info_clone (input_info, &single_h->out_info);
-
-  if (status != ML_ERROR_NONE)
-    ml_tensors_info_destroy (input_info);
-
-exit:
-  ML_SINGLE_HANDLE_UNLOCK (single_h);
-  return status;
-}
-
-/**
- * @brief Gets the information of required input data for the given handle.
- * @note information = (tensor dimension, type, name and so on)
- */
-int
-ml_single_get_input_info (ml_single_h single, ml_tensors_info_h * info)
-{
-  return ml_single_get_tensors_info (single, TRUE, info);
-}
-
-/**
- * @brief Gets the information of output data for the given handle.
- * @note information = (tensor dimension, type, name and so on)
- */
-int
-ml_single_get_output_info (ml_single_h single, ml_tensors_info_h * info)
-{
-  return ml_single_get_tensors_info (single, FALSE, info);
-}
-
-/**
- * @brief Sets the maximum amount of time to wait for an output, in milliseconds.
- */
-int
-ml_single_set_timeout (ml_single_h single, unsigned int timeout)
-{
-  ml_single *single_h;
-
-  check_feature_state ();
-
-  if (!single || timeout == 0)
-    return ML_ERROR_INVALID_PARAMETER;
-
-  ML_SINGLE_GET_VALID_HANDLE_LOCKED (single_h, single, 0);
-
-  single_h->timeout = (guint) timeout;
-
-  ML_SINGLE_HANDLE_UNLOCK (single_h);
-  return ML_ERROR_NONE;
-}
-
-/**
- * @brief Sets the information (tensor dimension, type, name and so on) of required input data for the given model.
- */
-int ml_single_set_input_info (ml_single_h single, const ml_tensors_info_h info)
-{
-  ml_single *single_h;
-  GTensorFilterSingleClass *klass;
-  int status = ML_ERROR_NONE;
-  ml_tensors_info_s *in_info;
-  GstTensorsInfo gst_in_info, gst_out_info;
-  bool valid = false;
-
-  check_feature_state ();
-
-  if (!single || !info)
-    return ML_ERROR_INVALID_PARAMETER;
-
-  if (!ml_tensors_info_is_valid (info, valid))
-    return ML_ERROR_INVALID_PARAMETER;
-
-  ML_SINGLE_GET_VALID_HANDLE_LOCKED (single_h, single, 0);
-
-  in_info = (ml_tensors_info_s *) info;
-  switch (single_h->nnfw) {
-    case ML_NNFW_TYPE_TENSORFLOW_LITE:
-      ml_tensors_info_copy_from_ml (&gst_in_info, in_info);
-
-      klass = g_type_class_peek (G_TYPE_TENSOR_FILTER_SINGLE);
-      if (klass == NULL || klass->set_input_info (
-            single_h->filter, &gst_in_info, &gst_out_info) == FALSE) {
-        status = ML_ERROR_INVALID_PARAMETER;
-        goto exit;
-      }
-
-      ml_tensors_info_copy_from_gst (&single_h->in_info, &gst_in_info);
-      ml_tensors_info_copy_from_gst (&single_h->out_info, &gst_out_info);
-      break;
-    default:
-      status = ML_ERROR_NOT_SUPPORTED;
-  }
-
-exit:
-  ML_SINGLE_HANDLE_UNLOCK (single_h);
-  return status;
-}
index cb73aa7..359b30b 100644 (file)
  */
 /**
  * @file nnstreamer-capi-single.c
- * @date 08 May 2019
+ * @date 29 Aug 2019
  * @brief 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>
+ * @author Parichay Kapoor <pk.kapoor@samsung.com>
  * @bug No known bugs except for NYI items
  */
 
-#include <string.h>
-#include <gst/app/app.h>
+#include <nnstreamer-single.h>
+#include <nnstreamer-capi-private.h>
+#include <nnstreamer_plugin_api.h>
 
-#include "nnstreamer.h"         /* Uses NNStreamer/Pipeline C-API */
-#include "nnstreamer-single.h"
-#include "nnstreamer-capi-private.h"
-#include "nnstreamer_plugin_api.h"
-
-#undef ML_SINGLE_SUPPORT_TIMEOUT
-#if (GST_VERSION_MAJOR > 1 || (GST_VERSION_MAJOR == 1 && GST_VERSION_MINOR >= 10))
-#define ML_SINGLE_SUPPORT_TIMEOUT
-#endif
+#include "tensor_filter_single.h"
 
 #define ML_SINGLE_MAGIC 0xfeedfeed
 
 /**
+ * @brief Default time to wait for an output in appsink (3 seconds).
+ */
+#define SINGLE_DEFAULT_TIMEOUT 3000
+
+/**
  * @brief Global lock for single shot API
  * @detail This lock ensures that ml_single_close is thread safe. All other API
  *         functions use the mutex from the single handle. However for close,
@@ -63,7 +62,7 @@ G_LOCK_DEFINE_STATIC (magic);
   } \
   if (reset) \
     single_h->magic = 0; \
-  g_mutex_lock (&single_h->lock); \
+  g_mutex_lock (&single_h->mutex); \
   G_UNLOCK (magic); \
 } while (0)
 
@@ -71,82 +70,195 @@ G_LOCK_DEFINE_STATIC (magic);
  * @brief This is for the symmetricity with ML_SINGLE_GET_VALID_HANDLE_LOCKED
  * @param[in] single_h The casted handle (ml_single *).
  */
-#define ML_SINGLE_HANDLE_UNLOCK(single_h) \
-  g_mutex_unlock (&single_h->lock);
+#define ML_SINGLE_HANDLE_UNLOCK(single_h) g_mutex_unlock (&single_h->mutex);
+
+/** define string names for input/output */
+#define INPUT_STR "input"
+#define OUTPUT_STR "output"
+#define TYPE_STR "type"
+#define NAME_STR "name"
+
+/** concat string from #define */
+#define CONCAT_MACRO_STR(STR1,STR2) STR1 STR2
+
+/** States for invoke thread */
+typedef enum {
+  IDLE = 0,           /**< ready to accept next input */
+  RUNNING,            /**< running an input, cannot accept more input */
+  JOIN_REQUESTED,     /**< should join the thread, will exit soon */
+  ERROR               /**< error on thread, will exit soon */
+} thread_state;
+
+/** ML single api data structure for handle */
+typedef struct
+{
+  GTensorFilterSingle *filter;  /**< tensor filter element */
+  ml_tensors_info_s in_info;    /**< info about input */
+  ml_tensors_info_s out_info;   /**< info about output */
+  ml_nnfw_type_e nnfw;          /**< nnfw type for this filter */
+  guint magic;                  /**< code to verify valid handle */
+
+  GThread *thread;              /**< thread for invoking */
+  GMutex mutex;                 /**< mutex for synchronization */
+  GCond cond;                   /**< condition for synchronization */
+  ml_tensors_data_h input;      /**< input received from user */
+  ml_tensors_data_h *output;    /**< output to be sent back to user */
+  guint timeout;                /**< timeout for invoking */
+  thread_state state;           /**< current state of the thread */
+  gboolean ignore_output;       /**< ignore and free the output */
+  int status;                   /**< status of processing */
+} ml_single;
 
 /**
- * @brief Default time to wait for an output in appsink (3 seconds).
+ * @brief thread to execute calls to invoke
  */
-#define SINGLE_DEFAULT_TIMEOUT 3000
-
-typedef struct
+static void *
+invoke_thread (void *arg)
 {
-  ml_pipeline_h pipe;
+  ml_single *single_h;
+  GTensorFilterSingleClass *klass;
+  GstTensorMemory in_tensors[NNS_TENSOR_SIZE_LIMIT];
+  GstTensorMemory out_tensors[NNS_TENSOR_SIZE_LIMIT];
+  ml_tensors_data_s *in_data, *out_data;
+  int i, status = ML_ERROR_NONE;
 
-  GstElement *src;
-  GstElement *sink;
-  GstElement *filter;
+  single_h = (ml_single *) arg;
 
-  ml_tensors_info_s in_info;
-  ml_tensors_info_s out_info;
+  g_mutex_lock (&single_h->mutex);
 
-  guint magic; /**< code to verify valid handle */
-  GMutex lock; /**< Lock for internal values */
-  gboolean clear_previous_buffer; /**< Previous buffer was timed out, need to clear old buffer */
-  guint timeout; /**< Timeout in milliseconds */
-} ml_single;
+  /** get the tensor_filter element */
+  klass = g_type_class_peek (G_TYPE_TENSOR_FILTER_SINGLE);
+  if (!klass) {
+    single_h->state = ERROR;
+    goto exit;
+  }
+
+  while (single_h->state <= RUNNING) {
+
+    /** wait for data */
+    while (single_h->state != RUNNING) {
+      g_cond_wait (&single_h->cond, &single_h->mutex);
+      if (single_h->state >= JOIN_REQUESTED)
+        goto exit;
+    }
+
+    in_data = (ml_tensors_data_s *) single_h->input;
+
+    /** Setup input buffer */
+    for (i = 0; i < in_data->num_tensors; i++) {
+      in_tensors[i].data = in_data->tensors[i].tensor;
+      in_tensors[i].size = in_data->tensors[i].size;
+      in_tensors[i].type = (tensor_type) single_h->in_info.info[i].type;
+    }
+
+    /** Setup output buffer */
+    for (i = 0; i < single_h->out_info.num_tensors; i++) {
+      /** memory will be allocated by tensor_filter_single */
+      out_tensors[i].data = NULL;
+      out_tensors[i].size =
+          ml_tensor_info_get_size (&single_h->out_info.info[i]);
+      out_tensors[i].type = (tensor_type) single_h->out_info.info[i].type;
+    }
+    g_mutex_unlock (&single_h->mutex);
+
+    /** invoke the thread */
+    if (klass->invoke (single_h->filter, in_tensors, out_tensors) == FALSE) {
+      status = ML_ERROR_INVALID_PARAMETER;
+      g_mutex_lock (&single_h->mutex);
+      goto wait_for_next;
+    }
+
+    g_mutex_lock (&single_h->mutex);
+    /** Allocate output buffer */
+    if (single_h->ignore_output == FALSE) {
+      status = ml_tensors_data_create_no_alloc (&single_h->out_info,
+          single_h->output);
+      if (status != ML_ERROR_NONE) {
+        ml_loge ("Failed to allocate the memory block.");
+        (*single_h->output) = NULL;
+        goto wait_for_next;
+      }
+
+      /** set the result */
+      out_data = (ml_tensors_data_s *) (*single_h->output);
+      for (i = 0; i < single_h->out_info.num_tensors; i++) {
+        out_data->tensors[i].tensor = out_tensors[i].data;
+      }
+    } else {
+      /**
+       * Caller of the invoke thread has returned back with timeout
+       * so, free the memory allocated by the invoke as their is no receiver
+       */
+      for (i = 0; i < single_h->out_info.num_tensors; i++)
+        g_free (out_tensors[i].data);
+    }
+
+    /** loop over to wait for the next element */
+  wait_for_next:
+    single_h->status = status;
+    if (single_h->state == RUNNING)
+      single_h->state = IDLE;
+    g_cond_broadcast (&single_h->cond);
+  }
+
+exit:
+  if (single_h->state != ERROR)
+    single_h->state = IDLE;
+  g_mutex_unlock (&single_h->mutex);
+  return NULL;
+}
 
 /**
  * @brief Gets the tensors info from tensor-filter.
  */
 static void
-ml_single_get_tensors_info_from_filter (GstElement * filter, gboolean is_input,
+ml_single_get_tensors_info_from_filter (GTensorFilterSingle * filter, gboolean is_input,
     ml_tensors_info_h * info)
 {
   ml_tensors_info_s *input_info;
   GstTensorsInfo gst_info;
   gchar *val;
   guint rank;
-  gchar *prop_prefix, *prop_name;
+  const gchar *prop_prefix, *prop_name, *prop_type;
 
-  if (is_input)
-    prop_prefix = g_strdup ("input");
-  else
-    prop_prefix = g_strdup ("output");
+  if (is_input) {
+    prop_prefix = INPUT_STR;
+    prop_type = CONCAT_MACRO_STR (INPUT_STR, TYPE_STR);
+    prop_name = CONCAT_MACRO_STR (INPUT_STR, NAME_STR);
+  } else {
+    prop_prefix = OUTPUT_STR;
+    prop_type = CONCAT_MACRO_STR (OUTPUT_STR, TYPE_STR);
+    prop_name = CONCAT_MACRO_STR (OUTPUT_STR, NAME_STR);
+  }
 
   /* allocate handle for tensors info */
   ml_tensors_info_create (info);
   input_info = (ml_tensors_info_s *) (*info);
 
+  ml_tensors_info_initialize (input_info);
   gst_tensors_info_init (&gst_info);
 
   /* get dimensions */
-  prop_name = g_strdup_printf ("%s", prop_prefix);
-  g_object_get (filter, prop_name, &val, NULL);
+  g_object_get (filter, prop_prefix, &val, NULL);
   rank = gst_tensors_info_parse_dimensions_string (&gst_info, val);
   g_free (val);
-  g_free (prop_name);
 
   /* set the number of tensors */
   gst_info.num_tensors = rank;
 
   /* get types */
-  prop_name = g_strdup_printf ("%s%s", prop_prefix, "type");
-  g_object_get (filter, prop_name, &val, NULL);
+  g_object_get (filter, prop_type, &val, NULL);
   rank = gst_tensors_info_parse_types_string (&gst_info, val);
   g_free (val);
-  g_free (prop_name);
 
   if (gst_info.num_tensors != rank) {
     ml_logw ("Invalid state, tensor type is mismatched in filter.");
   }
 
   /* get names */
-  prop_name = g_strdup_printf ("%s%s", prop_prefix, "name");
   g_object_get (filter, prop_name, &val, NULL);
   rank = gst_tensors_info_parse_names_string (&gst_info, val);
   g_free (val);
-  g_free (prop_name);
 
   if (gst_info.num_tensors != rank) {
     ml_logw ("Invalid state, tensor name is mismatched in filter.");
@@ -154,43 +266,52 @@ ml_single_get_tensors_info_from_filter (GstElement * filter, gboolean is_input,
 
   ml_tensors_info_copy_from_gst (input_info, &gst_info);
   gst_tensors_info_free (&gst_info);
-
-  g_free (prop_prefix);
 }
 
 /**
- * @brief Internal static function to get the string of tensor-filter properties.
- * The returned string should be freed using g_free().
+ * @brief Set the info for input/output tensors
  */
-static gchar *
-ml_single_get_filter_option_string (gboolean is_input,
-    ml_tensors_info_s * tensors_info)
+static int
+ml_single_set_inout_tensors_info (GObject * object,
+    const gboolean is_input, ml_tensors_info_s * tensors_info)
 {
-  GstTensorsInfo gst_info;
-  gchar *str_prefix, *str_dim, *str_type, *str_name;
-  gchar *option;
-
-  ml_tensors_info_copy_from_ml (&gst_info, tensors_info);
+  int status = ML_ERROR_NONE;
+  GstTensorsInfo info;
+  gchar *str_dim, *str_type, *str_name;
+  const gchar *str_type_name, *str_name_name;
+  const gchar *prefix;
+
+  if (is_input) {
+    prefix = INPUT_STR;
+    str_type_name = CONCAT_MACRO_STR (INPUT_STR, TYPE_STR);
+    str_name_name = CONCAT_MACRO_STR (INPUT_STR, NAME_STR);
+  } else {
+    prefix = OUTPUT_STR;
+    str_type_name = CONCAT_MACRO_STR (OUTPUT_STR, TYPE_STR);
+    str_name_name = CONCAT_MACRO_STR (OUTPUT_STR, NAME_STR);
+  }
 
-  if (is_input)
-    str_prefix = g_strdup ("input");
-  else
-    str_prefix = g_strdup ("output");
+  ml_tensors_info_copy_from_ml (&info, tensors_info);
 
-  str_dim = gst_tensors_info_get_dimensions_string (&gst_info);
-  str_type = gst_tensors_info_get_types_string (&gst_info);
-  str_name = gst_tensors_info_get_names_string (&gst_info);
+  /* Set input option */
+  str_dim = gst_tensors_info_get_dimensions_string (&info);
+  str_type = gst_tensors_info_get_types_string (&info);
+  str_name = gst_tensors_info_get_names_string (&info);
 
-  option = g_strdup_printf ("%s=%s %stype=%s %sname=%s",
-      str_prefix, str_dim, str_prefix, str_type, str_prefix, str_name);
+  if (!str_dim || !str_type || !str_name || !str_type_name || !str_name_name) {
+    status = ML_ERROR_INVALID_PARAMETER;
+  } else {
+    g_object_set (object, prefix, str_dim, str_type_name, str_type,
+        str_name_name, str_name, NULL);
+  }
 
-  g_free (str_prefix);
   g_free (str_dim);
   g_free (str_type);
   g_free (str_name);
-  gst_tensors_info_free (&gst_info);
 
-  return option;
+  gst_tensors_info_free (&info);
+
+  return status;
 }
 
 /**
@@ -203,16 +324,28 @@ ml_single_set_info_in_handle (ml_single_h single, gboolean is_input,
   ml_single *single_h;
   ml_tensors_info_s *dest;
   bool valid = false;
+  gboolean configured = false;
+  GTensorFilterSingleClass *klass;
+  GObject * filter_obj;
 
   single_h = (ml_single *) single;
+  filter_obj = G_OBJECT (single_h->filter);
+  klass = g_type_class_peek (G_TYPE_TENSOR_FILTER_SINGLE);
+  if (!klass) {
+    return FALSE;
+  }
 
-  if (is_input)
+  if (is_input) {
     dest = &single_h->in_info;
-  else
+    configured = klass->input_configured (single_h->filter);
+  } else {
     dest = &single_h->out_info;
+    configured = klass->output_configured (single_h->filter);
+  }
 
   if (tensors_info) {
-    /* clone given tensors info */
+    if (!configured)
+      ml_single_set_inout_tensors_info (filter_obj, is_input, tensors_info);
     ml_tensors_info_clone (dest, tensors_info);
   } else {
     ml_tensors_info_h info;
@@ -239,15 +372,13 @@ ml_single_open (ml_single_h * single, const char *model,
     ml_nnfw_type_e nnfw, ml_nnfw_hw_e hw)
 {
   ml_single *single_h;
-  ml_pipeline_h pipe;
-  ml_pipeline *pipe_h;
-  GstElement *appsrc, *appsink, *filter;
-  GstCaps *caps;
+  GObject *filter_obj;
   int status = ML_ERROR_NONE;
-  gchar *pipeline_desc = NULL;
+  GTensorFilterSingleClass *klass;
   ml_tensors_info_s *in_tensors_info, *out_tensors_info;
   bool available = false;
   bool valid = false;
+  GError * error;
 
   check_feature_state ();
 
@@ -260,9 +391,6 @@ ml_single_open (ml_single_h * single, const char *model,
   /* init null */
   *single = NULL;
 
-  if ((status = ml_initialize_gstreamer ()) != ML_ERROR_NONE)
-    return status;
-
   in_tensors_info = (ml_tensors_info_s *) input_info;
   out_tensors_info = (ml_tensors_info_s *) output_info;
 
@@ -278,12 +406,17 @@ ml_single_open (ml_single_h * single, const char *model,
     return ML_ERROR_INVALID_PARAMETER;
   }
 
-  /* 1. Determine nnfw */
+  /**
+   * 1. Determine nnfw
+   */
   if ((status = ml_validate_model_file (model, &nnfw)) != ML_ERROR_NONE)
     return status;
 
-  /* 2. Determine hw */
-  /** @todo Now the param hw is ignored. (Supposed CPU only) Support others later. */
+  /**
+   * 2. Determine hw
+   * @todo Now the param hw is ignored.
+   * (Supposed CPU only) Support others later.
+   */
   status = ml_check_nnfw_availability (nnfw, hw, &available);
   if (status != ML_ERROR_NONE)
     return status;
@@ -293,117 +426,112 @@ ml_single_open (ml_single_h * single, const char *model,
     return ML_ERROR_NOT_SUPPORTED;
   }
 
-  /* 3. Construct a pipeline */
-  /* Set the pipeline desc with nnfw. */
+  /** Create ml_single object */
+  single_h = g_new0 (ml_single, 1);
+  if (single_h == NULL) {
+    ml_loge ("Failed to allocate the single handle.");
+    return ML_ERROR_OUT_OF_MEMORY;
+  }
+
+  single_h->magic = ML_SINGLE_MAGIC;
+
+  single_h->filter = g_object_new (G_TYPE_TENSOR_FILTER_SINGLE, NULL);
+  single_h->timeout = SINGLE_DEFAULT_TIMEOUT;
+  if (single_h->filter == NULL) {
+    status = ML_ERROR_INVALID_PARAMETER;
+    goto error;
+  }
+  filter_obj = G_OBJECT (single_h->filter);
+
+  /**
+   * 3. Construct a pipeline
+   * Set the pipeline desc with nnfw.
+   */
+  single_h->nnfw = nnfw;
   switch (nnfw) {
     case ML_NNFW_TYPE_CUSTOM_FILTER:
-      pipeline_desc =
-          g_strdup_printf
-          ("appsrc name=srcx ! tensor_filter name=filterx framework=custom model=%s ! appsink name=sinkx sync=false",
-          model);
+      g_object_set (filter_obj, "framework", "custom", "model", model, NULL);
       break;
     case ML_NNFW_TYPE_TENSORFLOW_LITE:
       /* We can get the tensor meta from tf-lite model. */
-      pipeline_desc =
-          g_strdup_printf
-          ("appsrc name=srcx ! tensor_filter name=filterx framework=tensorflow-lite model=%s ! appsink name=sinkx sync=false",
-          model);
+      g_object_set (filter_obj, "framework", "tensorflow-lite",
+          "model", model, NULL);
       break;
     case ML_NNFW_TYPE_TENSORFLOW:
       if (in_tensors_info && out_tensors_info) {
-        gchar *in_option, *out_option;
-
-        in_option = ml_single_get_filter_option_string (TRUE, in_tensors_info);
-        out_option = ml_single_get_filter_option_string (FALSE, out_tensors_info);
-
-        pipeline_desc =
-            g_strdup_printf
-            ("appsrc name=srcx ! tensor_filter name=filterx framework=tensorflow model=%s %s %s ! appsink name=sinkx sync=false",
-            model, in_option, out_option);
-
-        g_free (in_option);
-        g_free (out_option);
+        status = ml_single_set_inout_tensors_info (filter_obj, TRUE,
+            in_tensors_info);
+        if (status != ML_ERROR_NONE)
+          goto error;
+
+        status = ml_single_set_inout_tensors_info (filter_obj, FALSE,
+            out_tensors_info);
+        if (status != ML_ERROR_NONE)
+          goto error;
+
+        g_object_set (filter_obj, "framework", "tensorflow",
+            "model", model, NULL);
       } else {
-        ml_loge ("To run the pipeline with tensorflow model, input and output information should be initialized.");
-        return ML_ERROR_INVALID_PARAMETER;
+        ml_loge ("To run the pipeline with tensorflow model, \
+            input and output information should be initialized.");
+        status = ML_ERROR_INVALID_PARAMETER;
+        goto error;
       }
       break;
     default:
       /** @todo Add other fw later. */
       ml_loge ("The given nnfw is not supported.");
-      return ML_ERROR_NOT_SUPPORTED;
-  }
-
-  status = ml_pipeline_construct (pipeline_desc, NULL, NULL, &pipe);
-  g_free (pipeline_desc);
-  if (status != ML_ERROR_NONE) {
-    /* Failed to construct pipeline. */
-    return status;
+      status = ML_ERROR_NOT_SUPPORTED;
+      goto error;
   }
 
   /* 4. Allocate */
-  single_h = g_new0 (ml_single, 1);
-  if (single_h == NULL) {
-    ml_loge ("Failed to allocate the single handle.");
-    ml_pipeline_destroy (pipe);
-    return ML_ERROR_OUT_OF_MEMORY;
-  }
-
-  pipe_h = (ml_pipeline *) pipe;
-  appsrc = gst_bin_get_by_name (GST_BIN (pipe_h->element), "srcx");
-  appsink = gst_bin_get_by_name (GST_BIN (pipe_h->element), "sinkx");
-  filter = gst_bin_get_by_name (GST_BIN (pipe_h->element), "filterx");
-
-  single_h->magic = ML_SINGLE_MAGIC;
-  single_h->pipe = pipe;
-  single_h->src = appsrc;
-  single_h->sink = appsink;
-  single_h->filter = filter;
-  single_h->timeout = SINGLE_DEFAULT_TIMEOUT;
-  single_h->clear_previous_buffer = FALSE;
   ml_tensors_info_initialize (&single_h->in_info);
   ml_tensors_info_initialize (&single_h->out_info);
-  g_mutex_init (&single_h->lock);
 
-  /* 5. Set in/out caps and metadata */
+  /* 5. Start the nnfw to get inout configurations if needed */
+  klass = g_type_class_peek (G_TYPE_TENSOR_FILTER_SINGLE);
+  if (!klass) {
+    status = ML_ERROR_INVALID_PARAMETER;
+    goto error;
+  }
+  if (klass->start (single_h->filter) == FALSE) {
+    status = ML_ERROR_INVALID_PARAMETER;
+    goto error;
+  }
+
+  /* 6. Set in/out configs and metadata */
   if (!ml_single_set_info_in_handle (single_h, TRUE, in_tensors_info)) {
     ml_loge ("The input tensor info is invalid.");
     status = ML_ERROR_INVALID_PARAMETER;
     goto error;
   }
 
-  caps = ml_tensors_info_get_caps (&single_h->in_info);
-  gst_app_src_set_caps (GST_APP_SRC (appsrc), caps);
-  gst_caps_unref (caps);
-
   if (!ml_single_set_info_in_handle (single_h, FALSE, out_tensors_info)) {
     ml_loge ("The output tensor info is invalid.");
     status = ML_ERROR_INVALID_PARAMETER;
     goto error;
   }
 
-  caps = ml_tensors_info_get_caps (&single_h->out_info);
-  gst_app_sink_set_caps (GST_APP_SINK (appsink), caps);
-  gst_caps_unref (caps);
+  g_mutex_init (&single_h->mutex);
+  g_cond_init (&single_h->cond);
+  single_h->state = IDLE;
+  single_h->ignore_output = FALSE;
 
-  /* set max buffer in appsink (default unlimited) and drop old buffer */
-  gst_app_sink_set_max_buffers (GST_APP_SINK (appsink), 1);
-  gst_app_sink_set_drop (GST_APP_SINK (appsink), TRUE);
-
-  /* 6. Start pipeline */
-  status = ml_pipeline_start (pipe);
-  if (status != ML_ERROR_NONE) {
-    /* Failed to construct pipeline. */
+  single_h->thread = g_thread_try_new (NULL, invoke_thread, (gpointer) single_h,
+      &error);
+  if (single_h->thread == NULL) {
+    ml_loge ("Failed to create the invoke thread, error: %s.", error->message);
+    g_clear_error (&error);
+    status = ML_ERROR_UNKNOWN;
     goto error;
   }
 
-error:
-  if (status != ML_ERROR_NONE) {
-    ml_single_close (single_h);
-  } else {
-    *single = single_h;
-  }
+  *single = single_h;
+  return ML_ERROR_NONE;
 
+error:
+  ml_single_close (single_h);
   return status;
 }
 
@@ -414,7 +542,6 @@ int
 ml_single_close (ml_single_h single)
 {
   ml_single *single_h;
-  int status;
 
   check_feature_state ();
 
@@ -425,17 +552,18 @@ ml_single_close (ml_single_h single)
 
   ML_SINGLE_GET_VALID_HANDLE_LOCKED (single_h, single, 1);
 
-  if (single_h->src) {
-    gst_object_unref (single_h->src);
-    single_h->src = NULL;
-  }
+  single_h->state = JOIN_REQUESTED;
+  g_cond_broadcast (&single_h->cond);
 
-  if (single_h->sink) {
-    gst_object_unref (single_h->sink);
-    single_h->sink = NULL;
-  }
+  ML_SINGLE_HANDLE_UNLOCK (single_h);
+  g_thread_join (single_h->thread);
 
+  /** locking ensures correctness with parallel calls on close */
   if (single_h->filter) {
+    GTensorFilterSingleClass *klass;
+    klass = g_type_class_peek (G_TYPE_TENSOR_FILTER_SINGLE);
+    if (klass)
+      klass->stop (single_h->filter);
     gst_object_unref (single_h->filter);
     single_h->filter = NULL;
   }
@@ -443,13 +571,11 @@ ml_single_close (ml_single_h single)
   ml_tensors_info_free (&single_h->in_info);
   ml_tensors_info_free (&single_h->out_info);
 
-  status = ml_pipeline_destroy (single_h->pipe);
-
-  ML_SINGLE_HANDLE_UNLOCK(single_h);
-  g_mutex_clear (&single_h->lock);
+  g_cond_clear (&single_h->cond);
+  g_mutex_clear (&single_h->mutex);
 
   g_free (single_h);
-  return status;
+  return ML_ERROR_NONE;
 }
 
 /**
@@ -460,12 +586,8 @@ ml_single_invoke (ml_single_h single,
     const ml_tensors_data_h input, ml_tensors_data_h * output)
 {
   ml_single *single_h;
-  ml_tensors_data_s *in_data, *result;
-  GstSample *sample = NULL;
-  GstBuffer *buffer;
-  GstMemory *mem;
-  GstMapInfo mem_info;
-  GstFlowReturn ret;
+  ml_tensors_data_s *in_data;
+  gint64 end_time;
   int i, status = ML_ERROR_NONE;
 
   check_feature_state ();
@@ -480,117 +602,61 @@ ml_single_invoke (ml_single_h single,
   in_data = (ml_tensors_data_s *) input;
   *output = NULL;
 
+  if (!single_h->filter || single_h->state >= JOIN_REQUESTED) {
+    ml_loge ("The given param is invalid, model is missing.");
+    status = ML_ERROR_INVALID_PARAMETER;
+    goto exit;
+  }
+
   /* Validate input data */
   if (in_data->num_tensors != single_h->in_info.num_tensors) {
-    ml_loge ("The given param input is invalid, different number of memory blocks.");
+    ml_loge ("The given param input is invalid, \
+        different number of memory blocks.");
     status = ML_ERROR_INVALID_PARAMETER;
-    goto done;
+    goto exit;
   }
 
   for (i = 0; i < in_data->num_tensors; i++) {
     size_t raw_size = ml_tensor_info_get_size (&single_h->in_info.info[i]);
 
     if (!in_data->tensors[i].tensor || in_data->tensors[i].size != raw_size) {
-      ml_loge ("The given param input is invalid, different size of memory block.");
+      ml_loge ("The given param input is invalid, \
+          different size of memory block.");
       status = ML_ERROR_INVALID_PARAMETER;
-      goto done;
-    }
-  }
-
-#ifdef ML_SINGLE_SUPPORT_TIMEOUT
-  /* Try to clear old buffer in appsink before pushing the buffer */
-  if (single_h->clear_previous_buffer) {
-    ml_logw ("Previous buffer was timed out, try to clear old data.");
-
-    sample = gst_app_sink_pull_sample (GST_APP_SINK (single_h->sink));
-    if (sample) {
-      gst_sample_unref (sample);
-      sample = NULL;
+      goto exit;
     }
-
-    single_h->clear_previous_buffer = FALSE;
-  }
-#endif
-
-  /* Push input buffer */
-  buffer = gst_buffer_new ();
-
-  for (i = 0; i < in_data->num_tensors; i++) {
-    mem = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
-        in_data->tensors[i].tensor, in_data->tensors[i].size, 0,
-        in_data->tensors[i].size, NULL, NULL);
-    gst_buffer_append_memory (buffer, mem);
-  }
-
-  ret = gst_app_src_push_buffer (GST_APP_SRC (single_h->src), buffer);
-  if (ret != GST_FLOW_OK) {
-    ml_loge ("Cannot push a buffer into source element.");
-    gst_buffer_unref (buffer);
-    status = ML_ERROR_STREAMS_PIPE;
-    goto done;
-  }
-
-  /* Try to get the result */
-#ifdef ML_SINGLE_SUPPORT_TIMEOUT
-  /* gst_app_sink_try_pull_sample() is available at gstreamer-1.10 */
-  sample =
-      gst_app_sink_try_pull_sample (GST_APP_SINK (single_h->sink), GST_MSECOND * single_h->timeout);
-#else
-  sample = gst_app_sink_pull_sample (GST_APP_SINK (single_h->sink));
-#endif
-
-  if (!sample) {
-    ml_loge ("Failed to get the result from sink element.");
-#ifdef ML_SINGLE_SUPPORT_TIMEOUT
-    single_h->clear_previous_buffer = TRUE;
-    status = ML_ERROR_TIMED_OUT;
-#else
-    status = ML_ERROR_UNKNOWN;
-#endif
-    goto done;
   }
 
-  /* Allocate output buffer */
-  status = ml_tensors_data_create (&single_h->out_info, output);
-  if (status != ML_ERROR_NONE) {
-    ml_loge ("Failed to allocate the memory block.");
-    goto done;
+  if (single_h->state != IDLE) {
+    status = ML_ERROR_TRY_AGAIN;
+    goto exit;
   }
 
-  result = (ml_tensors_data_s *) (*output);
-
-  /* Copy the result */
-  buffer = gst_sample_get_buffer (sample);
-  for (i = 0; i < result->num_tensors; i++) {
-    mem = gst_buffer_peek_memory (buffer, i);
-    gst_memory_map (mem, &mem_info, GST_MAP_READ);
+  single_h->input = input;
+  single_h->output = output;
+  single_h->state = RUNNING;
+  single_h->ignore_output = FALSE;
 
-    /* validate the output size */
-    if (mem_info.size <= result->tensors[i].size) {
-      memcpy (result->tensors[i].tensor, mem_info.data, mem_info.size);
-    } else {
-      /* invalid output size */
-      status = ML_ERROR_STREAMS_PIPE;
-    }
-
-    gst_memory_unmap (mem, &mem_info);
-
-    if (status != ML_ERROR_NONE)
-      break;
-  }
+  end_time = g_get_monotonic_time () +
+    single_h->timeout * G_TIME_SPAN_MILLISECOND;
 
-done:
-  if (sample)
-    gst_sample_unref (sample);
+  g_cond_broadcast (&single_h->cond);
+  if (g_cond_wait_until (&single_h->cond, &single_h->mutex, end_time)) {
+    status = single_h->status;
+  } else {
+    ml_logw ("Wait for invoke has timed out");
+    status = ML_ERROR_TIMED_OUT;
+    /** This is set to notify invoke_thread to not process if timedout */
+    single_h->ignore_output = TRUE;
 
-  /* free allocated data if error occurs */
-  if (status != ML_ERROR_NONE) {
-    if (*output != NULL) {
-      ml_tensors_data_destroy (*output);
-      *output = NULL;
+    /** Free if any output memory was allocated */
+    if (*single_h->output != NULL) {
+      ml_tensors_data_destroy ((ml_tensors_data_h) *single_h->output);
+      *single_h->output = NULL;
     }
   }
 
+exit:
   ML_SINGLE_HANDLE_UNLOCK (single_h);
   return status;
 }
@@ -621,9 +687,12 @@ ml_single_get_tensors_info (ml_single_h single, gboolean is_input,
   input_info = (ml_tensors_info_s *) (*info);
 
   if (is_input)
-    ml_tensors_info_clone (input_info, &single_h->in_info);
+    status = ml_tensors_info_clone (input_info, &single_h->in_info);
   else
-    ml_tensors_info_clone (input_info, &single_h->out_info);
+    status = ml_tensors_info_clone (input_info, &single_h->out_info);
+
+  if (status != ML_ERROR_NONE)
+    ml_tensors_info_destroy (input_info);
 
 exit:
   ML_SINGLE_HANDLE_UNLOCK (single_h);
@@ -631,7 +700,8 @@ exit:
 }
 
 /**
- * @brief Gets the information (tensor dimension, type, name and so on) of required input data for the given handle.
+ * @brief Gets the information of required input data for the given handle.
+ * @note information = (tensor dimension, type, name and so on)
  */
 int
 ml_single_get_input_info (ml_single_h single, ml_tensors_info_h * info)
@@ -640,7 +710,8 @@ ml_single_get_input_info (ml_single_h single, ml_tensors_info_h * info)
 }
 
 /**
- * @brief Gets the information (tensor dimension, type, name and so on) of output data for the given handle.
+ * @brief Gets the information of output data for the given handle.
+ * @note information = (tensor dimension, type, name and so on)
  */
 int
 ml_single_get_output_info (ml_single_h single, ml_tensors_info_h * info)
@@ -654,9 +725,7 @@ ml_single_get_output_info (ml_single_h single, ml_tensors_info_h * info)
 int
 ml_single_set_timeout (ml_single_h single, unsigned int timeout)
 {
-#ifdef ML_SINGLE_SUPPORT_TIMEOUT
   ml_single *single_h;
-  int status = ML_ERROR_NONE;
 
   check_feature_state ();
 
@@ -668,10 +737,7 @@ ml_single_set_timeout (ml_single_h single, unsigned int timeout)
   single_h->timeout = (guint) timeout;
 
   ML_SINGLE_HANDLE_UNLOCK (single_h);
-  return status;
-#else
-  return ML_ERROR_NOT_SUPPORTED;
-#endif
+  return ML_ERROR_NONE;
 }
 
 /**
@@ -679,5 +745,43 @@ ml_single_set_timeout (ml_single_h single, unsigned int timeout)
  */
 int ml_single_set_input_info (ml_single_h single, const ml_tensors_info_h info)
 {
-  return ML_ERROR_NOT_SUPPORTED;
+  ml_single *single_h;
+  GTensorFilterSingleClass *klass;
+  int status = ML_ERROR_NONE;
+  ml_tensors_info_s *in_info;
+  GstTensorsInfo gst_in_info, gst_out_info;
+  bool valid = false;
+
+  check_feature_state ();
+
+  if (!single || !info)
+    return ML_ERROR_INVALID_PARAMETER;
+
+  if (!ml_tensors_info_is_valid (info, valid))
+    return ML_ERROR_INVALID_PARAMETER;
+
+  ML_SINGLE_GET_VALID_HANDLE_LOCKED (single_h, single, 0);
+
+  in_info = (ml_tensors_info_s *) info;
+  switch (single_h->nnfw) {
+    case ML_NNFW_TYPE_TENSORFLOW_LITE:
+      ml_tensors_info_copy_from_ml (&gst_in_info, in_info);
+
+      klass = g_type_class_peek (G_TYPE_TENSOR_FILTER_SINGLE);
+      if (klass == NULL || klass->set_input_info (
+            single_h->filter, &gst_in_info, &gst_out_info) == FALSE) {
+        status = ML_ERROR_INVALID_PARAMETER;
+        goto exit;
+      }
+
+      ml_tensors_info_copy_from_gst (&single_h->in_info, &gst_in_info);
+      ml_tensors_info_copy_from_gst (&single_h->out_info, &gst_out_info);
+      break;
+    default:
+      status = ML_ERROR_NOT_SUPPORTED;
+  }
+
+exit:
+  ML_SINGLE_HANDLE_UNLOCK (single_h);
+  return status;
 }
index cc066cf..28f1c42 100644 (file)
@@ -42,13 +42,15 @@ NNSTREAMER_PLUGINS_SRCS := \
 
 # nnstreamer c-api
 NNSTREAMER_CAPI_INCLUDES := \
+    $(NNSTREAMER_ROOT)/gst \
     $(NNSTREAMER_CAPI_HOME)/include/platform \
     $(NNSTREAMER_CAPI_HOME)/include
 
 NNSTREAMER_CAPI_SRCS := \
     $(NNSTREAMER_CAPI_HOME)/src/nnstreamer-capi-pipeline.c \
     $(NNSTREAMER_CAPI_HOME)/src/nnstreamer-capi-single.c \
-    $(NNSTREAMER_CAPI_HOME)/src/nnstreamer-capi-util.c
+    $(NNSTREAMER_CAPI_HOME)/src/nnstreamer-capi-util.c \
+    $(NNSTREAMER_CAPI_HOME)/src/tensor_filter_single.c
 
 # filter tensorflow
 NNSTREAMER_FILTER_TF_SRCS := \
index dd0db47..a9cefba 100644 (file)
@@ -183,21 +183,6 @@ Requires:  capi-nnstreamer = %{version}-%{release}
 %description -n capi-nnstreamer-devel
 Developmental kit for Tizen Native NNStreamer API.
 
-%package -n capi-nnstreamer-single-new
-Summary:       Tizen Native new single-shot API for NNStreamer
-Group:         Multimedia/Framework
-Requires:      %{name} = %{version}-%{release}
-%description -n capi-nnstreamer-single-new
-Tizen Native new single-shot API wrapper for NNStreamer.
-You can construct a data stream pipeline with neural networks easily.
-
-%package -n capi-nnstreamer-single-new-devel
-Summary:       Tizen Native API Devel Kit for NNStreamer
-Group:         Multimedia/Framework
-Requires:      capi-nnstreamer-single-new = %{version}-%{release}
-%description -n capi-nnstreamer-single-new-devel
-Developmental kit for Tizen Native new single-shot NNStreamer API.
-
 %package -n nnstreamer-tizen-internal-capi-devel
 Summary:       Tizen internal API to construct the pipeline
 Group:         Multimedia/Framework
@@ -277,7 +262,6 @@ ninja -C build %{?_smp_mflags}
     ./tests/unittest_plugins --gst-plugin-path=. --gtest_output="xml:unittest_plugins.xml"
     ./tests/unittest_src_iio --gst-plugin-path=. --gtest_output="xml:unittest_src_iio.xml"
     ./tests/tizen_capi/unittest_tizen_capi --gst-plugin-path=. --gtest_output="xml:unittest_tizen_capi.xml"
-    ./tests/tizen_capi/unittest_tizen_capi_single_new --gst-plugin-path=. --gtest_output="xml:unittest_tizen_capi_single_new.xml"
 %if 0%{?enable_nnfw_r}
     ./tests/tizen_nnfw_runtime/unittest_nnfw_runtime_raw --gst-plugin-path=. --gtest_output="xml:unittest_nnfw_runtime_raw.xml"
 %endif
@@ -425,17 +409,6 @@ cp -r result %{buildroot}%{_datadir}/nnstreamer/unittest/
 %{_libdir}/pkgconfig/capi-nnstreamer.pc
 %{_libdir}/libcapi-nnstreamer.a
 
-%files -n capi-nnstreamer-single-new
-%manifest capi-nnstreamer.manifest
-%license LICENSE
-%{_libdir}/libcapi-nnstreamer-single-new.so
-
-%files -n capi-nnstreamer-single-new-devel
-%{_includedir}/nnstreamer/nnstreamer.h
-%{_includedir}/nnstreamer/nnstreamer-single.h
-%{_libdir}/pkgconfig/capi-nnstreamer-single-new.pc
-%{_libdir}/libcapi-nnstreamer-single-new.a
-
 %files -n nnstreamer-tizen-internal-capi-devel
 %{_includedir}/nnstreamer/nnstreamer-tizen-internal.h
 %endif
index ae252de..73529ef 100644 (file)
@@ -11,17 +11,3 @@ unittest_tizen_capi = executable('unittest_tizen_capi',
   install_dir: unittest_install_dir
 )
 test('unittest_tizen_capi', unittest_tizen_capi, args: ['--gst-plugin-path=../..'])
-
-tizen_apptest_deps = [
-  nnstreamer_capi_single_new_dep,
-  gtest_dep,
-  glib_dep
-]
-
-unittest_tizen_capi_single_new = executable('unittest_tizen_capi_single_new',
-  'unittest_tizen_capi.cpp',
-  dependencies: [tizen_apptest_deps],
-  install: get_option('install-test'),
-  install_dir: unittest_install_dir
-)
-test('unittest_tizen_capi_single_new', unittest_tizen_capi_single_new, args: ['--gst-plugin-path=../..'])