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:
Guillaume Roguez
2015-03-13 11:45:37 -04:00
committed by Alexandre Lision
parent df01eedd6c
commit ef9c62722c
13 changed files with 327 additions and 195 deletions

View File

@ -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/

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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_;

View File

@ -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();
}
}

View File

@ -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_ = {};

View File

@ -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_);
}
}
}

View File

@ -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();