Converters: support external subplugins
authorMyungJoo Ham <myungjoo.ham@samsung.com>
Fri, 6 Dec 2019 02:38:00 +0000 (11:38 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Fri, 13 Dec 2019 04:08:23 +0000 (13:08 +0900)
Allow to add external converters in run-time as subplugins.

Policies to be determined:
- Build & integration policies for external converters.
- Would we allow a few "basic" external converters available by default? (without dlopen)
- Tizen feature keys and privileges along with "NOT SUPPORTED" handling.
- Which profiles would use which of external converters?

TODO:
- Implement multiple external subplugins for unit tests

Changes in V2:
- Typo fixed (comments)

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
gst/nnstreamer/nnstreamer_conf.c
gst/nnstreamer/nnstreamer_conf.h
gst/nnstreamer/nnstreamer_plugin_api.h
gst/nnstreamer/nnstreamer_plugin_api_converter.h [new file with mode: 0644]
gst/nnstreamer/nnstreamer_subplugin.c
gst/nnstreamer/nnstreamer_subplugin.h
gst/nnstreamer/tensor_common.c
gst/nnstreamer/tensor_converter/tensor_converter.c
gst/nnstreamer/tensor_converter/tensor_converter.h
gst/nnstreamer/tensor_typedef.h
nnstreamer.ini.in

index c49ec44..49997ac 100644 (file)
@@ -32,6 +32,7 @@ static const gchar *subplugin_prefixes[] = {
   [NNSCONF_PATH_DECODERS] = NNSTREAMER_PREFIX_DECODER,
   [NNSCONF_PATH_CUSTOM_FILTERS] = NNSTREAMER_PREFIX_CUSTOMFILTERS,
   [NNSCONF_PATH_EASY_CUSTOM_FILTERS] = NNSTREAMER_PREFIX_CUSTOMFILTERS, /**< Same as Custom Filters */
+  [NNSCONF_PATH_CONVERTERS] = NNSTREAMER_PREFIX_CONVERTER,
   [NNSCONF_PATH_END] = NULL
 };
 
@@ -51,6 +52,7 @@ typedef struct
   gchar *pathFILTERS[CONF_SOURCES];        /**< directory paths for FILTERS */
   gchar *pathDECODERS[CONF_SOURCES];       /**< directory paths for DECODERS */
   gchar *pathCUSTOM_FILTERS[CONF_SOURCES]; /**< directory paths for CUSTOM FILTERS */
+  gchar *pathCONVERTERS[CONF_SOURCES]; /**< directory paths for CONVERTERS */
 
   /*************************************************
    * Processed Values                              *
@@ -58,10 +60,12 @@ typedef struct
   gchar **filesFILTERS;        /**< Null terminated list of full filepaths for FILTERS */
   gchar **filesDECODERS;;      /**< Null terminated list of full filepaths for DECODERS */
   gchar **filesCUSTOM_FILTERS; /**< Null terminated list of full filepaths for CUSTOM FILTERS */
+  gchar **filesCONVERTERS;     /**< Null terminated list of full filepaths for CONVERTERS */
 
   gchar **basenameFILTERS;        /**< Null terminated list of basenames for FILTERS */
   gchar **basenameDECODERS;;      /**< Null terminated list of basenames for DECODERS */
   gchar **basenameCUSTOM_FILTERS; /**< Null terminated list of basenames for CUSTOM FILTERS */
+  gchar **basenameCONVERTERS;     /**< Null terminated list of basenames for CONVERTERS */
 } confdata;
 
 static confdata conf = { 0 };
@@ -191,6 +195,10 @@ _get_subplugin_with_type (nnsconf_type_path type, gchar *** basename,
       vstr = conf.basenameCUSTOM_FILTERS;
       vstrFull = conf.filesCUSTOM_FILTERS;
       break;
+    case NNSCONF_PATH_CONVERTERS:
+      vstr = conf.basenameCONVERTERS;
+      vstrFull = conf.filesCONVERTERS;
+      break;
     default:
       /* unknown type */
       g_critical ("Failed to get sub-plugins, unknown sub-plugin type.");
@@ -288,14 +296,17 @@ nnsconf_loadconf (gboolean force_reload)
       g_free (conf.pathFILTERS[i]);
       g_free (conf.pathDECODERS[i]);
       g_free (conf.pathCUSTOM_FILTERS[i]);
+      g_free (conf.pathCONVERTERS[i]);
     }
 
     g_strfreev (conf.filesFILTERS);
     g_strfreev (conf.filesDECODERS);
     g_strfreev (conf.filesCUSTOM_FILTERS);
