瀏覽代碼

Encoder detects frame rate automatically.

jcsyshc 1 年之前
父節點
當前提交
1432142d57

+ 26 - 1
src/core/impl/object_manager.cpp

@@ -25,7 +25,18 @@ object_manager::impl::query_info(name_type obj_name) {
     auto iter = obj_pool.find(obj_name);
     if (iter == obj_pool.end()) [[unlikely]] return {};
     auto &st = iter->second;
-    return obj_info{.type = st.type, .pl_ptr = st.ptr, .sig = &st.sig};
+    return obj_info{
+            .type = st.type, .pl_ptr = st.ptr,
+            .sig = &st.sig, .last_save_ts = st.last_save_ts};
+}
+
+object_manager::obj_stats
+object_manager::impl::query_obj_stats(name_type obj_name) {
+    auto obj_st = query_st(obj_name);
+    auto ret = obj_stats();
+    ret.save_interval = ba::rolling_mean(obj_st->interval_acc) * 1e3f; // ms
+    ret.save_frequency = 1e3f / ret.save_interval; // Hz
+    return ret;
 }
 
 void object_manager::impl::create_placeholder(name_type obj_name, std::type_index obj_type,
@@ -37,6 +48,7 @@ void object_manager::impl::create_placeholder(name_type obj_name, std::type_inde
 }
 
 void object_manager::impl::notify_signal(name_type obj_name) {
+    auto cur_ts = current_timestamp();
     auto obj_st = query_st(obj_name);
     if (!obj_st->is_pending) {
         post(*ctx, [=] {
@@ -45,6 +57,14 @@ void object_manager::impl::notify_signal(name_type obj_name) {
         });
         obj_st->is_pending = true;
     }
+
+    // stats
+    if (obj_st->last_save_ts != 0) {
+        assert(cur_ts > obj_st->last_save_ts);
+        auto interval = (cur_ts - obj_st->last_save_ts) / 1e6f;
+        obj_st->interval_acc(interval);
+    }
+    obj_st->last_save_ts = cur_ts;
 }
 
 io_context *object_manager::impl::switch_ctx() const {
@@ -69,6 +89,11 @@ object_manager::query_info(name_type obj_name) {
     return pimpl->query_info(obj_name);
 }
 
+object_manager::obj_stats
+object_manager::query_obj_stats(name_type obj_name) {
+    return pimpl->query_obj_stats(obj_name);
+}
+
 io_context *object_manager::switch_ctx() {
     return pimpl->switch_ctx();
 }

+ 17 - 0
src/core/impl/object_manager_impl.h

@@ -3,10 +3,16 @@
 
 #include "core/object_manager.h"
 
+#include <boost/accumulators/accumulators.hpp>
+#include <boost/accumulators/statistics.hpp>
+#include <boost/accumulators/statistics/rolling_mean.hpp>
+
 #include <list>
 #include <thread>
 #include <unordered_map>
 
+namespace ba = boost::accumulators;
+
 struct object_manager::impl {
 
     struct obj_st_type { // object store type
@@ -15,6 +21,15 @@ struct object_manager::impl {
         std::type_index type;
         bool is_pending = false; // whether signal is queued.
         obj_sig_type sig;
+
+        // statistical information
+        timestamp_type last_save_ts = 0;
+
+        static constexpr auto acc_window_size = 5;
+        using interval_acc_type = ba::accumulator_set<
+                float, ba::stats<ba::tag::lazy_rolling_mean>>;
+        interval_acc_type interval_acc = interval_acc_type(
+                ba::tag::rolling_window::window_size = acc_window_size);
     };
 
     using obj_pool_type = std::unordered_map<name_type, obj_st_type>;
@@ -27,6 +42,8 @@ struct object_manager::impl {
 
     obj_st_type *query_st(name_type obj_name);
 
+    obj_stats query_obj_stats(name_type obj_name);
+
     std::optional<obj_info> query_info(name_type obj_name);
 
     void create_placeholder(name_type obj_name, std::type_index obj_type,

+ 22 - 0
src/core/object_manager.h

@@ -1,6 +1,8 @@
 #ifndef DEPTHGUIDE_OBJECT_MANAGER_H
 #define DEPTHGUIDE_OBJECT_MANAGER_H
 
+#include "core/utility.hpp"
+
 #include <boost/asio/post.hpp>
 #include <boost/asio/io_context.hpp>
 #include <boost/signals2.hpp>
@@ -67,6 +69,12 @@ public:
         return info_o->type;
     }
 
+    timestamp_type query_save_ts(name_type obj_name) {
+        auto info_o = query_info(obj_name);
+        assert(info_o.has_value());
+        return info_o->last_save_ts;
+    }
+
     using obj_sig_type = boost::signals2::signal<void(name_type)>;
 
     obj_sig_type *query_signal(name_type obj_name) {
@@ -75,6 +83,13 @@ public:
         return info_o->sig;
     }
 
+    struct obj_stats {
+        float save_interval = 0.0f; // in ms
+        float save_frequency = 0.0f; // in Hz
+    };
+
+    obj_stats query_obj_stats(name_type obj_name);
+
 private:
 
     using del_func_type = void (*)(void *);
@@ -83,6 +98,7 @@ private:
         std::type_index type = typeid(void);
         void *pl_ptr = nullptr;
         obj_sig_type *sig = nullptr;
+        timestamp_type last_save_ts = 0;
     };
 
     std::optional<obj_info> query_info(name_type obj_name);
@@ -118,6 +134,12 @@ extern object_manager *main_ob;
 #define OBJ_TYPE(name) \
     main_ob->query_type(name)
 
+#define OBJ_SAVE_TS(name) \
+    main_ob->query_save_ts(name)
+
+#define OBJ_STATS(name) \
+    main_ob->query_obj_stats(name)
+
 #define OBJ_SAVE(name, val) \
     main_ob->save(name, val)
 

+ 4 - 2
src/device/impl/orb_camera_ui.cpp

@@ -146,14 +146,16 @@ void orb_camera_ui::impl::show() {
 
     ImGui::SeparatorText("Info");
     auto c_img = OBJ_QUERY(image_u8c3, cam_s_conf.color.name);
+    auto c_interval = OBJ_STATS(cam_s_conf.color.name).save_interval;
     if (c_img != nullptr) {
         auto size = c_img->size();
-        ImGui::Text("Color Stream: %dx%d", size.width, size.height);
+        ImGui::Text("Color Stream: %dx%d / %.2fms", size.width, size.height, c_interval);
     }
     auto d_img = OBJ_QUERY(image_f32c1, cam_s_conf.depth.name);
+    auto d_interval = OBJ_STATS(cam_s_conf.depth.name).save_interval;
     if (d_img != nullptr) {
         auto size = d_img->size();
-        ImGui::Text("Depth Stream: %dx%d", size.width, size.height);
+        ImGui::Text("Depth Stream: %dx%d / %.2fms", size.width, size.height, d_interval);
     }
 }
 

+ 2 - 1
src/impl/main_impl.cpp

@@ -147,7 +147,8 @@ void init_modules() {
     out_downloader = std::make_unique<viewport_downloader>(out_down_conf);
 
     auto out_streamer_conf = image_streamer::create_config{
-            .img_name = img_out, .cuda_ctx = &cuda_ctx, .stream = default_cuda_stream
+            .img_name = img_out, .asio_ctx = main_ctx,
+            .cuda_ctx = &cuda_ctx, .stream = default_cuda_stream
     };
     out_streamer = std::make_unique<image_streamer>(out_streamer_conf);
 }

+ 1 - 0
src/module/image_streamer.h

@@ -12,6 +12,7 @@ public:
     struct create_config {
         // image must be valid before start
         obj_name_type img_name = invalid_obj_name;
+        boost::asio::io_context *asio_ctx = nullptr;
 
         // for encoder
         CUcontext *cuda_ctx = nullptr;

+ 7 - 7
src/module/impl/image_streamer.cpp

@@ -5,10 +5,11 @@ void image_streamer::impl::create_encoder() {
     switch (chose_encoder_type) {
         case ENCODER_NVENC: {
             auto img_info = OBJ_QUERY(image_u8c4, conf.img_name)->as_info();
+            int img_freq = std::round(OBJ_STATS(conf.img_name).save_frequency);
 
             auto enc_conf = encoder_nvenc::create_config();
             enc_conf.frame_size = img_info.size;
-            enc_conf.frame_rate = 30; // TODO: determine in runtime
+            enc_conf.frame_rate = img_freq;
             enc_conf.bitrate_mbps = enc_bitrate_mbps;
             enc_conf.save_file = enc_save_file;
             enc_conf.save_length = enc_save_length;
@@ -31,7 +32,7 @@ void image_streamer::impl::create_sender() {
         assert(aux_ctx != nullptr);
         sender_ctx = aux_ctx.get();
     } else {
-        sender_ctx = main_ctx;
+        sender_ctx = ctx;
     }
 
     assert(sender == nullptr);
@@ -63,7 +64,7 @@ void image_streamer::impl::create_sender() {
 
     if (enable_aux_thread) {
         sender->sig_req_idr.connect([this] {
-            post(*main_ctx, [this] {
+            post(*ctx, [this] {
                 enc_idr_requested = true;
             });
         });
@@ -230,11 +231,11 @@ void image_streamer::impl::show() {
     ImGui::SeparatorText("Actions");
     if (!is_running) {
         if (ImGui::Button("Start")) {
-            post(*main_ctx, [this] { start(); });
+            post(*ctx, [this] { start(); });
         }
     } else {
         if (ImGui::Button("Close")) {
-            post(*main_ctx, [this] { stop(); });
+            post(*ctx, [this] { stop(); });
         }
         ImGui::SameLine();
         if (ImGui::Button("Request IDR")) {
@@ -245,8 +246,7 @@ void image_streamer::impl::show() {
 }
 
 image_streamer::image_streamer(create_config conf)
-        : pimpl(std::make_unique<impl>()) {
-    pimpl->conf = conf;
+        : pimpl(std::make_unique<impl>(conf)) {
     pimpl->q_this = this;
 }
 

+ 6 - 2
src/module/impl/image_streamer_impl.h

@@ -13,12 +13,11 @@
 using boost::asio::io_context;
 using boost::asio::post;
 
-extern io_context *main_ctx;
-
 struct image_streamer::impl {
 
     create_config conf;
     image_streamer *q_this = nullptr;
+    io_context *ctx = nullptr;
     bool is_running = false;
 
     encoder_type chose_encoder_type = ENCODER_NVENC;
@@ -60,6 +59,11 @@ struct image_streamer::impl {
     using conn_type = boost::signals2::connection;
     conn_type img_cb_conn;
 
+    explicit impl(create_config _conf) {
+        conf = _conf;
+        ctx = conf.asio_ctx;
+    }
+
     ~impl() {
         if (is_running) {
             stop();