mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
extcam: split into threads
This commit is contained in:
@@ -233,12 +233,10 @@ driver_ui::set_cursor( bool const Visible ) {
|
|||||||
void
|
void
|
||||||
driver_ui::render_() {
|
driver_ui::render_() {
|
||||||
const std::string *rec_name = m_trainingcardpanel.is_recording();
|
const std::string *rec_name = m_trainingcardpanel.is_recording();
|
||||||
if (rec_name && m_cameraviewpanel.set_state(ui::cameraview_panel::RECORDING)) {
|
if (rec_name && m_cameraviewpanel.set_state(true)) {
|
||||||
m_cameraviewpanel.rec_name = *rec_name;
|
m_cameraviewpanel.rec_name = *rec_name;
|
||||||
} else if (!rec_name && m_cameraviewpanel.is_open)
|
} else if (!rec_name)
|
||||||
m_cameraviewpanel.set_state(ui::cameraview_panel::PREVIEW);
|
m_cameraviewpanel.set_state(false);
|
||||||
else if (!rec_name)
|
|
||||||
m_cameraviewpanel.set_state(ui::cameraview_panel::IDLE);
|
|
||||||
|
|
||||||
// pause/quit modal
|
// pause/quit modal
|
||||||
auto const popupheader { STR_C("Simulation Paused") };
|
auto const popupheader { STR_C("Simulation Paused") };
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
#include "Logs.h"
|
#include "Logs.h"
|
||||||
|
|
||||||
#ifdef __unix__
|
#ifdef __unix__
|
||||||
piped_proc::piped_proc(std::string cmd)
|
piped_proc::piped_proc(std::string cmd, bool write)
|
||||||
{
|
{
|
||||||
file = popen(cmd.c_str(), "r");
|
file = popen(cmd.c_str(), write ? "w" : "r");
|
||||||
}
|
}
|
||||||
|
|
||||||
piped_proc::~piped_proc()
|
piped_proc::~piped_proc()
|
||||||
@@ -25,8 +25,16 @@ size_t piped_proc::read(unsigned char *buf, size_t len)
|
|||||||
|
|
||||||
return fread(buf, 1, len, file);
|
return fread(buf, 1, len, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t piped_proc::write(unsigned char *buf, size_t len)
|
||||||
|
{
|
||||||
|
if (!file)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return fwrite(buf, 1, len, file);
|
||||||
|
}
|
||||||
#elif _WIN32
|
#elif _WIN32
|
||||||
piped_proc::piped_proc(std::string cmd)
|
piped_proc::piped_proc(std::string cmd, bool write)
|
||||||
{
|
{
|
||||||
PROCESS_INFORMATION process;
|
PROCESS_INFORMATION process;
|
||||||
STARTUPINFO siStartInfo;
|
STARTUPINFO siStartInfo;
|
||||||
@@ -45,14 +53,17 @@ piped_proc::piped_proc(std::string cmd)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SetHandleInformation(pipe_rd, HANDLE_FLAG_INHERIT, 0)) {
|
if (!SetHandleInformation(write ? pipe_wr : pipe_rd, HANDLE_FLAG_INHERIT, 0)) {
|
||||||
ErrorLog("piped_proc: SetHandleInformation failed!");
|
ErrorLog("piped_proc: SetHandleInformation failed!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
siStartInfo.cb = sizeof(STARTUPINFO);
|
siStartInfo.cb = sizeof(STARTUPINFO);
|
||||||
siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||||
siStartInfo.hStdOutput = pipe_wr;
|
if (!write)
|
||||||
|
siStartInfo.hStdOutput = pipe_wr;
|
||||||
|
else
|
||||||
|
siStartInfo.hStdInput = pipe_rd;
|
||||||
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
|
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
|
||||||
|
|
||||||
if (!CreateProcessA(NULL, (char*)cmd.c_str(), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &siStartInfo, &process)) {
|
if (!CreateProcessA(NULL, (char*)cmd.c_str(), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &siStartInfo, &process)) {
|
||||||
@@ -71,10 +82,8 @@ piped_proc::~piped_proc()
|
|||||||
CloseHandle(pipe_wr);
|
CloseHandle(pipe_wr);
|
||||||
if (pipe_rd)
|
if (pipe_rd)
|
||||||
CloseHandle(pipe_rd);
|
CloseHandle(pipe_rd);
|
||||||
if (proc_h) {
|
if (proc_h)
|
||||||
WaitForSingleObject(proc_h, INFINITE);
|
|
||||||
CloseHandle(proc_h);
|
CloseHandle(proc_h);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t piped_proc::read(unsigned char *buf, size_t len)
|
size_t piped_proc::read(unsigned char *buf, size_t len)
|
||||||
@@ -87,4 +96,15 @@ size_t piped_proc::read(unsigned char *buf, size_t len)
|
|||||||
|
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t piped_proc::write(unsigned char *buf, size_t len)
|
||||||
|
{
|
||||||
|
if (!pipe_wr)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
DWORD wrote = 0;
|
||||||
|
BOOL ret = WriteFile(pipe_wr, buf, len, &wrote, NULL);
|
||||||
|
|
||||||
|
return wrote;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ HANDLE pipe_wr = nullptr;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
piped_proc(std::string cmd);
|
piped_proc(std::string cmd, bool write = false);
|
||||||
size_t read(unsigned char *buf, size_t len);
|
size_t read(unsigned char *buf, size_t len);
|
||||||
|
size_t write(unsigned char *buf, size_t len);
|
||||||
~piped_proc();
|
~piped_proc();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,23 +20,41 @@ void cameraview_window_callback(ImGuiSizeCallbackData *data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ui::cameraview_panel::~cameraview_panel() {
|
ui::cameraview_panel::~cameraview_panel() {
|
||||||
exit_thread = true;
|
record_state = STOPPING;
|
||||||
if (workthread.joinable())
|
if (record_workthread.joinable())
|
||||||
workthread.detach();
|
record_workthread.join();
|
||||||
|
|
||||||
|
capture_state = STOPPING;
|
||||||
|
if (capture_workthread.joinable())
|
||||||
|
capture_workthread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ui::cameraview_panel::render()
|
void ui::cameraview_panel::render()
|
||||||
{
|
{
|
||||||
if (!is_open && state != RECORDING) {
|
if (Global.extcam_cmd.empty())
|
||||||
exit_thread = true;
|
return;
|
||||||
|
|
||||||
|
if (record_state == STOPPING_DONE) {
|
||||||
|
record_workthread.join();
|
||||||
|
record_state = STOPPED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exit_thread) {
|
if (capture_state == STOPPING_DONE) {
|
||||||
if (workthread.joinable())
|
capture_workthread.join();
|
||||||
workthread.join();
|
capture_state = STOPPED;
|
||||||
exit_thread = false;
|
}
|
||||||
if (state != IDLE && !Global.extcam_cmd.empty())
|
|
||||||
workthread = std::thread(&cameraview_panel::workthread_func, this);
|
if (capture_state == STOPPED) {
|
||||||
|
capture_state = STARTING;
|
||||||
|
capture_workthread = std::thread(&cameraview_panel::capture_func, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recording && !(record_state == RUNNING || record_state == STARTING)) {
|
||||||
|
record_state = STARTING;
|
||||||
|
record_workthread = std::thread(&cameraview_panel::record_func, this);
|
||||||
|
}
|
||||||
|
if (!recording && !(record_state == STOPPED || record_state == STOPPING_DONE)) {
|
||||||
|
record_state = STOPPING;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_open)
|
if (!is_open)
|
||||||
@@ -52,15 +70,14 @@ void ui::cameraview_panel::render()
|
|||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ui::cameraview_panel::set_state(state_e e)
|
bool ui::cameraview_panel::set_state(bool rec)
|
||||||
{
|
{
|
||||||
if (state != e) {
|
if (recording == rec)
|
||||||
exit_thread = true;
|
return false;
|
||||||
state = e;
|
|
||||||
is_open = (state != IDLE);
|
recording = rec;
|
||||||
return true;
|
|
||||||
}
|
return true;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ui::cameraview_panel::render_contents()
|
void ui::cameraview_panel::render_contents()
|
||||||
@@ -99,18 +116,9 @@ void ui::cameraview_panel::render_contents()
|
|||||||
ImGui::Image(reinterpret_cast<void*>(texture->id), surface_size);
|
ImGui::Image(reinterpret_cast<void*>(texture->id), surface_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ui::cameraview_panel::workthread_func()
|
void ui::cameraview_panel::capture_func()
|
||||||
{
|
{
|
||||||
std::string cmdline = Global.extcam_cmd;
|
std::string cmdline = Global.extcam_cmd;
|
||||||
if (state == RECORDING)
|
|
||||||
cmdline += " " + Global.extcam_rec;
|
|
||||||
|
|
||||||
if (!rec_name.empty()) {
|
|
||||||
const std::string magic{"{RECORD}"};
|
|
||||||
size_t pos = cmdline.find(magic);
|
|
||||||
if (pos != -1)
|
|
||||||
cmdline.replace(pos, magic.size(), rec_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
piped_proc proc(cmdline);
|
piped_proc proc(cmdline);
|
||||||
|
|
||||||
@@ -119,7 +127,9 @@ void ui::cameraview_panel::workthread_func()
|
|||||||
uint8_t *active_buffer = new uint8_t[frame_size];
|
uint8_t *active_buffer = new uint8_t[frame_size];
|
||||||
|
|
||||||
size_t bufpos = 0;
|
size_t bufpos = 0;
|
||||||
while (!exit_thread) {
|
|
||||||
|
capture_state = RUNNING;
|
||||||
|
while (capture_state == RUNNING) {
|
||||||
size_t read = 0;
|
size_t read = 0;
|
||||||
read = proc.read(read_buffer + bufpos, frame_size - bufpos);
|
read = proc.read(read_buffer + bufpos, frame_size - bufpos);
|
||||||
|
|
||||||
@@ -136,10 +146,54 @@ void ui::cameraview_panel::workthread_func()
|
|||||||
image_ptr = read_buffer;
|
image_ptr = read_buffer;
|
||||||
read_buffer = active_buffer;
|
read_buffer = active_buffer;
|
||||||
active_buffer = image_ptr;
|
active_buffer = image_ptr;
|
||||||
|
|
||||||
|
frame_cnt++;
|
||||||
|
notify_var.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
image_ptr = nullptr;
|
image_ptr = nullptr;
|
||||||
delete[] read_buffer;
|
delete[] read_buffer;
|
||||||
delete[] active_buffer;
|
delete[] active_buffer;
|
||||||
|
|
||||||
|
capture_state = STOPPING_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ui::cameraview_panel::record_func()
|
||||||
|
{
|
||||||
|
std::string cmdline = Global.extcam_rec;
|
||||||
|
|
||||||
|
if (!rec_name.empty()) {
|
||||||
|
const std::string magic{"{RECORD}"};
|
||||||
|
size_t pos = cmdline.find(magic);
|
||||||
|
if (pos != -1)
|
||||||
|
cmdline.replace(pos, magic.size(), rec_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
piped_proc proc(cmdline, true);
|
||||||
|
|
||||||
|
size_t frame_size = Global.extcam_res.x * Global.extcam_res.y * 3;
|
||||||
|
uint8_t *read_buffer = new uint8_t[frame_size];
|
||||||
|
uint32_t last_cnt = 0;
|
||||||
|
size_t bufpos = frame_size;
|
||||||
|
|
||||||
|
record_state = RUNNING;
|
||||||
|
while (record_state == RUNNING) {
|
||||||
|
if (bufpos == frame_size)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(mutex);
|
||||||
|
auto r = notify_var.wait_for(lock, std::chrono::milliseconds(50), [this, last_cnt]{return last_cnt != frame_cnt;});
|
||||||
|
last_cnt = frame_cnt;
|
||||||
|
if (!image_ptr || !r)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bufpos = 0;
|
||||||
|
memcpy(read_buffer, image_ptr, frame_size);
|
||||||
|
}
|
||||||
|
bufpos += proc.write(read_buffer + bufpos, frame_size - bufpos);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] read_buffer;
|
||||||
|
|
||||||
|
record_state = STOPPING_DONE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,25 +11,33 @@ namespace ui
|
|||||||
{
|
{
|
||||||
class cameraview_panel : public ui_panel
|
class cameraview_panel : public ui_panel
|
||||||
{
|
{
|
||||||
public:
|
private:
|
||||||
enum state_e {
|
|
||||||
IDLE,
|
enum thread_state {
|
||||||
PREVIEW,
|
STARTING,
|
||||||
RECORDING
|
RUNNING,
|
||||||
|
STOPPING,
|
||||||
|
STOPPING_DONE,
|
||||||
|
STOPPED,
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
std::atomic<thread_state> capture_state = STOPPED;
|
||||||
std::atomic_bool exit_thread = true;
|
std::atomic<thread_state> record_state = STOPPED;
|
||||||
std::thread workthread;
|
std::thread capture_workthread;
|
||||||
|
std::thread record_workthread;
|
||||||
|
|
||||||
|
std::condition_variable notify_var;
|
||||||
|
uint32_t frame_cnt = 0;
|
||||||
|
|
||||||
uint8_t* image_ptr = nullptr;
|
uint8_t* image_ptr = nullptr;
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
|
|
||||||
state_e state = IDLE;
|
bool recording = false;
|
||||||
|
|
||||||
std::optional<opengl_texture> texture;
|
std::optional<opengl_texture> texture;
|
||||||
|
|
||||||
void workthread_func();
|
void capture_func();
|
||||||
|
void record_func();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cameraview_panel();
|
cameraview_panel();
|
||||||
@@ -37,7 +45,7 @@ private:
|
|||||||
|
|
||||||
void render() override;
|
void render() override;
|
||||||
void render_contents() override;
|
void render_contents() override;
|
||||||
bool set_state(state_e e);
|
bool set_state(bool rec);
|
||||||
|
|
||||||
std::string rec_name;
|
std::string rec_name;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user