mirror of
https://git.jami.net/savoirfairelinux/jami-daemon.git
synced 2025-08-12 22:09:25 +08:00
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>
This commit is contained in:

committed by
Alexandre Lision

parent
df01eedd6c
commit
ef9c62722c
@ -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/
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()};
|
||||
}
|
||||
}
|
||||
|
||||
SinkClient::~SinkClient()
|
||||
~SemGuardLock() {
|
||||
::sem_post(&m_);
|
||||
}
|
||||
|
||||
private:
|
||||
sem_t& m_;
|
||||
};
|
||||
|
||||
class ShmHolder
|
||||
{
|
||||
stop();
|
||||
public:
|
||||
ShmHolder(const std::string& shm_name);
|
||||
~ShmHolder();
|
||||
|
||||
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
|
||||
{
|
||||
shm_area_ = static_cast<SHMHeader*>(::mmap(nullptr, desired_length,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, fd_, 0));
|
||||
}
|
||||
|
||||
bool
|
||||
SinkClient::start()
|
||||
ShmHolder::ShmHolder(const std::string& shm_name) : shm_name_ {shm_name}
|
||||
{
|
||||
if (fd_ != -1) {
|
||||
RING_ERR("fd must be -1");
|
||||
return false;
|
||||
}
|
||||
|
||||
const int flags = O_RDWR | O_CREAT | O_TRUNC | O_EXCL;
|
||||
const int perms = S_IRUSR | S_IWUSR;
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
provider.fillBuffer(static_cast<void*>(shm_area_->data));
|
||||
shm_area_->buffer_size = bytes;
|
||||
shm_area_->buffer_gen++;
|
||||
sem_post(&shm_area_->notification);
|
||||
shm_unlock();
|
||||
return static_cast<bool>(shm_);
|
||||
}
|
||||
|
||||
void
|
||||
SinkClient::shm_lock()
|
||||
bool
|
||||
SinkClient::stop() noexcept
|
||||
{
|
||||
sem_wait(&shm_area_->mutex);
|
||||
shm_.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
SinkClient::shm_unlock()
|
||||
#else // HAVE_SHM
|
||||
|
||||
std::string
|
||||
SinkClient::openedName() const noexcept
|
||||
{
|
||||
sem_post(&shm_area_->mutex);
|
||||
return {};
|
||||
}
|
||||
|
||||
bool
|
||||
SinkClient::start() noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SinkClient::stop() noexcept
|
||||
{
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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_;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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_ = {};
|
||||
|
@ -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_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
Reference in New Issue
Block a user