diff options
Diffstat (limited to 'video-opencv')
| -rw-r--r-- | video-opencv/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | video-opencv/camera-impl.cpp | 173 | ||||
| -rw-r--r-- | video-opencv/camera-names.cpp | 106 | ||||
| -rw-r--r-- | video-opencv/camera-names.hpp | 18 | ||||
| -rw-r--r-- | video-opencv/export.hpp | 11 | ||||
| -rw-r--r-- | video-opencv/lang/nl_NL.ts | 4 | ||||
| -rw-r--r-- | video-opencv/lang/ru_RU.ts | 4 | ||||
| -rw-r--r-- | video-opencv/lang/stub.ts | 4 | ||||
| -rw-r--r-- | video-opencv/lang/zh_CN.ts | 4 | ||||
| -rw-r--r-- | video-opencv/video-property-page.cpp | 165 | ||||
| -rw-r--r-- | video-opencv/video-property-page.hpp | 13 | 
11 files changed, 508 insertions, 0 deletions
| diff --git a/video-opencv/CMakeLists.txt b/video-opencv/CMakeLists.txt new file mode 100644 index 00000000..d8b9b896 --- /dev/null +++ b/video-opencv/CMakeLists.txt @@ -0,0 +1,6 @@ +find_package(OpenCV QUIET) + +if(OpenCV_FOUND) +    otr_module(video-opencv) +    target_link_libraries(${self} opencv_core opencv_videoio opentrack-video) +endif() diff --git a/video-opencv/camera-impl.cpp b/video-opencv/camera-impl.cpp new file mode 100644 index 00000000..ca18fd4b --- /dev/null +++ b/video-opencv/camera-impl.cpp @@ -0,0 +1,173 @@ +/* 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. + */ + +#include "compat/sleep.hpp" +#include "video/camera.hpp" + +#include "camera-names.hpp" +#include "video-property-page.hpp" + +#include <optional> + +#include <opencv2/core.hpp> +#include <opencv2/videoio.hpp> + +using namespace video::impl; + +struct cam; + +struct metadata : camera_ +{ +    metadata(); +    std::vector<QString> camera_names() const override; +    std::unique_ptr<camera> make_camera(const QString& name) override; +    bool can_show_dialog(const QString& camera_name) override; +    bool show_dialog(const QString& camera_name) override; +}; + +struct cam final : camera +{ +    cam(int idx); +    ~cam() override; + +    bool start(const info& args) override; +    void stop() override; +    bool is_open() override; +    std::tuple<const frame&, bool> get_frame() override; +    bool show_dialog() override; + +    bool get_frame_(); + +    std::optional<cv::VideoCapture> cap; +    cv::Mat mat; +    frame frame_; +    int idx = -1; +}; + +metadata::metadata() = default; + +std::unique_ptr<camera> metadata::make_camera(const QString& name) +{ +    int idx = camera_name_to_index(name); +    if (idx != -1) +        return std::make_unique<cam>(idx); +    else +        return nullptr; +} + +std::vector<QString> metadata::camera_names() const +{ +    return get_camera_names(); +} + +bool metadata::can_show_dialog(const QString& camera_name) +{ +    return camera_name_to_index(camera_name) != -1; +} + +bool metadata::show_dialog(const QString& camera_name) +{ +    int idx = camera_name_to_index(camera_name); +    if (idx != -1) +    { +        video_property_page::show(idx); +        return true; +    } +    else +        return false; +} + +cam::cam(int idx) : idx(idx) +{ +} + +cam::~cam() +{ +    stop(); +} + +void cam::stop() +{ +    if (cap) +    { +        if (cap->isOpened()) +            cap->release(); +        cap = std::nullopt; +    } +    mat = cv::Mat(); +    frame_ = { {}, false }; +} + +bool cam::is_open() +{ +    return !!cap; +} + +bool cam::start(const info& args) +{ +    stop(); +    cap.emplace(idx); + +    if (args.width > 0 && args.height > 0) +    { +        cap->set(cv::CAP_PROP_FRAME_WIDTH,  args.width); +        cap->set(cv::CAP_PROP_FRAME_HEIGHT, args.height); +    } +    if (args.fps > 0) +        cap->set(cv::CAP_PROP_FPS, args.fps); + +    if (!cap->isOpened()) +        goto fail; + +    if (!get_frame_()) +        goto fail; + +    return true; + +fail: +    stop(); +    return false; +} + +bool cam::get_frame_() +{ +    if (!is_open()) +        return false; + +    for (unsigned i = 0; i < 10; i++) +    { +        if (cap->read(mat)) +        { +            frame_.data = mat.data; +            frame_.width = mat.cols; +            frame_.height = mat.rows; +            frame_.stride = mat.step.p[0]; +            frame_.channels = mat.channels(); + +            return true; +        } +        portable::sleep(50); +    } + +    return false; +} + +std::tuple<const frame&, bool> cam::get_frame() +{ +    bool ret = get_frame_(); +    return { frame_, ret }; +} + +bool cam::show_dialog() +{ +    if (is_open()) +        return video_property_page::show_from_capture(*cap, idx); +    else +        return video_property_page::show(idx); +} + +OTR_REGISTER_CAMERA(metadata) diff --git a/video-opencv/camera-names.cpp b/video-opencv/camera-names.cpp new file mode 100644 index 00000000..69926e5a --- /dev/null +++ b/video-opencv/camera-names.cpp @@ -0,0 +1,106 @@ +#include "camera-names.hpp" + +#include <algorithm> +#include <iterator> + +#ifdef _WIN32 +#   include <cwchar> +#   define NO_DSHOW_STRSAFE +#   include <dshow.h> +#elif defined(__unix) || defined(__linux) || defined(__APPLE__) +#   include <unistd.h> +#endif + +#ifdef __linux +#   include <fcntl.h> +#   include <sys/ioctl.h> +#   include <linux/videodev2.h> +#   include <cerrno> +#   include <cstring> +#endif + +#include <QDebug> + +int camera_name_to_index(const QString &name) +{ +    auto list = get_camera_names(); +    auto it = std::find(list.cbegin(), list.cend(), name); +    if (it != list.cend()) +        return std::distance(list.cbegin(), it); + +    return -1; +} + +std::vector<QString> get_camera_names() +{ +    std::vector<QString> ret; +#ifdef _WIN32 +    // Create the System Device Enumerator. +    HRESULT hr; +    CoInitialize(nullptr); +    ICreateDevEnum *pSysDevEnum = nullptr; +    hr = CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum); +    if (FAILED(hr)) +    { +        qDebug() << "failed CLSID_SystemDeviceEnum" << hr; +        return ret; +    } +    // Obtain a class enumerator for the video compressor category. +    IEnumMoniker *pEnumCat = nullptr; +    hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumCat, 0); + +    if (hr == S_OK) { +        // Enumerate the monikers. +        IMoniker *pMoniker = nullptr; +        ULONG cFetched; +        while (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) +        { +            IPropertyBag *pPropBag; +            hr = pMoniker->BindToStorage(nullptr, nullptr, IID_IPropertyBag, (void **)&pPropBag); +            if (SUCCEEDED(hr))	{ +                // To retrieve the filter's friendly name, do the following: +                VARIANT var; +                VariantInit(&var); +                hr = pPropBag->Read(L"FriendlyName", &var, nullptr); +                if (SUCCEEDED(hr)) +                { +                    // Display the name in your UI somehow. +                    QString str((QChar*)var.bstrVal, int(std::wcslen(var.bstrVal))); +                    ret.push_back(str); +                } +                VariantClear(&var); +                pPropBag->Release(); +            } +            pMoniker->Release(); +        } +        pEnumCat->Release(); +    } +    else +        qDebug() << "failed CLSID_VideoInputDeviceCategory" << hr; + +    pSysDevEnum->Release(); +#endif + +#ifdef __linux +    for (int i = 0; i < 16; i++) { +        char buf[32]; +        snprintf(buf, sizeof(buf), "/dev/video%d", i); + +        if (access(buf, R_OK | W_OK) == 0) { +            int fd = open(buf, O_RDONLY); +            if (fd == -1) +                continue; +            struct v4l2_capability video_cap; +            if(ioctl(fd, VIDIOC_QUERYCAP, &video_cap) == -1) +            { +                qDebug() << "VIDIOC_QUERYCAP" << errno; +                close(fd); +                continue; +            } +            ret.push_back(QString((const char*)video_cap.card)); +            close(fd); +        } +    } +#endif +    return ret; +} diff --git a/video-opencv/camera-names.hpp b/video-opencv/camera-names.hpp new file mode 100644 index 00000000..9f0883f5 --- /dev/null +++ b/video-opencv/camera-names.hpp @@ -0,0 +1,18 @@ +/* Copyright (c) 2014-2015, 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 <vector> +#include <QString> + +#include "export.hpp" + +std::vector<QString> get_camera_names(); +int camera_name_to_index(const QString &name); + diff --git a/video-opencv/export.hpp b/video-opencv/export.hpp new file mode 100644 index 00000000..1d43a9f1 --- /dev/null +++ b/video-opencv/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_OPENCV +#   define OTR_VIDEO_OPENCV_EXPORT OTR_GENERIC_EXPORT +#else +#   define OTR_VIDEO_OPENCV_EXPORT OTR_GENERIC_IMPORT +#endif diff --git a/video-opencv/lang/nl_NL.ts b/video-opencv/lang/nl_NL.ts new file mode 100644 index 00000000..6401616d --- /dev/null +++ b/video-opencv/lang/nl_NL.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +</TS> diff --git a/video-opencv/lang/ru_RU.ts b/video-opencv/lang/ru_RU.ts new file mode 100644 index 00000000..6401616d --- /dev/null +++ b/video-opencv/lang/ru_RU.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +</TS> diff --git a/video-opencv/lang/stub.ts b/video-opencv/lang/stub.ts new file mode 100644 index 00000000..6401616d --- /dev/null +++ b/video-opencv/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-opencv/lang/zh_CN.ts b/video-opencv/lang/zh_CN.ts new file mode 100644 index 00000000..6401616d --- /dev/null +++ b/video-opencv/lang/zh_CN.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +</TS> diff --git a/video-opencv/video-property-page.cpp b/video-opencv/video-property-page.cpp new file mode 100644 index 00000000..92abd887 --- /dev/null +++ b/video-opencv/video-property-page.cpp @@ -0,0 +1,165 @@ +/* Copyright (c) 2016 Stanislaw Halik + * + * 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. + */ + +#include "video-property-page.hpp" + +#ifdef _WIN32 + +#include "camera-names.hpp" +#include "compat/sleep.hpp" +#include "compat/run-in-thread.hpp" +#include "compat/library-path.hpp" + +#include <cstring> + +#include <opencv2/videoio.hpp> + +#include <QApplication> +#include <QProcess> +#include <QThread> +#include <QMessageBox> + +#include <QDebug> + +bool video_property_page::show_from_capture(cv::VideoCapture& cap, int /*index */) +{ +    return cap.set(cv::CAP_PROP_SETTINGS, 0); +} + +struct prop_settings_worker final : QThread +{ +    explicit prop_settings_worker(int idx); +    ~prop_settings_worker() override; + +private: +    void open_prop_page(); +    void run() override; + +    cv::VideoCapture cap; +    int idx = -1; +}; + +prop_settings_worker::prop_settings_worker(int idx_) +{ +    int ret = (int)cap.get(cv::CAP_PROP_SETTINGS); + +    if (ret != 0) +        run_in_thread_async(qApp, [] { +            QMessageBox::warning(nullptr, +                                 "Camera properties", +                                 "Camera dialog already opened", +                                 QMessageBox::Cancel, +                                 QMessageBox::NoButton); +        }); +    else +    { +        idx = idx_; +        // DON'T MOVE IT +        // ps3 eye will reset to default settings if done from another thread +        open_prop_page(); +    } +} + +void prop_settings_worker::open_prop_page() +{ +    cap.open(idx); + +    if (cap.isOpened()) +    { +        cv::Mat tmp; + +        for (unsigned k = 0; k < 2000/50; k++) +        { +            if (cap.read(tmp)) +            { +                qDebug() << "got frame" << tmp.rows << tmp.cols; +                goto ok; +            } +            portable::sleep(50); +        } +    } + +    qDebug() << "property-page: can't open camera"; +    idx = -1; + +    return; + +ok: +    portable::sleep(100); + +    qDebug() << "property-page: opening for" << idx; + +    if (!cap.set(cv::CAP_PROP_SETTINGS, 0)) +    { +        run_in_thread_async(qApp, [] { +            QMessageBox::warning(nullptr, +                                 "Camera properties", +                                 "Can't open camera dialog", +                                 QMessageBox::Cancel, +                                 QMessageBox::NoButton); +        }); +    } +} + +prop_settings_worker::~prop_settings_worker() +{ +    if (idx != -1) +    { +        // ax filter is race condition-prone +        portable::sleep(250); +        cap.release(); +        // idem +        portable::sleep(250); + +        qDebug() << "property-page: closed" << idx; +    } +} + +void prop_settings_worker::run() +{ +    if (idx != -1) +    { +        while (cap.get(cv::CAP_PROP_SETTINGS) > 0) +            portable::sleep(1000); +    } +} + +bool video_property_page::show(int idx) +{ +    auto thread = new prop_settings_worker(idx); + +    // XXX is this a race condition? +    thread->moveToThread(qApp->thread()); +    QObject::connect(thread, &QThread::finished, qApp, [thread] { thread->deleteLater(); }, Qt::DirectConnection); + +    thread->start(); + +    return true; +} + +#elif defined(__linux) +#   include <QProcess> +#   include "compat/camera-names.hpp" + +bool video_property_page::show(int idx) +{ +    const QList<QString> camera_names(get_camera_names()); + +    if (idx >= 0 && idx < camera_names.size()) +        return QProcess::startDetached("qv4l2", QStringList { "-d", QString("/dev/video%1").arg(idx) }); +    else +        return false; +} + +bool video_property_page::show_from_capture(cv::VideoCapture&, int idx) +{ +    return show(idx); +} +#else +bool video_property_page::show(int) { return false; } +bool video_property_page::show_from_capture(cv::VideoCapture&, int) { return false; } +#endif diff --git a/video-opencv/video-property-page.hpp b/video-opencv/video-property-page.hpp new file mode 100644 index 00000000..c2b9525d --- /dev/null +++ b/video-opencv/video-property-page.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include <QString> +#include <opencv2/videoio.hpp> + +struct video_property_page final +{ +    video_property_page() = delete; +    static bool show(int id); +    static bool show_from_capture(cv::VideoCapture& cap, int index); +private: +}; + | 
