37#include <opendht/thread_pool.h>
52 std::lock_guard lock(mutex_);
53 auto newFrame = std::make_shared<VideoFrame>();
60 std::lock_guard lock(mutex_);
82 , loop_([] {
return true; }, std::bind(&VideoMixer::process,
this), [] {})
91 nextProcess_ = std::chrono::steady_clock::now();
93 JAMI_DBG(
"[mixer:%s] New instance created", id_.c_str());
103 JAMI_DBG(
"[mixer:%s] Instance destroyed", id_.c_str());
111 std::lock_guard
lk(localInputsMtx_);
114 for (
const auto& input :
inputs) {
119 auto it = std::find(localInputs_.cbegin(), localInputs_.cend(),
videoInput);
123 localInputs_.erase(
it);
132 for (
size_t i = 0;
i < localInputs_.size(); ++
i) {
133 auto& input = localInputs_[
i];
139VideoMixer::stopInput(
const std::shared_ptr<VideoFrameActiveWriter>& input)
148 for (
auto& input : localInputs_)
150 localInputs_.clear();
163 if (activeStream_ ==
"")
170 const std::string& callId,
171 const std::string& streamId)
175 JAMI_DBG(
"Attaching video with streamId %s", streamId.c_str());
177 std::lock_guard
lk(videoToStreamInfoMtx_);
189 std::unique_lock
lk(videoToStreamInfoMtx_);
190 auto it = videoToStreamInfo_.find(
frame);
191 if (
it != videoToStreamInfo_.end()) {
192 JAMI_DBG(
"Detaching video of call %s",
it->second.callId.c_str());
198 videoToStreamInfo_.erase(
it);
208 std::unique_lock lock(rwMutex_);
211 src->render_frame = std::make_shared<VideoFrame>();
214 sources_.emplace_back(std::move(
src));
215 JAMI_DEBUG(
"Total sources: {:d}", sources_.size());
222 std::unique_lock lock(rwMutex_);
224 for (
const auto& x : sources_) {
225 if (x->source ==
ob) {
226 JAMI_DBG(
"Remove source [%p]", x.get());
228 JAMI_DEBUG(
"Total sources: {:d}", sources_.size());
238 std::shared_lock lock(rwMutex_);
240 for (
const auto& x : sources_) {
241 if (x->source ==
ob) {
243 std::shared_ptr<VideoFrame>
frame;
247 x->atomic_copy(*std::static_pointer_cast<VideoFrame>(
frame));
248 }
catch (
const std::runtime_error&
e) {
249 JAMI_ERR(
"[mixer:%s] Accel failure: %s", id_.c_str(),
e.what());
253 x->atomic_copy(*std::static_pointer_cast<VideoFrame>(
frame_p));
263 nextProcess_ += std::chrono::duration_cast<std::chrono::microseconds>(
FRAME_DURATION);
264 const auto delay = nextProcess_ - std::chrono::steady_clock::now();
265 if (
delay.count() > 0)
266 std::this_thread::sleep_for(
delay);
269 if (width_ == 0
or height_ == 0) {
275 output.reserve(format_, width_, height_);
276 }
catch (
const std::bad_alloc&
e) {
277 JAMI_ERR(
"[mixer:%s] VideoFrame::allocBuffer() failed", id_.c_str());
284 std::lock_guard
lk(audioOnlySourcesMtx_);
285 std::shared_lock lock(rwMutex_);
292 sourcesInfo.reserve(sources_.size() + audioOnlySources_.size());
294 for (
auto& [callId, streamId] : audioOnlySources_) {
309 for (
auto& x : sources_) {
319 std::shared_ptr<VideoFrame> input = x->getRenderFrame();
320 std::shared_ptr<VideoFrame>
fooInput = std::make_shared<VideoFrame>();
335 auto hasVideo = x->hasVideo;
338 if (!input->height()
or !input->width()) {
340 fooInput->reserve(format_, width_, height_);
348 if (x->rotation !=
fooInput->getOrientation()
or !x->w
or !x->h) {
360 JAMI_WARN(
"[mixer:%s] Nothing to render for %p", id_.c_str(), x->source);
364 if (hasVideo != x->hasVideo) {
380 if (layoutUpdated_ == 0) {
381 for (
auto& x : sources_) {
384 SourceInfo {x->source, x->x, x->y, x->w, x->h, x->hasVideo,
sinfo.callId,
sinfo.streamId});
386 if (onSourcesUpdated_)
396 lastTimestamp_ =
output.pointer()->pts;
402 const std::shared_ptr<VideoFrame>& input,
403 std::unique_ptr<VideoMixerSource>& source)
405 if (!width_
or !height_
or !input->pointer()
or input->pointer()->format == -1)
410 int xoff = source->x;
411 int yoff = source->y;
413 int angle = input->getOrientation();
414 const constexpr char filterIn[] =
"mixin";
415 if (
angle != source->rotation) {
416 source->rotationFilter
418 source->rotation =
angle;
420 std::shared_ptr<VideoFrame>
frame;
421 if (source->rotationFilter) {
422 source->rotationFilter->feedInput(input->pointer(),
filterIn);
423 frame = std::static_pointer_cast<VideoFrame>(std::shared_ptr<MediaFrame>(source->rotationFilter->readOutput()));
433VideoMixer::calc_position(std::unique_ptr<VideoMixerSource>& source,
const std::shared_ptr<VideoFrame>& input,
int index)
435 if (!width_
or !height_)
440 const int n = currentLayout_ ==
Layout::ONE_BIG ? 1 :
static_cast<int>(sources_.size());
489 float inputW =
static_cast<float>(input->width());
490 float inputH =
static_cast<float>(input->height());
492 if (input->getOrientation() % 180) {
521 std::unique_lock lock(rwMutex_);
538VideoMixer::startSink()
542 if (width_ == 0
or height_ == 0) {
543 JAMI_WARN(
"[mixer:%s] MX: unable to start with zero-sized output", id_.c_str());
547 if (
not sink_->start()) {
548 JAMI_ERR(
"[mixer:%s] MX: sink startup failed", id_.c_str());
552 if (this->
attach(sink_.get()))
553 sink_->setFrameSize(width_, height_);
557VideoMixer::stopSink()
559 this->
detach(sink_.get());
Manager (controller) of daemon.
bool attach(Observer< std::shared_ptr< MediaFrame > > *o)
bool detach(Observer< std::shared_ptr< MediaFrame > > *o)
bool isRunning() const noexcept
static std::unique_ptr< VideoFrame > transferToMainMemory(const VideoFrame &frame, AVPixelFormat desiredFormat)
Transfers hardware frame to main memory.
std::shared_ptr< VideoFrame > obtainLastFrame()
VideoFrame & getNewFrame()
StreamInfo streamInfo(Observable< std::shared_ptr< MediaFrame > > *frame) const
void attached(Observable< std::shared_ptr< MediaFrame > > *ob) override
void update(Observable< std::shared_ptr< MediaFrame > > *ob, const std::shared_ptr< MediaFrame > &v) override
void switchInputs(const std::vector< std::string > &inputs)
Set all inputs at once.
AVPixelFormat getPixelFormat() const override
void detachVideo(Observable< std::shared_ptr< MediaFrame > > *frame)
VideoMixer(const std::string &id, const std::string &localInput={}, bool attachHost=false)
void setParameters(int width, int height, AVPixelFormat format=AV_PIX_FMT_YUV422P)
void detached(Observable< std::shared_ptr< MediaFrame > > *ob) override
void setActiveStream(const std::string &id)
void stopInputs()
Stop all inputs.
MediaStream getStream(const std::string &name) const
bool verifyActive(const std::string &id)
int getHeight() const override
int getWidth() const override
void attachVideo(Observable< std::shared_ptr< MediaFrame > > *frame, const std::string &callId, const std::string &streamId)
void scale_and_pad(const VideoFrame &input, VideoFrame &output, unsigned xoff, unsigned yoff, unsigned dest_width, unsigned dest_height, bool keep_aspect)
#define JAMI_DEBUG(formatstr,...)
void fillWithBlack(AVFrame *frame)
std::string streamId(const std::string &callId, std::string_view label)
constexpr std::string_view DEFAULT_VIDEO_STREAMID
static constexpr const auto MIXER_FRAMERATE
std::unique_ptr< MediaFilter > getTransposeFilter(int rotation, std::string inputName, int width, int height, int format, bool rescale)
static constexpr const auto FRAME_DURATION
void emitSignal(Args... args)
libjami::VideoFrame VideoFrame
std::shared_ptr< VideoFrame > getRenderFrame()
std::shared_ptr< VideoFrame > render_frame
void atomic_copy(const VideoFrame &other)
std::unique_ptr< MediaFilter > rotationFilter
Observable< std::shared_ptr< MediaFrame > > * source
static constexpr auto MIN_LINE_ZOOM