|
|
@@ -2,6 +2,8 @@
|
|
|
#include "core/local_connection.h"
|
|
|
#include "core/timestamp_helper.hpp"
|
|
|
#include "cuda_helper.hpp"
|
|
|
+#include "frame_encoder/encoder_nvenc.h"
|
|
|
+#include "frame_encoder/encoder_jpeg.h"
|
|
|
#include "frame_sender/sender_tcp.h"
|
|
|
#include "frame_sender/sender_udp_fec.h"
|
|
|
#include "image_process.h"
|
|
|
@@ -11,7 +13,6 @@
|
|
|
#include "third_party/scope_guard.hpp"
|
|
|
#include "utility.hpp"
|
|
|
#include "variable_defs.h"
|
|
|
-#include "video_encoder.h"
|
|
|
#include "vtk_viewer.h"
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
@@ -75,7 +76,11 @@ std::unique_ptr<vtk_viewer> augment_viewer;
|
|
|
std::unique_ptr<std::thread> encoder_thread;
|
|
|
bool output_full_frame = false;
|
|
|
int output_width = -1, output_height = -1;
|
|
|
-encoder_config main_encoder_conf;
|
|
|
+encoder_type chosen_encoder = ENCODER_JPEG;
|
|
|
+nvenc_config main_nvenc_conf;
|
|
|
+nvjpeg_config main_nvjpeg_conf;
|
|
|
+bool encoder_save_file = false;
|
|
|
+bool encoder_save_length = false;
|
|
|
std::unique_ptr<smart_frame_buffer> output_fbo;
|
|
|
std::unique_ptr<cv::cuda::Stream> output_stream;
|
|
|
cudaStream_t output_cuda_stream = nullptr;
|
|
|
@@ -229,6 +234,7 @@ void initialize_cuda() {
|
|
|
right = std::make_unique<camera_related>();
|
|
|
output_stream = std::make_unique<cv::cuda::Stream>();
|
|
|
output_cuda_stream = (cudaStream_t) output_stream->cudaPtr();
|
|
|
+ mq().update_variable(CUDA_STREAM_OUTPUT, output_cuda_stream);
|
|
|
output_frame_dev = std::make_shared<cv::cuda::GpuMat>();
|
|
|
}
|
|
|
|
|
|
@@ -242,7 +248,7 @@ void load_config() {
|
|
|
right_camera_name = camera_names["right"].as<std::string>();
|
|
|
auto capture_param = camera_conf["capture"];
|
|
|
capture_conf.frame_rate = capture_param["frame_rate"].as<int>();
|
|
|
- main_encoder_conf.frame_rate = capture_conf.frame_rate;
|
|
|
+ main_nvenc_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>();
|
|
|
|
|
|
@@ -268,7 +274,8 @@ void load_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>();
|
|
|
+ main_nvenc_conf.bitrate_mbps = output_conf["hevc_bitrate"].as<float>();
|
|
|
+ main_nvjpeg_conf.quality = output_conf["jpeg_quality"].as<int>();
|
|
|
left->process_conf.resample_height = output_height; // use output height as resample height
|
|
|
right->process_conf.resample_height = output_height;
|
|
|
|
|
|
@@ -482,8 +489,7 @@ void sender_thread_work() {
|
|
|
break;
|
|
|
}
|
|
|
default: {
|
|
|
- assert(false);
|
|
|
- unreachable();
|
|
|
+ RET_ERROR;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -493,8 +499,25 @@ void sender_thread_work() {
|
|
|
|
|
|
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));
|
|
|
+ std::unique_ptr<encoder_base> encoder;
|
|
|
+ bool save_file = false;
|
|
|
+ switch (chosen_encoder) {
|
|
|
+ case ENCODER_NVENC: {
|
|
|
+ auto conf = mq().query_variable<nvenc_config>(ENCODER_CONFIG, &last_conf_cnt);
|
|
|
+ save_file = conf.save_file;
|
|
|
+ encoder.reset(encoder_nvenc::create(conf));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case ENCODER_JPEG: {
|
|
|
+ auto conf = mq().query_variable<nvjpeg_config>(ENCODER_CONFIG, &last_conf_cnt);
|
|
|
+ save_file = conf.save_file;
|
|
|
+ encoder.reset(encoder_jpeg::create(conf));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ default: {
|
|
|
+ RET_ERROR;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
uint64_t frame_cnt = 0;
|
|
|
for (;;) {
|
|
|
@@ -504,17 +527,37 @@ void encoder_thread_work() {
|
|
|
// test stop flag
|
|
|
if (mq().query_variable<bool>(ENCODER_SHOULD_STOP)) break;
|
|
|
|
|
|
- if (!(conf.save_file ||
|
|
|
+ if (!(save_file ||
|
|
|
mq().query_variable<bool>(SENDER_CONNECTED))) {
|
|
|
continue; // no need to encode frame
|
|
|
}
|
|
|
|
|
|
// 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;
|
|
|
+ switch (chosen_encoder) {
|
|
|
+ case ENCODER_NVENC: {
|
|
|
+ auto conf = mq().query_variable<nvenc_config>(ENCODER_CONFIG, &cur_conf_cnt);
|
|
|
+ if (cur_conf_cnt > last_conf_cnt) {
|
|
|
+ auto real_encoder = dynamic_cast<encoder_nvenc *>(encoder.get());
|
|
|
+ assert(real_encoder != nullptr);
|
|
|
+ real_encoder->change_config(conf);
|
|
|
+ last_conf_cnt = cur_conf_cnt;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case ENCODER_JPEG: {
|
|
|
+ auto conf = mq().query_variable<nvjpeg_config>(ENCODER_CONFIG, &cur_conf_cnt);
|
|
|
+ if (cur_conf_cnt > last_conf_cnt) {
|
|
|
+ auto real_encoder = dynamic_cast<encoder_jpeg *>(encoder.get());
|
|
|
+ assert(real_encoder != nullptr);
|
|
|
+ real_encoder->change_config(conf);
|
|
|
+ last_conf_cnt = cur_conf_cnt;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ default : {
|
|
|
+ RET_ERROR;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
bool force_idr = false;
|
|
|
@@ -523,7 +566,7 @@ void encoder_thread_work() {
|
|
|
mq().update_variable(REQUEST_IDR, false);
|
|
|
}
|
|
|
|
|
|
- assert(frame != nullptr);
|
|
|
+ if (frame == nullptr) continue;
|
|
|
auto frame_data = std::make_unique<video_nal>();
|
|
|
encoder->encode(*frame, frame_data.get(), force_idr);
|
|
|
if (mq().query_variable<bool>(SENDER_CONNECTED)) {
|
|
|
@@ -556,22 +599,53 @@ bool is_encoding() {
|
|
|
}
|
|
|
|
|
|
void upload_encoder_config() {
|
|
|
- mq().update_variable(ENCODER_CONFIG, main_encoder_conf);
|
|
|
+ switch (chosen_encoder) {
|
|
|
+ case ENCODER_NVENC: {
|
|
|
+ main_nvenc_conf.save_file = encoder_save_file;
|
|
|
+ main_nvenc_conf.save_length = encoder_save_length;
|
|
|
+ mq().update_variable(ENCODER_CONFIG, main_nvenc_conf);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case ENCODER_JPEG: {
|
|
|
+ main_nvjpeg_conf.save_file = encoder_save_file;
|
|
|
+ main_nvjpeg_conf.save_length = encoder_save_length;
|
|
|
+ mq().update_variable(ENCODER_CONFIG, main_nvjpeg_conf);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ default: {
|
|
|
+ RET_ERROR;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
void start_encoder() {
|
|
|
+ // determine output frame size
|
|
|
+ cv::Size frame_size;
|
|
|
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);
|
|
|
+ frame_size.width = left->img_dev->size().width * 2;
|
|
|
+ frame_size.height = left->img_dev->size().height;
|
|
|
} else {
|
|
|
- main_encoder_conf.frame_size = cv::Size{output_width, output_height};
|
|
|
- output_fbo->create(main_encoder_conf.frame_size);
|
|
|
+ frame_size = {output_width, output_height};
|
|
|
+ }
|
|
|
+ output_fbo->create(frame_size);
|
|
|
+
|
|
|
+ // update config
|
|
|
+ switch (chosen_encoder) {
|
|
|
+ case ENCODER_NVENC: {
|
|
|
+ main_nvenc_conf.frame_size = frame_size;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case ENCODER_JPEG: {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ default: {
|
|
|
+ RET_ERROR;
|
|
|
+ }
|
|
|
}
|
|
|
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);
|
|
|
@@ -658,7 +732,7 @@ void prepare_imgui_frame() {
|
|
|
ImGui::SeparatorText("Configs");
|
|
|
if (ImGui::DragInt("Frame Rate (fps)", &capture_conf.frame_rate, 1, 1, 60)) {
|
|
|
simple_eq.emplace(upload_capture_config);
|
|
|
- main_encoder_conf.frame_rate = capture_conf.frame_rate;
|
|
|
+ main_nvenc_conf.frame_rate = capture_conf.frame_rate;
|
|
|
simple_eq.emplace(upload_encoder_config);
|
|
|
}
|
|
|
if (ImGui::DragFloat("Exposure Time (ms)", &capture_conf.expo_time_ms,
|
|
|
@@ -783,6 +857,21 @@ void prepare_imgui_frame() {
|
|
|
if (ImGui::CollapsingHeader("Video Encoder")) {
|
|
|
ImGui::PushID("Encoder");
|
|
|
|
|
|
+ ImGui::SeparatorText("Method");
|
|
|
+ if (is_encoding()) {
|
|
|
+ ImGui::BeginDisabled();
|
|
|
+ }
|
|
|
+ if (ImGui::RadioButton("NvEnc", chosen_encoder == ENCODER_NVENC)) {
|
|
|
+ simple_eq.emplace([] { chosen_encoder = ENCODER_NVENC; });
|
|
|
+ }
|
|
|
+ ImGui::SameLine();
|
|
|
+ if (ImGui::RadioButton("nvJPEG", chosen_encoder == ENCODER_JPEG)) {
|
|
|
+ simple_eq.emplace([] { chosen_encoder = ENCODER_JPEG; });
|
|
|
+ }
|
|
|
+ if (is_encoding()) {
|
|
|
+ ImGui::EndDisabled();
|
|
|
+ }
|
|
|
+
|
|
|
ImGui::SeparatorText("Actions");
|
|
|
if (!is_encoding()) {
|
|
|
if (ImGui::Button("Start")) {
|
|
|
@@ -795,20 +884,33 @@ void prepare_imgui_frame() {
|
|
|
}
|
|
|
|
|
|
ImGui::SeparatorText("Configs");
|
|
|
- if (ImGui::DragFloat("Bitrate (Mbps)", &main_encoder_conf.bitrate_mbps,
|
|
|
- 0.1, 1, 20, "%.01f")) {
|
|
|
- simple_eq.emplace(upload_encoder_config);
|
|
|
+ switch (chosen_encoder) {
|
|
|
+ case ENCODER_NVENC: {
|
|
|
+ if (ImGui::DragFloat("Bitrate (Mbps)", &main_nvenc_conf.bitrate_mbps,
|
|
|
+ 0.1, 1, 20, "%.01f")) {
|
|
|
+ simple_eq.emplace(upload_encoder_config);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case ENCODER_JPEG: {
|
|
|
+ if (ImGui::DragInt("Quality (%)", &main_nvjpeg_conf.quality, 1, 1, 100)) {
|
|
|
+ simple_eq.emplace(upload_encoder_config);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ default: {
|
|
|
+ RET_ERROR;
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
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::Checkbox("Save Video", &encoder_save_file);
|
|
|
+ if (encoder_save_file) {
|
|
|
ImGui::SameLine();
|
|
|
- ImGui::Checkbox("Save Frame Length", &main_encoder_conf.save_length);
|
|
|
+ ImGui::Checkbox("Save Frame Length", &encoder_save_length);
|
|
|
}
|
|
|
if (is_encoding()) {
|
|
|
ImGui::EndDisabled();
|