[Converter] Add tensor converter sub-plugin protobuf
authorgichan-jang <gichan2.jang@samsung.com>
Tue, 9 Jun 2020 05:18:52 +0000 (14:18 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Mon, 6 Jul 2020 02:14:18 +0000 (11:14 +0900)
Add tensor converter subplugin protobuf
Add protobuf unittest

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

index db29497..33ecd0b 100644 (file)
@@ -9,8 +9,8 @@ Build-Depends: gcc-9 | gcc-8 | gcc-7 | gcc-6 | gcc-5 (>=5.4),
  libgtest-dev, ssat, libpng-dev, libopencv-dev, liborc-0.4-dev,
  python, python-numpy, python3, python3-dev, python3-numpy,
  tensorflow-lite-dev, pytorch, libedgetpu1-std (>=12), libedgetpu-dev (>=12),
- libflatbuffers-dev, flatbuffers-compiler,
- tensorflow-dev [amd64], python2.7-dev, libprotobuf-dev [amd64 arm64 armhf]
+ libflatbuffers-dev, flatbuffers-compiler, protobuf-compiler17,
+ tensorflow-dev [amd64], python2.7-dev, libprotobuf-dev [amd64 arm64 armhf],
 Standards-Version: 3.9.6
 Homepage: https://github.com/nnstreamer/nnstreamer
 
index 034e5a9..710c521 100644 (file)
@@ -1,6 +1,6 @@
 syntax = "proto3";
 
-package NNStreamer;
+package nnstreamer.protobuf;
 
 message Tensor {
   string name = 1;
index 96802a9..e887176 100644 (file)
@@ -17,3 +17,22 @@ if flatbuf_support_is_available
     install_dir: converter_subplugin_install_dir
   )
 endif
+
+# protocol buffer
+if protobuf_support_is_available
+  converter_sub_protobuf_sources = [
+    'tensor_converter_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
+
+  shared_library('nnstreamer_converter_protobuf',
+    nnstreamer_converter_protobuf_sources,
+    dependencies: [nnstreamer_dep, glib_dep, gst_dep, protobuf_dep],
+    install: true,
+    install_dir: converter_subplugin_install_dir
+  )
+endif
diff --git a/ext/nnstreamer/tensor_converter/tensor_converter_protobuf.cc b/ext/nnstreamer/tensor_converter/tensor_converter_protobuf.cc
new file mode 100644 (file)
index 0000000..079bfac
--- /dev/null
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/**
+ * GStreamer / NNStreamer tensor_converter subplugin, "protobuf"
+ * Copyright (C) 2020 Gichan Jang <gichan2.jang@samsung.com>
+ */
+ /**
+ * @file        tensor_converter_protobuf.cc
+ * @date        2 June 2020
+ * @brief       NNStreamer tensor-converter subplugin, "protobuf",
+ *              which converts protobuf byte stream to tensors.
+ * @see         https://github.com/nnsuite/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 <iostream>
+#include <fstream>
+#include <typeinfo>
+#include <glib.h>
+#include <gst/gstinfo.h>
+#include <nnstreamer_plugin_api.h>
+#include <string>
+#include "nnstreamer_plugin_api_converter.h"
+#include <nnstreamer_log.h>
+#include "nnstreamer.pb.h"      /* Generated by `protoc` */
+
+namespace nnstreamer {
+namespace protobuf {
+
+  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)
+{
+  return gst_caps_from_string (GST_PROTOBUF_TENSOR_CAP_DEFAULT);
+}
+
+/** @brief tensor converter plugin's NNStreamerExternalConverter callback */
+static gboolean
+    pbc_get_out_config (const GstCaps * in_cap, GstTensorsConfig * config)
+{
+  GstStructure *structure;
+
+  g_return_val_if_fail (config != NULL, FALSE);
+  gst_tensors_config_init (config);
+  g_return_val_if_fail (in_cap != NULL, FALSE);
+
+  structure = gst_caps_get_structure (in_cap, 0);
+  g_return_val_if_fail (structure != NULL, FALSE);
+
+  /* All tensor info should be updated later in chain function. */
+    config->info.info[0].type = _NNS_UINT8;
+    config->info.num_tensors = 1;
+  if (gst_tensor_parse_dimension ("1:1:1:1",
+          config->info.info[0].dimension) == 0) {
+    ml_loge ("Failed to set initial dimension for subplugin");
+    return FALSE;
+  }
+
+  if (gst_structure_has_field (structure, "framerate"))
+  {
+    gst_structure_get_fraction (structure, "framerate", &config->rate_n,
+        &config->rate_d);
+  } else {
+    /* cannot get the framerate */
+    config->rate_n = 0;
+    config->rate_d = 1;
+  }
+  return TRUE;
+}
+
+/** @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 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
+  };
+
+/** @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);
+  }
+
+/** @brief Destruct this object for tensor converter sub-plugin */
+  void fini_pbc (void)
+  {
+    unregisterExternalConverter (protobuf.name);
+    google::protobuf::ShutdownProtobufLibrary ();
+  }
+
+}; /* Namespace protobuf */
+}; /* Namespace nnstreamer */
index c87b1de..00a6cb8 100644 (file)
@@ -135,6 +135,7 @@ if protobuf_support_is_available
     install: true,
     install_dir: decoder_subplugin_install_dir
   )
