[Protobuf] Add protobuf util function
authorgichan-jang <gichan2.jang@samsung.com>
Fri, 12 Jun 2020 07:21:53 +0000 (16:21 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Mon, 6 Jul 2020 02:14:18 +0000 (11:14 +0900)
The problem happens when multiple objects using .pb.cc file are loaded simultaneously.
I changed to tensor converter and decoder of protobuf subplugin shared libnnstreamer_protobuf.so

Signed-off-by: gichan-jang <gichan2.jang@samsung.com>
ext/nnstreamer/extra/meson.build [new file with mode: 0644]
ext/nnstreamer/extra/nnstreamer_protobuf.cc [new file with mode: 0644]
ext/nnstreamer/extra/nnstreamer_protobuf.h [new file with mode: 0644]
ext/nnstreamer/meson.build
ext/nnstreamer/tensor_converter/meson.build
ext/nnstreamer/tensor_converter/tensor_converter_protobuf.cc
ext/nnstreamer/tensor_decoder/meson.build
ext/nnstreamer/tensor_decoder/tensordec-protobuf.cc
packaging/nnstreamer.spec
tests/nnstreamer_protobuf/runTest.sh

diff --git a/ext/nnstreamer/extra/meson.build b/ext/nnstreamer/extra/meson.build
new file mode 100644 (file)
index 0000000..39d07a8
--- /dev/null
@@ -0,0 +1,22 @@
+if protobuf_support_is_available
+  converter_sub_protobuf_sources = [
+    'nnstreamer_protobuf.cc'
+  ]
+
+  nnstreamer_converter_protobuf_sources = [pb_gen_src]
+  foreach s : converter_sub_protobuf_sources
+    nnstreamer_converter_protobuf_sources += join_paths(meson.current_source_dir(), s)
+  endforeach
+
+  protobuf_util_shared = shared_library('nnstreamer_protobuf',
+    nnstreamer_converter_protobuf_sources,
+    dependencies: [nnstreamer_dep, glib_dep, gst_dep, protobuf_dep],
+    install: true,
+    install_dir: nnstreamer_libdir
+  )
+
+  protobuf_util_dep = declare_dependency(link_with: protobuf_util_shared,
+    dependencies: protobuf_dep,
+    include_directories: include_directories('.')
+  )
+endif
diff --git a/ext/nnstreamer/extra/nnstreamer_protobuf.cc b/ext/nnstreamer/extra/nnstreamer_protobuf.cc
new file mode 100644 (file)
index 0000000..0ade6b7
--- /dev/null
@@ -0,0 +1,180 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/**
+ * GStreamer / NNStreamer subplugin "protobuf" to support tensor converter and decoder
+ * Copyright (C) 2020 Gichan Jang <gichan2.jang@samsung.com>
+ */
+ /**
+ * @file        nnstreamer_protobuf.cc
+ * @date        16 June 2020
+ * @brief       Protobuf util function for nnstreamer
+ * @see         https://github.com/nnstreamer/nnstreamer
+ * @author      Gichan Jang <gichan2.jang@samsung.com>
+ * @bug         No known bugs except for NYI items
+ *
+ */
+/**
+ * Install protobuf
+ * We assume that you use Ubuntu linux distribution.
+ * You may simply download binary packages from PPA
+ *
+ * $ sudo apt-add-repository ppa:nnstreamer
+ * $ sudo apt update
+ * $ sudo apt install libprotobuf-dev libprotobuf-lite17 libprotobuf17 protobuf-compiler17
+ */
+
+#include <nnstreamer_plugin_api.h>
+#include <nnstreamer_log.h>
+#include "nnstreamer.pb.h"      /* Generated by `protoc` */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+namespace nnstreamer {
+namespace protobuf {
+
+/** @brief tensordec-plugin's GstTensorDecoderDef callback */
+GstFlowReturn
+gst_tensor_decoder_protobuf (const GstTensorsConfig * config,
+    const GstTensorMemory * input, GstBuffer * outbuf)
+{
+  GstMapInfo out_info;
+  GstMemory *out_mem;
+  size_t size, outbuf_size;
+  Tensors tensors;
+  Tensors::frame_rate *fr = NULL;
+  const unsigned int num_tensors = config->info.num_tensors;
+
+  if (NULL == outbuf) {
+    ml_loge ("GStreamer internal error : outbuf is empty");
+    return GST_FLOW_ERROR;
+  }
+
+  if (num_tensors <= 0 || num_tensors > NNS_TENSOR_SIZE_LIMIT) {
+    ml_loge ("The number of input tenosrs "
+        "exceeds more than NNS_TENSOR_SIZE_LIMIT, %s",
+        NNS_TENSOR_SIZE_LIMIT_STR);
+    return GST_FLOW_ERROR;
+  }
+  tensors.set_num_tensor (num_tensors);
+
+  fr = tensors.mutable_fr ();
+  if (!fr) {
+    nns_loge ("Failed to get pointer of tensors / tensordec-protobuf");
+    return GST_FLOW_ERROR;
+  }
+
+  fr->set_rate_n (config->rate_n);
+  fr->set_rate_d (config->rate_d);
+  
+  for (unsigned int i = 0; i < num_tensors; ++i) {
+    Tensor *tensor = tensors.add_tensor ();
+    gchar *name = NULL;
+
+    name = config->info.info[i].name;
+    if (name == NULL) {
+      tensor->set_name ("Anonymous");
+    } else {
+      tensor->set_name (name);
+    }
+
+    tensor->set_type ((Tensor::Tensor_type)
+                          config->info.info[i].type);
+
+    for (int j = 0; j < NNS_TENSOR_RANK_LIMIT; ++j) {
+      tensor->add_dimension (config->info.info[i].dimension[j]);
+    }
+
+    tensor->set_data (input[i].data, (int) input[i].size);
+  }
+
+  size = tensors.ByteSizeLong ();
+  outbuf_size = gst_buffer_get_size (outbuf);
+  
+  if (outbuf_size == 0) {
+    out_mem = gst_allocator_alloc (NULL, size, NULL);
+  } else {
+    if (outbuf_size < size) {
+      gst_buffer_set_size (outbuf, size);
+    }
+    out_mem = gst_buffer_get_all_memory (outbuf);
+  }
+
+  if (FALSE == gst_memory_map (out_mem, &out_info, GST_MAP_WRITE)) {
+    nns_loge ("Cannot map output memory / tensordec-protobuf");
+    return GST_FLOW_ERROR;
+  }
+
+  tensors.SerializeToArray (out_info.data, size);
+
+  gst_memory_unmap (out_mem, &out_info);
+
+  if (gst_buffer_get_size (outbuf) == 0) {
+    gst_buffer_append_memory (outbuf, out_mem);
+  }
+  
+  return GST_FLOW_OK;
+}
+
+/** @brief tensor converter plugin's NNStreamerExternalConverter callback */
+GstBuffer *
+gst_tensor_converter_protobuf (GstBuffer * in_buf, gsize * frame_size,
+    guint * frames_in, GstTensorsConfig * config)
+{
+  Tensors tensors;
+  Tensors::frame_rate * fr = NULL;
+  GstMemory *in_mem, *out_mem;
+  GstMapInfo in_info;
+  GstBuffer *out_buf;
+  guint mem_size;
+  gpointer mem_data;
+
+  in_mem = gst_buffer_peek_memory (in_buf, 0);
+
+  if (FALSE == gst_memory_map (in_mem, &in_info, GST_MAP_READ)) {
+    nns_loge ("Cannot map input memory / tensor_converter_protobuf");
+    return NULL;
+  }
+
+  tensors.ParseFromArray (in_info.data, in_info.size);
+
+  config->info.num_tensors = tensors.num_tensor ();
+  fr = tensors.mutable_fr ();
+  config->rate_n = fr->rate_n ();
+  config->rate_d = fr->rate_d ();
+  *frames_in = 1;
+  *frame_size = 0;
+  out_buf = gst_buffer_new ();
+
+  for (guint i = 0; i < config->info.num_tensors; i++) {
+    const Tensor *tensor = &tensors.tensor (i);
+    config->info.info[i].name = g_strdup (tensor->name ().c_str ());
+    config->info.info[i].type = (tensor_type) tensor->type ();
+    for (guint j = 0; j < NNS_TENSOR_RANK_LIMIT; j++) {
+      config->info.info[i].dimension[j] = tensor->dimension (j);
+    }
+    mem_size = tensor->data ().length ();
+    *frame_size += mem_size;
+
+    mem_data = g_memdup (tensor->data ().c_str (), mem_size);
+
+    out_mem = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
+        mem_data, mem_size, 0, mem_size, NULL, NULL);
+
+    gst_buffer_append_memory (out_buf, out_mem);
+  }
+
+/** copy timestamps */
+  gst_buffer_copy_into (out_buf, in_buf,
+      (GstBufferCopyFlags) GST_BUFFER_COPY_METADATA, 0, -1);
+  gst_memory_unmap (in_mem, &in_info);
+
+  return out_buf;
+}
+
+}; /* Namespace protobuf */
+}; /* Namespace nnstreamer */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
diff --git a/ext/nnstreamer/extra/nnstreamer_protobuf.h b/ext/nnstreamer/extra/nnstreamer_protobuf.h
new file mode 100644 (file)
index 0000000..73aa077
--- /dev/null
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/**
+ * GStreamer / NNStreamer subplugin "protobuf" to support tensor converter and decoder
+ * Copyright (C) 2020 Gichan Jang <gichan2.jang@samsung.com>
+ */
+ /**
+ * @file        nnstreamer_protobuf.h
+ * @date        16 June 2020
+ * @brief       Protobuf util function for nnstreamer
+ * @see         https://github.com/nnstreamer/nnstreamer
+ * @author      Gichan Jang <gichan2.jang@samsung.com>
+ * @bug         No known bugs except for NYI items
+ *
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @brief Default static capability for Protocol Buffers
+ * protobuf converter will convert this capability to other/tensor(s)
+ */
+#define GST_PROTOBUF_TENSOR_CAP_DEFAULT \
+    "other/protobuf-tensor, " \
+    "framerate = " GST_TENSOR_RATE_RANGE
+
+/**
+ * @brief tensordec-plugin's GstTensorDecoderDef callback
+ * @param[in] config The structure of input tensor info.
+ * @param[in] input The array of input tensor data. The maximum array size of input data is NNS_TENSOR_SIZE_LIMIT.
+ * @param[out] outbuf A sub-plugin should update or append proper memory for the negotiated media type.
+ * @return GST_FLOW_OK if OK.
+ */
+GstFlowReturn
+gst_tensor_decoder_protobuf (const GstTensorsConfig * config,
+    const GstTensorMemory * input, GstBuffer * outbuf);
+
+/**
+ * @brief tensor converter plugin's NNStreamerExternalConverter callback 
+ * @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.
+ * @param[out] config tensors config structure to be filled
+ * @retval Return input buffer(in_buf) if the data is to be kept untouched.
+ * @retval Return a new GstBuf if the data is to be modified.
+ */
+GstBuffer *
+gst_tensor_converter_protobuf (GstBuffer * in_buf, gsize * frame_size,
+    guint * frames_in, GstTensorsConfig * config);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
index 362cccd..db51e62 100644 (file)
@@ -17,6 +17,7 @@ if protobuf_support_is_available
   pb_gen_src = pb_gen.process('./include/nnstreamer.proto')
 endif
 