+    g_strfreev (conf.filesCONVERTERS);
     g_strfreev (conf.basenameFILTERS);
     g_strfreev (conf.basenameDECODERS);
     g_strfreev (conf.basenameCUSTOM_FILTERS);
+    g_strfreev (conf.basenameCONVERTERS);
 
     /* init with 0 */
     memset (&conf, 0, sizeof (confdata));
@@ -343,6 +354,8 @@ nnsconf_loadconf (gboolean force_reload)
         g_key_file_get_string (key_file, "decoder", "decoders", NULL);
     conf.pathCUSTOM_FILTERS[1] =
         g_key_file_get_string (key_file, "filter", "customfilters", NULL);
+    conf.pathCONVERTERS[1] =
+        g_key_file_get_string (key_file, "converter", "converters", NULL);
   }
 
   /* Read from env variables. */
@@ -351,12 +364,14 @@ nnsconf_loadconf (gboolean force_reload)
     conf.pathDECODERS[0] = _strdup_getenv (NNSTREAMER_ENVVAR_DECODERS);
     conf.pathCUSTOM_FILTERS[0] =
         _strdup_getenv (NNSTREAMER_ENVVAR_CUSTOMFILTERS);
+    conf.pathCONVERTERS[0] = _strdup_getenv (NNSTREAMER_ENVVAR_CONVERTERS);
   }
 
   /* Strdup the hardcoded */
   conf.pathFILTERS[2] = g_strdup (NNSTREAMER_FILTERS);
   conf.pathDECODERS[2] = g_strdup (NNSTREAMER_DECODERS);
   conf.pathCUSTOM_FILTERS[2] = g_strdup (NNSTREAMER_CUSTOM_FILTERS);
+  conf.pathCONVERTERS[2] = g_strdup (NNSTREAMER_CONVERTERS);
 
   /* Fill in conf.files* */
   _fill_in_vstr (&conf.filesFILTERS, &conf.basenameFILTERS, conf.pathFILTERS,
@@ -365,6 +380,8 @@ nnsconf_loadconf (gboolean force_reload)
       NNSCONF_PATH_DECODERS);
   _fill_in_vstr (&conf.filesCUSTOM_FILTERS, &conf.basenameCUSTOM_FILTERS,
       conf.pathCUSTOM_FILTERS, NNSCONF_PATH_CUSTOM_FILTERS);
+  _fill_in_vstr (&conf.filesCONVERTERS, &conf.basenameCONVERTERS,
+      conf.pathCONVERTERS, NNSCONF_PATH_CONVERTERS);
 
   conf.loaded = TRUE;
   return TRUE;
index 6ac8836..a7d77c6 100644 (file)
@@ -58,6 +58,7 @@ G_BEGIN_DECLS
 #define NNSTREAMER_ENVVAR_FILTERS       "NNSTREAMER_FILTERS"
 #define NNSTREAMER_ENVVAR_DECODERS      "NNSTREAMER_DECODERS"
 #define NNSTREAMER_ENVVAR_CUSTOMFILTERS "NNSTREAMER_CUSTOMFILTERS"
+#define NNSTREAMER_ENVVAR_CONVERTERS   "NNSTREAMER_CONVERTERS"
 
 /* Internal Hardcoded Values */
 #define NNSTREAMER_DEFAULT_CONF_FILE    "/etc/nnstreamer.ini"
@@ -67,6 +68,7 @@ G_BEGIN_DECLS
 #define NNSTREAMER_FILTERS              "/usr/lib/nnstreamer/filters/"
 #define NNSTREAMER_DECODERS             "/usr/lib/nnstreamer/decoders/"
 #define NNSTREAMER_CUSTOM_FILTERS       "/usr/lib/nnstreamer/customfilters/"
+#define NNSTREAMER_CONVERTERS          "/usr/lib/nnstreamer/converters/"
 /**
  *  Note that users still can place their custom filters anywhere if they
  * designate them with the full path.
@@ -76,6 +78,7 @@ G_BEGIN_DECLS
 #define NNSTREAMER_PREFIX_DECODER      "libnnstreamer_decoder_"
 #define NNSTREAMER_PREFIX_FILTER       "libnnstreamer_filter_"
 #define NNSTREAMER_PREFIX_CUSTOMFILTERS        ""
+#define NNSTREAMER_PREFIX_CONVERTER    ""
 /* Custom filter does not have prefix */
 
 /* struct for sub-plugins info (name and full path) */
