mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-17 22:39:17 +02:00
extcam: split into threads
This commit is contained in:
@@ -233,12 +233,10 @@ driver_ui::set_cursor( bool const Visible ) {
|
||||
void
|
||||
driver_ui::render_() {
|
||||
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;
|
||||
} else if (!rec_name && m_cameraviewpanel.is_open)
|
||||
m_cameraviewpanel.set_state(ui::cameraview_panel::PREVIEW);
|
||||
else if (!rec_name)
|
||||
m_cameraviewpanel.set_state(ui::cameraview_panel::IDLE);
|
||||
} else if (!rec_name)
|
||||
m_cameraviewpanel.set_state(false);
|
||||
|
||||
// pause/quit modal
|
||||
auto const popupheader { STR_C("Simulation Paused") };
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
#include "Logs.h"
|
||||
|
||||
#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()
|
||||
@@ -25,8 +25,16 @@ size_t piped_proc::read(unsigned char *buf, size_t len)
|
||||
|
||||
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
|
||||
piped_proc::piped_proc(std::string cmd)
|
||||
piped_proc::piped_proc(std::string cmd, bool write)
|
||||
{
|
||||
PROCESS_INFORMATION process;
|
||||
STARTUPINFO siStartInfo;
|
||||
@@ -45,14 +53,17 @@ piped_proc::piped_proc(std::string cmd)
|
||||
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!");
|
||||
return;
|
||||
}
|
||||
|
||||
siStartInfo.cb = sizeof(STARTUPINFO);
|
||||
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;
|
||||
|
||||
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);
|
||||
if (pipe_rd)
|
||||
CloseHandle(pipe_rd);
|
||||
if (proc_h) {
|
||||
WaitForSingleObject(proc_h, INFINITE);
|
||||
if (proc_h)
|
||||
CloseHandle(proc_h);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -23,7 +23,8 @@ HANDLE pipe_wr = nullptr;
|
||||
#endif
|
||||
|
||||
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 write(unsigned char *buf, size_t len);
|
||||
~piped_proc();
|
||||
};
|
||||
|
||||
@@ -20,23 +20,41 @@ void cameraview_window_callback(ImGuiSizeCallbackData *data) {
|
||||
}
|
||||
|
||||
ui::cameraview_panel::~cameraview_panel() {
|
||||
exit_thread = true;
|
||||
if (workthread.joinable())
|
||||
workthread.detach();
|
||||
record_state = STOPPING;
|
||||
if (record_workthread.joinable())
|
||||
record_workthread.join();
|
||||
|
||||
capture_state = STOPPING;
|
||||
if (capture_workthread.joinable())
|
||||
capture_workthread.join();
|
||||
}
|
||||
|
||||
void ui::cameraview_panel::render()
|
||||
{
|
||||
if (!is_open && state != RECORDING) {
|
||||
exit_thread = true;
|
||||
if (Global.extcam_cmd.empty())
|
||||
return;
|
||||
|
||||
if (record_state == STOPPING_DONE) {
|
||||
record_workthread.join();
|
||||
record_state = STOPPED;
|
||||
}
|
||||
|
||||
if (exit_thread) {
|
||||
if (workthread.joinable())
|
||||
workthread.join();
|
||||
exit_thread = false;
|
||||
if (state != IDLE && !Global.extcam_cmd.empty())
|
||||
workthread = std::thread(&cameraview_panel::workthread_func, this);
|
||||
if (capture_state == STOPPING_DONE) {
|
||||
capture_workthread.join();
|
||||
capture_state = STOPPED;
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -52,15 +70,14 @@ void ui::cameraview_panel::render()
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
bool ui::cameraview_panel::set_state(state_e e)
|
||||
bool ui::cameraview_panel::set_state(bool rec)
|
||||
{
|
||||
if (state != e) {
|
||||
exit_thread = true;
|
||||
state = e;
|
||||
is_open = (state != IDLE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
if (recording == rec)
|
||||
return false;
|
||||
|
||||
recording = rec;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void ui::cameraview_panel::workthread_func()
|
||||
void ui::cameraview_panel::capture_func()
|
||||
{
|
||||
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);
|
||||
|
||||
@@ -119,7 +127,9 @@ void ui::cameraview_panel::workthread_func()
|
||||
uint8_t *active_buffer = new uint8_t[frame_size];
|
||||
|
||||
size_t bufpos = 0;
|
||||
while (!exit_thread) {
|
||||
|
||||
capture_state = RUNNING;
|
||||
while (capture_state == RUNNING) {
|
||||
size_t read = 0;
|
||||
read = proc.read(read_buffer + bufpos, frame_size - bufpos);
|
||||
|
||||
@@ -136,10 +146,54 @@ void ui::cameraview_panel::workthread_func()
|
||||
image_ptr = read_buffer;
|
||||
read_buffer = active_buffer;
|
||||
active_buffer = image_ptr;
|
||||
|
||||
frame_cnt++;
|
||||
notify_var.notify_one();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
image_ptr = nullptr;
|
||||
delete[] read_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
|
||||
{
|
||||
public:
|
||||
enum state_e {
|
||||
IDLE,
|
||||
PREVIEW,
|
||||
RECORDING
|
||||
private:
|
||||
|
||||
enum thread_state {
|
||||
STARTING,
|
||||
RUNNING,
|
||||
STOPPING,
|
||||
STOPPING_DONE,
|
||||
STOPPED,
|
||||
};
|
||||
|
||||
private:
|
||||
std::atomic_bool exit_thread = true;
|
||||
std::thread workthread;
|
||||
std::atomic<thread_state> capture_state = STOPPED;
|
||||
std::atomic<thread_state> record_state = STOPPED;
|
||||
std::thread capture_workthread;
|
||||
std::thread record_workthread;
|
||||
|
||||
std::condition_variable notify_var;
|
||||
uint32_t frame_cnt = 0;
|
||||
|
||||
uint8_t* image_ptr = nullptr;
|
||||
std::mutex mutex;
|
||||
|
||||
state_e state = IDLE;
|
||||
bool recording = false;
|
||||
|
||||
std::optional<opengl_texture> texture;
|
||||
|
||||
void workthread_func();
|
||||
void capture_func();
|
||||
void record_func();
|
||||
|
||||
public:
|
||||
cameraview_panel();
|
||||
@@ -37,7 +45,7 @@ private:
|
||||
|
||||
void render() override;
|
||||
void render_contents() override;
|
||||
bool set_state(state_e e);
|
||||
bool set_state(bool rec);
|
||||
|
||||
std::string rec_name;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user