+subdir('extra')
 subdir('tensor_decoder')
 subdir('tensor_filter')
 subdir('tensor_source')
index e887176..c81112c 100644 (file)
@@ -24,14 +24,14 @@ if protobuf_support_is_available
     'tensor_converter_protobuf.cc'
   ]
 
-  nnstreamer_converter_protobuf_sources = [pb_gen_src]
+  nnstreamer_converter_protobuf_sources = []
   foreach s : converter_sub_protobuf_sources
     nnstreamer_converter_protobuf_sources += join_paths(meson.current_source_dir(), s)
   endforeach
 
   shared_library('nnstreamer_converter_protobuf',
     nnstreamer_converter_protobuf_sources,
-    dependencies: [nnstreamer_dep, glib_dep, gst_dep, protobuf_dep],
+    dependencies: [nnstreamer_dep, glib_dep, gst_dep, protobuf_util_dep],
     install: true,
     install_dir: converter_subplugin_install_dir
   )
index 079bfac..cc3e8d5 100644 (file)
@@ -8,7 +8,7 @@
  * @date        2 June 2020
  * @brief       NNStreamer tensor-converter subplugin, "protobuf",
  *              which converts protobuf byte stream to tensors.
- * @see         https://github.com/nnsuite/nnstreamer
+ * @see         https://github.com/nnstreamer/nnstreamer
  * @author      Gichan Jang <gichan2.jang@samsung.com>
  * @bug         No known bugs except for NYI items
  *
 #include <string>
 #include "nnstreamer_plugin_api_converter.h"
 #include <nnstreamer_log.h>
