From 268ac1e4e18b8f5c2247584e904d36dc6a6b9514 Mon Sep 17 00:00:00 2001
From: Nicolas Pope <nicolas.pope@utu.fi>
Date: Wed, 10 Aug 2022 13:36:10 +0300
Subject: [PATCH] Add msgpack codec helpers

---
 CMakeLists.txt                       |  1 +
 include/ftl/codec/data.hpp           | 28 +++++++++++++++++++
 include/ftl/codec/msgpack.hpp        | 20 +++++++++++++
 include/ftl/data/camera.hpp          | 24 ++++++++++++++++
 include/ftl/data/capabilities.hpp    | 28 +++++++++++++++++++
 include/ftl/utility/vectorbuffer.hpp | 29 +++++++++++++++++++
 src/codecs/data.cpp                  | 42 ++++++++++++++++++++++++++++
 test/CMakeLists.txt                  | 11 ++++++++
 test/datacodec.cpp                   | 30 ++++++++++++++++++++
 9 files changed, 213 insertions(+)
 create mode 100644 include/ftl/codec/data.hpp
 create mode 100644 include/ftl/codec/msgpack.hpp
 create mode 100644 include/ftl/data/camera.hpp
 create mode 100644 include/ftl/data/capabilities.hpp
 create mode 100644 include/ftl/utility/vectorbuffer.hpp
 create mode 100644 src/codecs/data.cpp
 create mode 100644 test/datacodec.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 12c759a..67542ca 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -196,6 +196,7 @@ add_library(beyond-protocol STATIC
     src/service.cpp
     src/codecs/golomb.cpp
     src/codecs/h264.cpp
+    src/codecs/data.cpp
 )
 
 target_include_directories(beyond-protocol PUBLIC
diff --git a/include/ftl/codec/data.hpp b/include/ftl/codec/data.hpp
new file mode 100644
index 0000000..916432d
--- /dev/null
+++ b/include/ftl/codec/data.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <vector>
+#include <string>
+#include <tuple>
+#include <cstdint>
+
+#include <ftl/data/camera.hpp>
+#include <ftl/protocol/channels.hpp>
+
+namespace ftl {
+namespace data {
+
+using Pose = std::vector<double>;
+using StereoPose = std::tuple<Pose, Pose>;
+using Intrinsics = std::tuple<ftl::data::Camera, int, int>;
+
+}
+namespace codec {
+
+template <typename T>
+void pack(const T &v, std::vector<uint8_t> &out);
+
+template <typename T>
+T unpack(const std::vector<uint8_t> &in);
+
+}
+}
diff --git a/include/ftl/codec/msgpack.hpp b/include/ftl/codec/msgpack.hpp
new file mode 100644
index 0000000..4f8eba9
--- /dev/null
+++ b/include/ftl/codec/msgpack.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <msgpack.hpp>
+#include <ftl/codec/data.hpp>
+#include <ftl/utility/vectorbuffer.hpp>
+
+template <typename T>
+void ftl::codec::pack(const T &v, std::vector<uint8_t> &out) {
+    out.resize(0);
+    ftl::util::FTLVectorBuffer buf(out);
+    msgpack::pack(buf, v);
+}
+
+template <typename T>
+T ftl::codec::unpack(const std::vector<uint8_t> &in) {
+    auto unpacked = msgpack::unpack((const char*)in.data(), in.size());
+    T t;
+    unpacked.get().convert<T>(t);
+    return t;
+}
diff --git a/include/ftl/data/camera.hpp b/include/ftl/data/camera.hpp
new file mode 100644
index 0000000..6a931fe
--- /dev/null
+++ b/include/ftl/data/camera.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+namespace ftl {
+namespace data {
+
+/**
+ * All properties associated with cameras. This structure is designed to
+ * operate on CPU and GPU.
+ */
+struct Camera {
+    float fx;               // Focal length X
+    float fy;               // Focal length Y (usually same as fx)
+    float cx;               // Principle point Y
+    float cy;               // Principle point Y
+    unsigned int width;     // Pixel width
+    unsigned int height;    // Pixel height
+    float minDepth;         // Near clip in meters
+    float maxDepth;         // Far clip in meters
+    float baseline;         // For stereo pair
+    float doffs;            // Disparity offset
+};
+
+}  // namespace data
+}  // namespace ftl
diff --git a/include/ftl/data/capabilities.hpp b/include/ftl/data/capabilities.hpp
new file mode 100644
index 0000000..f81f4c7
--- /dev/null
+++ b/include/ftl/data/capabilities.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+namespace ftl {
+namespace data {
+
+/**
+ * To be added to the capabilities channel to indicate what the source device
+ * is capable of. These properties should be features of the source that
+ * cannot be determined by simply checking for channels, and may include
+ * status information about processing that has been performed.
+ */
+enum class Capability : int {
+    MOVABLE = 0,  // Is a pose controllable camera
+    ACTIVE,     // An active depth sensor
+    VIDEO,      // Is video and not just static
+    ADJUSTABLE,  // Camera properties can be changed (exposure etc)
+    VIRTUAL,    // Is not a physical camera
+    TOUCH,      // Touch related feedback supported
+    VR,         // Is a VR device, so provides own active pose etc
+    LIVE,       // Live, not recorded (removed from ftl file sources)
+    FUSED,      // Reconstruction has been performed
+    STREAMED,   // Means it came from a stream and not device
+    EQUI_RECT,  // 360 rendered (Equirectangular Render)
+    STEREO      // Side-by-side stereo render
+};
+
+}  // namespace data
+}  // namespace ftl
diff --git a/include/ftl/utility/vectorbuffer.hpp b/include/ftl/utility/vectorbuffer.hpp
new file mode 100644
index 0000000..104ca31
--- /dev/null
+++ b/include/ftl/utility/vectorbuffer.hpp
@@ -0,0 +1,29 @@
+/**
+ * @file vectorbuffer.hpp
+ * @copyright Copyright (c) 2020 University of Turku, MIT License
+ * @author Nicolas Pope
+ */
+
+#pragma once
+
+#include <vector>
+
+namespace ftl {
+namespace util {
+
+/**
+ * Used for msgpack encoding into an existing std::vector object.
+ */
+class FTLVectorBuffer {
+ public:
+    inline explicit FTLVectorBuffer(std::vector<unsigned char> &v) : vector_(v) {}
+
+    inline void write(const char *data, std::size_t size) {
+        vector_.insert(vector_.end(), (const unsigned char*)data, (const unsigned char*)data+size);
+    }
+
+ private:
+    std::vector<unsigned char> &vector_;
+};
+}  // namespace util
+}  // namespace ftl
diff --git a/src/codecs/data.cpp b/src/codecs/data.cpp
new file mode 100644
index 0000000..ab13665
--- /dev/null
+++ b/src/codecs/data.cpp
@@ -0,0 +1,42 @@
+#include <ftl/codec/msgpack.hpp>
+
+using ftl::codec::pack;
+using ftl::codec::unpack;
+
+struct CameraMSGPACK : public ftl::data::Camera {
+    MSGPACK_DEFINE(fx, fy, cx, cy, width, height, minDepth, maxDepth, baseline, doffs);
+};
+
+// Instantiations supported without the msgpack.hpp header
+template void pack<int>(const int &v, std::vector<uint8_t> &out);
+template void pack<float>(const float &v, std::vector<uint8_t> &out);
+template void pack<std::string>(const std::string &v, std::vector<uint8_t> &out);
+template void pack<double>(const double &v, std::vector<uint8_t> &out);
+template void pack<std::vector<float>>(const std::vector<float> &v, std::vector<uint8_t> &out);
+template void pack<ftl::data::Pose>(const ftl::data::Pose &v, std::vector<uint8_t> &out);
+template void pack<std::vector<int>>(const std::vector<int> &v, std::vector<uint8_t> &out);
+template void pack<std::vector<std::string>>(const std::vector<std::string> &v, std::vector<uint8_t> &out);
+template void pack<ftl::data::StereoPose>(const ftl::data::StereoPose &v, std::vector<uint8_t> &out);
+template <> void ftl::codec::pack(const ftl::data::Intrinsics &v, std::vector<uint8_t> &out) {
+    std::tuple<CameraMSGPACK, int, int> data;
+    reinterpret_cast<ftl::data::Camera&>(std::get<0>(data)) = std::get<0>(v);
+    std::get<1>(data) = std::get<1>(v);
+    std::get<2>(data) = std::get<2>(v);
+    pack(data, out);
+}
+template void pack<ftl::data::Intrinsics>(const ftl::data::Intrinsics &v, std::vector<uint8_t> &out);
+
+template int unpack<int>(const std::vector<uint8_t> &in);
+template float unpack<float>(const std::vector<uint8_t> &in);
+template std::string unpack<std::string>(const std::vector<uint8_t> &in);
+template double unpack<double>(const std::vector<uint8_t> &in);
+template std::vector<float> unpack<std::vector<float>>(const std::vector<uint8_t> &in);
+template ftl::data::Pose unpack<ftl::data::Pose>(const std::vector<uint8_t> &in);
+template std::vector<int> unpack<std::vector<int>>(const std::vector<uint8_t> &in);
+template std::vector<std::string> unpack<std::vector<std::string>>(const std::vector<uint8_t> &in);
+template ftl::data::StereoPose unpack<ftl::data::StereoPose>(const std::vector<uint8_t> &in);
+template <> ftl::data::Intrinsics ftl::codec::unpack(const std::vector<uint8_t> &in) {
+    auto data = unpack<std::tuple<CameraMSGPACK, int, int>>(in);
+    return data;
+}
+template ftl::data::Intrinsics unpack<ftl::data::Intrinsics>(const std::vector<uint8_t> &in);
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 5788d79..e6f9fe3 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -151,3 +151,14 @@ target_link_libraries(rpc_integration beyond-protocol
 	${URIPARSER_LIBRARIES})
 
 add_test(RPCIntegrationTest rpc_integration)
+
+### Data Codec Unit ############################################################
+add_executable(datacodec_unit
+	$<TARGET_OBJECTS:CatchTestFTL>
+	./datacodec.cpp)
+target_include_directories(datacodec_unit PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../include")
+target_link_libraries(datacodec_unit beyond-protocol
+	Threads::Threads ${OS_LIBS}
+	${URIPARSER_LIBRARIES})
+
+add_test(DataCodecTest datacodec_unit)
diff --git a/test/datacodec.cpp b/test/datacodec.cpp
new file mode 100644
index 0000000..e94b1c4
--- /dev/null
+++ b/test/datacodec.cpp
@@ -0,0 +1,30 @@
+#include "catch.hpp"
+#include <ftl/codec/data.hpp>
+
+SCENARIO( "Intrinsics pack/unpack" ) {
+	GIVEN( "a valid instrincs object it packs" ) {
+		ftl::data::Intrinsics intrin;
+        std::get<0>(intrin).fx = 10.0f;
+
+        std::vector<uint8_t> buffer;
+        ftl::codec::pack(intrin, buffer);
+        REQUIRE(buffer.size() > 0);
+
+        auto result = ftl::codec::unpack<ftl::data::Intrinsics>(buffer);
+        REQUIRE(std::get<0>(result).fx == 10.0f);
+	}
+}
+
+SCENARIO( "Vector of strings pack/unpack" ) {
+	GIVEN( "a valid instrincs object it packs" ) {
+		std::vector<std::string> data = {"hello", "world"};
+
+        std::vector<uint8_t> buffer;
+        ftl::codec::pack(data, buffer);
+        REQUIRE(buffer.size() > 0);
+
+        auto result = ftl::codec::unpack<std::vector<std::string>>(buffer);
+        REQUIRE(result[0] == "hello");
+        REQUIRE(result[1] == "world");
+	}
+}
-- 
GitLab