extcam: split into threads

This commit is contained in:
milek7
2019-11-07 00:04:30 +01:00
parent 6a17b134dc
commit b823e0a5b6
5 changed files with 136 additions and 55 deletions

View File

@@ -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") };

View File

@@ -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

View File

@@ -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();
}; };

View File

@@ -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;
} }

View File

@@ -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;
}; };