-#include "nnstreamer.pb.h"      /* Generated by `protoc` */
+#include "nnstreamer_protobuf.h"
 
-namespace nnstreamer {
-namespace protobuf {
-
-  void init_pbc (void) __attribute__ ((constructor));
-  void fini_pbc (void) __attribute__ ((destructor));
+void init_pbc (void) __attribute__ ((constructor));
+void fini_pbc (void) __attribute__ ((destructor));
 
 /** @brief tensor converter plugin's NNStreamerExternalConverter callback */
 static GstCaps *pbc_query_caps (const GstTensorsConfig * config)
@@ -49,7 +46,7 @@ static GstCaps *pbc_query_caps (const GstTensorsConfig * config)
 
 /** @brief tensor converter plugin's NNStreamerExternalConverter callback */
 static gboolean
-    pbc_get_out_config (const GstCaps * in_cap, GstTensorsConfig * config)
+pbc_get_out_config (const GstCaps * in_cap, GstTensorsConfig * config)
 {
   GstStructure *structure;
 
@@ -82,83 +79,31 @@ static gboolean
 }
 
 /** @brief tensor converter plugin's NNStreamerExternalConverter callback */
-  static GstBuffer *pbc_convert (GstBuffer * in_buf, gsize * frame_size,
-      guint * frames_in, GstTensorsConfig * config)
-  {
-    Tensors tensors;
-    Tensors::frame_rate * fr = NULL;
-    GstMemory *in_mem, *out_mem;
-    GstMapInfo in_info;
-    GstBuffer *out_buf;
-    guint mem_size;
-    gpointer mem_data;
-
-    in_mem = gst_buffer_peek_memory (in_buf, 0);
-    g_assert (gst_memory_map (in_mem, &in_info, GST_MAP_READ));
-
-    tensors.ParseFromArray (in_info.data, in_info.size);
-
-    config->info.num_tensors = tensors.num_tensor ();
-    fr = tensors.mutable_fr ();
-    config->rate_n = fr->rate_n ();
-    config->rate_d = fr->rate_d ();
-
-    out_buf = gst_buffer_new ();
-
-    for (guint i = 0; i < config->info.num_tensors; i++) {
-      const Tensor *tensor = &tensors.tensor (i);
-      config->info.info[i].name = g_strdup (tensor->name ().c_str ());
-      config->info.info[i].type = (tensor_type) tensor->type ();
-      for (guint j = 0; j < NNS_TENSOR_RANK_LIMIT; j++) {
-        config->info.info[i].dimension[j] = tensor->dimension (j);
-      }
-      *frames_in = 1;
-      *frame_size = mem_size = tensor->data ().length ();
-
-      mem_data = g_memdup (tensor->data ().c_str (), mem_size);
-
-      out_mem = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
-          mem_data, mem_size, 0, mem_size, NULL, NULL);
-
-      gst_buffer_append_memory (out_buf, out_mem);
-    }
-
-  /** copy timestamps */
-    gst_buffer_copy_into (out_buf, in_buf,
-        (GstBufferCopyFlags) GST_BUFFER_COPY_METADATA, 0, -1);
-    gst_memory_unmap (in_mem, &in_info);
-
-    return out_buf;
-  }
+static GstBuffer *pbc_convert (GstBuffer * in_buf, gsize * frame_size,
+    guint * frames_in, GstTensorsConfig * config)
+{
+  return gst_tensor_converter_protobuf (in_buf, frame_size, frames_in, config);
+}
 
-  static gchar converter_subplugin_protobuf[] =
-      "libnnstreamer_converter_protobuf";
+static gchar converter_subplugin_protobuf[] =
+    "libnnstreamer_converter_protobuf";
 
 /** @brief protobuf tensor converter sub-plugin NNStreamerExternalConverter instance */
-  static NNStreamerExternalConverter protobuf = {
-    .name = converter_subplugin_protobuf,
-    .convert = pbc_convert,
-    .get_out_config = pbc_get_out_config,
-    .query_caps = pbc_query_caps
-  };
+static NNStreamerExternalConverter protobuf = {
+  .name = converter_subplugin_protobuf,
+  .convert = pbc_convert,
+  .get_out_config = pbc_get_out_config,
+  .query_caps = pbc_query_caps
+};
 
 /** @brief Initialize this object for tensor converter sub-plugin */
-  void init_pbc (void)
-  {
-    /**
-      * Verify that the version of the library we linked is
-      * compatibile with the headers.
-      */
-    GOOGLE_PROTOBUF_VERIFY_VERSION;
-    registerExternalConverter (&protobuf);
-  }
+void init_pbc (void)
+{
+  registerExternalConverter (&protobuf);
+}
 
 /** @brief Destruct this object for tensor converter sub-plugin */
-  void fini_pbc (void)
-  {
-    unregisterExternalConverter (protobuf.name);
-    google::protobuf::ShutdownProtobufLibrary ();
-  }
-
-}; /* Namespace protobuf */
-}; /* Namespace nnstreamer */
+void fini_pbc (void)
+{
+  unregisterExternalConverter (protobuf.name);
+}
index 00a6cb8..f5f5cd0 100644 (file)
@@ -124,18 +124,17 @@ if protobuf_support_is_available
     'tensordec-protobuf.cc',
   ]
 
