summaryrefslogtreecommitdiffhomepage
path: root/video
diff options
context:
space:
mode:
Diffstat (limited to 'video')
-rw-r--r--video/CMakeLists.txt1
-rw-r--r--video/camera.cpp89
-rw-r--r--video/camera.hpp111
-rw-r--r--video/export.hpp11
-rw-r--r--video/lang/de_DE.ts4
-rw-r--r--video/lang/nl_NL.ts4
-rw-r--r--video/lang/ru_RU.ts4
-rw-r--r--video/lang/stub.ts4
-rw-r--r--video/lang/zh_CN.ts4
-rw-r--r--video/video-widget.cpp90
-rw-r--r--video/video-widget.hpp49
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);
+};