|
|
@@ -3,7 +3,9 @@
|
|
|
#include "mvs_camera.h"
|
|
|
#include "simple_mq.h"
|
|
|
#include "simple_opengl.h"
|
|
|
+#include "third_party/timestamp_helper.hpp"
|
|
|
#include "variable_defs.h"
|
|
|
+#include "video_encoder.h"
|
|
|
|
|
|
#include <spdlog/spdlog.h>
|
|
|
|
|
|
@@ -20,16 +22,22 @@
|
|
|
#include <imgui_impl_opengl3.h>
|
|
|
|
|
|
#include <cassert>
|
|
|
+#include <thread>
|
|
|
#include <queue>
|
|
|
|
|
|
using namespace simple_mq_singleton;
|
|
|
+using namespace sophiar;
|
|
|
+
|
|
|
+// make sophiar happy
|
|
|
+local_time_type sophiar::program_start_time;
|
|
|
|
|
|
// global variable definition
|
|
|
-CUcontext cuda_ctx;
|
|
|
-GLFWwindow *main_window;
|
|
|
+CUcontext cuda_ctx = nullptr;
|
|
|
+int main_window_width = -1, main_window_height = -1;
|
|
|
+GLFWwindow *main_window = nullptr;
|
|
|
std::string left_camera_name, right_camera_name;
|
|
|
std::unique_ptr<mvs::camera> left_camera, right_camera;
|
|
|
-mvs::capture_config capture_conf;
|
|
|
+mvs::capture_config capture_conf = {};
|
|
|
int preview_camera_index = 0; // 0 for left, 1 for right
|
|
|
uint64_t left_raw_cnt = 0, right_raw_cnt = 0;
|
|
|
std::unique_ptr<cv::cuda::GpuMat> left_img_dev, right_img_dev;
|
|
|
@@ -37,6 +45,15 @@ std::unique_ptr<cv::cuda::Stream> left_stream, right_stream;
|
|
|
cudaStream_t left_cuda_stream = nullptr, right_cuda_stream = nullptr;
|
|
|
std::unique_ptr<monocular_processor> left_processor, right_processor;
|
|
|
std::unique_ptr<simple_render> opengl_render;
|
|
|
+float process_frame_rate = 0;
|
|
|
+std::unique_ptr<std::thread> encoder_thread;
|
|
|
+bool output_full_frame = false;
|
|
|
+int output_width = -1, output_height = -1;
|
|
|
+encoder_config main_encoder_conf;
|
|
|
+std::unique_ptr<smart_frame_buffer> output_fbo;
|
|
|
+std::unique_ptr<cv::cuda::Stream> output_stream;
|
|
|
+cudaStream_t output_cuda_stream = nullptr;
|
|
|
+std::shared_ptr<cv::cuda::GpuMat> output_frame_dev;
|
|
|
|
|
|
std::queue<void (*)()> simple_eq;
|
|
|
|
|
|
@@ -56,6 +73,7 @@ void initialize_cuda() {
|
|
|
CUdevice cuda_device;
|
|
|
CUDA_API_CHECK(cuDeviceGet(&cuda_device, default_cuda_device_id));
|
|
|
CUDA_API_CHECK(cuCtxCreate(&cuda_ctx, CU_CTX_SCHED_AUTO, cuda_device));
|
|
|
+ mq().update_variable(CUDA_CONTEXT, cuda_ctx);
|
|
|
|
|
|
// elegant cleanup
|
|
|
std::atexit([] {
|
|
|
@@ -71,6 +89,9 @@ void initialize_cuda() {
|
|
|
right_cuda_stream = (cudaStream_t) right_stream->cudaPtr();
|
|
|
left_processor = std::make_unique<monocular_processor>();
|
|
|
right_processor = std::make_unique<monocular_processor>();
|
|
|
+ output_stream = std::make_unique<cv::cuda::Stream>();
|
|
|
+ output_cuda_stream = (cudaStream_t) output_stream->cudaPtr();
|
|
|
+ output_frame_dev = std::make_shared<cv::cuda::GpuMat>();
|
|
|
}
|
|
|
|
|
|
void load_config() {
|
|
|
@@ -82,14 +103,21 @@ void load_config() {
|
|
|
left_camera_name = camera_names["left"].as<std::string>();
|
|
|
right_camera_name = camera_names["right"].as<std::string>();
|
|
|
auto capture_param = camera_conf["capture"];
|
|
|
- capture_conf.frame_rate = capture_param["frame_rate"].as<float>();
|
|
|
+ capture_conf.frame_rate = capture_param["frame_rate"].as<int>();
|
|
|
+ main_encoder_conf.frame_rate = capture_conf.frame_rate;
|
|
|
capture_conf.expo_time_ms = capture_param["expo_time_ms"].as<float>();
|
|
|
capture_conf.gain_db = capture_param["gain_db"].as<float>();
|
|
|
|
|
|
// load main window config
|
|
|
auto window_conf = conf["main_window"];
|
|
|
- mq().update_variable(MAIN_WINDOW_WIDTH, window_conf["width"].as<int>());
|
|
|
- mq().update_variable(MAIN_WINDOW_HEIGHT, window_conf["height"].as<int>());
|
|
|
+ main_window_width = window_conf["width"].as<int>();
|
|
|
+ main_window_height = window_conf["height"].as<int>();
|
|
|
+
|
|
|
+ // load output config
|
|
|
+ auto output_conf = conf["output"];
|
|
|
+ output_width = output_conf["width"].as<int>();
|
|
|
+ output_height = output_conf["height"].as<int>();
|
|
|
+ main_encoder_conf.bitrate_mbps = output_conf["bitrate"].as<float>();
|
|
|
}
|
|
|
|
|
|
void initialize_main_window() {
|
|
|
@@ -104,9 +132,8 @@ void initialize_main_window() {
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
|
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
|
- auto window_width = mq().query_variable<int>(MAIN_WINDOW_WIDTH);
|
|
|
- auto window_height = mq().query_variable<int>(MAIN_WINDOW_HEIGHT);
|
|
|
- main_window = glfwCreateWindow(window_width, window_height, "RemoteAR V3.-1", nullptr, nullptr);
|
|
|
+ main_window = glfwCreateWindow(main_window_width, main_window_height,
|
|
|
+ "RemoteAR V3.-1", nullptr, nullptr);
|
|
|
assert(main_window != nullptr);
|
|
|
glfwMakeContextCurrent(main_window);
|
|
|
|
|
|
@@ -140,8 +167,9 @@ void initialize_main_window() {
|
|
|
ImGui_ImplGlfw_InitForOpenGL(main_window, true);
|
|
|
ImGui_ImplOpenGL3_Init();
|
|
|
|
|
|
- // initialize OpenGL render
|
|
|
+ // initialize OpenGL objects
|
|
|
opengl_render = std::make_unique<simple_render>();
|
|
|
+ output_fbo = std::make_unique<smart_frame_buffer>();
|
|
|
|
|
|
// elegant cleanup
|
|
|
std::atexit([] {
|
|
|
@@ -218,11 +246,72 @@ void open_cameras() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void encoder_thread_work() {
|
|
|
+ uint64_t last_conf_cnt;
|
|
|
+ auto conf = mq().query_variable<encoder_config>(ENCODER_CONFIG, &last_conf_cnt);
|
|
|
+ auto encoder = std::unique_ptr<video_encoder>(video_encoder::create(conf));
|
|
|
+
|
|
|
+ uint64_t frame_cnt = 0;
|
|
|
+ auto frame_data = std::make_unique<video_nal>();
|
|
|
+ for (;;) {
|
|
|
+ mq().wait_variable(OUTPUT_FRAME, frame_cnt);
|
|
|
+ auto frame = mq().query_variable_ptr<cv::cuda::GpuMat>(OUTPUT_FRAME, &frame_cnt);
|
|
|
+
|
|
|
+ // test stop flag
|
|
|
+ if (mq().query_variable<bool>(ENCODER_SHOULD_STOP)) break;
|
|
|
+
|
|
|
+ // check for config update
|
|
|
+ uint64_t cur_conf_cnt;
|
|
|
+ conf = mq().query_variable<encoder_config>(ENCODER_CONFIG, &cur_conf_cnt);
|
|
|
+ if (cur_conf_cnt > last_conf_cnt) {
|
|
|
+ encoder->change_config(conf);
|
|
|
+ last_conf_cnt = cur_conf_cnt;
|
|
|
+ }
|
|
|
+
|
|
|
+ assert(frame != nullptr);
|
|
|
+ encoder->encode(*frame, frame_data.get());
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+bool is_encoding() {
|
|
|
+ return encoder_thread != nullptr;
|
|
|
+}
|
|
|
+
|
|
|
+void upload_encoder_config() {
|
|
|
+ mq().update_variable(ENCODER_CONFIG, main_encoder_conf);
|
|
|
+}
|
|
|
+
|
|
|
+void start_encoder() {
|
|
|
+ if (output_full_frame) {
|
|
|
+ assert(!left_img_dev->empty() && !right_img_dev->empty());
|
|
|
+ assert(left_img_dev->size() == right_img_dev->size());
|
|
|
+ main_encoder_conf.frame_size = cv::Size{
|
|
|
+ left_img_dev->size().width * 2,
|
|
|
+ left_img_dev->size().height};
|
|
|
+ output_fbo->create(main_encoder_conf.frame_size);
|
|
|
+ } else {
|
|
|
+ main_encoder_conf.frame_size = cv::Size{output_width, output_height};
|
|
|
+ output_fbo->create(main_encoder_conf.frame_size);
|
|
|
+ }
|
|
|
+ upload_encoder_config();
|
|
|
+ mq().update_variable(ENCODER_SHOULD_STOP, false);
|
|
|
+ mq().update_variable(ENCODER_BUSY, false); // make variable exist
|
|
|
+ encoder_thread = std::make_unique<std::thread>(encoder_thread_work);
|
|
|
+}
|
|
|
+
|
|
|
+void stop_encoder() {
|
|
|
+ mq().update_variable(ENCODER_SHOULD_STOP, true);
|
|
|
+ mq().update_variable_ptr<cv::cuda::GpuMat>(OUTPUT_FRAME, nullptr);
|
|
|
+ encoder_thread->join();
|
|
|
+ encoder_thread.reset();
|
|
|
+}
|
|
|
+
|
|
|
void cleanup() {
|
|
|
close_cameras();
|
|
|
|
|
|
// avoid cudaErrorCudartUnloading
|
|
|
opengl_render.reset();
|
|
|
+ output_fbo.reset();
|
|
|
}
|
|
|
|
|
|
void prepare_imgui_frame() {
|
|
|
@@ -232,6 +321,8 @@ void prepare_imgui_frame() {
|
|
|
ImGui_ImplGlfw_NewFrame();
|
|
|
ImGui::NewFrame();
|
|
|
|
|
|
+ ImGui::ShowDemoWindow();
|
|
|
+
|
|
|
if (ImGui::Begin("Remote AR Control")) {
|
|
|
ImGui::PushItemWidth(200);
|
|
|
|
|
|
@@ -269,9 +360,10 @@ void prepare_imgui_frame() {
|
|
|
|
|
|
// camera configs
|
|
|
ImGui::SeparatorText("Configs");
|
|
|
- if (ImGui::DragFloat("Frame Rate (fps)", &capture_conf.frame_rate,
|
|
|
- 0.5, 1, 60, "%.01f")) {
|
|
|
+ if (ImGui::DragInt("Frame Rate (fps)", &capture_conf.frame_rate, 1, 1, 60)) {
|
|
|
simple_eq.push(upload_capture_config);
|
|
|
+ main_encoder_conf.frame_rate = capture_conf.frame_rate;
|
|
|
+ simple_eq.push(upload_encoder_config);
|
|
|
}
|
|
|
if (ImGui::DragFloat("Exposure Time (ms)", &capture_conf.expo_time_ms,
|
|
|
0.1, 0.1, 1e3f / capture_conf.frame_rate, "%.01f")) {
|
|
|
@@ -289,6 +381,11 @@ void prepare_imgui_frame() {
|
|
|
ImGui::SameLine();
|
|
|
ImGui::RadioButton("Right", &preview_camera_index, 1);
|
|
|
|
|
|
+ ImGui::SeparatorText("Infos");
|
|
|
+ ImGui::BeginDisabled();
|
|
|
+ ImGui::DragFloat("Process Frame Rate (fps)", &process_frame_rate, 0, 0, 60, "%.01f");
|
|
|
+ ImGui::EndDisabled();
|
|
|
+
|
|
|
// auto save raw config
|
|
|
// ImGui::SeparatorText("Auto Shoot");
|
|
|
// ImGui::PushID("Auto Shoot");
|
|
|
@@ -320,8 +417,43 @@ void prepare_imgui_frame() {
|
|
|
// if (auto_save_raw) {
|
|
|
// ImGui::EndDisabled();
|
|
|
// }
|
|
|
+ }
|
|
|
+
|
|
|
+ ImGui::PopID();
|
|
|
+ }
|
|
|
|
|
|
+ if (is_capturing() && ImGui::CollapsingHeader("Video Encoder")) {
|
|
|
+ ImGui::PushID("Encoder");
|
|
|
|
|
|
+ ImGui::SeparatorText("Actions");
|
|
|
+ if (!is_encoding()) {
|
|
|
+ if (ImGui::Button("Start")) {
|
|
|
+ simple_eq.push(start_encoder);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (ImGui::Button("Close")) {
|
|
|
+ simple_eq.push(stop_encoder);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ImGui::SeparatorText("Configs");
|
|
|
+ if (ImGui::DragFloat("Bitrate (Mbps)", &main_encoder_conf.bitrate_mbps,
|
|
|
+ 0.1, 1, 20, "%.01f")) {
|
|
|
+ simple_eq.push(upload_encoder_config);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (is_encoding()) {
|
|
|
+ ImGui::BeginDisabled();
|
|
|
+ }
|
|
|
+ ImGui::Checkbox("Full Resolution", &output_full_frame);
|
|
|
+ ImGui::SameLine();
|
|
|
+ ImGui::Checkbox("Save Video", &main_encoder_conf.save_file);
|
|
|
+ if (main_encoder_conf.save_file) {
|
|
|
+ ImGui::SameLine();
|
|
|
+ ImGui::Checkbox("Save Frame Length", &main_encoder_conf.save_length);
|
|
|
+ }
|
|
|
+ if (is_encoding()) {
|
|
|
+ ImGui::EndDisabled();
|
|
|
}
|
|
|
|
|
|
ImGui::PopID();
|
|
|
@@ -396,4 +528,44 @@ void render_main_window() {
|
|
|
|
|
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
|
|
glfwSwapBuffers(main_window);
|
|
|
+
|
|
|
+ // calculate process frame rate
|
|
|
+ static auto last_ts = current_timestamp();
|
|
|
+ auto cur_ts = current_timestamp();
|
|
|
+ process_frame_rate = 1e6f / (cur_ts - last_ts);
|
|
|
+ last_ts = cur_ts;
|
|
|
+}
|
|
|
+
|
|
|
+void generate_output_frame() {
|
|
|
+ // offline drawing
|
|
|
+ assert(output_fbo->id != 0);
|
|
|
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, output_fbo->id);
|
|
|
+ glViewport(0, 0, output_fbo->size().width, output_fbo->size().height);
|
|
|
+ simple_rect left_rect, right_rect;
|
|
|
+ if (output_full_frame) {
|
|
|
+ left_rect = simple_rect{-1, -1, 1, 2};
|
|
|
+ right_rect = simple_rect{0, -1, 1, 2};
|
|
|
+ } else {
|
|
|
+ float width_normal = left_img_dev->size().aspectRatio() /
|
|
|
+ output_fbo->size().aspectRatio();
|
|
|
+ left_rect = simple_rect{-0.5f - width_normal / 2, -1, width_normal, 2};
|
|
|
+ right_rect = simple_rect{0.5f - width_normal / 2, -1, width_normal, 2};
|
|
|
+ }
|
|
|
+ opengl_render->render_rect(*left_img_dev, left_rect, false, left_cuda_stream);
|
|
|
+ opengl_render->render_rect(*right_img_dev, right_rect, false, right_cuda_stream);
|
|
|
+
|
|
|
+ // wait encoder idle
|
|
|
+ for (uint64_t cur_cnt = 0;;) {
|
|
|
+ if (mq().query_variable<bool>(ENCODER_BUSY, &cur_cnt)) {
|
|
|
+ mq().wait_variable(ENCODER_BUSY, cur_cnt);
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // download output frame
|
|
|
+ output_fbo->download(output_frame_dev.get(), output_cuda_stream);
|
|
|
+
|
|
|
+ // upload output frame
|
|
|
+ mq().update_variable_ptr(OUTPUT_FRAME, output_frame_dev);
|
|
|
}
|