@@ -90,6 +93,7 @@ typedef enum {
   NNSCONF_PATH_DECODERS,
   NNSCONF_PATH_CUSTOM_FILTERS,
   NNSCONF_PATH_EASY_CUSTOM_FILTERS,
+  NNSCONF_PATH_CONVERTERS,
 
   NNSCONF_PATH_END,
 } nnsconf_type_path;
index 6d654cc..c92b30c 100644 (file)
@@ -211,7 +211,7 @@ gst_tensor_config_is_equal (const GstTensorConfig * c1,
 /**
  * @brief Get media type from structure
  * @param structure structure to be interpreted
- * @return corresponding media type (returns _NNS_MEDIA_END for unsupported type)
+ * @return corresponding media type (returns _NNS_MEDIA_INVALID for unsupported type)
  */
 extern media_type
 gst_tensor_media_type_from_structure (const GstStructure * structure);
diff --git a/gst/nnstreamer/nnstreamer_plugin_api_converter.h b/gst/nnstreamer/nnstreamer_plugin_api_converter.h
new file mode 100644 (file)
index 0000000..3ed9123
--- /dev/null
@@ -0,0 +1,93 @@
+/**
+ * NNStreamer API for Tensor_Decoder Sub-Plugins
+ * Copyright (C) 2019 MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * 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_plugin_api_converter.h
+ * @date  09 Dec 2019
+ * @brief Mandatory APIs for NNStreamer Converter sub-plugins (Need Gst Devel)
+ * @see https://github.com/nnsuite/nnstreamer
+ * @author  MyungJoo Ham <myungjoo.ham@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+#ifndef __NNS_PLUGIN_API_CONVERTER_H__
+#define __NNS_PLUGIN_API_CONVERTER_H__
+
+#include "tensor_typedef.h"
+#include <gst/gst.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/***********************************************
+* External Converters                          *
+************************************************/
+
+/**
+ * @brief Converter's subplugin implementation.
+ */
+struct _NNStreamerExternalConverter {
+  const char *media_type_name;
+
+  /** 1. chain func, data handling. */
+  GstBuffer * (*convert) (GstTensorConverter * self,
+      const GstBuffer * buf, gsize * frame_size, guint * frames_in);
+      /**< Convert the given input stream to tensor/tensors stream.
+       * @param[in/out] self A pointer designating "this".
+       * @param[in] buf The input stream buffer
+       * @param[out] frame_size The size of each frame (output buffer)
+       * @param[out] frames_in The number of frames in the given input buffer.
+       * @retval Return inbuf if the data is to be kept untouched.
+       * @retval Retrun a new GstBuf if the data is to be modified.
+       */
+
+  /** 2. parse_caps (type conv, input(media) to output(tensor)) */
+  gboolean (*get_caps) (GstTensorConverter * self, const GstStructure * st,
+      GstTensorConfig * config);
+      /**< Set the tensor config structure from the given stream frame
+       * @param[in/out] self A pointer designating "this".
+       * @param[in] st The input (original/media data) stream's metadata
+       * @param[out] config The output (tensor/tensors) emtadata
+       */
+  /** 3. query_cap (tpye conf, output(tensor) to input(media)) */
+  gboolean (*query_caps) (GstTensorConverter * self,
+      const GstTensorConfig *config, GstStructure *st);
+      /**< Filters (narrows down) the GstCap (st) with the given config.
+       * @param[in/out] self A pointer designating "this".
+       * @param[in] config The config of output tensor/tensors
+       * @param[in/out] st The gstcap of input to be filtered with config.
+       */
+};
+
+
+/**
+ * @brief Converter's sub-plugin should call this function to register itself.
+ * @param[in] ex Converter sub-plugin to be registered.
+ * @return TRUE if registered. FALSE is failed or duplicated.
+ */
+extern int
+registerExternalConverter (NNStreamerExternalConverter *ex);
+
+/**
+ * @brief Converter's sub-plugin may call this to unregister itself.
+ * @param[in] prefix The name of converter sub-plugin.
+ */
+extern void
+unregisterExternalConverter (const char *prefix);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __NNS_PLUGIN_API_CONVERTER_H__ */
index 2b85232..84c6477 100644 (file)
@@ -51,6 +51,21 @@ _spdata_destroy (gpointer _data)
   g_free (data);
 }
 