-  nnstreamer_decoder_protobuf_sources = [pb_gen_src]
+  nnstreamer_decoder_protobuf_sources = []
   foreach s : decoder_sub_protobuf_sources
     nnstreamer_decoder_protobuf_sources += join_paths(meson.current_source_dir(), s)
   endforeach
   
   shared_library('nnstreamer_decoder_protobuf',
     nnstreamer_decoder_protobuf_sources,
-    dependencies: [nnstreamer_dep, glib_dep, gst_dep, protobuf_dep],
+    dependencies: [nnstreamer_dep, glib_dep, gst_dep, protobuf_util_dep],
     install: true,
     install_dir: decoder_subplugin_install_dir
   )
-<<<<<<< c5239c0bfc9a1af65cecc494f1ffc1daa121d6fd
 endif
 
 # flatbuffer
@@ -155,6 +154,4 @@ if flatbuf_support_is_available
     install: true,
     install_dir: decoder_subplugin_install_dir,
   )
-=======
->>>>>>> [Converter] Add tensor converter sub-plugin protobuf
 endif
index 4f7d2aa..777b55c 100644 (file)
 #include <nnstreamer_plugin_api_decoder.h>
 #include <nnstreamer_plugin_api.h>
 #include <nnstreamer_log.h>
-
-#include "nnstreamer.pb.h"    /* Generated by `protoc` */
-
-namespace nnstreamer {
-namespace protobuf {
+#include "nnstreamer_protobuf.h"
 
 void init_pb (void) __attribute__ ((constructor));
 void fini_pb (void) __attribute__ ((destructor));
@@ -35,11 +31,6 @@ void fini_pb (void) __attribute__ ((destructor));
 static int
 pb_init (void **pdata)
 {
-  /**
-   * Verify that the version of the library we linked is
-   * compatibile with the headers.
-   */
-  GOOGLE_PROTOBUF_VERIFY_VERSION;
   *pdata = NULL; /* no private data are needed for this sub-plugin */
   return TRUE;
 }
@@ -50,7 +41,6 @@ pb_init (void **pdata)
 static void
 pb_exit (void **pdata)
 {
-  google::protobuf::ShutdownProtobufLibrary ();
   return;
 }
 
@@ -77,75 +67,7 @@ static GstFlowReturn
 pb_decode (void **pdata, const GstTensorsConfig * config,
     const GstTensorMemory * input, GstBuffer * outbuf)
 {
-  GstMapInfo out_info;
-  GstMemory *out_mem;
-  size_t size, outbuf_size;
-
-  Tensors tensors;
-  Tensors::frame_rate *fr = NULL;
-
-  const unsigned int num_tensors = config->info.num_tensors;
-  g_assert (num_tensors > 0);
-  tensors.set_num_tensor (num_tensors);
-
-  fr = tensors.mutable_fr ();
-  if (!fr) {
-    nns_loge ("Failed to get pointer of tensors / tensordec-protobuf");
-    return GST_FLOW_ERROR;
-  }
-
-  fr->set_rate_n (config->rate_n);
-  fr->set_rate_d (config->rate_d);
-  
-  for (unsigned int i = 0; i < num_tensors; ++i) {
-    Tensor *tensor = tensors.add_tensor ();
-    gchar *name = NULL;
-
-    name = config->info.info[i].name;
-    if (name == NULL) {
-      tensor->set_name ("Anonymous");
-    } else {
-      tensor->set_name (name);
-    }
-
-    tensor->set_type ((Tensor::Tensor_type)
-                          config->info.info[i].type);
-
-    for (int j = 0; j < NNS_TENSOR_RANK_LIMIT; ++j) {
-      tensor->add_dimension (config->info.info[i].dimension[j]);
-    }
-
-    tensor->set_data (input[i].data, (int) input[i].size);
-  }
-
-  g_assert (outbuf);
-
-  size = tensors.ByteSizeLong ();
-  outbuf_size = gst_buffer_get_size (outbuf);
-  
-  if (outbuf_size == 0) {
-    out_mem = gst_allocator_alloc (NULL, size, NULL);
-  } else {
-    if (outbuf_size < size) {
-      gst_buffer_set_size (outbuf, size);
-    }
-    out_mem = gst_buffer_get_all_memory (outbuf);
-  }
-
-  if (FALSE == gst_memory_map (out_mem, &out_info, GST_MAP_WRITE)) {
-    nns_loge ("Cannot map aoutput memory / tensordec-protobuf");
-    return GST_FLOW_ERROR;
-  }
-
-  tensors.SerializeToArray (out_info.data, size);
-
-  gst_memory_unmap (out_mem, &out_info);
-
-  if (gst_buffer_get_size (outbuf) == 0) {
-    gst_buffer_append_memory (outbuf, out_mem);
-  }
-  
-  return GST_FLOW_OK;
+  return gst_tensor_decoder_protobuf (config, input, outbuf);
 }
 
 static gchar decoder_subplugin_protobuf[] = "protobuf";
@@ -177,6 +99,3 @@ fini_pb (void)
 {
   nnstreamer_decoder_exit (protobuf.modename);
 }
-
-}; /* Namespace protobuf */
-}; /* Namespace nnstreamer */
index 23050e6..dc5b342 100644 (file)
@@ -2,12 +2,12 @@
 %define                gstpostfix      gstreamer-1.0
 %define                gstlibdir       %{_libdir}/%{gstpostfix}
 %define                nnstexampledir  /usr/lib/nnstreamer/bin
-%define                tensorflow_support  0
+%define                tensorflow_support 0
 %define                tensorflow_lite_support 1
 %define                armnn_support 0
 %define                vivante_support 0
 %define                flatbuf_support 1
-%define                protobuf_support  1
+%define                protobuf_support 1
 
 %if 0%{tizen_version_major} >= 5
 %define                python_support 1
@@ -244,12 +244,21 @@ Requires:   gst-plugins-good-extra
 Requires:   gst-libav
 %description vivante
 NNStreamer filter subplugin for Verisicon Vivante.
-
 %define enable_vivante -Denable-vivante=true
 %else
 %define enable_vivante -Denable-vivante=false
 %endif
 
+# for protobuf
+%if 0%{?protobuf_support}
+%package    nnstreamer-protobuf
+Summary:       NNStreamer Protobuf Support
+Requires:      nnstreamer = %{version}-%{release}
+Requires:      protobuf
+%description nnstreamer-protobuf
+NNStreamer's tensor_converter and decoder subplugin of Protobuf.
+%endif
+
 %package devel
 Summary:       Development package for custom tensor operator developers (tensor_filter/custom)
 Requires:      nnstreamer = %{version}-%{release}
@@ -566,12 +575,13 @@ cp -r result %{buildroot}%{_datadir}/nnstreamer/unittest/
 %defattr(-,root,root,-)
 %license LICENSE
 %{_prefix}/lib/nnstreamer/decoders/libnnstreamer_decoder_*.so
-%{_prefix}/lib/nnstreamer/converters/libnnstreamer_converter_*.so
 %{_prefix}/lib/nnstreamer/filters/libnnstreamer_filter_cpp.so
-%{_prefix}/lib/nnstreamer/converters/libnnstreamer_converter_*.so
 %{gstlibdir}/libnnstreamer.so
 %{_libdir}/libnnstreamer.so
 %{_sysconfdir}/nnstreamer.ini
+%if 0%{?flatbuf_support}
+%{_prefix}/lib/nnstreamer/converters/libnnstreamer_converter_flatbuf.so
+%endif
 
 # for tensorflow
 %if 0%{?tensorflow_support}
@@ -597,6 +607,15 @@ cp -r result %{buildroot}%{_datadir}/nnstreamer/unittest/
 %{python_sitelib}/nnstreamer_python.so
 %endif
 
+%if 0%{?protobuf_support}
+%files nnstreamer-protobuf
+%manifest nnstreamer.manifest
+%defattr(-,root,root,-)
+%{_libdir}/libnnstreamer_protobuf.so
+%{_prefix}/lib/nnstreamer/decoders/libnnstreamer_decoder_protobuf.so
+%{_prefix}/lib/nnstreamer/converters/libnnstreamer_converter_protobuf.so
+%endif
+
 %files devel
 %{_includedir}/nnstreamer/tensor_typedef.h
 %{_includedir}/nnstreamer/tensor_filter_custom.h
index 0b48856..cb8c397 100755 (executable)
@@ -6,15 +6,14 @@
 ## @author Gichan Jang <gichan2.jang@samsung.com>
 ## @date June 08 2020
 ## @brief SSAT Test Cases for protobuf subplugin of tensor converter and decoder
