38#include <opendht/thread_pool.h>
54 std::lock_guard lock(mutex_);
55 auto newFrame = std::make_shared<VideoFrame>();
62 std::lock_guard lock(mutex_);
84 , loop_([] {
return true; }, std::bind(&VideoMixer::process,
this), [] {})
95 nextProcess_ = std::chrono::steady_clock::now();
97 JAMI_DBG(
"[mixer:%s] New instance created", id_.c_str());
107 JAMI_DBG(
"[mixer:%s] Instance destroyed", id_.c_str());
115 std::lock_guard
lk(localInputsMtx_);
118 for (
const auto& input :
inputs) {
123 auto it = std::find(localInputs_.cbegin(), localInputs_.cend(),
videoInput);
127 localInputs_.erase(
it);
136 for (
size_t i = 0;
i < localInputs_.size(); ++
i) {
137 auto& input = localInputs_[
i];
143VideoMixer::stopInput(
const std::shared_ptr<VideoFrameActiveWriter>& input)
152 for (
auto& input : localInputs_)
154 localInputs_.clear();
167 if (activeStream_ ==
"")
174 const std::string& callId,
175 const std::string& streamId)
179 JAMI_DBG(
"Attaching video with streamId %s", streamId.c_str());
181 std::lock_guard
lk(videoToStreamInfoMtx_);
193 std::unique_lock
lk(videoToStreamInfoMtx_);
194 auto it = videoToStreamInfo_.find(
frame);
195 if (
it != videoToStreamInfo_.end()) {
196 JAMI_DBG(
"Detaching video of call %s",
it->second.callId.c_str());
202 videoToStreamInfo_.erase(
it);
212 std::unique_lock lock(rwMutex_);
215 src->render_frame = std::make_shared<VideoFrame>();
218 sources_.emplace_back(std::move(
src));
219 JAMI_DEBUG(
"Total sources: {:d}", sources_.size());
226 std::unique_lock lock(rwMutex_);
228 for (
const auto& x : sources_) {
229 if (x->source ==
ob) {
230 JAMI_DBG(
"Remove source [%p]", x.get());
232 JAMI_DEBUG(
"Total sources: {:d}", sources_.size());
241 const std::shared_ptr<MediaFrame>&
frame_p)
243 std::shared_lock lock(rwMutex_);
245 for (
const auto& x : sources_) {
246 if (x->source ==
ob) {
248 std::shared_ptr<VideoFrame>
frame;
253 x->atomic_copy(*std::static_pointer_cast<VideoFrame>(
frame));
254 }
catch (
const std::runtime_error&
e) {
255 JAMI_ERR(
"[mixer:%s] Accel failure: %s", id_.c_str(),
e.what());
259 x->atomic_copy(*std::static_pointer_cast<VideoFrame>(
frame_p));
269 nextProcess_ += std::chrono::duration_cast<std::chrono::microseconds>(
FRAME_DURATION);
270 const auto delay = nextProcess_ - std::chrono::steady_clock::now();
271 if (
delay.count() > 0)
272 std::this_thread::sleep_for(
delay);
275 if (width_ == 0
or height_ == 0) {
281 output.reserve(format_, width_, height_);
282 }
catch (
const std::bad_alloc&
e) {
283 JAMI_ERR(
"[mixer:%s] VideoFrame::allocBuffer() failed", id_.c_str());
290 std::lock_guard
lk(audioOnlySourcesMtx_);
291 std::shared_lock lock(rwMutex_);
298 sourcesInfo.reserve(sources_.size() + audioOnlySources_.size());
300 for (
auto& [callId, streamId] : audioOnlySources_) {
315 for (
auto& x : sources_) {
325 std::shared_ptr<VideoFrame> input = x->getRenderFrame();
326 std::shared_ptr<VideoFrame>
fooInput = std::make_shared<VideoFrame>();
341 auto hasVideo = x->hasVideo;
344 if (!input->height()
or !input->width()) {
346 fooInput->reserve(format_, width_, height_);
354 if (x->rotation !=
fooInput->getOrientation()
or !x->w
or !x->h) {
366 JAMI_WARN(
"[mixer:%s] Nothing to render for %p", id_.c_str(), x->source);
370 if (hasVideo != x->hasVideo) {
386 if (layoutUpdated_ == 0) {
387 for (
auto& x : sources_) {
398 if (onSourcesUpdated_)
409 lastTimestamp_ =
output.pointer()->pts;
415 const std::shared_ptr<VideoFrame>& input,
416 std::unique_ptr<VideoMixerSource>& source)
418 if (!width_
or !height_
or !input->pointer()
or input->pointer()->format == -1)
423 int xoff = source->x;
424 int yoff = source->y;
426 int angle = input->getOrientation();
427 const constexpr char filterIn[] =
"mixin";
428 if (
angle != source->rotation) {
435 source->rotation =
angle;
437 std::shared_ptr<VideoFrame>
frame;
438 if (source->rotationFilter) {
439 source->rotationFilter->feedInput(input->pointer(),
filterIn);
440 frame = std::static_pointer_cast<VideoFrame>(
441 std::shared_ptr<MediaFrame>(source->rotationFilter->readOutput()));
451VideoMixer::calc_position(std::unique_ptr<VideoMixerSource>& source,
452 const std::shared_ptr<VideoFrame>& input,
455 if (!width_
or !height_)
509 if (input->getOrientation() % 180) {
536 std::unique_lock lock(rwMutex_);
553VideoMixer::startSink()
557 if (width_ == 0
or height_ == 0) {
558 JAMI_WARN(
"[mixer:%s] MX: unable to start with zero-sized output", id_.c_str());
562 if (
not sink_->start()) {
563 JAMI_ERR(
"[mixer:%s] MX: sink startup failed", id_.c_str());
567 if (this->
attach(sink_.get()))
568 sink_->setFrameSize(width_, height_);
572VideoMixer::stopSink()
574 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