From ef9c62722c58239c3aa3bcc99eac6ddf4f3cc8a6 Mon Sep 17 00:00:00 2001 From: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com> Date: Fri, 13 Mar 2015 11:45:37 -0400 Subject: [PATCH] video: implement frame data direct access and make shm optional This patchset permits OSX client access. This one doesn't use shared memory and requiers a direct access to video frame. This is done by a client side callback. Refs #67977 Change-Id: Ib6780efe4beeab027bb868d3a124d2cec59de915 Signed-off-by: Alexandre Lision <alexandre.lision@savoirfairelinux.com> --- daemon/configure.ac | 3 + daemon/src/client/videomanager.cpp | 29 ++ daemon/src/dring/videomanager_interface.h | 2 + daemon/src/managerimpl.cpp | 29 ++ daemon/src/managerimpl.h | 11 + daemon/src/media/video/sinkclient.cpp | 289 ++++++++++-------- daemon/src/media/video/sinkclient.h | 56 ++-- daemon/src/media/video/video_input.cpp | 30 +- daemon/src/media/video/video_input.h | 7 +- daemon/src/media/video/video_mixer.cpp | 26 +- daemon/src/media/video/video_mixer.h | 5 +- .../src/media/video/video_receive_thread.cpp | 27 +- daemon/src/media/video/video_receive_thread.h | 6 +- 13 files changed, 326 insertions(+), 194 deletions(-) diff --git a/daemon/configure.ac b/daemon/configure.ac index 3bde0a30a3..da33d01eb4 100644 --- a/daemon/configure.ac +++ b/daemon/configure.ac @@ -543,6 +543,9 @@ AS_IF([test "x$with_upnp" = "xyes"], AC_DEFINE([HAVE_LIBUPNP], 0, [Define if you have libupnp])]) ]) +AC_DEFINE_UNQUOTED([HAVE_SHM], `if test -z "${HAVE_LINUX_TRUE}"; then echo 1; else echo 0; fi`, + [Define if you have shared memory support]) + # DOXYGEN # required dependency(ies): doxygen # check for doxygen, mostly stolen from http://log4cpp.sourceforge.net/ diff --git a/daemon/src/client/videomanager.cpp b/daemon/src/client/videomanager.cpp index 822b244d03..73658c54ec 100644 --- a/daemon/src/client/videomanager.cpp +++ b/daemon/src/client/videomanager.cpp @@ -40,6 +40,12 @@ #include "manager.h" #include "system_codec_container.h" #include "client/signal.h" +#include "video/sinkclient.h" + +#include <functional> +#include <memory> +#include <string> +#include <vector> namespace DRing { @@ -142,6 +148,29 @@ hasCameraStarted() return videoManager.started; } +template <class T> +static void +registerSinkTarget_(const std::string& sinkId, T&& cb) +{ + if (auto sink = ring::Manager::instance().getSinkClient(sinkId)) + sink->registerTarget(std::forward<T>(cb)); + else + RING_WARN("No sink found for id '%s'", sinkId.c_str()); +} + +void +registerSinkTarget(const std::string& sinkId, + const std::function<void(unsigned char*)>& cb) +{ + registerSinkTarget_(sinkId, cb); +} + +void +registerSinkTarget(const std::string& sinkId, + std::function<void(unsigned char*)>&& cb) +{ + registerSinkTarget_(sinkId, cb); +} } // namespace DRing diff --git a/daemon/src/dring/videomanager_interface.h b/daemon/src/dring/videomanager_interface.h index e346531989..a078229729 100644 --- a/daemon/src/dring/videomanager_interface.h +++ b/daemon/src/dring/videomanager_interface.h @@ -74,6 +74,8 @@ void stopCamera(); bool hasCameraStarted(); bool switchInput(const std::string& resource); bool switchToCamera(); +void registerSinkTarget(const std::string& sinkId, const std::function<void(unsigned char*)>& cb); +void registerSinkTarget(const std::string& sinkId, std::function<void(unsigned char*)>&& cb); } // namespace DRing diff --git a/daemon/src/managerimpl.cpp b/daemon/src/managerimpl.cpp index 1c11127a0a..8ef5b23162 100644 --- a/daemon/src/managerimpl.cpp +++ b/daemon/src/managerimpl.cpp @@ -83,6 +83,9 @@ #include "gnutls_support.h" #endif +#include "libav_utils.h" +#include "video/sinkclient.h" + #include <cerrno> #include <algorithm> #include <ctime> @@ -2853,4 +2856,30 @@ ManagerImpl::newOutgoingCall(const std::string& toUrl, return account->newOutgoingCall(finalToUrl); } +std::shared_ptr<video::SinkClient> +ManagerImpl::createSinkClient(const std::string& id) +{ + const auto& iter = sinkMap_.find(id); + if (iter != std::end(sinkMap_)) { + if (iter->second.expired()) + sinkMap_.erase(iter); + else + return nullptr; + } + + auto sink = std::make_shared<video::SinkClient>(id); + sinkMap_.emplace(id, sink); + return sink; +} + +std::shared_ptr<video::SinkClient> +ManagerImpl::getSinkClient(const std::string& id) +{ + const auto& iter = sinkMap_.find(id); + if (iter != std::end(sinkMap_)) + if (auto sink = iter->second.lock()) + return sink; + return nullptr; +} + } // namespace ring diff --git a/daemon/src/managerimpl.h b/daemon/src/managerimpl.h index 3bc5208664..3d38b5fb9d 100644 --- a/daemon/src/managerimpl.h +++ b/daemon/src/managerimpl.h @@ -72,6 +72,10 @@ namespace tls { class GnuTlsGlobalInit; } +namespace video { +class SinkClient; +} + class PluginManager; class AudioFile; class DTMF; @@ -951,6 +955,10 @@ class ManagerImpl { void addTask(const std::function<bool()>&& task); + std::shared_ptr<video::SinkClient> createSinkClient(const std::string& id=""); + + std::shared_ptr<video::SinkClient> getSinkClient(const std::string& id); + private: NON_COPYABLE(ManagerImpl); @@ -991,6 +999,9 @@ class ManagerImpl { std::unique_ptr<IceTransportFactory> ice_tf_; std::unique_ptr<tls::GnuTlsGlobalInit> gnutlGIG_; + + /* Sink ID mapping */ + std::map<std::string, std::weak_ptr<video::SinkClient>> sinkMap_; }; } // namespace ring diff --git a/daemon/src/media/video/sinkclient.cpp b/daemon/src/media/video/sinkclient.cpp index ceca878300..5f8e8999d9 100644 --- a/daemon/src/media/video/sinkclient.cpp +++ b/daemon/src/media/video/sinkclient.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2012-2015 Savoir-Faire Linux Inc. * Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com> + * Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com> * * Portions derived from GStreamer: * Copyright (C) <2009> Collabora Ltd @@ -38,11 +39,15 @@ #endif #include "sinkclient.h" + +#if HAVE_SHM #include "shm_header.h" +#endif // HAVE_SHM #include "video_scaler.h" #include "media_buffer.h" #include "logger.h" +#include "noncopyable.h" #include <sys/mman.h> #include <fcntl.h> @@ -51,54 +56,85 @@ #include <unistd.h> #include <cerrno> #include <cstring> +#include <stdexcept> namespace ring { namespace video { -SinkClient::SinkClient(const std::string &shm_name) : - shm_name_(shm_name) - , fd_(-1) - , shm_area_(static_cast<SHMHeader*>(MAP_FAILED)) - , shm_area_len_(0) - , opened_name_() -#ifdef DEBUG_FPS - , frameCount_(0u) - , lastFrameDebug_(std::chrono::system_clock::now()) -#endif -{} +#if HAVE_SHM +// RAII class helper on sem_wait/sem_post sempahore operations +class SemGuardLock { + public: + explicit SemGuardLock(sem_t& mutex) : m_(mutex) { + auto ret = ::sem_wait(&m_); + if (ret < 0) { + std::ostringstream msg; + msg << "SHM mutex@" << &m_ + << " lock failed (" << ret << ")"; + throw std::logic_error {msg.str()}; + } + } + + ~SemGuardLock() { + ::sem_post(&m_); + } + + private: + sem_t& m_; +}; -SinkClient::~SinkClient() +class ShmHolder { - stop(); -} + public: + ShmHolder(const std::string& shm_name); + ~ShmHolder(); -bool -SinkClient::start() + std::string openedName() const noexcept { + return opened_name_; + } + + void render_frame(VideoFrame& src); + + private: + bool resize_area(std::size_t desired_length); + void alloc_area(std::size_t desired_length) noexcept; + + std::string shm_name_; + std::string opened_name_; + std::size_t shm_area_len_ {0}; + SHMHeader* shm_area_ {static_cast<SHMHeader*>(MAP_FAILED)}; + int fd_ {-1}; +}; + +void +ShmHolder::alloc_area(std::size_t desired_length) noexcept { - if (fd_ != -1) { - RING_ERR("fd must be -1"); - return false; - } + shm_area_ = static_cast<SHMHeader*>(::mmap(nullptr, desired_length, + PROT_READ | PROT_WRITE, + MAP_SHARED, fd_, 0)); +} - const int flags = O_RDWR | O_CREAT | O_TRUNC | O_EXCL; - const int perms = S_IRUSR | S_IWUSR; +ShmHolder::ShmHolder(const std::string& shm_name) : shm_name_ {shm_name} +{ + static constexpr int flags = O_RDWR | O_CREAT | O_TRUNC | O_EXCL; + static constexpr int perms = S_IRUSR | S_IWUSR; if (not shm_name_.empty()) { - fd_ = shm_open(shm_name_.c_str(), flags, perms); + fd_ = ::shm_open(shm_name_.c_str(), flags, perms); if (fd_ < 0) { - RING_ERR("could not open shm area \"%s\"", shm_name_.c_str()); - strErr(); - return false; + std::ostringstream msg; + msg << "could not open shm area \"" + << shm_name_.c_str() + << "\""; + throw std::runtime_error {msg.str()}; } } else { for (int i = 0; fd_ < 0; ++i) { std::ostringstream name; name << PACKAGE_NAME << "_shm_" << getpid() << "_" << i; shm_name_ = name.str(); - fd_ = shm_open(shm_name_.c_str(), flags, perms); - if (fd_ < 0 and errno != EEXIST) { - strErr(); - return false; - } + fd_ = ::shm_open(shm_name_.c_str(), flags, perms); + if (fd_ < 0 and errno != EEXIST) + throw std::runtime_error {"shm_open() failed"}; } } @@ -107,106 +143,73 @@ SinkClient::start() shm_area_len_ = sizeof(SHMHeader); - if (ftruncate(fd_, shm_area_len_)) { + if (::ftruncate(fd_, shm_area_len_)) { RING_ERR("Could not make shm area large enough for header"); strErr(); - return false; + throw std::runtime_error {"could not make shm area large enough for header"}; } - shm_area_ = static_cast<SHMHeader*>(mmap(NULL, shm_area_len_, - PROT_READ | PROT_WRITE, - MAP_SHARED, fd_, 0)); + alloc_area(shm_area_len_); - if (shm_area_ == MAP_FAILED) { - RING_ERR("Could not map shm area, mmap failed"); - return false; - } + if (shm_area_ == MAP_FAILED) + throw std::runtime_error {"could not map shm area, mmap failed"}; - memset(shm_area_, 0, shm_area_len_); - if (sem_init(&shm_area_->notification, 1, 0) != 0) { - RING_ERR("sem_init: notification initialization failed"); - return false; - } - if (sem_init(&shm_area_->mutex, 1, 1) != 0) { - RING_ERR("sem_init: mutex initialization failed"); - return false; - } - return true; + std::memset(shm_area_, 0, shm_area_len_); + + if (::sem_init(&shm_area_->notification, 1, 0) != 0) + throw std::runtime_error {"sem_init: notification initialization failed"}; + + if (::sem_init(&shm_area_->mutex, 1, 1) != 0) + throw std::runtime_error {"sem_init: mutex initialization failed"}; } -bool -SinkClient::stop() +ShmHolder::~ShmHolder() { - if (fd_ >= 0 and close(fd_) == -1) + if (fd_ >= 0 and ::close(fd_) == -1) strErr(); - fd_ = -1; + if (not opened_name_.empty()) + ::shm_unlink(opened_name_.c_str()); - if (not opened_name_.empty()) { - shm_unlink(opened_name_.c_str()); - opened_name_ = ""; + if (shm_area_ != MAP_FAILED) { + ::sem_post(&shm_area_->notification); + if (::munmap(shm_area_, shm_area_len_) < 0) + strErr(); } - - if (shm_area_ != MAP_FAILED) - munmap(shm_area_, shm_area_len_); - shm_area_len_ = 0; - shm_area_ = static_cast<SHMHeader*>(MAP_FAILED); - - return true; } bool -SinkClient::resize_area(size_t desired_length) +ShmHolder::resize_area(size_t desired_length) { if (desired_length <= shm_area_len_) return true; - shm_unlock(); - - if (munmap(shm_area_, shm_area_len_)) { + if (::munmap(shm_area_, shm_area_len_)) { RING_ERR("Could not unmap shared area"); strErr(); return false; } - if (ftruncate(fd_, desired_length)) { + if (::ftruncate(fd_, desired_length)) { RING_ERR("Could not resize shared area"); strErr(); return false; } - shm_area_ = static_cast<SHMHeader*>(mmap(NULL, desired_length, - PROT_READ | PROT_WRITE, - MAP_SHARED, fd_, 0)); - shm_area_len_ = desired_length; + alloc_area(desired_length); if (shm_area_ == MAP_FAILED) { - shm_area_ = 0; + shm_area_len_ = 0; RING_ERR("Could not remap shared area"); return false; } - shm_lock(); + shm_area_len_ = desired_length; return true; } void -SinkClient::render(const std::vector<unsigned char>& data) -{ - shm_lock(); - - if (!resize_area(sizeof(SHMHeader) + data.size())) - return; - - memcpy(shm_area_->data, data.data(), data.size()); - shm_area_->buffer_size = data.size(); - shm_area_->buffer_gen++; - sem_post(&shm_area_->notification); - shm_unlock(); -} - -void -SinkClient::render_frame(VideoFrame& src) +ShmHolder::render_frame(VideoFrame& src) { VideoFrame dst; VideoScaler scaler; @@ -214,71 +217,101 @@ SinkClient::render_frame(VideoFrame& src) const int width = src.width(); const int height = src.height(); const int format = VIDEO_PIXFMT_BGRA; - size_t bytes = videoFrameSize(format, width, height); - - shm_lock(); + const auto bytes = videoFrameSize(format, width, height); if (!resize_area(sizeof(SHMHeader) + bytes)) { RING_ERR("Could not resize area"); return; } + SemGuardLock lk{shm_area_->mutex}; + dst.setFromMemory(shm_area_->data, format, width, height); scaler.scale(src, dst); -#ifdef DEBUG_FPS - const std::chrono::time_point<std::chrono::system_clock> currentTime = \ - std::chrono::system_clock::now(); - const std::chrono::duration<double> seconds = currentTime - lastFrameDebug_; - frameCount_++; - if (seconds.count() > 1) { - RING_DBG("%s: FPS %f", shm_name_.c_str(), frameCount_ / seconds.count()); - frameCount_ = 0; - lastFrameDebug_ = currentTime; - } -#endif - shm_area_->buffer_size = bytes; - shm_area_->buffer_gen++; + ++shm_area_->buffer_gen; sem_post(&shm_area_->notification); - shm_unlock(); } -void -SinkClient::render_callback(VideoProvider &provider, size_t bytes) +std::string +SinkClient::openedName() const noexcept { - shm_lock(); + return shm_->openedName(); +} - if (!resize_area(sizeof(SHMHeader) + bytes)) { - RING_ERR("Could not resize area"); - return; +bool +SinkClient::start() noexcept +{ + if (not shm_) { + try { + shm_ = std::make_shared<ShmHolder>(id_); + } catch (const std::runtime_error& e) { + strErr(); + RING_ERR("SHMHolder ctor failure: %s", e.what()); + } } + return static_cast<bool>(shm_); +} - provider.fillBuffer(static_cast<void*>(shm_area_->data)); - shm_area_->buffer_size = bytes; - shm_area_->buffer_gen++; - sem_post(&shm_area_->notification); - shm_unlock(); +bool +SinkClient::stop() noexcept +{ + shm_.reset(); + return true; } -void -SinkClient::shm_lock() +#else // HAVE_SHM + +std::string +SinkClient::openedName() const noexcept { - sem_wait(&shm_area_->mutex); + return {}; } -void -SinkClient::shm_unlock() +bool +SinkClient::start() noexcept +{ + return true; +} + +bool +SinkClient::stop() noexcept { - sem_post(&shm_area_->mutex); + return true; } +#endif // !HAVE_SHM + +SinkClient::SinkClient(const std::string& id) : id_ {id} +{} + void -SinkClient::update(Observable<std::shared_ptr<VideoFrame> >* /*obs*/, - std::shared_ptr<VideoFrame> &frame_p) +SinkClient::update(Observable<std::shared_ptr<VideoFrame>>* /*obs*/, + std::shared_ptr<VideoFrame>& frame_p) { auto f = frame_p; // keep a local reference during rendering - render_frame(*f.get()); + +#if HAVE_SHM + shm_->render_frame(*f.get()); +#endif + + if (target_) { + VideoFrame dst; + VideoScaler scaler; + + const int width = f->width(); + const int height = f->height(); + const int format = VIDEO_PIXFMT_BGRA; + const auto bytes = videoFrameSize(format, width, height); + + targetData_.resize(bytes); + auto data = targetData_.data(); + + dst.setFromMemory(data, format, width, height); + scaler.scale(*f, dst); + target_(data); + } } }} // namespace ring::video diff --git a/daemon/src/media/video/sinkclient.h b/daemon/src/media/video/sinkclient.h index 7c6725a787..24f6d0bfab 100644 --- a/daemon/src/media/video/sinkclient.h +++ b/daemon/src/media/video/sinkclient.h @@ -35,51 +35,55 @@ #pragma once -#include "noncopyable.h" +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "video_provider.h" #include "video_base.h" #include <string> #include <vector> - -class SHMHeader; +#include <memory> namespace ring { namespace video { +#if HAVE_SHM +class ShmHolder; +#endif // HAVE_SHM + class SinkClient : public VideoFramePassiveReader { public: - SinkClient(const std::string &shm_name = ""); - std::string openedName() const { return opened_name_; } - ~SinkClient(); + SinkClient(const std::string& id=""); - bool start(); - bool stop(); + const std::string& getId() const noexcept { + return id_; + } - bool resize_area(size_t desired_length); - - void render(const std::vector<unsigned char> &data); - void render_frame(VideoFrame& src); - void render_callback(VideoProvider &provider, size_t bytes); + std::string openedName() const noexcept; // as VideoFramePassiveReader void update(Observable<std::shared_ptr<ring::VideoFrame>>*, - std::shared_ptr<ring::VideoFrame> &); + std::shared_ptr<ring::VideoFrame>&); + + bool start() noexcept; + bool stop() noexcept; + + template <class T> + void registerTarget(T&& cb) noexcept { + target_ = std::forward<T>(cb); + } private: - NON_COPYABLE(SinkClient); + const std::string id_; + std::function<void(unsigned char*)> target_; + std::vector<unsigned char> targetData_; - void shm_lock(); - void shm_unlock(); - std::string shm_name_; - int fd_; - SHMHeader *shm_area_; - size_t shm_area_len_; - std::string opened_name_; -#ifdef DEBUG_FPS - unsigned frameCount_; - std::chrono::time_point<std::chrono::system_clock> lastFrameDebug_; -#endif +#if HAVE_SHM + // using shared_ptr and not unique_ptr as ShmHolder is forwared only + std::shared_ptr<ShmHolder> shm_; +#endif // HAVE_SHM }; }} // namespace ring::video diff --git a/daemon/src/media/video/video_input.cpp b/daemon/src/media/video/video_input.cpp index 8cc499a0f8..a9483cbe9d 100644 --- a/daemon/src/media/video/video_input.cpp +++ b/daemon/src/media/video/video_input.cpp @@ -37,6 +37,7 @@ #include "manager.h" #include "client/videomanager.h" #include "client/signal.h" +#include "sinkclient.h" #include "logger.h" #include <map> @@ -47,9 +48,9 @@ namespace ring { namespace video { -VideoInput::VideoInput() : - VideoGenerator::VideoGenerator() - , sink_() +VideoInput::VideoInput() + : VideoGenerator::VideoGenerator() + , sink_ {Manager::instance().createSinkClient("local")} , loop_(std::bind(&VideoInput::setup, this), std::bind(&VideoInput::process, this), std::bind(&VideoInput::cleanup, this)) @@ -63,11 +64,11 @@ VideoInput::~VideoInput() bool VideoInput::setup() { /* Sink setup */ - if (!sink_.start()) { + if (!sink_->start()) { RING_ERR("Cannot start shared memory sink"); return false; } - if (not attach(&sink_)) + if (not attach(sink_.get())) RING_WARN("Failed to attach sink"); return true; @@ -92,11 +93,14 @@ void VideoInput::process() if (newDecoderCreated) { /* Signal the client about the new sink */ - emitSignal<DRing::VideoSignal::DecodingStarted>(sinkID_, sink_.openedName(), - decoder_->getWidth(), decoder_->getHeight(), false); + emitSignal<DRing::VideoSignal::DecodingStarted>(sink_->getId(), + sink_->openedName(), + decoder_->getWidth(), + decoder_->getHeight(), + false); RING_DBG("LOCAL: shm sink <%s> started: size = %dx%d", - sink_.openedName().c_str(), decoder_->getWidth(), - decoder_->getHeight()); + sink_->openedName().c_str(), decoder_->getWidth(), + decoder_->getHeight()); } } @@ -104,8 +108,8 @@ void VideoInput::cleanup() { deleteDecoder(); - if (detach(&sink_)) - sink_.stop(); + if (detach(sink_.get())) + sink_->stop(); } void VideoInput::clearOptions() @@ -191,7 +195,9 @@ VideoInput::deleteDecoder() if (not decoder_) return; - emitSignal<DRing::VideoSignal::DecodingStopped>(sinkID_, sink_.openedName(), false); + emitSignal<DRing::VideoSignal::DecodingStopped>(sink_->getId(), + sink_->openedName(), + false); flushFrames(); delete decoder_; decoder_ = nullptr; diff --git a/daemon/src/media/video/video_input.h b/daemon/src/media/video/video_input.h index 056b35b4f2..0e4610ea97 100644 --- a/daemon/src/media/video/video_input.h +++ b/daemon/src/media/video/video_input.h @@ -35,7 +35,6 @@ #define __VIDEO_INPUT_H__ #include "noncopyable.h" -#include "sinkclient.h" #include "threadloop.h" #include "media/media_device.h" // DeviceParams @@ -50,6 +49,8 @@ class MediaDecoder; namespace ring { namespace video { +class SinkClient; + class VideoInput : public VideoGenerator { public: @@ -67,10 +68,8 @@ public: private: NON_COPYABLE(VideoInput); - std::string sinkID_ = "local"; - MediaDecoder *decoder_ = nullptr; - SinkClient sink_; + std::shared_ptr<SinkClient> sink_; std::atomic<bool> switchPending_ = {false}; DeviceParams decOpts_; diff --git a/daemon/src/media/video/video_mixer.cpp b/daemon/src/media/video/video_mixer.cpp index ce23ccbe4b..f699550a89 100644 --- a/daemon/src/media/video/video_mixer.cpp +++ b/daemon/src/media/video/video_mixer.cpp @@ -37,6 +37,7 @@ #include "client/videomanager.h" #include "client/signal.h" #include "manager.h" +#include "sinkclient.h" #include "logger.h" #include <cmath> @@ -49,8 +50,10 @@ static const double FRAME_DURATION = 1/30.; VideoMixer::VideoMixer(const std::string &id) : VideoGenerator::VideoGenerator() , id_(id) - , sink_(id) - , loop_([]{return true;}, std::bind(&VideoMixer::process, this), []{}) + , sink_ {Manager::instance().createSinkClient(id)} + , loop_([]{return true;}, + std::bind(&VideoMixer::process, this), + []{}) { // Local video camera is the main participant videoLocal_ = getVideoCamera(); @@ -190,11 +193,14 @@ void VideoMixer::setDimensions(int width, int height) void VideoMixer::start_sink() { - if (sink_.start()) { - if (this->attach(&sink_)) { - emitSignal<DRing::VideoSignal::DecodingStarted>(id_, sink_.openedName(), width_, height_, true); + if (sink_->start()) { + if (this->attach(sink_.get())) { + emitSignal<DRing::VideoSignal::DecodingStarted>(id_, + sink_->openedName(), + width_, height_, + true); RING_DBG("MX: shm sink <%s> started: size = %dx%d", - sink_.openedName().c_str(), width_, height_); + sink_->openedName().c_str(), width_, height_); } } else RING_WARN("MX: sink startup failed"); @@ -202,9 +208,11 @@ void VideoMixer::start_sink() void VideoMixer::stop_sink() { - if (this->detach(&sink_)) { - emitSignal<DRing::VideoSignal::DecodingStopped>(id_, sink_.openedName(), true); - sink_.stop(); + if (this->detach(sink_.get())) { + emitSignal<DRing::VideoSignal::DecodingStopped>(id_, + sink_->openedName(), + true); + sink_->stop(); } } diff --git a/daemon/src/media/video/video_mixer.h b/daemon/src/media/video/video_mixer.h index b2ee6838af..6333cb52a8 100644 --- a/daemon/src/media/video/video_mixer.h +++ b/daemon/src/media/video/video_mixer.h @@ -35,7 +35,6 @@ #include "noncopyable.h" #include "video_base.h" #include "video_scaler.h" -#include "sinkclient.h" #include "threadloop.h" #include "rw_mutex.h" @@ -45,6 +44,8 @@ namespace ring { namespace video { +class SinkClient; + struct VideoMixerSource { Observable<std::shared_ptr<VideoFrame>>* source = nullptr; std::unique_ptr<VideoFrame> update_frame; @@ -93,7 +94,7 @@ private: std::list<VideoMixerSource *> sources_ = {}; rw_mutex rwMutex_ = {}; - SinkClient sink_; + std::shared_ptr<SinkClient> sink_; ThreadLoop loop_; std::chrono::time_point<std::chrono::system_clock> lastProcess_ = {}; diff --git a/daemon/src/media/video/video_receive_thread.cpp b/daemon/src/media/video/video_receive_thread.cpp index 10bb00bb4d..3500d3c4f8 100644 --- a/daemon/src/media/video/video_receive_thread.cpp +++ b/daemon/src/media/video/video_receive_thread.cpp @@ -37,6 +37,7 @@ #include "manager.h" #include "client/videomanager.h" #include "client/signal.h" +#include "sinkclient.h" #include "logger.h" #include <unistd.h> @@ -55,7 +56,7 @@ VideoReceiveThread::VideoReceiveThread(const std::string& id, , id_(id) , stream_(sdp) , sdpContext_(stream_.str().size(), false, &readFunction, 0, 0, this) - , sink_(id) + , sink_ {Manager::instance().createSinkClient(id)} , requestKeyFrameCallback_(0) , loop_(std::bind(&VideoReceiveThread::setup, this), std::bind(&VideoReceiveThread::process, this), @@ -128,7 +129,7 @@ bool VideoReceiveThread::setup() dstHeight_ = videoDecoder_->getHeight(); } - EXIT_IF_FAIL(sink_.start(), "RX: sink startup failed"); + EXIT_IF_FAIL(sink_->start(), "RX: sink startup failed"); auto conf = Manager::instance().getConferenceFromCallID(id_); if (!conf) @@ -142,9 +143,11 @@ void VideoReceiveThread::process() void VideoReceiveThread::cleanup() { - if (detach(&sink_)) - emitSignal<DRing::VideoSignal::DecodingStopped>(id_, sink_.openedName(), false); - sink_.stop(); + if (detach(sink_.get())) + emitSignal<DRing::VideoSignal::DecodingStopped>(id_, + sink_->openedName(), + false); + sink_->stop(); videoDecoder_.reset(); demuxContext_.reset(); @@ -202,9 +205,11 @@ void VideoReceiveThread::enterConference() if (!loop_.isRunning()) return; - if (detach(&sink_)) { - emitSignal<DRing::VideoSignal::DecodingStopped>(id_, sink_.openedName(), false); - RING_DBG("RX: shm sink <%s> detached", sink_.openedName().c_str()); + if (detach(sink_.get())) { + emitSignal<DRing::VideoSignal::DecodingStopped>(id_, + sink_->openedName(), + false); + RING_DBG("RX: shm sink <%s> detached", sink_->openedName().c_str()); } } @@ -214,13 +219,13 @@ void VideoReceiveThread::exitConference() return; if (dstWidth_ > 0 && dstHeight_ > 0) { - if (attach(&sink_)) { + if (attach(sink_.get())) { emitSignal<DRing::VideoSignal::DecodingStarted>(id_, - sink_.openedName(), + sink_->openedName(), dstWidth_, dstHeight_, false); RING_DBG("RX: shm sink <%s> started: size = %dx%d", - sink_.openedName().c_str(), dstWidth_, dstHeight_); + sink_->openedName().c_str(), dstWidth_, dstHeight_); } } } diff --git a/daemon/src/media/video/video_receive_thread.h b/daemon/src/media/video/video_receive_thread.h index 321f4d7f27..e93c15d341 100644 --- a/daemon/src/media/video/video_receive_thread.h +++ b/daemon/src/media/video/video_receive_thread.h @@ -32,10 +32,10 @@ #ifndef _VIDEO_RECEIVE_THREAD_H_ #define _VIDEO_RECEIVE_THREAD_H_ +#include "video_base.h" #include "media_codec.h" #include "media_io_handle.h" #include "media_device.h" -#include "sinkclient.h" #include "threadloop.h" #include "noncopyable.h" @@ -52,6 +52,8 @@ class MediaDecoder; namespace ring { namespace video { +class SinkClient; + class VideoReceiveThread : public VideoGenerator { public: VideoReceiveThread(const std::string &id, const std::string &sdp); @@ -83,7 +85,7 @@ private: std::istringstream stream_; MediaIOHandle sdpContext_; std::unique_ptr<MediaIOHandle> demuxContext_; - SinkClient sink_; + std::shared_ptr<SinkClient> sink_; void (*requestKeyFrameCallback_)(const std::string &); void openDecoder(); bool decodeFrame(); -- GitLab