-## @details After decoding the tensor into protobuf, convert it to tensor again to check if it matches the original
+## @details After decoding the tensor into protobuf, convert it to tensor(s) again to check if it matches the original
 ##
 if [[ "$SSATAPILOADED" != "1" ]]; then
     SILENT=0
     INDEPENDENT=1
     search="ssat-api.sh"
     source $search
-    printf "${Blue}Independent Mode${NC}
-"
+    printf "${Blue}Independent Mode${NC}"
 fi
 
 # This is compatible with SSAT (https://github.com/myungjoo/SSAT)
@@ -25,7 +24,7 @@ if [ "$SKIPGEN" == "YES" ]; then
     sopath=$2
 else
     echo "Test Case Generation Started"
-    python ../nnstreamer_converter/generateGoldenTestResult.py
+    python ../nnstreamer_converter/generateGoldenTestResult.py 9
     python ../nnstreamer_merge/generateTest.py
     sopath=$1
 fi
@@ -33,63 +32,57 @@ convertBMP2PNG
 
 PATH_TO_PLUGIN="../../build"
 
-gst-launch-1.0 -m -v --gst-plugin-path=${PATH_TO_PLUGIN} videotestsrc num-buffers=1 ! video/x-raw,format=RGB,width=280,height=40,framerate=0/1 ! tee name=t ! queue ! videoconvert ! video/x-raw, format=BGRx ! tensor_converter silent=TRUE ! filesink location="test.bgrx.log" sync=true t. ! queue ! filesink location="test.rgb.log" sync=true
-
-gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} videotestsrc num-buffers=1 ! video/x-raw,format=RGB,width=280,height=40,framerate=0/1 ! tee name=t ! queue ! videoconvert ! video/x-raw, format=BGRx ! tensor_converter silent=TRUE ! tensor_decoder mode=protobuf ! other/protobuf-tensor ! tensor_converter ! filesink location=\"test.bgrx.log\" sync=true t. ! queue ! filesink location=\"test.rgb.log\" sync=true" 1R 0 0 $PERFORMANCE
-
-gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} videotestsrc num-buffers=1 ! video/x-raw,format=GRAY8,width=280,height=40,framerate=0/1 ! queue ! tensor_converter silent=TRUE ! tensor_decoder mode=protobuf ! other/protobuf-tensor ! tensor_converter ! filesink location=\"test.gray8.log\" sync=true" 1G 0 0 $PERFORMANCE
-
-callCompareTest testcase01.bgrx.golden test.bgrx.log 1-1 "BGRX Golden Test" 1 0
-callCompareTest testcase01.rgb.golden test.rgb.log 1-2 "RGB Golden Test" 1 0
-callCompareTest testcase01.gray8.golden test.gray8.log 1-3 "Gray8 Golden Test" 1 0
-
 ##
-## @brief Execute gstreamer pipeline and do golden-test with the output of the pipeline.
+## @brief Execute gstreamer pipeline and compare the output of the pipeline
 ## @param $1 Colorspace
 ## @param $2 Width
 ## @param $3 Height
 ## @param $4 Test Case Number
 function do_test() {
-    gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} filesrc location=testcase02_${1}_${2}x${3}.png ! pngdec ! videoscale ! imagefreeze ! videoconvert ! video/x-raw,format=${1},width=${2},height=${3},framerate=0/1 ! tensor_converter silent=TRUE ! tensor_decoder mode=protobuf ! other/protobuf-tensor ! tensor_converter ! filesink location=\"testcase02_${1}_${2}x${3}.log\" sync=true" ${4} 0 0 $PERFORMANCE
+    gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} videotestsrc num-buffers=3 pattern=13 ! video/x-raw,format=${1},width=${2},height=${3},framerate=5/1 ! \
+    tee name=t ! queue ! multifilesink location=\"raw_${1}_${2}x${3}_%1d.log\"
+    t. ! queue ! tensor_converter ! tensor_decoder mode=protobuf ! other/protobuf-tensor ! tensor_converter ! multifilesink location=\"protob_${1}_${2}x${3}_%1d.log\" sync=true" ${4} 0 0 $PERFORMANCE
 
-    callCompareTest testcase02_${1}_${2}x${3}.golden testcase02_${1}_${2}x${3}.log ${4} "PNG Golden Testing ${4}" 1 0
+    callCompareTest raw_${1}_${2}x${3}_0.log protob_${1}_${2}x${3}_0.log "${4}-1" "protobuf conversion test ${4}-1" 1 0
+    callCompareTest raw_${1}_${2}x${3}_1.log protob_${1}_${2}x${3}_1.log "${4}-2" "protobuf conversion test ${4}-2" 1 0
+    callCompareTest raw_${1}_${2}x${3}_2.log protob_${1}_${2}x${3}_2.log "${4}-3" "protobuf conversion test ${4}-3" 1 0
 }
-
-do_test BGRx 640 480 2-1
-do_test RGB 640 480 2-2
-do_test GRAY8 640 480 2-3
-do_test BGRx 642 480 2-4
-do_test RGB 642 480 2-5
-do_test GRAY8 642 480 2-6
-
-# Fail Test: YUV is given
-gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} videotestsrc num-buffers=1 ! video/x-raw,format=YUV,width=280,height=40,framerate=0/1 ! videoconvert ! video/x-raw, format=YUV ! tensor_converter silent=TRUE ! tensor_decoder mode=protobuf ! other/protobuf-tensor ! tensor_converter ! filesink location=\"test.yuv.fail.log\" sync=true" 5F_n 0 1 $PERFORMANCE
-
-# Fail Test: Unknown property is given
-gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} videotestsrc num-buffers=1 ! video/x-raw,format=RGB,width=280,height=40,framerate=0/1 ! videoconvert ! video/x-raw, format=RGB ! tensor_converter silent=TRUE whatthehell=isthis ! tensor_decoder mode=protobuf ! other/protobuf-tensor ! tensor_converter ! filesink location=\"test.yuv.fail.log\" sync=true" 6F_n 0 1 $PERFORMANCE
+# The width and height of video should be multiple of 4
+do_test BGRx 320 240 1-1
+do_test RGB 320 240 1-2
+do_test GRAY8 320 240 1-3
 
 # audio format S16LE, 8k sample rate, samples per buffer 8000
-gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} audiotestsrc num-buffers=1 samplesperbuffer=8000 ! audioconvert ! audio/x-raw,format=S16LE,rate=8000 ! tee name=t ! queue ! audioconvert ! tensor_converter frames-per-tensor=8000 ! tensor_decoder mode=protobuf ! other/protobuf-tensor ! tensor_converter ! filesink location=\"test.audio8k.s16le.log\" sync=true t. ! queue ! filesink location=\"test.audio8k.s16le.origin.log\" sync=true" 7-1 0 0 $PERFORMANCE
-callCompareTest test.audio8k.s16le.origin.log test.audio8k.s16le.log 7-2 "Audio8k-s16le Golden Test" 0 0
+gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} audiotestsrc num-buffers=1 samplesperbuffer=8000 ! audioconvert ! audio/x-raw,format=S16LE,rate=8000 ! \
+    tee name=t ! queue ! audioconvert ! tensor_converter frames-per-tensor=8000 ! tensor_decoder mode=protobuf ! \
+        other/protobuf-tensor ! tensor_converter ! filesink location=\"test.audio8k.s16le.log\" sync=true \
+    t. ! queue ! filesink location=\"test.audio8k.s16le.origin.log\" sync=true" 2-1 0 0 $PERFORMANCE
+callCompareTest test.audio8k.s16le.origin.log test.audio8k.s16le.log 2-2 "Audio8k-s16le Golden Test" 0 0
 
 # audio format U8, 16k sample rate, samples per buffer 8000
-gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} audiotestsrc num-buffers=1 samplesperbuffer=8000 ! audioconvert ! audio/x-raw,format=U8,rate=16000 ! tee name=t ! queue ! audioconvert ! tensor_converter frames-per-tensor=8000 ! tensor_decoder mode=protobuf ! other/protobuf-tensor ! tensor_converter ! filesink location=\"test.audio16k.u8.log\" sync=true t. ! queue ! filesink location=\"test.audio16k.u8.origin.log\" sync=true" 7-3 0 0 $PERFORMANCE
-callCompareTest test.audio16k.u8.origin.log test.audio16k.u8.log 7-4 "Audio16k-u8 Golden Test" 0 0
+gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} audiotestsrc num-buffers=1 samplesperbuffer=8000 ! audioconvert ! audio/x-raw,format=U8,rate=16000 ! \
+    tee name=t ! queue ! audioconvert ! tensor_converter frames-per-tensor=8000 ! tensor_decoder mode=protobuf ! \
+        other/protobuf-tensor ! tensor_converter ! filesink location=\"test.audio16k.u8.log\" sync=true \
+    t. ! queue ! filesink location=\"test.audio16k.u8.origin.log\" sync=true" 2-3 0 0 $PERFORMANCE
+callCompareTest test.audio16k.u8.origin.log test.audio16k.u8.log 2-4 "Audio16k-u8 Golden Test" 0 0
 
 # audio format U16LE, 16k sample rate, 2 channels, samples per buffer 8000
-gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} audiotestsrc num-buffers=1 samplesperbuffer=8000 ! audioconvert ! audio/x-raw,format=U16LE,rate=16000,channels=2 ! tee name=t ! queue ! audioconvert ! tensor_converter frames-per-tensor=8000 ! tensor_decoder mode=protobuf ! other/protobuf-tensor ! tensor_converter ! filesink location=\"test.audio16k2c.u16le.log\" sync=true t. ! queue ! filesink location=\"test.audio16k2c.u16le.origin.log\" sync=true" 7-5 0 0 $PERFORMANCE
-callCompareTest test.audio16k2c.u16le.origin.log test.audio16k2c.u16le.log 7-6 "Audio16k2c-u16le Golden Test" 0 0
-
-# Stream test case (genCase08 in generateGoldenTestResult.py)
-gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} multifilesrc location=\"testsequence_%1d.png\" index=0 caps=\"image/png,framerate=\(fraction\)30/1\" ! pngdec ! videoconvert ! tensor_converter ! tensor_decoder mode=protobuf ! other/protobuf-tensor ! tensor_converter ! filesink location=\"testcase08.log\"" 8 0 0 $PERFORMANCE
-callCompareTest testcase08.golden testcase08.log 8 "PNG Stream Test" 0 0
+gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} audiotestsrc num-buffers=1 samplesperbuffer=8000 ! audioconvert ! audio/x-raw,format=U16LE,rate=16000,channels=2 ! \
+    tee name=t ! queue ! audioconvert ! tensor_converter frames-per-tensor=8000 ! tensor_decoder mode=protobuf ! \
+        other/protobuf-tensor ! tensor_converter ! filesink location=\"test.audio16k2c.u16le.log\" sync=true \
+    t. ! queue ! filesink location=\"test.audio16k2c.u16le.origin.log\" sync=true" 2-5 0 0 $PERFORMANCE
+callCompareTest test.audio16k2c.u16le.origin.log test.audio16k2c.u16le.log 2-6 "Audio16k2c-u16le Golden Test" 0 0
 
 # tensor merge test (The output is always in the format of other/tensor)