+<<<<<<< c5239c0bfc9a1af65cecc494f1ffc1daa121d6fd
 endif
 
 # flatbuffer
@@ -154,4 +155,6 @@ if flatbuf_support_is_available
     install: true,
     install_dir: decoder_subplugin_install_dir,
   )
+=======
+>>>>>>> [Converter] Add tensor converter sub-plugin protobuf
 endif
index dfbfd52..4f7d2aa 100644 (file)
@@ -23,6 +23,9 @@
 
 #include "nnstreamer.pb.h"    /* Generated by `protoc` */
 
+namespace nnstreamer {
+namespace protobuf {
+
 void init_pb (void) __attribute__ ((constructor));
 void fini_pb (void) __attribute__ ((destructor));
 
@@ -78,8 +81,8 @@ pb_decode (void **pdata, const GstTensorsConfig * config,
   GstMemory *out_mem;
   size_t size, outbuf_size;
 
-  NNStreamer::Tensors tensors;
-  NNStreamer::Tensors::frame_rate *fr = NULL;
+  Tensors tensors;
+  Tensors::frame_rate *fr = NULL;
 
   const unsigned int num_tensors = config->info.num_tensors;
   g_assert (num_tensors > 0);
@@ -95,7 +98,7 @@ pb_decode (void **pdata, const GstTensorsConfig * config,
   fr->set_rate_d (config->rate_d);
   
   for (unsigned int i = 0; i < num_tensors; ++i) {
-    NNStreamer::Tensor *tensor = tensors.add_tensor ();
+    Tensor *tensor = tensors.add_tensor ();
     gchar *name = NULL;
 
     name = config->info.info[i].name;
@@ -105,7 +108,7 @@ pb_decode (void **pdata, const GstTensorsConfig * config,
       tensor->set_name (name);
     }
 
-    tensor->set_type ((NNStreamer::Tensor::Tensor_type)
+    tensor->set_type ((Tensor::Tensor_type)
                           config->info.info[i].type);
 
     for (int j = 0; j < NNS_TENSOR_RANK_LIMIT; ++j) {
@@ -174,3 +177,6 @@ fini_pb (void)
 {
   nnstreamer_decoder_exit (protobuf.modename);
 }
+
+}; /* Namespace protobuf */
+}; /* Namespace nnstreamer */
index c71b4a0..23050e6 100644 (file)
@@ -2,11 +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
 
 %if 0%{tizen_version_major} >= 5
 %define                python_support 1
@@ -107,9 +108,12 @@ BuildRequires: tensorflow-lite-devel
 BuildRequires: opencv-devel
 # For './testAll.sh' time limit.
 BuildRequires: procps
+# for protobuf
+%if 0%{?protobuf_support}
+BuildRequires: protobuf-devel >= 3.4.0
+%endif
 # for tensorflow
 %if 0%{?tensorflow_support}
-BuildRequires: protobuf-devel >= 3.4.0
 BuildRequires: tensorflow
 BuildRequires: tensorflow-devel
 %endif
@@ -564,6 +568,7 @@ cp -r result %{buildroot}%{_datadir}/nnstreamer/unittest/
 %{_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
diff --git a/tests/nnstreamer_protobuf/runTest.sh b/tests/nnstreamer_protobuf/runTest.sh
new file mode 100755 (executable)
index 0000000..0b48856
--- /dev/null
@@ -0,0 +1,112 @@
+#!/usr/bin/env bash
+##
+## SPDX-License-Identifier: LGPL-2.1-only
+##
+## @file runTest.sh
+## @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
+##
+if [[ "$SSATAPILOADED" != "1" ]]; then
+    SILENT=0
+    INDEPENDENT=1
+    search="ssat-api.sh"
+    source $search
+    printf "${Blue}Independent Mode${NC}
+"
+fi
+
+# This is compatible with SSAT (https://github.com/myungjoo/SSAT)
+testInit $1
+
+if [ "$SKIPGEN" == "YES" ]; then
+    echo "Test Case Generation Skipped"
+    sopath=$2
+else
+    echo "Test Case Generation Started"
+    python ../nnstreamer_converter/generateGoldenTestResult.py
+    python ../nnstreamer_merge/generateTest.py
+    sopath=$1
+fi
+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.
+## @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
+
+    callCompareTest testcase02_${1}_${2}x${3}.golden testcase02_${1}_${2}x${3}.log ${4} "PNG Golden Testing ${4}" 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
+
+# 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
+
+# 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
+
+# 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
+
+# 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
+
+# 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 \
+    tensor_mux name=tensor_mux0  sync_mode=slowest ! tensors_mux.sink_0 \
+    tensor_mux name=tensor_mux1  sync_mode=slowest ! tensors_mux.sink_1 \
+    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 ! tensor_mux0.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_mux0.sink_1 \
+    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
+
+report