+typedef enum
+{
+  NNS_SEARCH_FILENAME,
+  NNS_SEARCH_GETALL,
+  NNS_SEARCH_NO_OP,
+} subpluginSearchLogic;
+
+static subpluginSearchLogic searchAlgorithm[] = {
+  [NNS_SUBPLUGIN_FILTER] = NNS_SEARCH_FILENAME,
+  [NNS_SUBPLUGIN_DECODER] = NNS_SEARCH_FILENAME,
+  [NNS_EASY_CUSTOM_FILTER] = NNS_SEARCH_FILENAME,
+  [NNS_SUBPLUGIN_CONVERTER] = NNS_SEARCH_GETALL,
+  [NNS_SUBPLUGIN_END] = NNS_SEARCH_NO_OP,
+};
+
 /** @brief Public function defined in the header */
 const void *
 get_subplugin (subpluginType type, const char *name)
@@ -69,9 +84,32 @@ get_subplugin (subpluginType type, const char *name)
         _spdata_destroy);
 
   table = subplugins[type];
-  data = g_hash_table_lookup (table, name);
 
-  if (data == NULL) {
+  if (searchAlgorithm[type] == NNS_SEARCH_GETALL) {
+    nnsconf_type_path conf_type = (nnsconf_type_path) type;
+    subplugin_info_s info;
+    guint i;
+    guint ret = nnsconf_get_subplugin_info (conf_type, &info);
+
+    for (i = 0; i < ret; i++) {
+      const gchar *fullpath = info.paths[i];
+
+      dlerror ();
+      G_UNLOCK (splock);
+      handle = dlopen (fullpath, RTLD_NOW);
+      /* If this is a correct subplugin, it will register itself */
+      G_LOCK (splock);
+      if (NULL == handle) {
+        g_critical ("Cannot dlopen %s with error %s.", fullpath, dlerror ());
+        continue;
+      }
+    }
+
+    searchAlgorithm[type] = NNS_SEARCH_NO_OP;
+  }
+
+  data = g_hash_table_lookup (table, name);
+  if (data == NULL && searchAlgorithm[type] == NNS_SEARCH_FILENAME) {
     /** Search and register if found with the conf */
     nnsconf_type_path conf_type = (nnsconf_type_path) type;
     const gchar *fullpath = nnsconf_get_fullpath (name, conf_type);
@@ -127,6 +165,7 @@ register_subplugin (subpluginType type, const char *name, const void *data)
     case NNS_SUBPLUGIN_FILTER:
     case NNS_SUBPLUGIN_DECODER:
     case NNS_EASY_CUSTOM_FILTER:
+    case NNS_SUBPLUGIN_CONVERTER:
       break;
     default:
       /* unknown sub-plugin type */
index 16a3e9d..b0d68ec 100644 (file)
@@ -39,6 +39,7 @@ typedef enum {
   NNS_SUBPLUGIN_FILTER = NNSCONF_PATH_FILTERS,
   NNS_SUBPLUGIN_DECODER = NNSCONF_PATH_DECODERS,
   NNS_EASY_CUSTOM_FILTER = NNSCONF_PATH_EASY_CUSTOM_FILTERS,
+  NNS_SUBPLUGIN_CONVERTER = NNSCONF_PATH_CONVERTERS,
 
   NNS_SUBPLUGIN_END,
 } subpluginType;
index c1cdbeb..d258753 100644 (file)
@@ -64,7 +64,7 @@ static const guint tensor_element_size[] = {
 /**
  * @brief Get media type from structure
  * @param structure structure to be interpreted
- * @return corresponding media type (returns _NNS_MEDIA_END for unsupported type)
+ * @return corresponding media type (returns _NNS_MEDIA_INVALID for unsupported type)
  */
 media_type
 gst_tensor_media_type_from_structure (const GstStructure * structure)
@@ -73,7 +73,7 @@ gst_tensor_media_type_from_structure (const GstStructure * structure)
 
   name = gst_structure_get_name (structure);
 
-  g_return_val_if_fail (name != NULL, _NNS_MEDIA_END);
+  g_return_val_if_fail (name != NULL, _NNS_MEDIA_INVALID);
 
   if (g_str_has_prefix (name, "video/")) {
     return _NNS_VIDEO;
@@ -92,7 +92,7 @@ gst_tensor_media_type_from_structure (const GstStructure * structure)
   }
 
   /* unknown or unsupported type */
-  return _NNS_MEDIA_END;
+  return _NNS_MEDIA_INVALID;
 }
 
 /**
index eb617e0..cf5833c 100644 (file)
@@ -22,6 +22,8 @@
  * @see                https://github.com/nnsuite/nnstreamer
  * @author     MyungJoo Ham <myungjoo.ham@samsung.com>
  * @bug                No known bugs except for NYI items
+ * @todo        For flatbuffers, support other/tensors with properties
+ * @todo        Subplugins are not tested, yet.
  */
 
 /**
@@ -45,6 +47,8 @@
 #include <string.h>
 #include "tensor_converter.h"
 #include "converter-media-info.h"
+#include <nnstreamer_subplugin.h>
+#include <nnstreamer_plugin_api_converter.h>
 
 /**
  * @brief Macro for debug mode.
@@ -92,6 +96,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_tensor_converter_debug);
 
 /**
  * @brief tensor_converter properties
+ * @todo For flatbuffers, support other/tensors.
  */
 enum
 {
@@ -145,6 +150,9 @@ static GstCaps *gst_tensor_converter_query_caps (GstTensorConverter * self,
 static gboolean gst_tensor_converter_parse_caps (GstTensorConverter * self,
     const GstCaps * caps);
 
+static const NNStreamerExternalConverter *findExternalConverter (const char
+    *media_type_name);
+
 /**
  * @brief Initialize the tensor_converter's class.
  */
@@ -290,7 +298,7 @@ gst_tensor_converter_init (GstTensorConverter * self)
   self->silent = DEFAULT_SILENT;
   self->set_timestamp = DEFAULT_SET_TIMESTAMP;
   self->frames_per_tensor = DEFAULT_FRAMES_PER_TENSOR;
-  self->in_media_type = _NNS_MEDIA_END;
+  self->in_media_type = _NNS_MEDIA_INVALID;
   self->frame_size = 0;
   self->remove_padding = FALSE;
   gst_tensor_info_init (&self->tensor_info);
@@ -687,6 +695,19 @@ gst_tensor_converter_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
       g_assert ((buf_size % frame_size) == 0);
       frames_in = buf_size / frame_size;
       break;
+    case _NNS_MEDIA_PLUGINS:
+    {
+      if (self->externalConverter == NULL ||
+          self->externalConverter->convert == NULL)
+        return GST_FLOW_NOT_SUPPORTED;
+      inbuf = self->externalConverter->convert (self, buf, &frame_size,
+          &frames_in);
+
+      g_assert (inbuf != NULL);
+      g_assert (frame_size > 0);
+      g_assert ((buf_size % frame_size) == 0);
+      break;
+    }
     default:
       GST_ERROR_OBJECT (self, "Unsupported type %d\n", self->in_media_type);
       g_assert (0);
@@ -1304,6 +1325,29 @@ gst_tensor_converter_query_caps (GstTensorConverter * self, GstPad * pad,
                 }
               }
               break;
+            case _NNS_MEDIA_INVALID:   /* this could be MEDIA_PLUGIN */
+            {
+              const gchar *name = gst_structure_get_name (st);
+              const NNStreamerExternalConverter *ex;
+              gboolean ret;
+
+              if (name == NULL)
+                break;
+              ex = findExternalConverter (name);
+              if (ex == NULL)
+                break;
+
+              /** @todo What if this is inconsistent with self->ex? */
+
+              g_assert (ex->query_caps);
+              ret = ex->query_caps (self, &config, st);
+              if (ret == FALSE) {
+                GST_ERROR_OBJECT (self,
+                    "Failed to filter GstCap structure with the given config");
+              }
+
+              break;
+            }
             default:
               /* do nothing for text and octet stream */
               break;
@@ -1357,6 +1401,7 @@ gst_tensor_converter_parse_caps (GstTensorConverter * self,
 
   structure = gst_caps_get_structure (caps, 0);
   in_type = gst_tensor_media_type_from_structure (structure);
+  self->externalConverter = NULL;
 
   switch (in_type) {
     case _NNS_VIDEO:
@@ -1467,8 +1512,29 @@ gst_tensor_converter_parse_caps (GstTensorConverter * self,
       break;
     }
     default:
+    {
+      /* if found, configure in_mdeia_type = _NNS_MEDIA_PLUGINS */
+      const gchar *name = gst_structure_get_name (structure);
+      if (name != NULL) {
+        const NNStreamerExternalConverter *ex = findExternalConverter (name);
+        if (ex != NULL) {
+          in_type = _NNS_MEDIA_PLUGINS;
+          self->externalConverter = ex;
+
+          if (NULL == ex->get_caps || !ex->get_caps (self, structure, &config)) {
+            GST_ERROR_OBJECT (self,
+                "Failed to get tensor info from %s. Check the given options.",
+                name);
+            g_critical ("Please set the options property correctly.\n");
+            self->externalConverter = NULL;
+            return FALSE;
+          }
+          break;
+        }
+      }
       GST_ERROR_OBJECT (self, "Unsupported type %d\n", in_type);
       return FALSE;
+    }
   }
 
   /** set the number of frames in dimension */
@@ -1495,3 +1561,30 @@ gst_tensor_converter_parse_caps (GstTensorConverter * self,
   self->tensor_config = config;
   return TRUE;
 }
+
+/**
+ * @brief Converter's external subplugins should call this at init.
+ */
+int
+registerExternalConverter (NNStreamerExternalConverter * ex)
+{
+  return register_subplugin (NNS_SUBPLUGIN_CONVERTER, ex->media_type_name, ex);
+}
+
+/**
+ * @brief Converter's external subplugins should call this at exit.
+ */
+void
+unregisterExternalConverter (const char *media_type_name)
+{
+  unregister_subplugin (NNS_SUBPLUGIN_CONVERTER, media_type_name);
+}
+
+/**
+ * @brief Internal static function to find registered subplugins.
+ */
+static const NNStreamerExternalConverter *
+findExternalConverter (const char *media_type_name)
+{
+  return get_subplugin (NNS_SUBPLUGIN_CONVERTER, media_type_name);
+}
index 889f485..fd9dcd8 100644 (file)
@@ -53,6 +53,7 @@ G_BEGIN_DECLS
 
 typedef struct _GstTensorConverter GstTensorConverter;
 typedef struct _GstTensorConverterClass GstTensorConverterClass;
+typedef struct _NNStreamerExternalConverter NNStreamerExternalConverter;
 
 /**
  * @brief Internal data structure for tensor_converter instances.
@@ -72,6 +73,9 @@ struct _GstTensorConverter
   GstAdapter *adapter; /**< adapt incoming media stream */
 
   media_type in_media_type; /**< incoming media type */
+  const NNStreamerExternalConverter *externalConverter;
+      /**< used if in_media_type == _NNS_MEDIA_PLUGINS */
+
   gsize frame_size; /**< size of one frame */
   gboolean remove_padding; /**< If true, zero-padding must be removed */
   gboolean tensor_configured; /**< True if already successfully configured tensor metadata */
@@ -96,6 +100,7 @@ struct _GstTensorConverterClass
  */
 GType gst_tensor_converter_get_type (void);
 
+
 G_END_DECLS
 
 #endif /** __GST_TENSOR_CONVERTER_H__ */
index fb4fe41..ab8b8f9 100644 (file)
@@ -103,15 +103,18 @@ typedef enum _nns_tensor_type
  *
  * This is realted with media input stream to other/tensor.
  * There is no restrictions for the outputs.
+ *
+ * In order to prevent enum-mix issues between device profiles,
+ * we explicitly define numbers for each enum type.
  */
 typedef enum _nns_media_type
 {
+  _NNS_MEDIA_INVALID = -1, /**< Uninitialized */
   _NNS_VIDEO = 0, /**< supposedly video/x-raw */
-  _NNS_AUDIO, /**< supposedly audio/x-raw */
-  _NNS_TEXT, /**< supposedly text/x-raw */
-  _NNS_OCTET, /**< supposedly application/octet-stream */
-
-  _NNS_MEDIA_END, /**< End Marker */
+  _NNS_AUDIO = 1, /**< supposedly audio/x-raw */
+  _NNS_TEXT = 2, /**< supposedly text/x-raw */
+  _NNS_OCTET = 3, /**< supposedly application/octet-stream */
+  _NNS_MEDIA_PLUGINS = 0x1000, /**< external converters */
 } media_type;
 
 /**
index 1789909..911df3d 100644 (file)
@@ -11,6 +11,9 @@ customfilters=@SUBPLUGIN_INSTALL_PREFIX@/customfilters/
 [decoder]
 decoders=@SUBPLUGIN_INSTALL_PREFIX@/decoders/
 
+[converter]
+converters=@SUBPLUGIN_INSTALL_PREFIX@/converters/
+
 # Set 1 or True if you want to use NNAPI with tensorflow-lite, which enables to use NNAPI backend, which may use GPU or NPU/TPU.
 [tensorflowlite]
 enable_nnapi=False