-gstTest "--gst-plugin-path=${PATH_TO_PLUGIN}  tensor_merge name=merge mode=linear option=2 silent=true sync_mode=basepad sync_option=0:0 ! multifilesink location=testsynch08_%1d.log multifilesrc location=\"testsequence03_%1d.png\" index=0 caps=\"image/png, framerate=(fraction)10/1\" ! pngdec ! tensor_converter !  tensor_decoder mode=protobuf ! other/protobuf-tensor ! tensor_converter ! merge.sink_0 multifilesrc location=\"testsequence03_%1d.png\" index=0 caps=\"image/png, framerate=(fraction)30/1\" ! pngdec ! tensor_converter !  tensor_decoder mode=protobuf ! other/protobuf-tensor ! tensor_converter ! merge.sink_1" 9 0 0 $PERFORMANCE
-callCompareTest testsynch08_0.golden testsynch08_0.log 9-1 "Tensor merge Compare 9-1" 1 0
-callCompareTest testsynch08_1.golden testsynch08_1.log 9-2 "Tensor merge Compare 9-2" 1 0
-callCompareTest testsynch08_2.golden testsynch08_2.log 9-3 "Tensor merge Compare 9-3" 1 0
-callCompareTest testsynch08_3.golden testsynch08_3.log 9-4 "Tensor merge Compare 9-4" 1 0
+gstTest "--gst-plugin-path=${PATH_TO_PLUGIN}  tensor_merge name=merge mode=linear option=2 silent=true sync_mode=basepad sync_option=0:0 ! multifilesink location=testsynch08_%1d.log \
+    multifilesrc location=\"testsequence03_%1d.png\" index=0 caps=\"image/png, framerate=(fraction)10/1\" ! pngdec ! tensor_converter !  \
+        tensor_decoder mode=protobuf ! other/protobuf-tensor ! tensor_converter ! merge.sink_0 \
+    multifilesrc location=\"testsequence03_%1d.png\" index=0 caps=\"image/png, framerate=(fraction)30/1\" ! pngdec ! tensor_converter ! \
+        tensor_decoder mode=protobuf ! other/protobuf-tensor ! tensor_converter ! merge.sink_1" 3 0 0 $PERFORMANCE
+callCompareTest testsynch08_0.golden testsynch08_0.log 3-1 "Tensor merge Compare 3-1" 1 0
+callCompareTest testsynch08_1.golden testsynch08_1.log 3-2 "Tensor merge Compare 3-2" 1 0
+callCompareTest testsynch08_2.golden testsynch08_2.log 3-3 "Tensor merge Compare 3-3" 1 0
+callCompareTest testsynch08_3.golden testsynch08_3.log 3-4 "Tensor merge Compare 3-4" 1 0
 
 # tensor mux test (The output is always in the format of other/tensors)
 gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} tensor_mux name=tensors_mux sync_mode=basepad sync_option=1:50000000 ! multifilesink location=testsynch19_%1d.log \
@@ -102,11 +95,11 @@ gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} tensor_mux name=tensors_mux sync_mo
     multifilesrc location=\"testsequence03_%1d.png\" index=0 caps=\"image/png, framerate=(fraction)30/1\" ! pngdec ! \
         tensor_converter ! tensor_decoder mode=protobuf ! other/protobuf-tensor ! tensor_converter ! tensor_mux1.sink_0 \
     multifilesrc location=\"testsequence03_%1d.png\" index=0 caps=\"image/png, framerate=(fraction)20/1\" ! pngdec ! \
-        tensor_converter ! tensor_decoder mode=protobuf ! other/protobuf-tensor ! tensor_converter ! tensor_mux1.sink_1" 10 0 0 $PERFORMANCE
-callCompareTest testsynch19_0.golden testsynch19_0.log 10-1 "Tensor mux Compare 10-1" 1 0
-callCompareTest testsynch19_1.golden testsynch19_1.log 10-2 "Tensor mux Compare 10-2" 1 0
-callCompareTest testsynch19_2.golden testsynch19_2.log 10-3 "Tensor mux Compare 10-3" 1 0
-callCompareTest testsynch19_3.golden testsynch19_3.log 10-4 "Tensor mux Compare 10-4" 1 0
-callCompareTest testsynch19_4.golden testsynch19_4.log 10-5 "Tensor mux Compare 10-5" 1 0
+        tensor_converter ! tensor_decoder mode=protobuf ! other/protobuf-tensor ! tensor_converter ! tensor_mux1.sink_1" 4 0 0 $PERFORMANCE
+callCompareTest testsynch19_0.golden testsynch19_0.log 4-1 "Tensor mux Compare 4-1" 1 0
+callCompareTest testsynch19_1.golden testsynch19_1.log 4-2 "Tensor mux Compare 4-2" 1 0
+callCompareTest testsynch19_2.golden testsynch19_2.log 4-3 "Tensor mux Compare 4-3" 1 0
+callCompareTest testsynch19_3.golden testsynch19_3.log 4-4 "Tensor mux Compare 4-4" 1 0
+callCompareTest testsynch19_4.golden testsynch19_4.log 4-5 "Tensor mux Compare 4-5" 1 0
 
 report