From a1b8b13a6f6ad4c7fb6df8d1c7219509c7dc1036 Mon Sep 17 00:00:00 2001 From: atraczyk <andreastraczyk@gmail.com> Date: Wed, 28 Dec 2016 11:51:56 -0500 Subject: [PATCH] video: add video device implementation for UWP - adds a video device implementation and a barebones video device montior implementation for UWP. Change-Id: I69108c688f0e55fea1776708919b881afae42552 Tuleap: #790 --- src/client/ring_signal.cpp | 4 +- src/client/videomanager.cpp | 22 +- src/dring/videomanager_interface.h | 29 ++- .../video/androidvideo/video_device_impl.cpp | 2 +- src/media/video/osxvideo/video_device_impl.mm | 2 +- .../video/uwpvideo/video_device_impl.cpp | 219 ++++++++++++++++++ .../uwpvideo/video_device_monitor_impl.cpp | 95 ++++++++ src/media/video/v4l2/video_device_impl.cpp | 2 +- src/media/video/video_device.h | 3 +- src/media/video/video_device_monitor.cpp | 6 +- src/media/video/video_device_monitor.h | 2 +- src/media/video/video_input.cpp | 53 ++--- src/media/video/video_input.h | 10 +- .../video/winvideo/video_device_impl.cpp | 2 +- 14 files changed, 398 insertions(+), 53 deletions(-) create mode 100644 src/media/video/uwpvideo/video_device_impl.cpp create mode 100644 src/media/video/uwpvideo/video_device_monitor_impl.cpp diff --git a/src/client/ring_signal.cpp b/src/client/ring_signal.cpp index 45d495645b..0ff28dbd85 100644 --- a/src/client/ring_signal.cpp +++ b/src/client/ring_signal.cpp @@ -93,9 +93,11 @@ getSignalHandlers() #ifdef __ANDROID__ exported_callback<DRing::VideoSignal::GetCameraInfo>(), exported_callback<DRing::VideoSignal::SetParameters>(), +#endif exported_callback<DRing::VideoSignal::StartCapture>(), exported_callback<DRing::VideoSignal::StopCapture>(), -#endif + exported_callback<DRing::VideoSignal::DeviceAdded>(), + exported_callback<DRing::VideoSignal::ParametersChanged>(), #endif }; diff --git a/src/client/videomanager.cpp b/src/client/videomanager.cpp index 649f9c5d24..05072f651b 100644 --- a/src/client/videomanager.cpp +++ b/src/client/videomanager.cpp @@ -82,6 +82,22 @@ setDefaultDevice(const std::string& name) ring::Manager::instance().saveConfig(); } +std::map<std::string, std::string> +getDeviceParams(const std::string& name) +{ + auto params = ring::Manager::instance().getVideoManager().videoDeviceMonitor.getDeviceParams(name); + std::stringstream width, height, rate; + width << params.width; + height << params.height; + rate << params.framerate; + return { + {"format", params.format}, + {"width", width.str()}, + {"height", height.str()}, + {"rate", rate.str()} + }; +} + std::map<std::string, std::string> getSettings(const std::string& name) { @@ -146,11 +162,11 @@ registerSinkTarget(const std::string& sinkId, const SinkTarget& target) RING_WARN("No sink found for id '%s'", sinkId.c_str()); } -#ifdef __ANDROID__ +#if defined(__ANDROID__) || defined(RING_UWP) void -addVideoDevice(const std::string &node) +addVideoDevice(const std::string &node, std::vector<std::map<std::string, std::string>> const * devInfo) { - ring::Manager::instance().getVideoManager().videoDeviceMonitor.addDevice(node); + ring::Manager::instance().getVideoManager().videoDeviceMonitor.addDevice(node, devInfo); } void diff --git a/src/dring/videomanager_interface.h b/src/dring/videomanager_interface.h index 44c495e2fe..e9b85583b9 100644 --- a/src/dring/videomanager_interface.h +++ b/src/dring/videomanager_interface.h @@ -58,6 +58,9 @@ VideoCapabilities getCapabilities(const std::string& name); std::map<std::string, std::string> getSettings(const std::string& name); void applySettings(const std::string& name, const std::map<std::string, std::string>& settings); void setDefaultDevice(const std::string& name); + +std::map<std::string, std::string> getDeviceParams(const std::string& name); + std::string getDefaultDevice(); std::string getCurrentCodecName(const std::string& callID); void startCamera(); @@ -67,8 +70,8 @@ bool switchInput(const std::string& resource); bool switchToCamera(); void registerSinkTarget(const std::string& sinkId, const SinkTarget& target); -#ifdef __ANDROID__ -void addVideoDevice(const std::string &node); +#if defined(__ANDROID__) || defined(RING_UWP) +void addVideoDevice(const std::string &node, const std::vector<std::map<std::string, std::string>>* devInfo=nullptr); void removeVideoDevice(const std::string &node); void* obtainFrame(int length); void releaseFrame(void* frame); @@ -88,24 +91,32 @@ struct VideoSignal { constexpr static const char* name = "DecodingStopped"; using cb_type = void(const std::string& /*id*/, const std::string& /*shm_path*/, bool /*is_mixer*/); }; -#ifdef __ANDROID__ - struct GetCameraInfo { - constexpr static const char* name = "GetCameraInfo"; - using cb_type = void(const std::string& device, std::vector<int> *formats, std::vector<unsigned> *sizes, std::vector<unsigned> *rates); - }; +#if __ANDROID__ struct SetParameters { constexpr static const char* name = "SetParameters"; using cb_type = void(const std::string& device, const int format, const int width, const int height, const int rate); }; + struct GetCameraInfo { + constexpr static const char* name = "GetCameraInfo"; + using cb_type = void(const std::string& device, std::vector<int> *formats, std::vector<unsigned> *sizes, std::vector<unsigned> *rates); + }; +#endif struct StartCapture { constexpr static const char* name = "StartCapture"; - using cb_type = void(const std::string& device); + using cb_type = void(const std::string& /*device*/); }; struct StopCapture { constexpr static const char* name = "StopCapture"; using cb_type = void(void); }; -#endif + struct DeviceAdded { + constexpr static const char* name = "DeviceAdded"; + using cb_type = void(const std::string& /*device*/); + }; + struct ParametersChanged { + constexpr static const char* name = "ParametersChanged"; + using cb_type = void(const std::string& /*device*/); + }; }; } // namespace DRing diff --git a/src/media/video/androidvideo/video_device_impl.cpp b/src/media/video/androidvideo/video_device_impl.cpp index 50bb83ebda..a758a3aa8a 100644 --- a/src/media/video/androidvideo/video_device_impl.cpp +++ b/src/media/video/androidvideo/video_device_impl.cpp @@ -187,7 +187,7 @@ VideoDeviceImpl::setDeviceParams(const DeviceParams& params) emitSignal<DRing::VideoSignal::SetParameters>(name, fmt_->code, size_.first, size_.second, rate_.real()); } -VideoDevice::VideoDevice(const std::string& path) : +VideoDevice::VideoDevice(const std::string& path, const std::vector<std::map<std::string, std::string>>&) : deviceImpl_(new VideoDeviceImpl(path)) { name = deviceImpl_->name; diff --git a/src/media/video/osxvideo/video_device_impl.mm b/src/media/video/osxvideo/video_device_impl.mm index 0bc1996772..ab29efd79c 100644 --- a/src/media/video/osxvideo/video_device_impl.mm +++ b/src/media/video/osxvideo/video_device_impl.mm @@ -146,7 +146,7 @@ VideoDeviceImpl::getChannelList() const return {"default"}; } -VideoDevice::VideoDevice(const std::string& path) : +VideoDevice::VideoDevice(const std::string& path, const std::vector<std::map<std::string, std::string>>&) : deviceImpl_(new VideoDeviceImpl(path)) { node_ = path; diff --git a/src/media/video/uwpvideo/video_device_impl.cpp b/src/media/video/uwpvideo/video_device_impl.cpp new file mode 100644 index 0000000000..78f524e912 --- /dev/null +++ b/src/media/video/uwpvideo/video_device_impl.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2015-2017 Savoir-faire Linux Inc. + * + * Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com> + * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <array> + +#include "logger.h" +#include "../video_device.h" + +#include "ring_signal.h" + +//#include <ciso646> + +namespace ring { namespace video { + +typedef struct +{ + std::string name; + enum VideoPixelFormat ring_format; +} uwp_fmt; + +// have all formats map to bgra +static const std::array<uwp_fmt, 4> uwp_formats +{ + uwp_fmt { "MJPG", VIDEO_PIXFMT_BGRA }, + uwp_fmt { "RGB24", VIDEO_PIXFMT_BGRA }, + uwp_fmt { "NV12", VIDEO_PIXFMT_BGRA }, + uwp_fmt { "YUY2", VIDEO_PIXFMT_BGRA } +}; + +class VideoDeviceImpl +{ + public: + VideoDeviceImpl(const std::string& path, const std::vector<std::map<std::string, std::string>>& devInfo); + + std::string name; + + DeviceParams getDeviceParams() const; + + void setDeviceParams(const DeviceParams&); + void selectFormat(); + + std::vector<VideoSize> getSizeList() const; + std::vector<FrameRate> getRateList() const; + + private: + + VideoSize getSize(VideoSize size) const; + FrameRate getRate(FrameRate rate) const; + + std::vector<std::string> formats_ {}; + std::vector<VideoSize> sizes_ {}; + std::vector<FrameRate> rates_ {}; + + const uwp_fmt* fmt_ {nullptr}; + VideoSize size_ {}; + FrameRate rate_ {}; + +}; + +void +VideoDeviceImpl::selectFormat() +{ + unsigned best = UINT_MAX; + for(auto fmt : formats_) { + auto f = uwp_formats.begin(); + for (; f != uwp_formats.end(); ++f) { + if (f->name == fmt) { + auto pos = std::distance(uwp_formats.begin(), f); + if (pos < best) + best = pos; + break; + } + } + if (f == uwp_formats.end()) + RING_WARN("Video: No format matching %s", fmt.c_str()); + } + + if (best != UINT_MAX) { + fmt_ = &uwp_formats[best]; + RING_DBG("Video: picked format %s", fmt_->name.c_str()); + } + else { + fmt_ = &uwp_formats[0]; + RING_ERR("Video: Could not find a known format to use"); + } +} + +VideoDeviceImpl::VideoDeviceImpl(const std::string& path, const std::vector<std::map<std::string, std::string>>& devInfo) + : name(path) +{ + for (auto& setting : devInfo) { + formats_.emplace_back(setting.at("format")); + sizes_.emplace_back(std::stoi(setting.at("width")), std::stoi(setting.at("height"))); + rates_.emplace_back(std::stoi(setting.at("rate")), 1); + } + selectFormat(); +} + +VideoSize +VideoDeviceImpl::getSize(VideoSize size) const +{ + for (const auto &iter : sizes_) { + if (iter == size) + return iter; + } + + return sizes_.empty() ? VideoSize{0, 0} : sizes_.back(); +} + +FrameRate +VideoDeviceImpl::getRate(FrameRate rate) const +{ + for (const auto &iter : rates_) { + if (iter == rate) + return iter; + } + + return rates_.empty() ? FrameRate{0, 0} : rates_.back(); +} + +std::vector<VideoSize> +VideoDeviceImpl::getSizeList() const +{ + return sizes_; +} + +std::vector<FrameRate> +VideoDeviceImpl::getRateList() const +{ + return rates_; +} + +DeviceParams +VideoDeviceImpl::getDeviceParams() const +{ + DeviceParams params; + std::stringstream ss1, ss2; + + ss1 << fmt_->ring_format; + ss1 >> params.format; + + params.format = fmt_->name; + params.name = name; + params.input = name; + params.channel = 0; + params.width = size_.first; + params.height = size_.second; + params.framerate = rate_; + + return params; +} + +void +VideoDeviceImpl::setDeviceParams(const DeviceParams& params) +{ + size_ = getSize({params.width, params.height}); + rate_ = getRate(params.framerate); + emitSignal<DRing::VideoSignal::ParametersChanged>(name); +} + +VideoDevice::VideoDevice(const std::string& path, const std::vector<std::map<std::string, std::string>>& devInfo) + : deviceImpl_(new VideoDeviceImpl(path, devInfo)) +{ + node_ = path; + name = deviceImpl_->name; +} + +DeviceParams +VideoDevice::getDeviceParams() const +{ + return deviceImpl_->getDeviceParams(); +} + +void +VideoDevice::setDeviceParams(const DeviceParams& params) +{ + return deviceImpl_->setDeviceParams(params); +} + +std::vector<std::string> +VideoDevice::getChannelList() const +{ + return {"default"}; +} + +std::vector<VideoSize> +VideoDevice::getSizeList(const std::string& channel) const +{ + return deviceImpl_->getSizeList(); +} + +std::vector<FrameRate> +VideoDevice::getRateList(const std::string& channel, VideoSize size) const +{ + return deviceImpl_->getRateList(); +} + +VideoDevice::~VideoDevice() +{} + +}} // namespace ring::video diff --git a/src/media/video/uwpvideo/video_device_monitor_impl.cpp b/src/media/video/uwpvideo/video_device_monitor_impl.cpp new file mode 100644 index 0000000000..e2974ba31c --- /dev/null +++ b/src/media/video/uwpvideo/video_device_monitor_impl.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2015-2017 Savoir-faire Linux Inc. + * + * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> + * Author: Edric Milaret <edric.ladent-milaret@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <mutex> +#include <thread> + +#include "../video_device_monitor.h" +#include "logger.h" +#include "noncopyable.h" + +namespace ring { namespace video { + +class VideoDeviceMonitorImpl +{ + public: + /* + * This is the only restriction to the pImpl design: + * as the Linux implementation has a thread, it needs a way to notify + * devices addition and deletion. + * + * This class should maybe inherit from VideoDeviceMonitor instead of + * being its pImpl. + */ + VideoDeviceMonitorImpl(VideoDeviceMonitor* monitor); + ~VideoDeviceMonitorImpl(); + + void start(); + + private: + NON_COPYABLE(VideoDeviceMonitorImpl); + + VideoDeviceMonitor* monitor_; + + void run(); + + mutable std::mutex mutex_; + bool probing_; + std::thread thread_; +}; + +VideoDeviceMonitorImpl::VideoDeviceMonitorImpl(VideoDeviceMonitor* monitor) + : monitor_(monitor) + , mutex_() + , thread_() +{} + +void +VideoDeviceMonitorImpl::start() +{ + probing_ = true; + thread_ = std::thread(&VideoDeviceMonitorImpl::run, this); +} + +VideoDeviceMonitorImpl::~VideoDeviceMonitorImpl() +{ + probing_ = false; + if (thread_.joinable()) + thread_.join(); +} + +void +VideoDeviceMonitorImpl::run() +{ +} + +VideoDeviceMonitor::VideoDeviceMonitor() + : preferences_() + , devices_() + , monitorImpl_(new VideoDeviceMonitorImpl(this)) +{ + monitorImpl_->start(); +} + +VideoDeviceMonitor::~VideoDeviceMonitor() +{} + +}} // namespace ring::video diff --git a/src/media/video/v4l2/video_device_impl.cpp b/src/media/video/v4l2/video_device_impl.cpp index 66ffbc743b..65df9f2ced 100644 --- a/src/media/video/v4l2/video_device_impl.cpp +++ b/src/media/video/v4l2/video_device_impl.cpp @@ -569,7 +569,7 @@ VideoDeviceImpl::setDeviceParams(const DeviceParams& params) } } -VideoDevice::VideoDevice(const string& path) +VideoDevice::VideoDevice(const std::string& path, const std::vector<std::map<std::string, std::string>>&) : deviceImpl_(new VideoDeviceImpl(path)) { node_ = path; diff --git a/src/media/video/video_device.h b/src/media/video/video_device.h index 14914111b7..6aa3b8c129 100644 --- a/src/media/video/video_device.h +++ b/src/media/video/video_device.h @@ -31,6 +31,7 @@ #include <string> #include <vector> #include <algorithm> +#include <sstream> #include "videomanager_interface.h" #include "string_utils.h" @@ -46,7 +47,7 @@ class VideoDeviceImpl; class VideoDevice { public: - VideoDevice(const std::string& path); + VideoDevice(const std::string& path, const std::vector<std::map<std::string, std::string>>& devInfo); ~VideoDevice(); /* diff --git a/src/media/video/video_device_monitor.cpp b/src/media/video/video_device_monitor.cpp index fc7781c417..3185e63761 100644 --- a/src/media/video/video_device_monitor.cpp +++ b/src/media/video/video_device_monitor.cpp @@ -186,14 +186,15 @@ notify() } void -VideoDeviceMonitor::addDevice(const string& node) +VideoDeviceMonitor::addDevice(const string& node, const std::vector<std::map<std::string, std::string>>* devInfo) { if (findDeviceByNode(node) != devices_.end()) return; // instantiate a new unique device try { - VideoDevice dev {node}; + VideoDevice dev {node, *devInfo}; + if (dev.getChannelList().empty()) return; @@ -214,6 +215,7 @@ VideoDeviceMonitor::addDevice(const string& node) devices_.emplace_back(std::move(dev)); notify(); + } catch (const std::exception& e) { RING_ERR("Failed to add device %s: %s", node.c_str(), e.what()); return; diff --git a/src/media/video/video_device_monitor.h b/src/media/video/video_device_monitor.h index 1f63de9a52..b41ae12a41 100644 --- a/src/media/video/video_device_monitor.h +++ b/src/media/video/video_device_monitor.h @@ -57,7 +57,7 @@ class VideoDeviceMonitor : public Serializable std::string getMRLForDefaultDevice() const; void setDefaultDevice(const std::string& name); - void addDevice(const std::string &node); + void addDevice(const std::string &node, const std::vector<std::map<std::string, std::string>>* devInfo=nullptr); void removeDevice(const std::string &node); /** diff --git a/src/media/video/video_input.cpp b/src/media/video/video_input.cpp index ef3f8fc085..0e67ce0c7a 100644 --- a/src/media/video/video_input.cpp +++ b/src/media/video/video_input.cpp @@ -39,7 +39,11 @@ #include <string> #include <sstream> #include <cassert> +#ifdef RING_UWP +#include <io.h> // for access +#else #include <unistd.h> +#endif namespace ring { namespace video { @@ -49,21 +53,17 @@ static constexpr unsigned default_grab_height = 480; VideoInput::VideoInput() : VideoGenerator::VideoGenerator() , sink_ {Manager::instance().createSinkClient("local")} -#ifndef __ANDROID__ , loop_(std::bind(&VideoInput::setup, this), std::bind(&VideoInput::process, this), std::bind(&VideoInput::cleanup, this)) -#else - , loop_(std::bind(&VideoInput::setup, this), - std::bind(&VideoInput::processAndroid, this), - std::bind(&VideoInput::cleanupAndroid, this)) +#if defined(__ANDROID__) || defined(RING_UWP) , mutex_(), frame_cv_(), buffers_(8) #endif {} VideoInput::~VideoInput() { -#ifdef __ANDROID__ +#if defined(__ANDROID__) || defined(RING_UWP) /* we need to stop the loop and notify the condition variable * to unblock the process loop */ loop_.stop(); @@ -72,7 +72,7 @@ VideoInput::~VideoInput() loop_.join(); } -#ifdef __ANDROID__ +#if defined(__ANDROID__) || defined(RING_UWP) bool VideoInput::waitForBufferFull() { for(auto& buffer : buffers_) { @@ -84,7 +84,7 @@ bool VideoInput::waitForBufferFull() return !isCapturing(); } -void VideoInput::processAndroid() +void VideoInput::process() { foundDecOpts(decOpts_); @@ -132,7 +132,7 @@ void VideoInput::processAndroid() } } -void VideoInput::cleanupAndroid() +void VideoInput::cleanup() { emitSignal<DRing::VideoSignal::StopCapture>(); @@ -149,21 +149,7 @@ void VideoInput::cleanupAndroid() } } } -#endif - -bool VideoInput::setup() -{ - if (not attach(sink_.get())) { - RING_ERR("attach sink failed"); - return false; - } - - if (!sink_->start()) - RING_ERR("start sink failed"); - - RING_DBG("VideoInput ready to capture"); - return true; -} +#else void VideoInput::process() @@ -186,6 +172,21 @@ VideoInput::cleanup() RING_DBG("VideoInput closed"); } +#endif +bool VideoInput::setup() +{ + if (not attach(sink_.get())) { + RING_ERR("attach sink failed"); + return false; + } + + if (!sink_->start()) + RING_ERR("start sink failed"); + + RING_DBG("VideoInput ready to capture"); + return true; +} + void VideoInput::clearOptions() { decOpts_ = {}; @@ -230,7 +231,7 @@ bool VideoInput::captureFrame() } } -#ifdef __ANDROID__ +#if defined(__ANDROID__) || defined(RING_UWP) int VideoInput::allocateOneBuffer(struct VideoFrameBuffer& b, int length) { b.data = std::malloc(length); @@ -542,7 +543,7 @@ VideoInput::switchInput(const std::string& resource) return futureDecOpts_; } -#ifdef __ANDROID__ +#if defined(__ANDROID__) || defined(RING_UWP) int VideoInput::getWidth() const { return decOpts_.width; } diff --git a/src/media/video/video_input.h b/src/media/video/video_input.h index 0b88170e12..7ffeefbb0d 100644 --- a/src/media/video/video_input.h +++ b/src/media/video/video_input.h @@ -74,10 +74,10 @@ public: DeviceParams getParams() const; std::shared_future<DeviceParams> switchInput(const std::string& resource); -#ifdef __ANDROID__ +#if defined(__ANDROID__) || defined(RING_UWP) /* - * these fonctions are used to pass buffer from/to the daemon - * to the Java application + * these functions are used to pass buffer from/to the daemon + * on the Android and UWP builds */ void* obtainFrame(int length); void releaseFrame(void *frame); @@ -120,9 +120,7 @@ private: bool captureFrame(); bool isCapturing() const noexcept; -#ifdef __ANDROID__ - void processAndroid(); - void cleanupAndroid(); +#if defined(__ANDROID__) || defined(RING_UWP) int allocateOneBuffer(struct VideoFrameBuffer& b, int length); void freeOneBuffer(struct VideoFrameBuffer& b); bool waitForBufferFull(); diff --git a/src/media/video/winvideo/video_device_impl.cpp b/src/media/video/winvideo/video_device_impl.cpp index 08b872d89e..ee058dd9c6 100644 --- a/src/media/video/winvideo/video_device_impl.cpp +++ b/src/media/video/winvideo/video_device_impl.cpp @@ -283,7 +283,7 @@ VideoDeviceImpl::getChannelList() const return {"default"}; } -VideoDevice::VideoDevice(const std::string& path) +VideoDevice::VideoDevice(const std::string& path, const std::vector<std::map<std::string, std::string>>&) : deviceImpl_(new VideoDeviceImpl(path)) { node_ = path; -- GitLab