diff --git a/configure.ac b/configure.ac index dd752fc6b05..925ce2c00ed 100644 --- a/configure.ac +++ b/configure.ac @@ -692,6 +692,7 @@ AC_CONFIG_FILES([Makefile \ src/media/video/v4l2/Makefile \ src/media/video/androidvideo/Makefile \ src/media/video/osxvideo/Makefile \ + src/media/video/iosvideo/Makefile \ src/media/video/winvideo/Makefile \ src/security/Makefile \ src/upnp/Makefile \ diff --git a/src/Makefile.am b/src/Makefile.am index f6a91771c90..d762b02c8ad 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,6 +17,10 @@ if HAVE_OSX RING_VIDEO_LIBS+= \ ./media/video/osxvideo/libosxvideo.la endif +if HAVE_IOS +RING_VIDEO_LIBS+= \ + ./media/video/iosvideo/libiosvideo.la +endif if HAVE_WIN32 RING_VIDEO_LIBS+= \ ./media/video/winvideo/libwinvideo.la diff --git a/src/client/videomanager.cpp b/src/client/videomanager.cpp index 8e966ed1856..f639290f8a7 100644 --- a/src/client/videomanager.cpp +++ b/src/client/videomanager.cpp @@ -181,7 +181,7 @@ setDecodingAccelerated(bool state) #endif } -#if defined(__ANDROID__) || defined(RING_UWP) +#if defined(__ANDROID__) || defined(RING_UWP) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS) void addVideoDevice(const std::string &node, std::vector> const * devInfo) { diff --git a/src/dring/videomanager_interface.h b/src/dring/videomanager_interface.h index ee18bd34136..54e2ffab588 100644 --- a/src/dring/videomanager_interface.h +++ b/src/dring/videomanager_interface.h @@ -31,6 +31,10 @@ #include "dring.h" +#if __APPLE__ +#import "TargetConditionals.h" +#endif + namespace DRing { /* FrameBuffer is a generic video frame container */ @@ -70,7 +74,7 @@ bool switchInput(const std::string& resource); bool switchToCamera(); void registerSinkTarget(const std::string& sinkId, const SinkTarget& target); -#if defined(__ANDROID__) || defined(RING_UWP) +#if defined(__ANDROID__) || defined(RING_UWP) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS) void addVideoDevice(const std::string &node, const std::vector>* devInfo=nullptr); void removeVideoDevice(const std::string &node); void* obtainFrame(int length); diff --git a/src/media/media_buffer.h b/src/media/media_buffer.h index 7ea646e2e0c..06993ad848c 100644 --- a/src/media/media_buffer.h +++ b/src/media/media_buffer.h @@ -28,7 +28,7 @@ class AVFrame; namespace DRing { -class FrameBuffer; // from dring/videomanager_interface.h +struct FrameBuffer; // from dring/videomanager_interface.h } namespace ring { diff --git a/src/media/video/Makefile.am b/src/media/video/Makefile.am index 9d5afe657ad..4617b906d7e 100644 --- a/src/media/video/Makefile.am +++ b/src/media/video/Makefile.am @@ -15,6 +15,11 @@ SUBDIRS= \ osxvideo endif +if HAVE_IOS +SUBDIRS= \ + iosvideo +endif + if HAVE_WIN32 SUBDIRS= \ winvideo diff --git a/src/media/video/iosvideo/Makefile.am b/src/media/video/iosvideo/Makefile.am new file mode 100644 index 00000000000..574cb8d2d7d --- /dev/null +++ b/src/media/video/iosvideo/Makefile.am @@ -0,0 +1,10 @@ +include $(top_srcdir)/globals.mk + +noinst_LTLIBRARIES = libiosvideo.la + +libiosvideo_la_SOURCES = \ + video_device_impl.cpp \ + video_device_monitor_impl.cpp + +#AM_CXXFLAGS = @UDEV_CFLAGS@ +#libiosvideo_la_LIBADD = @UDEV_LIBS@ diff --git a/src/media/video/iosvideo/video_device_impl.cpp b/src/media/video/iosvideo/video_device_impl.cpp new file mode 100644 index 00000000000..a21361648c6 --- /dev/null +++ b/src/media/video/iosvideo/video_device_impl.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2015-2017 Savoir-faire Linux Inc. + * + * Author: Rafaël Carré + * Author: Vivien Didelot + * Author: Andreas Traczyk + * + * 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 + +#include "logger.h" +#include "../video_device.h" + +#include "client/ring_signal.h" + +namespace ring { namespace video { + +typedef struct +{ + std::string name; + enum VideoPixelFormat ring_format; +} ios_fmt; + +static const std::array ios_formats +{ + ios_fmt { "RGBA", VIDEO_PIXFMT_RGBA }, + ios_fmt { "BGRA", VIDEO_PIXFMT_BGRA }, + ios_fmt { "YUV420P", VIDEO_PIXFMT_YUV420P } +}; + +class VideoDeviceImpl +{ +public: + VideoDeviceImpl(const std::string& path, const std::vector>& devInfo); + + std::string name; + + DeviceParams getDeviceParams() const; + + void setDeviceParams(const DeviceParams&); + void selectFormat(); + + std::vector getSizeList() const; + std::vector getRateList() const; + +private: + + VideoSize getSize(VideoSize size) const; + FrameRate getRate(FrameRate rate) const; + + std::vector formats_ {}; + std::vector sizes_ {}; + std::vector rates_ {}; + + const ios_fmt* fmt_ {nullptr}; + VideoSize size_ {}; + FrameRate rate_ {}; + +}; + +void +VideoDeviceImpl::selectFormat() +{ + unsigned best = UINT_MAX; + for(auto fmt : formats_) { + auto f = ios_formats.begin(); + for (; f != ios_formats.end(); ++f) { + if (f->name == fmt) { + auto pos = std::distance(ios_formats.begin(), f); + if (pos < best) + best = pos; + break; + } + } + if (f == ios_formats.end()) + RING_WARN("Video: No format matching %s", fmt.c_str()); + } + + if (best != UINT_MAX) { + fmt_ = &ios_formats[best]; + RING_DBG("Video: picked format %s", fmt_->name.c_str()); + } + else { + fmt_ = &ios_formats[0]; + RING_ERR("Video: Could not find a known format to use"); + } +} + +VideoDeviceImpl::VideoDeviceImpl(const std::string& path, const std::vector>& 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 +VideoDeviceImpl::getSizeList() const +{ + return sizes_; +} + +std::vector +VideoDeviceImpl::getRateList() const +{ + return rates_; +} + +DeviceParams +VideoDeviceImpl::getDeviceParams() const +{ + DeviceParams params; + std::stringstream ss1, ss2; + + ss1 << fmt_->ring_format; + ss1 >> params.format; + + 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(name); +} + +VideoDevice::VideoDevice(const std::string& path, const std::vector>& 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 +VideoDevice::getChannelList() const +{ + return {"default"}; +} + +std::vector +VideoDevice::getSizeList(const std::string& channel) const +{ + return deviceImpl_->getSizeList(); +} + +std::vector +VideoDevice::getRateList(const std::string& channel, VideoSize size) const +{ + return deviceImpl_->getRateList(); +} + +VideoDevice::~VideoDevice() +{} + +}} // namespace ring::video + diff --git a/src/media/video/iosvideo/video_device_monitor_impl.cpp b/src/media/video/iosvideo/video_device_monitor_impl.cpp new file mode 100644 index 00000000000..e8b69bf9f64 --- /dev/null +++ b/src/media/video/iosvideo/video_device_monitor_impl.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2015-2017 Savoir-faire Linux Inc. + * + * Author: Andreas Traczyk + * Author: Edric Milaret + * + * 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 +#include + +#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/sinkclient.cpp b/src/media/video/sinkclient.cpp index fb585afcd67..4df1a54663e 100644 --- a/src/media/video/sinkclient.cpp +++ b/src/media/video/sinkclient.cpp @@ -343,7 +343,7 @@ SinkClient::update(Observable>* /*obs*/, VideoScaler scaler; const int width = f.width(); const int height = f.height(); -#if (defined(__ANDROID__) || defined(__APPLE__)) +#if (defined(__ANDROID__) || (defined(__APPLE__)) && !defined(TARGET_OS_IPHONE)) const int format = VIDEO_PIXFMT_RGBA; #else const int format = VIDEO_PIXFMT_BGRA; diff --git a/src/media/video/video_input.cpp b/src/media/video/video_input.cpp index 2e966c412a3..61f3cdf2d6f 100644 --- a/src/media/video/video_input.cpp +++ b/src/media/video/video_input.cpp @@ -56,14 +56,14 @@ VideoInput::VideoInput() , loop_(std::bind(&VideoInput::setup, this), std::bind(&VideoInput::process, this), std::bind(&VideoInput::cleanup, this)) -#if defined(__ANDROID__) || defined(RING_UWP) +#if defined(__ANDROID__) || defined(RING_UWP) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS) , mutex_(), frame_cv_(), buffers_() #endif {} VideoInput::~VideoInput() { -#if defined(__ANDROID__) || defined(RING_UWP) +#if defined(__ANDROID__) || defined(RING_UWP) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS) /* 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(); } -#if defined(__ANDROID__) || defined(RING_UWP) +#if defined(__ANDROID__) || defined(RING_UWP) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS) bool VideoInput::waitForBufferFull() { for(auto& buffer : buffers_) { @@ -238,7 +238,7 @@ bool VideoInput::captureFrame() } } -#if defined(__ANDROID__) || defined(RING_UWP) +#if defined(__ANDROID__) || defined(RING_UWP) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS) int VideoInput::allocateOneBuffer(struct VideoFrameBuffer& b, int length) { b.data = std::malloc(length); @@ -550,7 +550,7 @@ VideoInput::switchInput(const std::string& resource) return futureDecOpts_; } -#if defined(__ANDROID__) || defined(RING_UWP) +#if defined(__ANDROID__) || defined(RING_UWP) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS) int VideoInput::getWidth() const { return decOpts_.width; } diff --git a/src/media/video/video_input.h b/src/media/video/video_input.h index 323792661af..6d5f6abd64f 100644 --- a/src/media/video/video_input.h +++ b/src/media/video/video_input.h @@ -36,6 +36,10 @@ #include #include +#if __APPLE__ +#import "TargetConditionals.h" +#endif + namespace ring { class MediaDecoder; } @@ -75,7 +79,7 @@ class VideoInput : public VideoGenerator, public std::enable_shared_from_this switchInput(const std::string& resource); -#if defined(__ANDROID__) || defined(RING_UWP) +#if defined(__ANDROID__) || defined(RING_UWP) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS) /* * these functions are used to pass buffer from/to the daemon * on the Android and UWP builds @@ -121,7 +125,7 @@ class VideoInput : public VideoGenerator, public std::enable_shared_from_this