diff options
Diffstat (limited to 'video')
-rw-r--r-- | video/CMakeLists.txt | 1 | ||||
-rw-r--r-- | video/camera.cpp | 89 | ||||
-rw-r--r-- | video/camera.hpp | 111 | ||||
-rw-r--r-- | video/export.hpp | 11 | ||||
-rw-r--r-- | video/lang/de_DE.ts | 4 | ||||
-rw-r--r-- | video/lang/nl_NL.ts | 4 | ||||
-rw-r--r-- | video/lang/ru_RU.ts | 4 | ||||
-rw-r--r-- | video/lang/stub.ts | 4 | ||||
-rw-r--r-- | video/lang/zh_CN.ts | 4 | ||||
-rw-r--r-- | video/video-widget.cpp | 90 | ||||
-rw-r--r-- | video/video-widget.hpp | 49 |
11 files changed, 371 insertions, 0 deletions
diff --git a/video/CMakeLists.txt b/video/CMakeLists.txt new file mode 100644 index 00000000..0a9dfd24 --- /dev/null +++ b/video/CMakeLists.txt @@ -0,0 +1 @@ +otr_module(video BIN) diff --git a/video/camera.cpp b/video/camera.cpp new file mode 100644 index 00000000..a66d8a59 --- /dev/null +++ b/video/camera.cpp @@ -0,0 +1,89 @@ +#include "camera.hpp" + +#include <algorithm> +#include <utility> +#include <QMutex> + +std::pair<std::vector<std::unique_ptr<video::impl::camera_>>&, QMutex&> get_metadata() +{ + static std::vector<std::unique_ptr<video::impl::camera_>> metadata; + static QMutex mtx; + return { metadata, mtx }; +} + +namespace video::impl { + +camera_::camera_() = default; +camera_::~camera_() = default; + +camera::camera() = default; +camera::~camera() = default; + +void register_camera(std::unique_ptr<impl::camera_> camera) +{ + auto [metadata, mtx] = get_metadata(); + QMutexLocker l(&mtx); + metadata.push_back(std::move(camera)); +} + +} // ns video::impl + +namespace video { + +bool show_dialog(const QString& camera_name) +{ + auto [metadata, mtx] = get_metadata(); + QMutexLocker l(&mtx); + + for (auto& camera : metadata) + for (const QString& name : camera->camera_names()) + if (name == camera_name) + return camera->show_dialog(camera_name); + + return false; +} + +std::unique_ptr<camera_impl> make_camera_(const QString& name) +{ + auto [metadata, mtx] = get_metadata(); + QMutexLocker l(&mtx); + + for (auto& camera : metadata) + for (const QString& name_ : camera->camera_names()) + if (name_ == name) + return camera->make_camera(name_); + + return nullptr; +} + +std::unique_ptr<camera_impl> make_camera(const QString& name) +{ + if (auto ret = make_camera_(name)) + return ret; + + auto [metadata, mtx] = get_metadata(); + for (auto& camera : metadata) + for (const QString& name_ : camera->camera_names()) + if (auto ret = camera->make_camera(name_)) + return ret; + + return nullptr; +} + +std::vector<QString> camera_names() +{ + auto [metadata, mtx] = get_metadata(); + + QMutexLocker l(&mtx); + std::vector<QString> names; names.reserve(32); + + for (auto& camera : metadata) + for (const QString& name : camera->camera_names()) + if (std::find(names.cbegin(), names.cend(), name) == names.cend()) + names.push_back(name); + + std::sort(names.begin(), names.end()); + return names; +} + +} // ns video diff --git a/video/camera.hpp b/video/camera.hpp new file mode 100644 index 00000000..6181dbf3 --- /dev/null +++ b/video/camera.hpp @@ -0,0 +1,111 @@ +/* Copyright (c) 2019 Stanislaw Halik <sthalik@misaki.pl> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#pragma once + +#include "export.hpp" + +#include <memory> +#include <vector> + +#include <QString> + +namespace video +{ + +struct frame final +{ + unsigned char* data = nullptr; + // the `stride' member can have a special value of zero, + // signifying stride equal to width * element size + int width = 0, height = 0, stride = 0, channels = 0, channel_size = 1; +}; + +} // ns video + +namespace video::impl { + +using namespace video; + +struct camera; + +struct OTR_VIDEO_EXPORT camera_ +{ + camera_(); + virtual ~camera_(); + + 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; +}; + +struct OTR_VIDEO_EXPORT camera +{ + struct info final + { + enum : unsigned char { channels_gray = 1, channels_bgr = 3 }; + // 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 + bool use_mjpeg = false; + int num_channels = channels_bgr; + }; + + 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; +}; + +OTR_VIDEO_EXPORT +void register_camera(std::unique_ptr<impl::camera_> metadata); + +} // ns video::impl + +#define OTR_REGISTER_CAMERA3(type, ctr) \ + static const char init_ ## ctr = \ + (::video::impl::register_camera(std::make_unique<type>()), 0); + +#ifdef _MSC_VER + // shared library targets without any symbols break cmake build +# define OTR_REGISTER_CAMERA_IMPL(type) \ + extern "C" [[maybe_unused]] __declspec(dllexport) \ + void _opentrack_module_video_ ##type (void) {} +# define OTR_REGISTER_CAMERA_IMPL2(type) \ + OTR_REGISTER_CAMERA_IMPL(type) +#else +# define OTR_REGISTER_CAMERA_IMPL2(type) +#endif + +#define OTR_REGISTER_CAMERA2(type, ctr) \ + OTR_REGISTER_CAMERA3(type, ctr) \ + OTR_REGISTER_CAMERA_IMPL2(type) +#define OTR_REGISTER_CAMERA(type) \ + OTR_REGISTER_CAMERA2(type, __COUNTER__) +namespace video +{ +using camera_impl = impl::camera; + +OTR_VIDEO_EXPORT std::unique_ptr<camera_impl> make_camera(const QString& name); +OTR_VIDEO_EXPORT std::unique_ptr<camera_impl> make_camera_(const QString& name); + +OTR_VIDEO_EXPORT +std::vector<QString> camera_names(); + +[[nodiscard]] +OTR_VIDEO_EXPORT +bool show_dialog(const QString& camera_name); + +} // ns video diff --git a/video/export.hpp b/video/export.hpp new file mode 100644 index 00000000..fc850193 --- /dev/null +++ b/video/export.hpp @@ -0,0 +1,11 @@ +// generates export.hpp for each module from compat/linkage.hpp + +#pragma once + +#include "compat/linkage-macros.hpp" + +#ifdef BUILD_VIDEO +# define OTR_VIDEO_EXPORT OTR_GENERIC_EXPORT +#else +# define OTR_VIDEO_EXPORT OTR_GENERIC_IMPORT +#endif diff --git a/video/lang/de_DE.ts b/video/lang/de_DE.ts new file mode 100644 index 00000000..1552582e --- /dev/null +++ b/video/lang/de_DE.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="de_DE"> +</TS> diff --git a/video/lang/nl_NL.ts b/video/lang/nl_NL.ts new file mode 100644 index 00000000..9e739505 --- /dev/null +++ b/video/lang/nl_NL.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="nl_NL"> +</TS> diff --git a/video/lang/ru_RU.ts b/video/lang/ru_RU.ts new file mode 100644 index 00000000..f62cf2e1 --- /dev/null +++ b/video/lang/ru_RU.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="ru_RU"> +</TS> diff --git a/video/lang/stub.ts b/video/lang/stub.ts new file mode 100644 index 00000000..6401616d --- /dev/null +++ b/video/lang/stub.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +</TS> diff --git a/video/lang/zh_CN.ts b/video/lang/zh_CN.ts new file mode 100644 index 00000000..e5ca8aa9 --- /dev/null +++ b/video/lang/zh_CN.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="zh_CN"> +</TS> diff --git a/video/video-widget.cpp b/video/video-widget.cpp new file mode 100644 index 00000000..3018a3c4 --- /dev/null +++ b/video/video-widget.cpp @@ -0,0 +1,90 @@ +#include "video-widget.hpp" + +#include "compat/check-visible.hpp" +#include "compat/math.hpp" + +#include <cstring> + +#include <QPainter> +#include <QtAlgorithms> +#include <QDebug> + +void video_widget::init_image_nolock() +{ + double dpi = devicePixelRatioF(); + size_.store({ iround(width() * dpi), iround(height() * dpi) }, std::memory_order_release); +} + +video_widget::video_widget(QWidget* parent) : QWidget(parent) +{ + if (parent) + setFixedSize(parent->size()); + else + setFixedSize(320, 240); + init_image_nolock(); + connect(&timer, &QTimer::timeout, this, &video_widget::draw_image, Qt::DirectConnection); + timer.start(15); +} + +void video_widget::update_image(const QImage& img) +{ + if (fresh()) + return; + + set_image(img.constBits(), img.width(), img.height(), + img.bytesPerLine(), img.format()); + set_fresh(true); +} + +void video_widget::set_image(const unsigned char* src, int width, int height, int stride, QImage::Format fmt) +{ + QMutexLocker l(&mtx); + + texture = QImage(); + unsigned nbytes = (unsigned)(stride * height); + vec.resize(nbytes); vec.shrink_to_fit(); + std::memcpy(vec.data(), src, nbytes); + texture = QImage((const unsigned char*)vec.data(), width, height, stride, fmt); +} + +void video_widget::paintEvent(QPaintEvent*) +{ + QPainter painter(this); + + QMutexLocker l(&mtx); + painter.drawImage(rect(), texture); +} + +void video_widget::draw_image() +{ + if (!fresh()) + return; + + if (!check_is_visible()) + return; + + repaint(); + set_fresh(false); +} + +void video_widget::resizeEvent(QResizeEvent*) +{ + QMutexLocker l(&mtx); + init_image_nolock(); +} + +std::tuple<int, int> video_widget::preview_size() const +{ + QSize sz = size_.load(std::memory_order_acquire); + return { sz.width(), sz.height() }; +} + +bool video_widget::fresh() const +{ + return fresh_.load(std::memory_order_acquire); +} + +void video_widget::set_fresh(bool x) +{ + fresh_.store(x, std::memory_order_release); +} diff --git a/video/video-widget.hpp b/video/video-widget.hpp new file mode 100644 index 00000000..4f54b2b9 --- /dev/null +++ b/video/video-widget.hpp @@ -0,0 +1,49 @@ +/* Copyright (c) 2014-2016, 2019 Stanislaw Halik <sthalik@misaki.pl> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#pragma once + +#include "compat/math.hpp" +#include "export.hpp" + +#include <vector> +#include <atomic> +#include <tuple> + +#include <QWidget> +#include <QImage> +#include <QTimer> + +#include <QMutex> + +struct OTR_VIDEO_EXPORT video_widget : QWidget +{ + video_widget(QWidget* parent = nullptr); + + void update_image(const QImage& image); + std::tuple<int, int> preview_size() const; + void resizeEvent(QResizeEvent*) override; + void paintEvent(QPaintEvent*) override; + void draw_image(); + bool fresh() const; + +protected: + mutable QMutex mtx { QMutex::NonRecursive }; + QImage texture; + std::vector<unsigned char> vec; + void set_fresh(bool x); + void set_image(const unsigned char* src, int width, int height, int stride, QImage::Format fmt); + +private: + void init_image_nolock(); + QTimer timer; + + std::atomic<QSize> size_ = QSize(320, 240); + std::atomic<bool> fresh_ { false }; + + static_assert(decltype(fresh_)::is_always_lock_free); +}; |