[Api/Tizen] base code to convert element in pipeline
authorJaeyun <jy1210.jung@samsung.com>
Tue, 13 Aug 2019 04:28:12 +0000 (13:28 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Sat, 7 Sep 2019 13:52:52 +0000 (22:52 +0900)
1. Add new file for Tizen dependent code.
2. Add base code to check predefined element name in pipeline description.

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

index 0138b18..b148252 100644 (file)
 #include "nnstreamer.h"
 #include "tensor_typedef.h"
 
-#define TAG_NAME "nnstreamer-capi"
-
-#define ML_INF_FEATURE_PATH "tizen.org/feature/machine_learning.inference"
-
+/* Tizen ML feature */
+#if defined (__TIZEN__)
 #define check_feature_state() \
   do { \
-    int feature_ret = ml_get_feature_enabled(); \
+    int feature_ret = ml_tizen_get_feature_enabled (); \
     if (ML_ERROR_NONE != feature_ret) \
       return feature_ret; \
   } while (0);
 
+#define set_feature_state(...) ml_tizen_set_feature_state(__VA_ARGS__)
+#define convert_tizen_element(...) ml_tizen_convert_element(__VA_ARGS__)
+#define get_tizen_resource(...) ml_tizen_get_resource(__VA_ARGS__)
+#define release_tizen_resource(...) ml_tizen_release_resource(__VA_ARGS__)
+#else
+#define check_feature_state()
+#define set_feature_state(...)
+#define convert_tizen_element(...) ML_ERROR_NONE
+#define get_tizen_resource(...) ML_ERROR_NONE
+#define release_tizen_resource(...)
+#endif
+
+#define TAG_NAME "nnstreamer-capi"
+
 #if defined(__TIZEN__)
   #include <dlog.h>
 
@@ -166,6 +178,14 @@ typedef struct {
 } pipeline_state_cb_s;
 
 /**
+ * @brief Internal data structure for the resource.
+ */
+typedef struct {
+  gchar *type; /**< resource type */
+  gpointer handle; /**< pointer to resource handle */
+} pipeline_resource_s;
+
+/**
  * @brief Internal private representation of pipeline handle.
  * @details This should not be exposed to applications
  */
@@ -175,6 +195,7 @@ struct _ml_pipeline {
   gulong signal_msg;      /**< The message signal (connected to bus) */
   GMutex lock;            /**< Lock for pipeline operations */
   GHashTable *namednodes; /**< hash table of "element"s. */
+  GHashTable *resources;  /**< hash table of resources to construct the pipeline */
   pipeline_state_cb_s state_cb; /**< Callback to notify the change of pipeline state */
 };
 
@@ -258,20 +279,37 @@ void ml_tensors_info_copy_from_ml (GstTensorsInfo *gst_info, const ml_tensors_in
 GstCaps * ml_tensors_info_get_caps (const ml_tensors_info_s *info);
 
 /**
+ * @brief Checks the availability of the plugin.
+ */
+int ml_check_plugin_availability (const char *plugin_name, const char *element_name);
+
+#if defined (__TIZEN__)
+/**
  * @brief Checks whether machine_learning.inference feature is enabled or not.
  */
-int ml_get_feature_enabled (void);
+int ml_tizen_get_feature_enabled (void);
 
 /**
  * @brief Set the feature status of machine_learning.inference.
  * This is only used for Unit test.
  */
-int ml_set_feature_status (int status);
+int ml_tizen_set_feature_state (int state);
 
 /**
- * @brief Checks the availability of the plugin.
+ * @brief Releases the resource handle of Tizen.
  */
-int ml_check_plugin_availability (const char *plugin_name, const char *element_name);
+void ml_tizen_release_resource (gpointer handle, const gchar * res_type);
+
+/**
+ * @brief Gets the resource handle of Tizen.
+ */
+int ml_tizen_get_resource (ml_pipeline_h pipe, const gchar * res_type);
+
+/**
+ * @brief Converts predefined element for Tizen.
+ */
+int ml_tizen_convert_element (ml_pipeline_h pipe, gchar ** result);
+#endif
 
 #ifdef __cplusplus
 }
index 33918b5..3184f48 100644 (file)
@@ -39,6 +39,22 @@ extern "C" {
  */
 
 /**
+ * @brief The virtual name to set the video source of camcorder in Tizen.
+ * @details If an application needs to access the camcorder to construct the pipeline, set the virtual name as a video source element.
+ *          Note that you have to add 'http://tizen.org/privilege/camera' into the manifest of your application.
+ * @since_tizen 5.5
+ */
+#define ML_TIZEN_CAM_VIDEO_SRC "tizencamvideosrc"
+
+/**
+ * @brief The virtual name to set the audio source of camcorder in Tizen.
+ * @details If an application needs to access the camcorder to construct the pipeline, set the virtual name as an audio source element.
+ *          Note that you have to add 'http://tizen.org/privilege/camera' into the manifest of your application.
+ * @since_tizen 5.5
+ */
+#define ML_TIZEN_CAM_AUDIO_SRC "tizencamaudiosrc"
+
+/**
  * @brief The maximum rank that NNStreamer supports with Tizen APIs.
  * @since_tizen 5.5
  */
index 6a1f09a..64e302f 100644 (file)
@@ -27,6 +27,10 @@ capi_main += join_paths(meson.current_source_dir(), 'src', 'nnstreamer-capi-pipe
 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')
 
+if get_option('enable-tizen')
+  capi_main += join_paths('src', 'nnstreamer-capi-tizen.c')
+endif
+
 capi_devel_main = []
 capi_devel_main += join_paths(meson.current_source_dir(), 'include', 'nnstreamer.h')
 capi_devel_main += join_paths(meson.current_source_dir(), 'include', 'nnstreamer-single.h')
@@ -36,6 +40,9 @@ tizen_deps = []
 if (get_option('enable-tizen'))
   message('CAPI is in Tizen mode')
   tizen_deps = [
+    dependency('mm-resource-manager'),
+    dependency('mm-camcorder'),
+    dependency('capi-privacy-privilege-manager'),
     dependency('capi-base-common'),
     dependency('capi-system-info'),
     dependency('dlog')
@@ -80,6 +87,10 @@ capi_single_new_main += join_paths(meson.current_source_dir(), 'src', 'nnstreame
 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
+
 nnstreamer_capi_single_new_lib = shared_library ('capi-nnstreamer-single-new',
   capi_single_new_main,
   dependencies: capi_deps,
index b4d575b..da11a99 100644 (file)
@@ -23,9 +23,6 @@
 
 #include <string.h>
 #include <glib.h>
-#include <glib-object.h>        /* Get GType from GObject Instances */
-#include <gmodule.h>
-
 #include <gst/gstbuffer.h>
 #include <gst/app/app.h>        /* To push data to pipeline */
 
@@ -333,6 +330,52 @@ cleanup_node (gpointer data)
 }
 
 /**
+ * @brief Private function to release the pipeline resources
+ */
+static void
+cleanup_resource (gpointer data)
+{
+  pipeline_resource_s *res = data;
+
+  /* check resource type and free data */
+  if (g_str_has_prefix (res->type, "tizen")) {
+    release_tizen_resource (res->handle, res->type);
+  }
+
+  g_free (res->type);
+  g_free (res);
+}
+
+/**
+ * @brief Converts predefined element in pipeline description.
+ */
+static int
+convert_element (ml_pipeline_h pipe, const gchar * description, gchar ** result)
+{
+  gchar *converted;
+  int status = ML_ERROR_NONE;
+
+  g_return_val_if_fail (pipe, ML_ERROR_INVALID_PARAMETER);
+  g_return_val_if_fail (description && result, ML_ERROR_INVALID_PARAMETER);
+
+  /* init null */
+  *result = NULL;
+
+  converted = g_strdup (description);
+
+  /* convert pre-defined element for Tizen */
+  status = convert_tizen_element (pipe, &converted);
+
+  if (status == ML_ERROR_NONE) {
+    *result = converted;
+  } else {
+    g_free (converted);
+  }
+
+  return status;
+}
+
+/**
  * @brief Construct the pipeline (more info in nnstreamer.h)
  */
 int
@@ -342,6 +385,7 @@ ml_pipeline_construct (const char *pipeline_description,
   GError *err = NULL;
   GstElement *pipeline;
   GstIterator *it = NULL;
+  gchar *description = NULL;
   int status = ML_ERROR_NONE;
 
   ml_pipeline *pipe_h;
@@ -376,7 +420,17 @@ ml_pipeline_construct (const char *pipeline_description,
   pipe_h->namednodes =
       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, cleanup_node);
 
-  pipeline = gst_parse_launch (pipeline_description, &err);
+  pipe_h->resources =
+      g_hash_table_new_full (g_str_hash, g_str_equal, g_free, cleanup_resource);
+
+  /* convert predefined element and launch the pipeline */
+  status = convert_element ((ml_pipeline_h) pipe_h, pipeline_description, &description);
+  if (status != ML_ERROR_NONE)
+    goto failed;
+
+  pipeline = gst_parse_launch (description, &err);
+  g_free (description);
+
   if (pipeline == NULL || err) {
     if (err) {
       ml_loge ("Cannot parse and launch the given pipeline = [%s], %s",
@@ -531,6 +585,7 @@ ml_pipeline_destroy (ml_pipeline_h pipe)
   }
 
   g_hash_table_remove_all (p->namednodes);
+  g_hash_table_remove_all (p->resources);
 
   if (p->element) {
     /* Pause the pipeline if it's playing */
@@ -562,9 +617,10 @@ ml_pipeline_destroy (ml_pipeline_h pipe)
     gst_object_unref (p->element);
   }
 
-  /* Destroy registered callback handles */
+  /* Destroy registered callback handles and resources */
   g_hash_table_destroy (p->namednodes);
-  p->namednodes = NULL;
+  g_hash_table_destroy (p->resources);
+  p->namednodes = p->resources = NULL;
 
   g_mutex_unlock (&p->lock);
   g_mutex_clear (&p->lock);
@@ -612,6 +668,7 @@ ml_pipeline_start (ml_pipeline_h pipe)
 {
   ml_pipeline *p = pipe;
   GstStateChangeReturn scret;
+  int status = ML_ERROR_NONE;
 
   check_feature_state ();
 
@@ -619,13 +676,30 @@ ml_pipeline_start (ml_pipeline_h pipe)
     return ML_ERROR_INVALID_PARAMETER;
 
   g_mutex_lock (&p->lock);
-  scret = gst_element_set_state (p->element, GST_STATE_PLAYING);
-  g_mutex_unlock (&p->lock);
 
+  /* check the resources when starting the pipeline */
+  if (g_hash_table_size (p->resources)) {
+    GHashTableIter iter;
+    gpointer key, value;
+
+    /* iterate all handle and acquire res if released */
+    g_hash_table_iter_init (&iter, p->resources);
+    while (g_hash_table_iter_next (&iter, &key, &value)) {
+      if (g_str_has_prefix (key, "tizen")) {
+        status = get_tizen_resource (pipe, key);
+        if (status != ML_ERROR_NONE)
+          goto done;
+      }
+    }
+  }
+
+  scret = gst_element_set_state (p->element, GST_STATE_PLAYING);
   if (scret == GST_STATE_CHANGE_FAILURE)
-    return ML_ERROR_STREAMS_PIPE;
+    status = ML_ERROR_STREAMS_PIPE;
 
-  return ML_ERROR_NONE;
+done:
+  g_mutex_unlock (&p->lock);
+  return status;
 }
 
 /**
diff --git a/api/capi/src/nnstreamer-capi-tizen.c b/api/capi/src/nnstreamer-capi-tizen.c
new file mode 100644 (file)
index 0000000..2027261
--- /dev/null
@@ -0,0 +1,770 @@
+/**
+ * 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-tizen.c
+ * @date 26 August 2019
+ * @brief NNStreamer/C-API Tizen dependent functions.
+ * @see        https://github.com/nnsuite/nnstreamer
+ * @author MyungJoo Ham <myungjoo.ham@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#if !defined (__TIZEN__)
+#error "This file can be included only in Tizen."
+#endif
+
+#include <glib.h>
+
+#include <system_info.h>
+#include <privacy_privilege_manager.h>
+#include <mm_resource_manager.h>
+#include <mm_camcorder.h>
+
+#include "nnstreamer.h"
+#include "nnstreamer-capi-private.h"
+#include "nnstreamer_plugin_api.h"
+
+/* Tizen multimedia framework */
+/* Defined in mm_camcorder_configure.h */
+
+/**
+ * @brief Structure to parse ini file for mmfw elements.
+ */
+typedef struct _type_int
+{
+  char *name;
+  int value;
+} type_int;
+
+/**
+ * @brief Structure to parse ini file for mmfw elements.
+ */
+typedef struct _type_string
+{
+  char *name;
+  char *value;
+} type_string;
+
+/**
+ * @brief Structure to parse ini file for mmfw elements.
+ */
+typedef struct _type_element
+{
+  char *name;
+  char *element_name;
+  type_int **value_int;
+  int count_int;
+  type_string **value_string;
+  int count_string;
+} type_element;
+
+/**
+ * @brief Structure to parse ini file for mmfw elements.
+ */
+typedef struct _conf_info
+{
+  int count;
+  void **detail_info;
+} conf_info;
+
+/**
+ * @brief Structure to parse ini file for mmfw elements.
+ */
+typedef struct _camera_conf
+{
+  int type;
+  conf_info **info;
+} camera_conf;
+
+#define MMFW_CONFIG_MAIN_FILE "mmfw_camcorder.ini"
+
+extern int
+_mmcamcorder_conf_get_info (int type, char *ConfFile, camera_conf ** configure_info);
+
+extern void
+_mmcamcorder_conf_release_info (camera_conf ** configure_info);
+
+extern int
+_mmcamcorder_conf_get_element (camera_conf * configure_info, int category, char *name, type_element ** element);
+
+extern int
+_mmcamcorder_conf_get_value_element_name (type_element * element, char **value);
+
+/**
+ * @brief Internal structure for tizen mm framework.
+ */
+typedef struct
+{
+  gboolean invalid; /**< flag to indicate rm handle is valid */
+  mm_resource_manager_h rm_h; /**< rm handle */
+  GHashTable *res_handles; /**< hash table of resource handles */
+} tizen_mm_handle_s;
+
+/**
+ * @brief Tizen resouce type for multimedia.
+ */
+#define TIZEN_RES_MM "tizen_res_mm"
+
+/**
+ * @brief Tizen ML feature.
+ */
+#define ML_INF_FEATURE_PATH "tizen.org/feature/machine_learning.inference"
+
+/**
+ * @brief Tizen Privilege Camera (See https://www.tizen.org/privilege)
+ */
+#define TIZEN_PRIVILEGE_CAMERA "http://tizen.org/privilege/camera"
+
+/**
+ * @brief Internal struct to control tizen feature support (machine_learning.inference).
+ * -1: Not checked yet, 0: Not supported, 1: Supported
+ */
+typedef struct
+{
+  GMutex mutex;
+  int feature_state;
+} feature_info_s;
+
+static feature_info_s *feature_info = NULL;
+
+/**
+ * @brief Internal function to initialize feature state.
+ */
+static void
+ml_tizen_initialize_feature_state (void)
+{
+  if (feature_info == NULL) {
+    feature_info = g_new0 (feature_info_s, 1);
+    g_assert (feature_info);
+
+    g_mutex_init (&feature_info->mutex);
+    feature_info->feature_state = -1;
+  }
+}
+
+/**
+ * @brief Set the feature status of machine_learning.inference.
+ */
+int
+ml_tizen_set_feature_state (int state)
+{
+  ml_tizen_initialize_feature_state ();
+  g_mutex_lock (&feature_info->mutex);
+
+  /**
+   * Update feature status
+   * -1: Not checked yet, 0: Not supported, 1: Supported
+   */
+  feature_info->feature_state = state;
+
+  g_mutex_unlock (&feature_info->mutex);
+  return ML_ERROR_NONE;
+}
+
+/**
+ * @brief Checks whether machine_learning.inference feature is enabled or not.
+ */
+int
+ml_tizen_get_feature_enabled (void)
+{
+  int ret;
+  int feature_enabled;
+
+  ml_tizen_initialize_feature_state ();
+
+  g_mutex_lock (&feature_info->mutex);
+  feature_enabled = feature_info->feature_state;
+  g_mutex_unlock (&feature_info->mutex);
+
+  if (0 == feature_enabled) {
+    ml_loge ("machine_learning.inference NOT supported");
+    return ML_ERROR_NOT_SUPPORTED;
+  } else if (-1 == feature_enabled) {
+    bool ml_inf_supported = false;
+    ret = system_info_get_platform_bool (ML_INF_FEATURE_PATH, &ml_inf_supported);
+    if (0 == ret) {
+      if (false == ml_inf_supported) {
+        ml_loge ("machine_learning.inference NOT supported");
+        ml_tizen_set_feature_state (0);
+        return ML_ERROR_NOT_SUPPORTED;
+      }
+
+      ml_tizen_set_feature_state (1);
+    } else {
+      switch (ret) {
+        case SYSTEM_INFO_ERROR_INVALID_PARAMETER:
+          ml_loge ("failed to get feature value because feature key is not vaild");
+          ret = ML_ERROR_NOT_SUPPORTED;
+          break;
+
+        case SYSTEM_INFO_ERROR_IO_ERROR:
+          ml_loge ("failed to get feature value because of input/output error");
+          ret = ML_ERROR_NOT_SUPPORTED;
+          break;
+
+        case SYSTEM_INFO_ERROR_PERMISSION_DENIED:
+          ml_loge ("failed to get feature value because of permission denied");
+          ret = ML_ERROR_PERMISSION_DENIED;
+          break;
+
+        default:
+          ml_loge ("failed to get feature value because of unknown error");
+          ret = ML_ERROR_NOT_SUPPORTED;
+          break;
+      }
+      return ret;
+    }
+  }
+
+  return ML_ERROR_NONE;
+}
+
+/**
+ * @brief Function to check tizen privilege.
+ */
+static int
+ml_tizen_check_privilege (const gchar * privilege)
+{
+  int status = ML_ERROR_NONE;
+  ppm_check_result_e priv_result;
+  int err;
+
+  /* check privilege */
+  err = ppm_check_permission (privilege, &priv_result);
+  if (err == PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE &&
+      priv_result == PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ALLOW) {
+    /* privilege allowed */
+  } else {
+    ml_loge ("Failed to check the privilege %s.", privilege);
+    status = ML_ERROR_PERMISSION_DENIED;
+  }
+
+  return status;
+}
+
+/**
+ * @brief Function to get key string of resource type to handle hash table.
+ */
+static gchar *
+ml_tizen_mm_res_get_key_string (mm_resource_manager_res_type_e type)
+{
+  gchar *res_key = NULL;
+
+  switch (type) {
+    case MM_RESOURCE_MANAGER_RES_TYPE_VIDEO_DECODER:
+      res_key = g_strdup ("tizen_mm_res_video_decoder");
+      break;
+    case MM_RESOURCE_MANAGER_RES_TYPE_VIDEO_OVERLAY:
+      res_key = g_strdup ("tizen_mm_res_video_overlay");
+      break;
+    case MM_RESOURCE_MANAGER_RES_TYPE_CAMERA:
+      res_key = g_strdup ("tizen_mm_res_camera");
+      break;
+    case MM_RESOURCE_MANAGER_RES_TYPE_VIDEO_ENCODER:
+      res_key = g_strdup ("tizen_mm_res_video_encoder");
+      break;
+    case MM_RESOURCE_MANAGER_RES_TYPE_RADIO:
+      res_key = g_strdup ("tizen_mm_res_radio");
+      break;
+    default:
+      ml_logw ("The resource type %d is invalid.", type);
+      break;
+  }
+
+  return res_key;
+}
+
+/**
+ * @brief Function to get resource type from key string to handle hash table.
+ */
+static mm_resource_manager_res_type_e
+ml_tizen_mm_res_get_type (const gchar * res_key)
+{
+  mm_resource_manager_res_type_e type = MM_RESOURCE_MANAGER_RES_TYPE_MAX;
+
+  g_return_val_if_fail (res_key, type);
+
+  if (g_str_equal (res_key, "tizen_mm_res_video_decoder")) {
+    type = MM_RESOURCE_MANAGER_RES_TYPE_VIDEO_DECODER;
+  } else if (g_str_equal (res_key, "tizen_mm_res_video_overlay")) {
+    type = MM_RESOURCE_MANAGER_RES_TYPE_VIDEO_OVERLAY;
+  } else if (g_str_equal (res_key, "tizen_mm_res_camera")) {
+    type = MM_RESOURCE_MANAGER_RES_TYPE_CAMERA;
+  } else if (g_str_equal (res_key, "tizen_mm_res_video_encoder")) {
+    type = MM_RESOURCE_MANAGER_RES_TYPE_VIDEO_ENCODER;
+  } else if (g_str_equal (res_key, "tizen_mm_res_radio")) {
+    type = MM_RESOURCE_MANAGER_RES_TYPE_RADIO;
+  }
+
+  return type;
+}
+
+/**
+ * @brief Callback to be called from mm resource manager.
+ */
+static int
+ml_tizen_mm_res_release_cb (mm_resource_manager_h rm,
+    mm_resource_manager_res_h resource_h, void *user_data)
+{
+  ml_pipeline *p;
+  pipeline_resource_s *res;
+  tizen_mm_handle_s *mm_handle;
+  mm_resource_manager_res_info_s res_info;
+  int err;
+
+  g_return_val_if_fail (user_data, FALSE);
+
+  p = (ml_pipeline *) user_data;
+  g_mutex_lock (&p->lock);
+
+  res = (pipeline_resource_s *) g_hash_table_lookup (p->resources, TIZEN_RES_MM);
+  if (!res) {
+    /* rm handle is not registered or removed */
+    goto done;
+  }
+
+  mm_handle = (tizen_mm_handle_s *) res->handle;
+  if (!mm_handle) {
+    /* supposed the rm handle is already released */
+    goto done;
+  }
+
+  /* pause pipeline */
+  gst_element_set_state (p->element, GST_STATE_PAUSED);
+
+  /* release resource handle */
+  err = mm_resource_manager_get_resource_info (rm, resource_h, &res_info);
+  if (err == MM_RESOURCE_MANAGER_ERROR_NONE) {
+    gchar *res_key = ml_tizen_mm_res_get_key_string (res_info.type);
+
+    if (res_key) {
+      pipeline_resource_s *mm_res;
+
+      mm_res = (pipeline_resource_s *) g_hash_table_lookup (mm_handle->res_handles, res_key);
+      if (mm_res && mm_res->handle) {
+        mm_resource_manager_mark_for_release (mm_handle->rm_h, mm_res->handle);
+        mm_resource_manager_commit (mm_handle->rm_h);
+        mm_res->handle = NULL;
+      }
+
+      g_free (res_key);
+    }
+  } else {
+    ml_loge ("Failed to get resource info in release callback.");
+    mm_handle->invalid = TRUE;
+  }
+
+done:
+  g_mutex_unlock (&p->lock);
+  return FALSE;
+}
+
+/**
+ * @brief Callback to be called from mm resource manager.
+ */
+static void
+ml_tizen_mm_res_status_cb (mm_resource_manager_h rm,
+    mm_resource_manager_status_e status, void *user_data)
+{
+  ml_pipeline *p;
+  pipeline_resource_s *res;
+  tizen_mm_handle_s *mm_handle;
+
+  g_return_if_fail (user_data);
+
+  p = (ml_pipeline *) user_data;
+  g_mutex_lock (&p->lock);
+
+  res = (pipeline_resource_s *) g_hash_table_lookup (p->resources, TIZEN_RES_MM);
+  if (!res) {
+    /* rm handle is not registered or removed */
+    goto done;
+  }
+
+  mm_handle = (tizen_mm_handle_s *) res->handle;
+  if (!mm_handle) {
+    /* supposed the rm handle is already released */
+    goto done;
+  }
+
+  switch (status) {
+    case MM_RESOURCE_MANAGER_STATUS_DISCONNECTED:
+      /* pause pipeline, rm handle should be released */
+      gst_element_set_state (p->element, GST_STATE_PAUSED);
+      mm_handle->invalid = TRUE;
+      break;
+    default:
+      break;
+  }
+
+done:
+  g_mutex_unlock (&p->lock);
+}
+
+/**
+ * @brief Function to get the handle of resource type.
+ */
+static int
+ml_tizen_mm_res_get_handle (mm_resource_manager_h rm,
+    mm_resource_manager_res_type_e res_type, gpointer * handle)
+{
+  mm_resource_manager_res_h rm_res_h;
+  int status = ML_ERROR_STREAMS_PIPE;
+  int err;
+
+  /* add resource handle */
+  err = mm_resource_manager_mark_for_acquire (rm, res_type,
+      MM_RESOURCE_MANAGER_RES_VOLUME_FULL, &rm_res_h);
+  if (err != MM_RESOURCE_MANAGER_ERROR_NONE)
+    goto rm_error;
+
+  err = mm_resource_manager_commit (rm);
+  if (err != MM_RESOURCE_MANAGER_ERROR_NONE)
+    goto rm_error;
+
+  *handle = rm_res_h;
+  status = ML_ERROR_NONE;
+
+rm_error:
+  return status;
+}
+
+/**
+ * @brief Function to release the resource handle of tizen mm resource manager.
+ */
+static void
+ml_tizen_mm_res_release (gpointer handle, gboolean destroy)
+{
+  tizen_mm_handle_s *mm_handle;
+
+  g_return_if_fail (handle);
+
+  mm_handle = (tizen_mm_handle_s *) handle;
+
+  /* release res handles */
+  if (g_hash_table_size (mm_handle->res_handles)) {
+    GHashTableIter iter;
+    gpointer key, value;
+    gboolean marked = FALSE;
+
+    g_hash_table_iter_init (&iter, mm_handle->res_handles);
+    while (g_hash_table_iter_next (&iter, &key, &value)) {
+      pipeline_resource_s *mm_res = value;
+
+      if (mm_res->handle) {
+        mm_resource_manager_mark_for_release (mm_handle->rm_h, mm_res->handle);
+        mm_res->handle = NULL;
+        marked = TRUE;
+      }
+
+      if (destroy)
+        g_free (mm_res->type);
+    }
+
+    if (marked)
+      mm_resource_manager_commit (mm_handle->rm_h);
+  }
+
+  mm_resource_manager_set_status_cb (mm_handle->rm_h, NULL, NULL);
+  mm_resource_manager_destroy (mm_handle->rm_h);
+  mm_handle->rm_h = NULL;
+
+  mm_handle->invalid = FALSE;
+
+  if (destroy) {
+    g_hash_table_remove_all (mm_handle->res_handles);
+    g_free (mm_handle);
+  }
+}
+
+/**
+ * @brief Function to initialize mm resource manager.
+ */
+static int
+ml_tizen_mm_res_initialize (ml_pipeline_h pipe)
+{
+  ml_pipeline *p;
+  pipeline_resource_s *res;
+  tizen_mm_handle_s *mm_handle;
+  int status = ML_ERROR_STREAMS_PIPE;
+
+  p = (ml_pipeline *) pipe;
+
+  res = (pipeline_resource_s *) g_hash_table_lookup (p->resources, TIZEN_RES_MM);
+
+  /* register new resource handle of tizen mmfw */
+  if (!res) {
+    res = g_new0 (pipeline_resource_s, 1);
+    if (!res)
+      goto rm_error;
+
+    res->type = g_strdup (TIZEN_RES_MM);
+    g_hash_table_insert (p->resources, g_strdup (TIZEN_RES_MM), res);
+  }
+
+  mm_handle = (tizen_mm_handle_s *) res->handle;
+  if (!mm_handle) {
+    mm_handle = g_new0 (tizen_mm_handle_s, 1);
+    if (!mm_handle)
+      goto rm_error;
+
+    mm_handle->res_handles =
+        g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+    /* set mm handle */
+    res->handle = mm_handle;
+  }
+
+  status = ML_ERROR_NONE;
+
+rm_error:
+  return status;
+}
+
+/**
+ * @brief Function to acquire the resource from mm resource manager.
+ */
+static int
+ml_tizen_mm_res_acquire (ml_pipeline_h pipe,
+    mm_resource_manager_res_type_e res_type)
+{
+  ml_pipeline *p;
+  pipeline_resource_s *res;
+  tizen_mm_handle_s *mm_handle;
+  gchar *res_key;
+  int status = ML_ERROR_STREAMS_PIPE;
+  int err;
+
+  p = (ml_pipeline *) pipe;
+
+  res = (pipeline_resource_s *) g_hash_table_lookup (p->resources, TIZEN_RES_MM);
+  if (!res)
+    goto rm_error;
+
+  mm_handle = (tizen_mm_handle_s *) res->handle;
+  if (!mm_handle)
+    goto rm_error;
+
+  /* check invalid handle */
+  if (mm_handle->invalid)
+    ml_tizen_mm_res_release (mm_handle, FALSE);
+
+  /* create rm handle */
+  if (!mm_handle->rm_h) {
+    mm_resource_manager_h rm_h;
+
+    err = mm_resource_manager_create (MM_RESOURCE_MANAGER_APP_CLASS_MEDIA,
+        ml_tizen_mm_res_release_cb, pipe, &rm_h);
+    if (err != MM_RESOURCE_MANAGER_ERROR_NONE)
+      goto rm_error;
+
+    /* add state change callback */
+    err = mm_resource_manager_set_status_cb (rm_h, ml_tizen_mm_res_status_cb, pipe);
+    if (err != MM_RESOURCE_MANAGER_ERROR_NONE) {
+      mm_resource_manager_destroy (rm_h);
+      goto rm_error;
+    }
+
+    mm_handle->rm_h = rm_h;
+  }
+
+  /* acquire resource */
+  if (res_type == MM_RESOURCE_MANAGER_RES_TYPE_MAX) {
+    GHashTableIter iter;
+    gpointer key, value;
+
+    /* iterate all handle and acquire res if released */
+    g_hash_table_iter_init (&iter, mm_handle->res_handles);
+    while (g_hash_table_iter_next (&iter, &key, &value)) {
+      pipeline_resource_s *mm_res = value;
+
+      if (!mm_res->handle) {
+        mm_resource_manager_res_type_e type;
+
+        type = ml_tizen_mm_res_get_type (mm_res->type);
+        if (type != MM_RESOURCE_MANAGER_RES_TYPE_MAX) {
+          status = ml_tizen_mm_res_get_handle (mm_handle->rm_h, type, &mm_res->handle);
+          if (status != ML_ERROR_NONE)
+            goto rm_error;
+        }
+      }
+    }
+  } else {
+    res_key = ml_tizen_mm_res_get_key_string (res_type);
+    if (res_key) {
+      pipeline_resource_s *mm_res;
+
+      mm_res = (pipeline_resource_s *) g_hash_table_lookup (mm_handle->res_handles, res_key);
+      if (!mm_res) {
+        mm_res = g_new0 (pipeline_resource_s, 1);
+        g_assert (mm_res);
+
+        mm_res->type = g_strdup (res_key);
+        g_hash_table_insert (mm_handle->res_handles, g_strdup (res_key), mm_res);
+      }
+
+      g_free (res_key);
+
+      if (!mm_res->handle) {
+        status = ml_tizen_mm_res_get_handle (mm_handle->rm_h, res_type, &mm_res->handle);
+        if (status != ML_ERROR_NONE)
+          goto rm_error;
+      }
+    }
+  }
+
+  /* done */
+  status = ML_ERROR_NONE;
+
+rm_error:
+  return status;
+}
+
+/**
+ * @brief Gets element name from mm conf and replaces element.
+ */
+static int
+ml_tizen_mm_replace_element (camera_conf * conf, gint category,
+    const gchar * name, const gchar * what, gchar ** description)
+{
+  type_element *element = NULL;
+  gchar *src_name = NULL;
+  guint changed = 0;
+
+  _mmcamcorder_conf_get_element (conf, category, (char *) name, &element);
+  _mmcamcorder_conf_get_value_element_name (element, &src_name);
+
+  if (!src_name) {
+    ml_loge ("Failed to get the name of %s.", name);
+    return ML_ERROR_STREAMS_PIPE;
+  }
+
+  *description = replace_string (*description, what, src_name, " !", &changed);
+  if (changed > 1) {
+    /* allow one src in the pipeline */
+    ml_loge ("Cannot parse duplicated src node.");
+    return ML_ERROR_STREAMS_PIPE;
+  }
+
+  return ML_ERROR_NONE;
+}
+
+/**
+ * @brief Converts predefined mmfw element.
+ */
+static int
+ml_tizen_mm_convert_element (ml_pipeline_h pipe, gchar ** result)
+{
+  gchar *converted;
+  gchar *video_src, *audio_src;
+  camera_conf *cam_conf = NULL;
+  int status = ML_ERROR_STREAMS_PIPE;
+  int err;
+
+  converted = *result;
+
+  video_src = g_strstr_len (converted, -1, ML_TIZEN_CAM_VIDEO_SRC);
+  audio_src = g_strstr_len (converted, -1, ML_TIZEN_CAM_AUDIO_SRC);
+
+  /* replace src element */
+  if (video_src || audio_src) {
+    status = ml_tizen_check_privilege (TIZEN_PRIVILEGE_CAMERA);
+    if (status != ML_ERROR_NONE)
+      goto mm_error;
+
+    /* read ini, type CONFIGURE_TYPE_MAIN */
+    err = _mmcamcorder_conf_get_info (0, (char *) MMFW_CONFIG_MAIN_FILE, &cam_conf);
+    if (err != MM_ERROR_NONE || !cam_conf) {
+      ml_loge ("Failed to load conf %s.", MMFW_CONFIG_MAIN_FILE);
+      goto mm_error;
+    }
+
+    if (video_src) {
+      /* category CONFIGURE_CATEGORY_MAIN_VIDEO_INPUT */
+      status = ml_tizen_mm_replace_element (cam_conf, 1, "VideosrcElement",
+          ML_TIZEN_CAM_VIDEO_SRC, &converted);
+      if (status != ML_ERROR_NONE)
+        goto mm_error;
+    }
+
+    if (audio_src) {
+      /* category CONFIGURE_CATEGORY_MAIN_AUDIO_INPUT */
+      status = ml_tizen_mm_replace_element (cam_conf, 2, "AudiosrcElement",
+          ML_TIZEN_CAM_AUDIO_SRC, &converted);
+      if (status != ML_ERROR_NONE)
+        goto mm_error;
+    }
+
+    /* initialize rm handle */
+    status = ml_tizen_mm_res_initialize (pipe);
+    if (status != ML_ERROR_NONE)
+      goto mm_error;
+
+    /* get the camera resource using mm resource manager */
+    status = ml_tizen_mm_res_acquire (pipe, MM_RESOURCE_MANAGER_RES_TYPE_CAMERA);
+    if (status != ML_ERROR_NONE)
+      goto mm_error;
+  }
+
+  /* done */
+  status = ML_ERROR_NONE;
+
+mm_error:
+  if (cam_conf)
+    _mmcamcorder_conf_release_info (&cam_conf);
+
+  return status;
+}
+
+/**
+ * @brief Releases the resource handle of Tizen.
+ */
+void
+ml_tizen_release_resource (gpointer handle, const gchar * res_type)
+{
+  if (g_str_equal (res_type, TIZEN_RES_MM)) {
+    ml_tizen_mm_res_release (handle, TRUE);
+  }
+}
+
+/**
+ * @brief Gets the resource handle of Tizen.
+ */
+int
+ml_tizen_get_resource (ml_pipeline_h pipe, const gchar * res_type)
+{
+  int status = ML_ERROR_NONE;
+
+  if (g_str_equal (res_type, TIZEN_RES_MM)) {
+    /* iterate all handle and acquire res if released */
+    status = ml_tizen_mm_res_acquire (pipe, MM_RESOURCE_MANAGER_RES_TYPE_MAX);
+  }
+
+  return status;
+}
+
+/**
+ * @brief Converts predefined element for Tizen.
+ */
+int
+ml_tizen_convert_element (ml_pipeline_h pipe, gchar ** result)
+{
+  int status;
+
+  /* convert predefined element of mulitmedia fw */
+  status = ml_tizen_mm_convert_element (pipe, result);
+
+  return status;
+}
index 241dee6..4890e13 100644 (file)
 #include "nnstreamer_plugin_api_filter.h"
 #include "nnstreamer_conf.h"
 
-#if defined(__TIZEN__)
-  #include <system_info.h>
-#endif
-
-/**
- * @brief Internal struct to control tizen feature support (machine_learning.inference).
- * -1: Not checked yet, 0: Not supported, 1: Supported
- */
-typedef struct
-{
-  GMutex mutex;
-  int feature_status;
-} feature_info_s;
-
-static feature_info_s *feature_info = NULL;
-
-/**
- * @brief Internal function to initialize feature status.
- */
-static void
-ml_initialize_feature_status (void)
-{
-  if (feature_info == NULL) {
-    feature_info = g_new0 (feature_info_s, 1);
-    g_assert (feature_info);
-
-    g_mutex_init (&feature_info->mutex);
-    feature_info->feature_status = -1;
-  }
-}
-
 /**
  * @brief Allocates a tensors information handle with default value.
  */
@@ -858,86 +827,6 @@ done:
 }
 
 /**
- * @brief Checks whether machine_learning.inference feature is enabled or not.
- */
-int
-ml_get_feature_enabled (void)
-{
-  ml_initialize_feature_status ();
-
-#if defined(__TIZEN__)
-  {
-    int ret;
-    int feature_enabled;
-
-    g_mutex_lock (&feature_info->mutex);
-    feature_enabled = feature_info->feature_status;
-    g_mutex_unlock (&feature_info->mutex);
-
-    if (0 == feature_enabled) {
-      ml_loge ("machine_learning.inference NOT supported");
-      return ML_ERROR_NOT_SUPPORTED;
-    } else if (-1 == feature_enabled) {
-      bool ml_inf_supported = false;
-      ret = system_info_get_platform_bool(ML_INF_FEATURE_PATH, &ml_inf_supported);
-      if (0 == ret) {
-        if (false == ml_inf_supported) {
-          ml_loge ("machine_learning.inference NOT supported");
-          ml_set_feature_status (0);
-          return ML_ERROR_NOT_SUPPORTED;
-        }
-
-        ml_set_feature_status (1);
-      } else {
-        switch (ret) {
-          case SYSTEM_INFO_ERROR_INVALID_PARAMETER:
-            ml_loge ("failed to get feature value because feature key is not vaild");
-            ret = ML_ERROR_NOT_SUPPORTED;
-            break;
-
-          case SYSTEM_INFO_ERROR_IO_ERROR:
-            ml_loge ("failed to get feature value because of input/output error");
-            ret = ML_ERROR_NOT_SUPPORTED;
-            break;
-
-          case SYSTEM_INFO_ERROR_PERMISSION_DENIED:
-            ml_loge ("failed to get feature value because of permission denied");
-            ret = ML_ERROR_PERMISSION_DENIED;
-            break;
-
-          default:
-            ml_loge ("failed to get feature value because of unknown error");
-            ret = ML_ERROR_NOT_SUPPORTED;
-            break;
-        }
-        return ret;
-      }
-    }
-  }
-#endif
-  return ML_ERROR_NONE;
-}
-
-/**
- * @brief Set the feature status of machine_learning.inference.
- */
-int
-ml_set_feature_status (int status)
-{
-  ml_initialize_feature_status ();
-  g_mutex_lock (&feature_info->mutex);
-
-  /**
-   * Update feature status
-   * -1: Not checked yet, 0: Not supported, 1: Supported
-   */
-  feature_info->feature_status = status;
-
-  g_mutex_unlock (&feature_info->mutex);
-  return ML_ERROR_NONE;
-}
-
-/**
  * @brief Checks the availability of the plugin.
  */
 int
index e51e38c..908dc00 100644 (file)
@@ -64,6 +64,9 @@ BuildRequires: lcov
 # BuildRequires:       taos-ci-unittest-coverage-assessment
 %endif
 %if %{with tizen}
+BuildRequires: pkgconfig(mm-resource-manager)
+BuildRequires: pkgconfig(mm-camcorder)
+BuildRequires: pkgconfig(capi-privacy-privilege-manager)
 BuildRequires: pkgconfig(capi-system-info)
 BuildRequires: pkgconfig(capi-base-common)
 BuildRequires: pkgconfig(dlog)
index 4769eb9..f1997f1 100644 (file)
@@ -23,6 +23,46 @@ typedef struct
   gboolean playing;
 } TestPipeState;
 
+#if defined (__TIZEN__)
+/**
+ * @brief Test NNStreamer pipeline construct with Tizen cam
+ * @details Failure case to check permission (camera privilege)
+ */
+TEST (nnstreamer_capi_construct_destruct, tizen_cam_fail_01_n)
+{
+  ml_pipeline_h handle;
+  gchar *pipeline;
+  int status;
+
+  pipeline = g_strdup_printf ("%s ! videoconvert ! videoscale ! video/x-raw,format=RGB,width=320,height=240 ! tensor_converter ! tensor_sink",
+      ML_TIZEN_CAM_VIDEO_SRC);
+
+  status = ml_pipeline_construct (pipeline, NULL, NULL, &handle);
+  EXPECT_EQ (status, ML_ERROR_PERMISSION_DENIED);
+
+  g_free (pipeline);
+}
+
+/**
+ * @brief Test NNStreamer pipeline construct with Tizen cam
+ * @details Failure case to check permission (camera privilege)
+ */
+TEST (nnstreamer_capi_construct_destruct, tizen_cam_fail_02_n)
+{
+  ml_pipeline_h handle;
+  gchar *pipeline;
+  int status;
+
+  pipeline = g_strdup_printf ("%s ! audioconvert ! audio/x-raw,format=S16LE,rate=16000 ! tensor_converter ! tensor_sink",
+      ML_TIZEN_CAM_AUDIO_SRC);
+
+  status = ml_pipeline_construct (pipeline, NULL, NULL, &handle);
+  EXPECT_EQ (status, ML_ERROR_PERMISSION_DENIED);
+
+  g_free (pipeline);
+}
+#endif /* __TIZEN__ */
+
 /**
  * @brief Test NNStreamer pipeline construct & destruct
  */
@@ -1818,11 +1858,11 @@ main (int argc, char **argv)
   testing::InitGoogleTest (&argc, argv);
 
   /* ignore tizen feature status while running the testcases */
-  ml_set_feature_status (1);
+  set_feature_state (1);
 
   result = RUN_ALL_TESTS ();
 
-  ml_set_feature_status (-1);
+  set_feature_state (-1);
 
   return result;
 }