Forráskód Böngészése

Implemented TCP frame sender.

jcsyshc 2 éve
szülő
commit
61c4a40c05

+ 3 - 1
CMakeLists.txt

@@ -5,7 +5,9 @@ set(CMAKE_CXX_STANDARD 20)
 
 add_executable(RemoteAR3 src/main.cpp
         src/main_ext.cpp
-        src/frame_sender.cpp
+        src/frame_sender/sender_base.cpp
+        src/frame_sender/sender_udp_fec.cpp
+        src/frame_sender/sender_tcp.cpp
         src/image_process.cpp
         src/simple_mq.cpp
         src/third_party/rs.c

+ 5 - 0
src/components/registration.cpp

@@ -191,6 +191,11 @@ struct registration::impl {
         is_collecting = false;
         CALL_CHECK(conn->stop_object(cur_target->collect_obj_name));
 
+        // clear point set
+        cur_target->pending_points->clear();
+        cur_target->current_point->clear();
+        cur_target->finished_points->clear();
+
         // switch actor
         if (cur_target->is_finished) {
             switch_viewer_mode(FINISHED);

+ 0 - 30
src/frame_sender.h

@@ -1,30 +0,0 @@
-#ifndef REMOTEAR3_FRAME_SENDER_H
-#define REMOTEAR3_FRAME_SENDER_H
-
-#include "video_encoder.h"
-
-#include <memory>
-
-struct sender_config {
-    uint16_t conn_mtu;
-    float parity_rate;
-    uint16_t listen_port;
-};
-
-class frame_sender {
-public:
-    ~frame_sender();
-
-    void send_frame(std::unique_ptr<video_nal> &&frame);
-
-    void run();
-
-    static frame_sender *create(const sender_config &conf);
-
-private:
-    struct impl;
-    std::unique_ptr<impl> pimpl;
-};
-
-
-#endif //REMOTEAR3_FRAME_SENDER_H

+ 115 - 0
src/frame_sender/sender_base.cpp

@@ -0,0 +1,115 @@
+#include "sender_base.h"
+#include "simple_mq.h"
+#include "variable_defs.h"
+
+#include <boost/asio/post.hpp>
+
+#include <spdlog/spdlog.h>
+
+#include <deque>
+#include <mutex>
+
+using boost::asio::io_context;
+using boost::asio::post;
+using namespace simple_mq_singleton;
+
+struct sender_base::impl {
+
+    using frame_ptr_type = std::unique_ptr<video_nal>;
+    using frame_list_type = std::deque<frame_ptr_type>;
+
+    frame_list_type frame_list;
+    std::mutex frame_list_mu;
+
+    sender_base *q_this = nullptr;
+    std::unique_ptr<io_context> ctx;
+    bool waiting_idr = false;
+
+    impl() {
+        ctx = std::make_unique<io_context>();
+    }
+
+    void clear_frame_list() {
+        auto lock = std::lock_guard{frame_list_mu};
+        frame_list.clear();
+    }
+
+    void request_idr_frame() {
+        mq().update_variable(REQUEST_IDR, true);
+        clear_frame_list();
+        waiting_idr = true;
+    }
+
+    frame_ptr_type retrieve_one_frame() {
+        auto lock = std::lock_guard{frame_list_mu};
+        if (frame_list.empty()) return nullptr;
+        auto frame = std::move(*frame_list.begin());
+        frame_list.pop_front();
+        if (waiting_idr) { // if idr frame is requested, only idr frame will be returned.
+            if (!frame->idr) return nullptr;
+            waiting_idr = false;
+        }
+        return std::move(frame);
+    }
+
+    void handle_frames() {
+        for (;;) {
+            // test stop flag
+            if (mq().query_variable<bool>(SENDER_SHOULD_STOP)) {
+                q_this->close_connection();
+                ctx->stop();
+                SPDLOG_INFO("Frame sender stopped.");
+                return;
+            }
+
+            auto frame = retrieve_one_frame();
+            if (frame == nullptr) return;
+            q_this->handle_frame(std::move(frame));
+        }
+    }
+
+    void push_frame_impl(frame_ptr_type &&frame) {
+        if (frame == nullptr) {
+            return; // signaling frame
+        }
+        auto lock = std::lock_guard{frame_list_mu};
+        assert(frame != nullptr);
+        if (frame->idr) {
+            frame_list.clear();
+        }
+        frame_list.emplace_back(std::move(frame));
+    }
+
+    void push_frame(frame_ptr_type &&frame) {
+        push_frame_impl(std::move(frame));
+        post(*ctx, [this] { handle_frames(); });
+    }
+
+};
+
+sender_base::sender_base()
+        : pimpl(std::make_unique<impl>()) {
+    pimpl->q_this = this;
+}
+
+sender_base::~sender_base() = default;
+
+io_context *sender_base::get_ctx() {
+    return pimpl->ctx.get();
+}
+
+void sender_base::send_frame(frame_ptr_type &&frame) {
+    pimpl->push_frame(std::move(frame));
+}
+
+void sender_base::request_idr_frame() {
+    pimpl->request_idr_frame();
+}
+
+void sender_base::run() {
+    // make run() block forever
+    auto blocker = boost::asio::make_work_guard(*pimpl->ctx);
+
+    SPDLOG_INFO("Frame sender started.");
+    pimpl->ctx->run();
+}

+ 43 - 0
src/frame_sender/sender_base.h

@@ -0,0 +1,43 @@
+#ifndef REMOTEAR3_SENDER_BASE_HPP
+#define REMOTEAR3_SENDER_BASE_HPP
+
+#include "video_encoder.h"
+
+#include <boost/asio/io_context.hpp>
+
+#include <memory>
+
+enum sender_type {
+    SENDER_TCP,
+    SENDER_UDP_FEC
+};
+
+class sender_base {
+public:
+
+    sender_base();
+
+    virtual ~sender_base();
+
+    void send_frame(std::unique_ptr<video_nal> &&frame);
+
+    void run();
+
+protected:
+
+    using frame_ptr_type = std::unique_ptr<video_nal>;
+
+    void request_idr_frame();
+
+    virtual void handle_frame(frame_ptr_type &&frame) = 0;
+
+    virtual void close_connection() = 0;
+
+    boost::asio::io_context *get_ctx();
+
+private:
+    struct impl;
+    std::unique_ptr<impl> pimpl;
+};
+
+#endif //REMOTEAR3_SENDER_BASE_HPP

+ 102 - 0
src/frame_sender/sender_tcp.cpp

@@ -0,0 +1,102 @@
+#include "sender_tcp.h"
+#include "sender_utility.hpp"
+#include "simple_mq.h"
+#include "variable_defs.h"
+
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/post.hpp>
+#include <boost/asio/write.hpp>
+
+#include <spdlog/spdlog.h>
+
+#include <deque>
+
+using namespace boost::asio::ip;
+using boost::asio::buffer;
+using boost::asio::io_context;
+using boost::asio::post;
+using boost::asio::write;
+using boost::system::error_code;
+
+using namespace sender_impl;
+using namespace simple_mq_singleton;
+
+struct sender_tcp::impl {
+
+    sender_tcp *q_this = nullptr;
+    std::unique_ptr<tcp::acceptor> acceptor;
+    std::unique_ptr<tcp::socket> socket;
+    smart_buffer<uint8_t> out_buf;
+
+    void close_connection() {
+        if (socket == nullptr) return;
+        auto remote_ep = socket->remote_endpoint();
+        SPDLOG_INFO("Client {}:{} left.",
+                    remote_ep.address().to_string(), remote_ep.port());
+        socket.reset();
+        mq().update_variable(SENDER_CONNECTED, false);
+    }
+
+    void send_frame(frame_ptr_type &&frame) {
+        if (socket == nullptr) return;
+
+        out_buf.create(frame->length + sizeof(frame->length));
+        auto ptr = write_binary_number(out_buf.ptr, frame->length);
+        memcpy(ptr, frame->ptr, frame->length);
+
+        assert(socket->is_open());
+        error_code err;
+        write(*socket, buffer(out_buf.ptr, out_buf.length), err);
+        if (err) {
+            SPDLOG_WARN("Error while sending frame: {}", err.to_string());
+            close_connection();
+            async_waiting_client();
+        }
+    }
+
+    void async_waiting_client() {
+        acceptor->async_accept(*socket, [this](error_code err) {
+            if (err) {
+                SPDLOG_ERROR("Error while accepting client: {}", err.to_string());
+                async_waiting_client();
+            }
+            assert(socket->is_open());
+            q_this->request_idr_frame();
+            mq().update_variable(SENDER_CONNECTED, true);
+
+            auto remote_ep = socket->remote_endpoint();
+            SPDLOG_INFO("New client from {}:{}.",
+                        remote_ep.address().to_string(), remote_ep.port());
+        });
+    }
+
+    static impl *create(uint16_t listen_port, sender_tcp *q_this) {
+        auto ret = new impl;
+        ret->q_this = q_this;
+        auto listen_ep = tcp::endpoint{tcp::v4(), listen_port};
+        ret->acceptor = std::make_unique<tcp::acceptor>(*q_this->get_ctx(), listen_ep);
+        ret->socket = std::make_unique<tcp::socket>(*q_this->get_ctx());
+        ret->async_waiting_client();
+        return ret;
+    }
+
+};
+
+sender_tcp::~sender_tcp() = default;
+
+sender_tcp *sender_tcp::create(uint16_t listen_port) {
+    auto ret = std::make_unique<sender_tcp>();
+    auto pimpl = impl::create(listen_port, ret.get());
+    if (pimpl == nullptr) return nullptr;
+    ret->pimpl.reset(pimpl);
+    return ret.release();
+}
+
+void sender_tcp::close_connection() {
+    pimpl->close_connection();
+}
+
+void sender_tcp::handle_frame(frame_ptr_type &&frame) {
+    pimpl->send_frame(std::move(frame));
+}

+ 27 - 0
src/frame_sender/sender_tcp.h

@@ -0,0 +1,27 @@
+#ifndef REMOTEAR3_SENDER_TCP_H
+#define REMOTEAR3_SENDER_TCP_H
+
+#include "sender_base.h"
+
+#include <memory>
+
+class sender_tcp : public sender_base {
+public:
+
+    ~sender_tcp() override;
+
+    static sender_tcp *create(uint16_t listen_port);
+
+protected:
+
+    void handle_frame(frame_ptr_type &&frame) override;
+
+    void close_connection() override;
+
+private:
+    struct impl;
+    std::unique_ptr<impl> pimpl;
+};
+
+
+#endif //REMOTEAR3_SENDER_TCP_H

+ 25 - 124
src/frame_sender.cpp → src/frame_sender/sender_udp_fec.cpp

@@ -1,4 +1,5 @@
-#include "frame_sender.h"
+#include "sender_udp_fec.h"
+#include "sender_utility.hpp"
 #include "core/timestamp_helper.hpp"
 #include "simple_mq.h"
 #include "third_party/scope_guard.hpp"
@@ -13,47 +14,19 @@ extern "C" {
 #include <boost/asio/ip/udp.hpp>
 #include <boost/asio/post.hpp>
 #include <boost/crc.hpp>
-#include <boost/endian.hpp>
 
 #include <spdlog/spdlog.h>
 
-#include <deque>
-#include <mutex>
-
 using namespace boost::asio::ip;
 using boost::asio::buffer;
 using boost::asio::io_context;
 using boost::asio::post;
 using boost::system::error_code;
+using namespace sender_impl;
 using namespace sophiar;
 using namespace simple_mq_singleton;
 
-namespace frame_sender_impl {
-
-    template<typename T>
-    struct smart_buffer {
-        T *ptr = nullptr;
-        size_t length = 0;
-
-        ~smart_buffer() {
-            free(ptr);
-        }
-
-        void create(size_t req_length) {
-            if (req_length > capacity) [[unlikely]] {
-                auto ptr_next = new T[req_length];
-                if (ptr != nullptr) {
-                    delete ptr;
-                }
-                ptr = ptr_next;
-                capacity = req_length;
-            }
-            length = req_length;
-        }
-
-    private:
-        size_t capacity = 0;
-    };
+namespace sender_udp_fec_impl {
 
     struct smart_reed_solomon {
         reed_solomon *rs = nullptr;
@@ -104,9 +77,9 @@ namespace frame_sender_impl {
 
 }
 
-using namespace frame_sender_impl;
+using namespace sender_udp_fec_impl;
 
-struct frame_sender::impl {
+struct sender_udp_fec::impl {
 
     struct frag_header {
         uint32_t frag_checksum;
@@ -137,14 +110,10 @@ struct frame_sender::impl {
     static constexpr int confirm_timeout = 10 * 1e6; // 10s
 
     using frame_ptr_type = std::unique_ptr<video_nal>;
-    using frame_list_type = std::deque<frame_ptr_type>;
 
-    std::unique_ptr<io_context> ctx;
+    sender_udp_fec *q_this = nullptr;
     std::unique_ptr<udp::socket> socket;
 
-    frame_list_type frame_list;
-    std::mutex frame_list_mu;
-
     udp::endpoint request_ep;
     std::unique_ptr<udp::endpoint> remote_ep;
     float parity_rate;
@@ -156,30 +125,6 @@ struct frame_sender::impl {
     smart_buffer<uint8_t> in_buf, out_buf;
     timestamp_type last_confirm_ts = 0;
 
-    template<typename T>
-    static uint8_t *write_binary_number(uint8_t *ptr, T val) {
-        static constexpr auto need_swap =
-                (boost::endian::order::native != boost::endian::order::big);
-        auto real_ptr = (T *) ptr;
-        if constexpr (need_swap) {
-            *real_ptr = boost::endian::endian_reverse(val);
-        } else {
-            *real_ptr = val;
-        }
-        return ptr + sizeof(T);
-    }
-
-    template<typename T>
-    static uint8_t *read_binary_number(uint8_t *ptr, T *val) {
-        static constexpr auto need_swap =
-                (boost::endian::order::native != boost::endian::order::big);
-        *val = *(T *) ptr;
-        if constexpr (need_swap) {
-            boost::endian::endian_reverse_inplace(*val);
-        }
-        return ptr + sizeof(T);
-    }
-
     static uint8_t *write_frag_header(uint8_t *ptr, const frag_header &header) {
 #define WRITE(member) ptr = write_binary_number(ptr, header.member)
         WRITE(frag_checksum);
@@ -240,7 +185,9 @@ struct frame_sender::impl {
         }
     }
 
-    void send_frame(const frame_ptr_type &frame) {
+    void send_frame(frame_ptr_type &&frame) {
+        if (remote_ep == nullptr) return;
+
         frag_header header;
         header.frame_type = frame->idr ? 'I' : 'P';
         header.frame_id = frame_id;
@@ -267,11 +214,6 @@ struct frame_sender::impl {
         socket->async_receive_from(buf, request_ep, std::bind(&impl::handle_request, this, _1, _2));
     }
 
-    void clear_frame_list() {
-        auto lock = std::lock_guard{frame_list_mu};
-        frame_list.clear();
-    }
-
     void handle_request(const error_code &ec, size_t length) {
         // prepare for next request when this function exited.
         auto closer = sg::make_scope_guard([this] {
@@ -312,8 +254,7 @@ struct frame_sender::impl {
             case 'I': {
                 remote_ep = std::make_unique<udp::endpoint>(request_ep);
                 last_confirm_ts = current_timestamp();
-                clear_frame_list();
-                mq().update_variable(REQUEST_IDR, true);
+                q_this->request_idr_frame();
                 mq().update_variable(SENDER_CONNECTED, true);
 
                 static uint32_t last_frame_id = 0;
@@ -332,7 +273,6 @@ struct frame_sender::impl {
     }
 
     void close_connection() {
-        clear_frame_list();
         remote_ep.reset();
         mq().update_variable(SENDER_CONNECTED, false);
     }
@@ -347,49 +287,11 @@ struct frame_sender::impl {
         return true;
     }
 
-    // return true to continue
-    bool handle_one_frame() {
-        frame_ptr_type frame;
-        {
-            auto lock = std::lock_guard{frame_list_mu};
-            if (frame_list.empty()) return false;
-            frame = std::move(*frame_list.begin());
-            frame_list.pop_front();
-        }
-        if (frame == nullptr) return false;
-        send_frame(frame);
-        return true;
-    }
-
-    void handle_frames() {
-        // test stop flag
-        if (mq().query_variable<bool>(SENDER_SHOULD_STOP)) {
-            close_connection();
-            ctx->stop();
-            SPDLOG_INFO("Frame sender stopped.");
-            return;
-        }
-
-        if (!is_connected()) return;
-        while (handle_one_frame());
-    }
-
-    void push_frame(frame_ptr_type &&frame) {
-        {
-            auto lock = std::lock_guard{frame_list_mu};
-            if (frame != nullptr && frame->idr) { // maybe nullptr
-                frame_list.clear();
-            }
-            frame_list.emplace_back(std::move(frame));
-        }
-        post(*ctx, std::bind(&impl::handle_frames, this));
-    }
-
-    static impl *create(const sender_config &conf) {
+    static impl *create(const config &conf, sender_udp_fec *q_this) {
         auto ret = new impl;
-        ret->ctx = std::make_unique<io_context>();
         auto local_ep = udp::endpoint{udp::v4(), conf.listen_port};
-        ret->socket = std::make_unique<udp::socket>(*(ret->ctx), local_ep);
+        ret->q_this = q_this;
+        ret->socket = std::make_unique<udp::socket>(*q_this->get_ctx(), local_ep);
         ret->socket->set_option(udp::socket::send_buffer_size{udp_buffer_size});
         ret->async_handle_request();
 
@@ -406,21 +308,20 @@ struct frame_sender::impl {
     }
 };
 
-frame_sender::~frame_sender() = default;
+sender_udp_fec::~sender_udp_fec() = default;
 
-void frame_sender::send_frame(std::unique_ptr<video_nal> &&frame) {
-    pimpl->push_frame(std::move(frame));
+sender_udp_fec *sender_udp_fec::create(const config &conf) {
+    auto ret = std::make_unique<sender_udp_fec>();
+    auto pimpl = impl::create(conf, ret.get());
+    if (pimpl == nullptr) return nullptr;
+    ret->pimpl.reset(pimpl);
+    return ret.release();
 }
 
-void frame_sender::run() {
-    SPDLOG_INFO("Frame sender started.");
-    pimpl->ctx->run();
+void sender_udp_fec::close_connection() {
+    pimpl->close_connection();
 }
 
-frame_sender *frame_sender::create(const sender_config &conf) {
-    auto pimpl = impl::create(conf);
-    if (pimpl == nullptr) return nullptr;
-    auto ret = new frame_sender;
-    ret->pimpl.reset(pimpl);
-    return ret;
+void sender_udp_fec::handle_frame(frame_ptr_type &&frame) {
+    pimpl->send_frame(std::move(frame));
 }

+ 32 - 0
src/frame_sender/sender_udp_fec.h

@@ -0,0 +1,32 @@
+#ifndef REMOTEAR3_SENDER_UDP_FEC_H
+#define REMOTEAR3_SENDER_UDP_FEC_H
+
+#include "sender_base.h"
+
+#include <memory>
+
+class sender_udp_fec : public sender_base {
+public:
+    ~sender_udp_fec() override;
+
+    struct config {
+        uint16_t conn_mtu;
+        float parity_rate;
+        uint16_t listen_port;
+    };
+
+    static sender_udp_fec *create(const config &conf);
+
+protected:
+
+    void handle_frame(frame_ptr_type &&frame) override;
+
+    void close_connection() override;
+
+private:
+    struct impl;
+    std::unique_ptr<impl> pimpl;
+};
+
+
+#endif //REMOTEAR3_SENDER_UDP_FEC_H

+ 56 - 0
src/frame_sender/sender_utility.hpp

@@ -0,0 +1,56 @@
+#ifndef REMOTEAR3_SENDER_UTILITY_HPP
+#define REMOTEAR3_SENDER_UTILITY_HPP
+
+#include <boost/endian.hpp>
+
+namespace sender_impl {
+
+    template<typename T>
+    struct smart_buffer {
+        T *ptr = nullptr;
+        size_t length = 0;
+
+        ~smart_buffer() {
+            delete ptr;
+        }
+
+        void create(size_t req_length) {
+            if (req_length > capacity) [[unlikely]] {
+                delete ptr;
+                ptr = new T[req_length];
+                capacity = req_length;
+            }
+            length = req_length;
+        }
+
+    private:
+        size_t capacity = 0;
+    };
+
+    template<typename T>
+    static uint8_t *write_binary_number(uint8_t *ptr, T val) {
+        static constexpr auto need_swap =
+                (boost::endian::order::native != boost::endian::order::big);
+        auto real_ptr = (T *) ptr;
+        if constexpr (need_swap) {
+            *real_ptr = boost::endian::endian_reverse(val);
+        } else {
+            *real_ptr = val;
+        }
+        return ptr + sizeof(T);
+    }
+
+    template<typename T>
+    static uint8_t *read_binary_number(uint8_t *ptr, T *val) {
+        static constexpr auto need_swap =
+                (boost::endian::order::native != boost::endian::order::big);
+        *val = *(T *) ptr;
+        if constexpr (need_swap) {
+            boost::endian::endian_reverse_inplace(*val);
+        }
+        return ptr + sizeof(T);
+    }
+
+}
+
+#endif //REMOTEAR3_SENDER_UTILITY_HPP

+ 33 - 17
src/main_ext.cpp

@@ -2,7 +2,8 @@
 #include "core/local_connection.h"
 #include "core/timestamp_helper.hpp"
 #include "cuda_helper.hpp"
-#include "frame_sender.h"
+#include "frame_sender/sender_tcp.h"
+#include "frame_sender/sender_udp_fec.h"
 #include "image_process.h"
 #include "mvs_camera.h"
 #include "simple_mq.h"
@@ -81,9 +82,11 @@ cudaStream_t output_cuda_stream = nullptr;
 std::shared_ptr<cv::cuda::GpuMat> output_frame_dev;
 
 std::unique_ptr<std::thread> sender_thread;
+std::unique_ptr<sender_base> sender;
+sender_type chosen_sender = SENDER_TCP;
 int sender_listen_port;
-sender_config main_sender_conf;
-std::unique_ptr<frame_sender> sender;
+uint16_t sender_mtu;
+float sender_parity_rate;
 
 std::string sophiar_config_path;
 std::unique_ptr<std::thread> sophiar_thread;
@@ -130,7 +133,7 @@ struct camera_related {
     }
 
     void load_intrinsic(YAML::Node conf) {
-        camera_intrinsic info;
+        camera_intrinsic info{};
         info.fx = conf["fx"].as<float>();
         info.fy = conf["fy"].as<float>();
         info.cx = conf["cx"].as<float>();
@@ -271,8 +274,8 @@ void load_config() {
 
     // load sender config
     auto sender_conf = conf["sender"];
-    main_sender_conf.conn_mtu = sender_conf["mtu"].as<int>();
-    main_sender_conf.parity_rate = sender_conf["parity"].as<float>();
+    sender_mtu = sender_conf["mtu"].as<int>();
+    sender_parity_rate = sender_conf["parity"].as<float>();
     sender_listen_port = sender_conf["port"].as<int>();
 
     // load sophiar config
@@ -463,11 +466,29 @@ void open_cameras() {
 }
 
 void sender_thread_work() {
+    // create sender
     assert(sender == nullptr);
-    auto conf = mq().query_variable<sender_config>(SENDER_CONFIG);
-    sender = std::unique_ptr<frame_sender>(frame_sender::create(conf));
+    switch (chosen_sender) {
+        case SENDER_UDP_FEC: {
+            sender_udp_fec::config conf{};
+            conf.listen_port = sender_listen_port;
+            conf.conn_mtu = sender_mtu;
+            conf.parity_rate = sender_parity_rate;
+            sender.reset(sender_udp_fec::create(conf));
+            break;
+        }
+        case SENDER_TCP: {
+            sender.reset(sender_tcp::create(sender_listen_port));
+            break;
+        }
+        default: {
+            assert(false);
+            unreachable();
+        }
+    }
+
+    // start sender
     sender->run();
-    sender.reset();
 }
 
 void encoder_thread_work() {
@@ -516,13 +537,7 @@ bool is_sending() {
     return sender_thread != nullptr;
 }
 
-void upload_sender_config() {
-    main_sender_conf.listen_port = sender_listen_port;
-    mq().update_variable(SENDER_CONFIG, main_sender_conf);
-}
-
 void start_sender() {
-    upload_sender_config();
     mq().update_variable(SENDER_SHOULD_STOP, false);
     sender_thread = std::make_unique<std::thread>(sender_thread_work);
 }
@@ -533,6 +548,7 @@ void stop_sender() {
     sender->send_frame(nullptr);
     sender_thread->join();
     sender_thread.reset();
+    sender.reset();
 }
 
 bool is_encoding() {
@@ -821,7 +837,7 @@ void prepare_imgui_frame() {
                 ImGui::BeginDisabled();
             }
             ImGui::InputInt("Listen Port", &sender_listen_port);
-            ImGui::DragFloat("Parity Rate", &main_sender_conf.parity_rate, 0.01, 0, 2, "%.02f");
+            ImGui::DragFloat("Parity Rate", &sender_parity_rate, 0.01, 0, 2, "%.02f");
             if (is_sending()) {
                 ImGui::EndDisabled();
             }
@@ -897,7 +913,7 @@ void render_main_window() {
 
     if (is_capturing()) {
         // draw preview camera frame
-        assert(left->img_dev->size() == right.img_dev->size());
+        assert(left->img_dev->size() == right->img_dev->size());
         float width_normal = left->img_dev->size().aspectRatio() / frame_size.aspectRatio();
         auto render_rect = simple_rect{
                 -width_normal, -1, 2 * width_normal, 2

+ 0 - 1
src/variable_defs.h

@@ -14,7 +14,6 @@ constexpr auto CUDA_CONTEXT = 6;
 constexpr auto REQUEST_IDR = 7;
 constexpr auto SENDER_CONNECTED = 8;
 constexpr auto SENDER_SHOULD_STOP = 9;
-constexpr auto SENDER_CONFIG = 10;
 
 // global variable declaration
 

+ 4 - 1
src/vtk_viewer.cpp

@@ -147,6 +147,9 @@ struct vtk_viewer::impl {
         auto view_pos = ImGui::GetWindowPos();
         auto x_pos = io.MousePos.x - view_pos.x;
         auto y_pos = io.MousePos.y - view_pos.y;
+
+        // flip X axis
+        x_pos = controller->GetSize()[0] - x_pos;
         controller->SetEventInformation(x_pos, y_pos, io.KeyCtrl, io.KeyShift);
 
         // dispatch event
@@ -189,7 +192,7 @@ struct vtk_viewer::impl {
         auto render_size_cv = to_cv_size(render_size);
         if (render_size_cv.area() <= 0)return;
         render(render_size_cv, true);
-        ImGui::Image(reinterpret_cast<void *>(get_tex()), render_size);
+        ImGui::Image(reinterpret_cast<void *>(get_tex()), render_size, {1, 0}, {0, 1});
         ImGui::EndChild();
     }