diff options
-rw-r--r-- | video-ps3eye/CMakeLists.txt | 2 | ||||
-rw-r--r-- | video-ps3eye/module.cpp | 156 | ||||
-rw-r--r-- | video-ps3eye/module.hpp | 67 | ||||
-rw-r--r-- | video-ps3eye/shm-layout.hpp | 4 | ||||
-rw-r--r-- | video-ps3eye/wrapper.cxx | 31 |
5 files changed, 205 insertions, 55 deletions
diff --git a/video-ps3eye/CMakeLists.txt b/video-ps3eye/CMakeLists.txt index 5f81b3fb..f357f965 100644 --- a/video-ps3eye/CMakeLists.txt +++ b/video-ps3eye/CMakeLists.txt @@ -49,7 +49,7 @@ endif() if(TARGET ps3eye-subprocess) otr_module(video-ps3eye) - link_libraries(ps3eye-driver) + target_link_libraries(${self} ps3eye-driver opentrack-video) if(WIN32) set(path "${SDK_LIBUSB}/libusb-1.0.dll") if(EXISTS "${path}") diff --git a/video-ps3eye/module.cpp b/video-ps3eye/module.cpp index ac6d8807..26b771e2 100644 --- a/video-ps3eye/module.cpp +++ b/video-ps3eye/module.cpp @@ -1,7 +1,19 @@ #include "module.hpp" -#if 0 +#include "compat/library-path.hpp" +#include "compat/sleep.hpp" +#include "compat/run-in-thread.hpp" + +#include <cstddef> + +#include <QCoreApplication> +#include <QMessageBox> + #include <libusb.h> +#ifdef __GNUG__ +# pragma clang diagnostic ignored "-Wcast-qual" +#endif + int device_count() { libusb_context * ctx = nullptr; @@ -50,8 +62,6 @@ bool check_device_exists() static const QString camera_name = QStringLiteral("PS3 Eye open driver"); -namespace video::impl { - std::vector<QString> ps3eye_camera_::camera_names() const { if (check_device_exists()) @@ -66,16 +76,150 @@ std::unique_ptr<camera> ps3eye_camera_::make_camera(const QString& name) else return {}; } -bool ps3eye_camera_::show_dialog(const QString& camera_name) +bool ps3eye_camera_::show_dialog(const QString&) { // TODO return false; } -bool ps3eye_camera_::can_show_dialog(const QString& camera_name) +bool ps3eye_camera_::can_show_dialog(const QString&) { return false; } -} // ns video::impl +ps3eye_camera::ps3eye_camera() +{ + if (!shm.success()) + return; + + static const QString library_path(OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH); + wrapper.setWorkingDirectory(library_path); +#ifdef _WIN32 + wrapper.setProgram("\"ps3eye-subprocess.exe\""); +#else + wrapper.setProgram("ps3eye-subprocess"); #endif +} + +ps3eye_camera::~ps3eye_camera() +{ + stop(); +} + +void ps3eye_camera::stop() +{ + open = false; + + if (wrapper.state() != QProcess::NotRunning) + { + if (shm.success()) + { + volatile auto& ptr = *(ps3eye::shm*)shm.ptr(); + ptr.in.do_exit = true; + if (!wrapper.waitForFinished(500)) + wrapper.kill(); + wrapper.waitForFinished(-1); + } + else + { + wrapper.kill(); + wrapper.waitForFinished(-1); + } + } +} + +bool ps3eye_camera::start(info& args) +{ + if (!shm.success()) + return false; + + volatile auto& ptr = *(ps3eye::shm*)shm.ptr(); + + using mode = ps3eye::shm_in::mode; + + open = false; + fr = {}; + fr.channels = 3; + fr.channel_size = 1; + + if (!args.width || args.width > 320) + { + ptr.in.resolution = mode::vga; + fr.width = 640; fr.height = 480; + } + else + { + ptr.in.resolution = mode::qvga; + fr.width = 320; fr.height = 240; + } + + ptr.in.framerate = (uint8_t)std::clamp(args.fps, 30, 187); + ptr.in.gain = (uint8_t)s.gain; + ptr.in.exposure = (uint8_t)s.exposure; + + wrapper.start(); + + constexpr int sleep_ms = 10, max_sleeps = 5000/sleep_ms; + + for (int i = 0; i < max_sleeps; i++) + { + if (ptr.out.timecode > 0) + goto ok; + portable::sleep(sleep_ms); + } + + run_in_thread_async(qApp, [&]() { + QString error; + if (ptr.out.error_string[0] == '\0') + error = "Unknown error"; + else + error = QString::fromLatin1((const char*)ptr.out.error_string, + strnlen((const char*)ptr.out.error_string, sizeof(ptr.out.error_string))); + + QMessageBox::critical(nullptr, "Can't open camera", "PS3 Eye driver error: " + error, QMessageBox::Close); + }); + + return false; + +ok: + open = true; + return true; +} + +std::tuple<const frame&, bool> ps3eye_camera::get_frame() +{ + volatile auto& ptr = *(ps3eye::shm*)shm.ptr(); + constexpr int sleep_ms = 10; + constexpr int max_sleeps = 2000/sleep_ms; + + if (!open) + goto fail; + + for (int i = 0; i < max_sleeps; i++) + { + unsigned new_timecode = ptr.out.timecode; + if (timecode != new_timecode) + { + timecode = new_timecode; + goto ok; + } + portable::sleep(sleep_ms); + } + +fail: + stop(); + return { fr, false }; + + static_assert(offsetof(decltype(ptr.out), data_640x480) == offsetof(decltype(ptr.out), data_320x240)); + +ok: + fr.data = (unsigned char*)ptr.out.data_640x480; + return { fr, true}; +} + +bool ps3eye_camera::show_dialog() +{ + return false; +} + +OTR_REGISTER_CAMERA(ps3eye_camera_) diff --git a/video-ps3eye/module.hpp b/video-ps3eye/module.hpp index 357af14c..73cdf04c 100644 --- a/video-ps3eye/module.hpp +++ b/video-ps3eye/module.hpp @@ -1,54 +1,51 @@ #pragma once #include "video/camera.hpp" +#include "shm-layout.hpp" +#include "compat/shm.h" +#include "options/options.hpp" +#include "compat/macros1.h" -#if 0 +using namespace options; -namespace video { +#include <QProcess> -struct OTR_VIDEO_EXPORT camera_ +using video::impl::camera; +using video::impl::camera_; +using video::frame; + +struct settings : opts { - camera_(); - virtual ~camera_(); + settings() : opts{"video-ps3eye"} {} - virtual std::vector<QString> camera_names() const = 0; - virtual std::unique_ptr<camera> make_camera(const QString& name) = 0; - virtual bool show_dialog(const QString& camera_name) = 0; - virtual bool can_show_dialog(const QString& camera_name) = 0; + value<slider_value> exposure{b, "exposure", {63, 0, 63}}; + value<slider_value> gain{b, "gain", {30, 0, 63}}; }; -struct OTR_VIDEO_EXPORT camera +struct ps3eye_camera final : video::impl::camera { - struct info final - { - // TODO: expose FOV-based focal length for regular webcams - int width = 0, height = 0, fps = 0; - double fx = 0, fy = 0; // focal length - double P_x = 0, P_y = 0; // principal point - double dist_c[8] {}; // distortion coefficients - }; - - camera(); - virtual ~camera(); - - [[nodiscard]] virtual bool start(info& args) = 0; - virtual void stop() = 0; - virtual bool is_open() = 0; - - virtual std::tuple<const frame&, bool> get_frame() = 0; - [[nodiscard]] virtual bool show_dialog() = 0; + QProcess wrapper; + shm_wrapper shm { "ps3eye-driver-shm", nullptr, sizeof(ps3eye::shm) }; + settings s; + frame fr; + bool open = false; + unsigned timecode = 0; + + ps3eye_camera(); + ~ps3eye_camera() override; + + bool start(info& args) override; + void stop() override; + bool is_open() override { return open; } + + std::tuple<const frame&, bool> get_frame() override; + [[nodiscard]] bool show_dialog() override; }; -} // ns video - -#endif - -namespace video::impl { -struct ps3eye_camera_ : camera_ +struct ps3eye_camera_ final : video::impl::camera_ { std::vector<QString> camera_names() const override; std::unique_ptr<camera> make_camera(const QString& name) override; bool show_dialog(const QString& camera_name) override; bool can_show_dialog(const QString& camera_name) override; }; -} // ns video::impl diff --git a/video-ps3eye/shm-layout.hpp b/video-ps3eye/shm-layout.hpp index eba4b8b0..2335eab8 100644 --- a/video-ps3eye/shm-layout.hpp +++ b/video-ps3eye/shm-layout.hpp @@ -8,7 +8,7 @@ struct shm_in { enum class status : uint8_t { starting, running, fail, terminate, }; uint32_t settings_updated; - uint16_t framerate; + uint8_t framerate; mode resolution; status status_; //uint8_t sharpness, contrast, brightness hue, saturation; @@ -19,11 +19,11 @@ struct shm_in { struct shm_out { uint32_t timecode; uint32_t settings_updated_ack; + char error_string[256]; union { uint8_t data_320x240[320][240][3]; uint8_t data_640x480[640][480][3]; }; - char error_string[256]; }; struct shm { diff --git a/video-ps3eye/wrapper.cxx b/video-ps3eye/wrapper.cxx index be9e338e..0f063097 100644 --- a/video-ps3eye/wrapper.cxx +++ b/video-ps3eye/wrapper.cxx @@ -3,6 +3,8 @@ #include "ps3eye-driver/ps3eye.hpp" +#include <thread> +#include <chrono> #include <cstdlib> #ifdef __clang__ @@ -32,7 +34,8 @@ static void error(volatile ps3eye::shm_out& out, const char (&error)[N], const x static void update_settings(ps3eye::camera& camera, const volatile ps3eye::shm_in& in) { - camera.set_framerate(in.framerate); + // TODO + //camera.set_framerate(in.framerate); camera.set_auto_gain(in.auto_gain); camera.set_gain(in.gain); camera.set_exposure(in.exposure); @@ -44,7 +47,7 @@ int main(int argc, char** argv) (void)argc; (void)argv; shm_wrapper mem_("ps3eye-driver-shm", nullptr, sizeof(ps3eye::shm)); volatile auto& ptr_ = *(ps3eye::shm*)mem_.ptr(); - const volatile auto& in = ptr_.in; + volatile auto& in = ptr_.in; volatile auto& out = ptr_.out; auto cameras = ps3eye::list_devices(); @@ -53,6 +56,7 @@ int main(int argc, char** argv) error(out, "no camera found"); auto& camera = cameras[0]; + camera->set_debug(false); uint8_t* frame; decltype(out.timecode) timecode = 0; @@ -75,12 +79,18 @@ int main(int argc, char** argv) if (!framerate) framerate = 60; - bool ret = camera->init(mode, framerate) && camera->start(); - if (!ret) + if (!camera->init(mode, framerate)) error(out, "camera init failed: %s", camera->error_string()); + + update_settings(*camera, in); + + if (!camera->start()) + error(out, "can't start camera: %s", camera->error_string()); } - update_settings(*camera, in); + out.timecode = 0; + in.do_exit = false; + FULL_BARRIER(); for (;;) { @@ -93,12 +103,11 @@ int main(int argc, char** argv) } } - bool success = true; - - success &= camera->get_frame(frame); - - if (!success) - error(out, "error %s", camera->error_string()); + if (!camera->get_frame(frame)) + { + std::this_thread::sleep_for(std::chrono::milliseconds{4}); + continue; + } out.timecode = ++timecode; |