diff --git a/src/media/video/sinkclient.cpp b/src/media/video/sinkclient.cpp index 07cf155cce3c7d5a6af9af2a27904a88047e945c..6d8ba4b65b80f1d4396724d741c5a8f6249bb078 100644 --- a/src/media/video/sinkclient.cpp +++ b/src/media/video/sinkclient.cpp @@ -336,13 +336,88 @@ SinkClient::SinkClient(const std::string& id, bool mixer) JAMI_DBG("[Sink:%p] Sink [%s] created", this, getId().c_str()); } +void +SinkClient::sendFrameDirect(const std::shared_ptr<jami::MediaFrame>& frame_p) +{ + notify(frame_p); + + DRing::FrameBuffer outFrame(av_frame_alloc()); + av_frame_ref(outFrame.get(), std::static_pointer_cast<VideoFrame>(frame_p)->pointer()); + if (crop_.w || crop_.h) { + outFrame->crop_top = crop_.y; + outFrame->crop_bottom = (size_t) outFrame->height - crop_.y - crop_.h; + outFrame->crop_left = crop_.x; + outFrame->crop_right = (size_t) outFrame->width - crop_.x - crop_.w; + av_frame_apply_cropping(outFrame.get(), AV_FRAME_CROP_UNALIGNED); + } + if (outFrame->height != height_ || outFrame->width != width_) { + setFrameSize(outFrame->width, outFrame->height); + return; + } + target_.push(std::move(outFrame)); +} + +void +SinkClient::sendFrameTransformed(AVFrame* frame) +{ + if (frame->width > 0 and frame->height > 0) { + if (auto buffer_ptr = target_.pull()) { + scaler_->scale(frame, buffer_ptr.get()); + target_.push(std::move(buffer_ptr)); + } + } +} + +std::shared_ptr<VideoFrame> +SinkClient::applyTransform(VideoFrame& frame_p) +{ + std::shared_ptr<VideoFrame> frame = std::make_shared<VideoFrame>(); +#ifdef RING_ACCEL + auto desc = av_pix_fmt_desc_get((AVPixelFormat)frame_p.format()); + if (desc && (desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) { + try { + frame = HardwareAccel::transferToMainMemory(frame_p, AV_PIX_FMT_NV12); + } catch (const std::runtime_error& e) { + JAMI_ERR("[Sink:%p] Transfert to hardware acceleration memory failed: %s", + this, + e.what()); + return {}; + } + } else +#endif + frame->copyFrom(frame_p); + + int angle = frame->getOrientation(); + if (angle != rotation_) { + filter_ = getTransposeFilter(angle, + FILTER_INPUT_NAME, + frame->width(), + frame->height(), + frame->format(), + false); + rotation_ = angle; + } + if (filter_) { + filter_->feedInput(frame->pointer(), FILTER_INPUT_NAME); + frame = std::static_pointer_cast<VideoFrame>(std::shared_ptr<MediaFrame>(filter_->readOutput())); + } + if (crop_.w || crop_.h) { + frame->pointer()->crop_top = crop_.y; + frame->pointer()->crop_bottom = (size_t) frame->height() - crop_.y - crop_.h; + frame->pointer()->crop_left = crop_.x; + frame->pointer()->crop_right = (size_t) frame->width() - crop_.x - crop_.w; + av_frame_apply_cropping(frame->pointer(), 0); + } + return frame; +} + void SinkClient::update(Observable<std::shared_ptr<MediaFrame>>* /*obs*/, const std::shared_ptr<MediaFrame>& frame_p) { #ifdef DEBUG_FPS auto currentTime = std::chrono::system_clock::now(); - const std::chrono::duration<double> seconds = currentTime - lastFrameDebug_; + std::chrono::duration<double> seconds = currentTime - lastFrameDebug_; ++frameCount_; if (seconds.count() > 1) { auto fps = frameCount_ / seconds.count(); @@ -354,75 +429,26 @@ SinkClient::update(Observable<std::shared_ptr<MediaFrame>>* /*obs*/, #endif std::unique_lock<std::mutex> lock(mtx_); - if (target_.push and not target_.pull) { - VideoFrame outFrame; - outFrame.copyFrom(*std::static_pointer_cast<VideoFrame>(frame_p)); - if (crop_.w || crop_.h) { - outFrame.pointer()->crop_top = crop_.y; - outFrame.pointer()->crop_bottom = (size_t) outFrame.height() - crop_.y - crop_.h; - outFrame.pointer()->crop_left = crop_.x; - outFrame.pointer()->crop_right = (size_t) outFrame.width() - crop_.x - crop_.w; - av_frame_apply_cropping(outFrame.pointer(), AV_FRAME_CROP_UNALIGNED); - } - if (outFrame.height() != height_ || outFrame.width() != width_) { - setFrameSize(outFrame.width(), outFrame.height()); - return; - } - notify(std::static_pointer_cast<MediaFrame>(frame_p)); - target_.push(outFrame.getFrame()); + bool hasObservers = getObserversCount() != 0; + bool hasDirectListener = target_.push and not target_.pull; + bool hasTransformedListener = target_.push and target_.pull; + + if (hasDirectListener) { + sendFrameDirect(frame_p); return; } - bool doTransfer = (target_.pull != nullptr); + bool doTransfer = hasTransformedListener or hasObservers; #if HAVE_SHM doTransfer |= (shm_ && doShmTransfer_); #endif if (doTransfer) { - std::shared_ptr<VideoFrame> frame = std::make_shared<VideoFrame>(); -#ifdef RING_ACCEL - auto desc = av_pix_fmt_desc_get( - (AVPixelFormat)(std::static_pointer_cast<VideoFrame>(frame_p))->format()); - if (desc && (desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) { - try { - frame = HardwareAccel::transferToMainMemory(*std::static_pointer_cast<VideoFrame>( - frame_p), - AV_PIX_FMT_NV12); - } catch (const std::runtime_error& e) { - JAMI_ERR("[Sink:%p] Transfert to hardware acceleration memory failed: %s", - this, - e.what()); - return; - } - } else -#endif - frame->copyFrom(*std::static_pointer_cast<VideoFrame>(frame_p)); - - int angle = frame->getOrientation(); - - if (angle != rotation_) { - filter_ = getTransposeFilter(angle, - FILTER_INPUT_NAME, - frame->width(), - frame->height(), - frame->format(), - false); - rotation_ = angle; - } - if (filter_) { - filter_->feedInput(frame->pointer(), FILTER_INPUT_NAME); - frame = std::static_pointer_cast<VideoFrame>( - std::shared_ptr<MediaFrame>(filter_->readOutput())); - } - notify(std::static_pointer_cast<MediaFrame>(frame)); + auto frame = applyTransform(*std::static_pointer_cast<VideoFrame>(frame_p)); + if (not frame) + return; - if (crop_.w || crop_.h) { - frame->pointer()->crop_top = crop_.y; - frame->pointer()->crop_bottom = (size_t) frame->height() - crop_.y - crop_.h; - frame->pointer()->crop_left = crop_.x; - frame->pointer()->crop_right = (size_t) frame->width() - crop_.x - crop_.w; - av_frame_apply_cropping(frame->pointer(), AV_FRAME_CROP_UNALIGNED); - } + notify(std::static_pointer_cast<MediaFrame>(frame)); if (frame->height() != height_ || frame->width() != width_) { lock.unlock(); @@ -433,16 +459,8 @@ SinkClient::update(Observable<std::shared_ptr<MediaFrame>>* /*obs*/, if (shm_ && doShmTransfer_) shm_->renderFrame(*frame); #endif - if (target_.pull) { - int width = frame->width(); - int height = frame->height(); - if (width > 0 && height > 0) { - if (auto buffer_ptr = target_.pull()) { - scaler_->scale(*frame, buffer_ptr.get()); - target_.push(std::move(buffer_ptr)); - } - } - } + if (hasTransformedListener) + sendFrameTransformed(frame->pointer()); } } diff --git a/src/media/video/sinkclient.h b/src/media/video/sinkclient.h index 4eee5523a62f8787e47a31a1a9bcf867f7c840ef..90313f487844e65034105ea309f62d406c2ef0f0 100644 --- a/src/media/video/sinkclient.h +++ b/src/media/video/sinkclient.h @@ -106,7 +106,16 @@ private: std::unique_ptr<MediaFilter> filter_; std::mutex mtx_; - void setRotation(int rotation); + void sendFrameDirect(const std::shared_ptr<jami::MediaFrame>&); + void sendFrameTransformed(AVFrame* frame); + + /** + * Apply required transformations before sending frames to clients/observers: + * - Transfer the frame from gpu to main memory, if needed. + * - Rotate the frame as needed. + * - Apply cropping as needed + */ + std::shared_ptr<VideoFrame> applyTransform(VideoFrame& frame); #ifdef DEBUG_FPS unsigned frameCount_; diff --git a/src/media/video/video_scaler.cpp b/src/media/video/video_scaler.cpp index 11e751ec3b6553a48baf2d4f6de1b28e2b7ae53f..daeac3144163b2673ddfd064125aa06932ce178a 100644 --- a/src/media/video/video_scaler.cpp +++ b/src/media/video/video_scaler.cpp @@ -42,14 +42,12 @@ VideoScaler::~VideoScaler() void VideoScaler::scale(const VideoFrame& input, VideoFrame& output){ - scale(input, output.pointer()); + scale(input.pointer(), output.pointer()); } void -VideoScaler::scale(const VideoFrame& input, AVFrame* output_frame) +VideoScaler::scale(const AVFrame* input_frame, AVFrame* output_frame) { - const auto input_frame = input.pointer(); - ctx_ = sws_getCachedContext(ctx_, input_frame->width, input_frame->height, diff --git a/src/media/video/video_scaler.h b/src/media/video/video_scaler.h index 50707c71acda47018afdb4ef26f2e5634c6dd679..907eb9ce3e586b6552e220fa8a4649a4782ed7f9 100644 --- a/src/media/video/video_scaler.h +++ b/src/media/video/video_scaler.h @@ -39,7 +39,7 @@ public: VideoScaler(); ~VideoScaler(); void reset(); - void scale(const VideoFrame& input, AVFrame* output); + void scale(const AVFrame* input, AVFrame* output); void scale(const VideoFrame& input, VideoFrame& output); void scale_with_aspect(const VideoFrame& input, VideoFrame& output); void scale_and_pad(const VideoFrame& input, diff --git a/src/observer.h b/src/observer.h index 121b3ecd5e3f658cfbc0a9ca599f2f6a452a571f..03619f0516aba8463ac01356cc69ad9002186e5c 100644 --- a/src/observer.h +++ b/src/observer.h @@ -111,10 +111,10 @@ public: return false; } - int getObserversCount() + size_t getObserversCount() { std::lock_guard<std::mutex> lk(mutex_); - return observers_.size(); + return observers_.size() + priority_observers_.size(); } protected: