LCOV - code coverage report
Current view: top level - src/media/video - video_rtp_session.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 454 543 83.6 %
Date: 2024-12-21 08:56:24 Functions: 46 54 85.2 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2004-2024 Savoir-faire Linux Inc.
       3             :  *
       4             :  *  This program is free software: you can redistribute it and/or modify
       5             :  *  it under the terms of the GNU General Public License as published by
       6             :  *  the Free Software Foundation, either version 3 of the License, or
       7             :  *  (at your option) any later version.
       8             :  *
       9             :  *  This program is distributed in the hope that it will be useful,
      10             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      12             :  *  GNU General Public License for more details.
      13             :  *
      14             :  *  You should have received a copy of the GNU General Public License
      15             :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "client/videomanager.h"
      19             : #include "video_rtp_session.h"
      20             : #include "video_sender.h"
      21             : #include "video_receive_thread.h"
      22             : #include "video_mixer.h"
      23             : #include "socket_pair.h"
      24             : #include "sip/sipvoiplink.h" // for enqueueKeyframeRequest
      25             : #include "manager.h"
      26             : #include "media_const.h"
      27             : #ifdef ENABLE_PLUGIN
      28             : #include "plugin/streamdata.h"
      29             : #include "plugin/jamipluginmanager.h"
      30             : #endif
      31             : #include "logger.h"
      32             : #include "string_utils.h"
      33             : #include "call.h"
      34             : #include "conference.h"
      35             : #include "congestion_control.h"
      36             : 
      37             : #include "account_const.h"
      38             : 
      39             : #include <dhtnet/ice_socket.h>
      40             : 
      41             : #include <asio/io_context.hpp>
      42             : #include <sstream>
      43             : #include <map>
      44             : #include <string>
      45             : #include <thread>
      46             : #include <chrono>
      47             : 
      48             : namespace jami {
      49             : namespace video {
      50             : 
      51             : using std::string;
      52             : 
      53             : static constexpr unsigned MAX_REMB_DEC {1};
      54             : 
      55             : constexpr auto DELAY_AFTER_RESTART = std::chrono::milliseconds(1000);
      56             : constexpr auto EXPIRY_TIME_RTCP = std::chrono::seconds(2);
      57             : constexpr auto DELAY_AFTER_REMB_INC = std::chrono::seconds(1);
      58             : constexpr auto DELAY_AFTER_REMB_DEC = std::chrono::milliseconds(500);
      59             : 
      60         315 : VideoRtpSession::VideoRtpSession(const string& callId,
      61             :                                  const string& streamId,
      62             :                                  const DeviceParams& localVideoParams,
      63         315 :                                  const std::shared_ptr<MediaRecorder>& rec)
      64             :     : RtpSession(callId, streamId, MediaType::MEDIA_VIDEO)
      65         315 :     , localVideoParams_(localVideoParams)
      66         315 :     , videoBitrateInfo_ {}
      67         976 :     , rtcpCheckerThread_([] { return true; }, [this] { processRtcpChecker(); }, [] {})
      68         630 :     , cc(std::make_unique<CongestionControl>())
      69             : {
      70         315 :     recorder_ = rec;
      71         315 :     setupVideoBitrateInfo(); // reset bitrate
      72         945 :     JAMI_LOG("[{:p}] Video RTP session created for call {} (recorder {:p})", fmt::ptr(this), callId_, fmt::ptr(recorder_));
      73         315 : }
      74             : 
      75         315 : VideoRtpSession::~VideoRtpSession()
      76             : {
      77         315 :     deinitRecorder();
      78         315 :     stop();
      79         945 :     JAMI_LOG("[{:p}] Video RTP session destroyed", fmt::ptr(this));
      80         315 : }
      81             : 
      82             : const VideoBitrateInfo&
      83          90 : VideoRtpSession::getVideoBitrateInfo()
      84             : {
      85          90 :     return videoBitrateInfo_;
      86             : }
      87             : 
      88             : /// Setup internal VideoBitrateInfo structure from media descriptors.
      89             : ///
      90             : void
      91         150 : VideoRtpSession::updateMedia(const MediaDescription& send, const MediaDescription& receive)
      92             : {
      93         150 :     BaseType::updateMedia(send, receive);
      94             :     // adjust send->codec bitrate info for higher video resolutions
      95         150 :     auto codecVideo = std::static_pointer_cast<jami::SystemVideoCodecInfo>(send_.codec);
      96         150 :     if (codecVideo) {
      97         150 :         auto const pixels = localVideoParams_.height * localVideoParams_.width;
      98         150 :         codecVideo->bitrate = std::max((unsigned int)(pixels * 0.001), SystemCodecInfo::DEFAULT_VIDEO_BITRATE);
      99         150 :         codecVideo->maxBitrate = std::max((unsigned int)(pixels * 0.0015), SystemCodecInfo::DEFAULT_MAX_BITRATE);
     100             :     }
     101         150 :     setupVideoBitrateInfo();
     102         150 : }
     103             : 
     104             : void
     105         150 : VideoRtpSession::setRequestKeyFrameCallback(std::function<void(void)> cb)
     106             : {
     107         150 :     cbKeyFrameRequest_ = std::move(cb);
     108         150 : }
     109             : 
     110             : void
     111         169 : VideoRtpSession::startSender()
     112             : {
     113         169 :     std::lock_guard lock(mutex_);
     114             : 
     115         169 :     JAMI_DBG("[%p] Start video RTP sender: input [%s] - muted [%s]",
     116             :              this,
     117             :              conference_ ? "Video Mixer" : input_.c_str(),
     118             :              send_.onHold ? "YES" : "NO");
     119             : 
     120         169 :     if (not socketPair_) {
     121             :         // Ignore if the transport is not set yet
     122           0 :         JAMI_WARN("[%p] Transport not set yet", this);
     123           0 :         return;
     124             :     }
     125             : 
     126         169 :     if (send_.enabled and not send_.onHold) {
     127         162 :         if (sender_) {
     128          18 :             if (videoLocal_)
     129          17 :                 videoLocal_->detach(sender_.get());
     130          18 :             if (videoMixer_)
     131          16 :                 videoMixer_->detach(sender_.get());
     132          18 :             JAMI_WARN("[%p] Restarting video sender", this);
     133             :         }
     134             : 
     135         162 :         if (not conference_) {
     136         113 :             videoLocal_ = getVideoInput(input_);
     137         113 :             if (videoLocal_) {
     138         113 :                 videoLocal_->setRecorderCallback(
     139           0 :                     [w=weak_from_this()](const MediaStream& ms) {
     140           0 :                         Manager::instance().ioContext()->post([w=std::move(w), ms]() {
     141           0 :                             if (auto shared = w.lock())
     142           0 :                                 shared->attachLocalRecorder(ms);
     143           0 :                         });
     144           0 :                     });
     145         113 :                 auto newParams = videoLocal_->getParams();
     146             :                 try {
     147         113 :                     if (newParams.valid()
     148         113 :                         && newParams.wait_for(NEWPARAMS_TIMEOUT) == std::future_status::ready) {
     149         103 :                         localVideoParams_ = newParams.get();
     150             :                     } else {
     151          10 :                         JAMI_ERR("[%p] No valid new video parameters", this);
     152          10 :                         return;
     153             :                     }
     154           0 :                 } catch (const std::exception& e) {
     155           0 :                     JAMI_ERR("Exception during retrieving video parameters: %s", e.what());
     156           0 :                     return;
     157           0 :                 }
     158         113 :             } else {
     159           0 :                 JAMI_WARN("Unable to lock video input");
     160           0 :                 return;
     161             :             }
     162             : 
     163             : #if (defined(__ANDROID__) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS))
     164             :             videoLocal_->setupSink(localVideoParams_.width, localVideoParams_.height);
     165             : #endif
     166             :         }
     167             : 
     168             :         // be sure to not send any packets before saving last RTP seq value
     169         152 :         socketPair_->stopSendOp();
     170             : 
     171         152 :         auto codecVideo = std::static_pointer_cast<jami::SystemVideoCodecInfo>(send_.codec);
     172         152 :         auto autoQuality = codecVideo->isAutoQualityEnabled;
     173             : 
     174         152 :         send_.linkableHW = conference_ == nullptr;
     175         152 :         send_.bitrate = videoBitrateInfo_.videoBitrateCurrent;
     176             :         // NOTE:
     177             :         // Current implementation does not handle resolution change
     178             :         // (needed by window sharing feature) with HW codecs, so HW
     179             :         // codecs will be disabled for now.
     180         152 :         bool allowHwAccel = (localVideoParams_.format != "x11grab" && localVideoParams_.format != "dxgigrab" && localVideoParams_.format != "lavfi");
     181             : 
     182         152 :         if (socketPair_)
     183         152 :             initSeqVal_ = socketPair_->lastSeqValOut();
     184             : 
     185             :         try {
     186         152 :             sender_.reset();
     187         152 :             socketPair_->stopSendOp(false);
     188             :             MediaStream ms
     189         152 :                 = !videoMixer_
     190             :                       ? MediaStream("video sender",
     191             :                                     AV_PIX_FMT_YUV420P,
     192         255 :                                     1 / static_cast<rational<int>>(localVideoParams_.framerate),
     193         109 :                                     localVideoParams_.width == 0 ? 1080u : localVideoParams_.width,
     194         109 :                                     localVideoParams_.height == 0 ? 720u : localVideoParams_.height,
     195         103 :                                     send_.bitrate,
     196             :                                     static_cast<rational<int>>(localVideoParams_.framerate))
     197         559 :                       : videoMixer_->getStream("Video Sender");
     198         152 :             sender_.reset(new VideoSender(
     199         304 :                 getRemoteRtpUri(), ms, send_, *socketPair_, initSeqVal_ + 1, mtu_, allowHwAccel));
     200         152 :             if (changeOrientationCallback_)
     201         152 :                 sender_->setChangeOrientationCallback(changeOrientationCallback_);
     202         152 :             if (socketPair_)
     203         152 :                 socketPair_->setPacketLossCallback([this]() { cbKeyFrameRequest_(); });
     204             : 
     205         152 :         } catch (const MediaEncoderException& e) {
     206           0 :             JAMI_ERR("%s", e.what());
     207           0 :             send_.enabled = false;
     208           0 :         }
     209         152 :         lastMediaRestart_ = clock::now();
     210         152 :         last_REMB_inc_ = clock::now();
     211         152 :         last_REMB_dec_ = clock::now();
     212         152 :         if (autoQuality and not rtcpCheckerThread_.isRunning())
     213         134 :             rtcpCheckerThread_.start();
     214          18 :         else if (not autoQuality and rtcpCheckerThread_.isRunning())
     215           0 :             rtcpCheckerThread_.join();
     216             :         // Block reads to received feedback packets
     217         152 :         if(socketPair_)
     218         152 :             socketPair_->setReadBlockingMode(true);
     219         152 :     }
     220         169 : }
     221             : 
     222             : void
     223          20 : VideoRtpSession::restartSender()
     224             : {
     225          20 :     std::lock_guard lock(mutex_);
     226             : 
     227             :     // ensure that start has been called before restart
     228          20 :     if (not socketPair_)
     229           1 :         return;
     230             : 
     231          19 :     startSender();
     232             : 
     233          19 :     if (conference_)
     234          17 :         setupConferenceVideoPipeline(*conference_, Direction::SEND);
     235             :     else
     236           2 :         setupVideoPipeline();
     237          20 : }
     238             : 
     239             : void
     240         691 : VideoRtpSession::stopSender(bool forceStopSocket)
     241             : {
     242             :     // Concurrency protection must be done by caller.
     243             : 
     244         691 :     JAMI_DBG("[%p] Stop video RTP sender: input [%s] - muted [%s]",
     245             :              this,
     246             :              conference_ ? "Video Mixer" : input_.c_str(),
     247             :              send_.onHold ? "YES" : "NO");
     248             : 
     249         691 :     if (sender_) {
     250         134 :         if (videoLocal_)
     251         101 :             videoLocal_->detach(sender_.get());
     252         134 :         if (videoMixer_)
     253           4 :             videoMixer_->detach(sender_.get());
     254         134 :         sender_.reset();
     255             :     }
     256             : 
     257         691 :     if (socketPair_) {
     258         155 :         bool const isReceivingVideo = receive_.enabled && !receive_.onHold;
     259         155 :         if(forceStopSocket || !isReceivingVideo) {
     260         152 :             socketPair_->stopSendOp();
     261         152 :             socketPair_->setReadBlockingMode(false);
     262             :         }
     263             :     }
     264         691 : }
     265             : 
     266             : void
     267         151 : VideoRtpSession::startReceiver()
     268             : {
     269             :     // Concurrency protection must be done by caller.
     270             : 
     271         151 :     JAMI_DBG("[%p] Starting receiver", this);
     272             : 
     273         151 :     if (receive_.enabled and not receive_.onHold) {
     274         145 :         if (receiveThread_)
     275          13 :             JAMI_WARN("[%p] Already has a receiver, restarting", this);
     276         145 :         receiveThread_.reset(
     277         145 :             new VideoReceiveThread(callId_, !conference_, receive_.receiving_sdp, mtu_));
     278             : 
     279             :         // ensure that start has been called
     280         145 :         if (not socketPair_)
     281           0 :             return;
     282             : 
     283             :         // XXX keyframe requests can timeout if unanswered
     284         145 :         receiveThread_->addIOContext(*socketPair_);
     285         145 :         receiveThread_->setSuccessfulSetupCb(onSuccessfulSetup_);
     286         145 :         receiveThread_->startLoop();
     287         145 :         receiveThread_->setRequestKeyFrameCallback([this]() { cbKeyFrameRequest_(); });
     288         290 :         receiveThread_->setRotation(rotation_.load());
     289         145 :         if (videoMixer_ and conference_) {
     290             :             // Note, this should be managed differently, this is a bit hacky
     291          34 :             auto audioId = streamId_;
     292          34 :             string_replace(audioId, "video", "audio");
     293          34 :             auto activeStream = videoMixer_->verifyActive(audioId);
     294          34 :             videoMixer_->removeAudioOnlySource(callId_, audioId);
     295          34 :             if (activeStream)
     296           0 :                 videoMixer_->setActiveStream(streamId_);
     297          34 :         }
     298         145 :         receiveThread_->setRecorderCallback([w=weak_from_this()](const MediaStream& ms) {
     299          30 :             Manager::instance().ioContext()->post([w=std::move(w), ms]() {
     300          30 :                 if (auto shared = w.lock())
     301          30 :                     shared->attachRemoteRecorder(ms);
     302          30 :             });
     303          30 :         });
     304         145 :     } else {
     305           6 :         JAMI_DBG("[%p] Video receiver disabled", this);
     306           6 :         if (receiveThread_ and videoMixer_ and conference_) {
     307             :             // Note, this should be managed differently, this is a bit hacky
     308           0 :             auto audioId_ = streamId_;
     309           0 :             string_replace(audioId_, "video", "audio");
     310           0 :             auto activeStream = videoMixer_->verifyActive(streamId_);
     311           0 :             videoMixer_->addAudioOnlySource(callId_, audioId_);
     312           0 :             receiveThread_->detach(videoMixer_.get());
     313           0 :             if (activeStream)
     314           0 :                 videoMixer_->setActiveStream(audioId_);
     315           0 :         }
     316             :     }
     317         151 :     if (socketPair_)
     318         151 :         socketPair_->setReadBlockingMode(true);
     319             : }
     320             : 
     321             : void
     322         691 : VideoRtpSession::stopReceiver(bool forceStopSocket)
     323             : {
     324             :     // Concurrency protection must be done by caller.
     325             : 
     326         691 :     JAMI_DBG("[%p] Stopping receiver", this);
     327             : 
     328         691 :     if (not receiveThread_)
     329         390 :         return;
     330             : 
     331         301 :     if (videoMixer_) {
     332           6 :         auto activeStream = videoMixer_->verifyActive(streamId_);
     333           6 :         auto audioId = streamId_;
     334           6 :         string_replace(audioId, "video", "audio");
     335           6 :         videoMixer_->addAudioOnlySource(callId_, audioId);
     336           6 :         receiveThread_->detach(videoMixer_.get());
     337           6 :         if (activeStream)
     338           0 :             videoMixer_->setActiveStream(audioId);
     339           6 :     }
     340             : 
     341             :     // We need to disable the read operation, otherwise the
     342             :     // receiver thread will block since the peer stopped sending
     343             :     // RTP packets.
     344         301 :     bool const isSendingVideo = send_.enabled && !send_.onHold;
     345         301 :     if (socketPair_) {
     346         150 :         if (forceStopSocket || !isSendingVideo) {
     347         147 :             socketPair_->setReadBlockingMode(false);
     348         147 :             socketPair_->stopSendOp();
     349             :         }
     350             :     }
     351             : 
     352         301 :     auto ms = receiveThread_->getInfo();
     353         301 :     if (auto ob = recorder_->getStream(ms.name)) {
     354          30 :         receiveThread_->detach(ob);
     355          30 :         recorder_->removeStream(ms);
     356             :     }
     357             : 
     358         301 :     if(forceStopSocket || !isSendingVideo)
     359         294 :         receiveThread_->stopLoop();
     360         301 :     receiveThread_->stopSink();
     361         301 : }
     362             : 
     363             : void
     364         154 : VideoRtpSession::start(std::unique_ptr<dhtnet::IceSocket> rtp_sock, std::unique_ptr<dhtnet::IceSocket> rtcp_sock)
     365             : {
     366         154 :     std::lock_guard lock(mutex_);
     367             : 
     368         154 :     if (not send_.enabled and not receive_.enabled) {
     369           4 :         stop();
     370           4 :         return;
     371             :     }
     372             : 
     373             :     try {
     374         150 :         if (rtp_sock and rtcp_sock) {
     375         146 :             if (send_.addr) {
     376         146 :                 rtp_sock->setDefaultRemoteAddress(send_.addr);
     377             :             }
     378             : 
     379         146 :             auto& rtcpAddr = send_.rtcp_addr ? send_.rtcp_addr : send_.addr;
     380         146 :             if (rtcpAddr) {
     381         146 :                 rtcp_sock->setDefaultRemoteAddress(rtcpAddr);
     382             :             }
     383         146 :             socketPair_.reset(new SocketPair(std::move(rtp_sock), std::move(rtcp_sock)));
     384             :         } else {
     385           4 :             socketPair_.reset(new SocketPair(getRemoteRtpUri().c_str(), receive_.addr.getPort()));
     386             :         }
     387             : 
     388         150 :         last_REMB_inc_ = clock::now();
     389         150 :         last_REMB_dec_ = clock::now();
     390             : 
     391         150 :         socketPair_->setRtpDelayCallback(
     392        5240 :             [&](int gradient, int deltaT) { delayMonitor(gradient, deltaT); });
     393             : 
     394         150 :         if (send_.crypto and receive_.crypto) {
     395         600 :             socketPair_->createSRTP(receive_.crypto.getCryptoSuite().c_str(),
     396         300 :                                     receive_.crypto.getSrtpKeyInfo().c_str(),
     397         300 :                                     send_.crypto.getCryptoSuite().c_str(),
     398         300 :                                     send_.crypto.getSrtpKeyInfo().c_str());
     399             :         }
     400           0 :     } catch (const std::runtime_error& e) {
     401           0 :         JAMI_ERR("[%p] Socket creation failed: %s", this, e.what());
     402           0 :         return;
     403           0 :     }
     404             : 
     405         150 :     startSender();
     406         150 :     startReceiver();
     407             : 
     408         150 :     if (conference_) {
     409          34 :         if (send_.enabled and not send_.onHold) {
     410          33 :             setupConferenceVideoPipeline(*conference_, Direction::SEND);
     411             :         }
     412          34 :         if (receive_.enabled and not receive_.onHold) {
     413          33 :             setupConferenceVideoPipeline(*conference_, Direction::RECV);
     414             :         }
     415             :     } else {
     416         116 :         setupVideoPipeline();
     417             :     }
     418         154 : }
     419             : 
     420             : void
     421         680 : VideoRtpSession::stop()
     422             : {
     423         680 :     std::lock_guard lock(mutex_);
     424             : 
     425         680 :     stopSender(true);
     426         680 :     stopReceiver(true);
     427             : 
     428         680 :     if (socketPair_)
     429         150 :         socketPair_->interrupt();
     430             : 
     431         680 :     rtcpCheckerThread_.join();
     432             : 
     433             :     // reset default video quality if exist
     434         680 :     if (videoBitrateInfo_.videoQualityCurrent != SystemCodecInfo::DEFAULT_NO_QUALITY)
     435         117 :         videoBitrateInfo_.videoQualityCurrent = SystemCodecInfo::DEFAULT_CODEC_QUALITY;
     436             : 
     437         680 :     videoBitrateInfo_.videoBitrateCurrent = SystemCodecInfo::DEFAULT_VIDEO_BITRATE;
     438         680 :     storeVideoBitrateInfo();
     439             : 
     440         680 :     socketPair_.reset();
     441         680 :     videoLocal_.reset();
     442         680 : }
     443             : 
     444             : void
     445         330 : VideoRtpSession::setMuted(bool mute, Direction dir)
     446             : {
     447         330 :     std::lock_guard lock(mutex_);
     448             : 
     449             :     // Sender
     450         330 :     if (dir == Direction::SEND) {
     451         158 :         if (send_.onHold == mute) {
     452         146 :             JAMI_DBG("[%p] Local already %s", this, mute ? "muted" : "un-muted");
     453         146 :             return;
     454             :         }
     455             : 
     456          12 :         if ((send_.onHold = mute)) {
     457          11 :             if (videoLocal_) {
     458           5 :                 auto ms = videoLocal_->getInfo();
     459           5 :                 if (auto ob = recorder_->getStream(ms.name)) {
     460           0 :                     videoLocal_->detach(ob);
     461           0 :                     recorder_->removeStream(ms);
     462             :                 }
     463           5 :             }
     464          11 :             stopSender();
     465             :         } else {
     466           1 :             restartSender();
     467             :         }
     468          12 :         return;
     469             :     }
     470             : 
     471             :     // Receiver
     472         172 :     if (receive_.onHold == mute) {
     473         160 :         JAMI_DBG("[%p] Remote already %s", this, mute ? "muted" : "un-muted");
     474         160 :         return;
     475             :     }
     476             : 
     477          12 :     if ((receive_.onHold = mute)) {
     478          11 :         if (receiveThread_) {
     479           9 :             auto ms = receiveThread_->getInfo();
     480           9 :             if (auto ob = recorder_->getStream(ms.name)) {
     481           0 :                 receiveThread_->detach(ob);
     482           0 :                 recorder_->removeStream(ms);
     483             :             }
     484           9 :         }
     485          11 :         stopReceiver();
     486             :     } else {
     487           1 :         startReceiver();
     488           1 :         if (conference_ and not receive_.onHold) {
     489           1 :             setupConferenceVideoPipeline(*conference_, Direction::RECV);
     490             :         }
     491             :     }
     492         330 : }
     493             : 
     494             : void
     495         150 : VideoRtpSession::forceKeyFrame()
     496             : {
     497         150 :     std::lock_guard lock(mutex_);
     498             : #if __ANDROID__
     499             :     if (videoLocal_)
     500             :         emitSignal<libjami::VideoSignal::RequestKeyFrame>(videoLocal_->getName());
     501             : #else
     502         150 :     if (sender_)
     503          74 :         sender_->forceKeyFrame();
     504             : #endif
     505         150 : }
     506             : 
     507             : void
     508         363 : VideoRtpSession::setRotation(int rotation)
     509             : {
     510         363 :     rotation_.store(rotation);
     511         363 :     if (receiveThread_)
     512          48 :         receiveThread_->setRotation(rotation);
     513         363 : }
     514             : 
     515             : void
     516         118 : VideoRtpSession::setupVideoPipeline()
     517             : {
     518         118 :     if (sender_) {
     519         103 :         if (videoLocal_) {
     520         103 :             JAMI_DBG("[%p] Setup video pipeline on local capture device", this);
     521         103 :             videoLocal_->attach(sender_.get());
     522             :         }
     523             :     } else {
     524          15 :         videoLocal_.reset();
     525             :     }
     526         118 : }
     527             : 
     528             : void
     529         101 : VideoRtpSession::setupConferenceVideoPipeline(Conference& conference, Direction dir)
     530             : {
     531         101 :     if (dir == Direction::SEND) {
     532          50 :         JAMI_DBG("[%p] Setup video sender pipeline on conference %s for call %s",
     533             :                  this,
     534             :                  conference.getConfId().c_str(),
     535             :                  callId_.c_str());
     536          50 :         videoMixer_ = conference.getVideoMixer();
     537          50 :         if (sender_) {
     538             :             // Swap sender from local video to conference video mixer
     539          49 :             if (videoLocal_)
     540          15 :                 videoLocal_->detach(sender_.get());
     541          49 :             if (videoMixer_)
     542          49 :                 videoMixer_->attach(sender_.get());
     543             :         } else {
     544           1 :             JAMI_WARN("[%p] no sender", this);
     545             :         }
     546             :     } else {
     547          51 :         JAMI_DBG("[%p] Setup video receiver pipeline on conference %s for call %s",
     548             :                  this,
     549             :                  conference.getConfId().c_str(),
     550             :                  callId_.c_str());
     551          51 :         if (receiveThread_) {
     552          51 :             receiveThread_->stopSink();
     553          51 :             if (videoMixer_)
     554          51 :                 videoMixer_->attachVideo(receiveThread_.get(), callId_, streamId_);
     555             :         } else {
     556           0 :             JAMI_WARN("[%p] no receiver", this);
     557             :         }
     558             :     }
     559         101 : }
     560             : 
     561             : void
     562          59 : VideoRtpSession::enterConference(Conference& conference)
     563             : {
     564          59 :     std::lock_guard lock(mutex_);
     565             : 
     566          59 :     exitConference();
     567             : 
     568          59 :     conference_ = &conference;
     569          59 :     videoMixer_ = conference.getVideoMixer();
     570          59 :     JAMI_DBG("[%p] enterConference (conf: %s)", this, conference.getConfId().c_str());
     571             : 
     572          59 :     if (send_.enabled or receiveThread_) {
     573             :         // Restart encoder with conference parameter ON in order to unlink HW encoder
     574             :         // from HW decoder.
     575          17 :         restartSender();
     576          17 :         if (conference_) {
     577          17 :             setupConferenceVideoPipeline(conference, Direction::RECV);
     578             :         }
     579             :     }
     580          59 : }
     581             : 
     582             : void
     583         117 : VideoRtpSession::exitConference()
     584             : {
     585         117 :     std::lock_guard lock(mutex_);
     586             : 
     587         117 :     if (!conference_)
     588          58 :         return;
     589             : 
     590          59 :     JAMI_DBG("[%p] exitConference (conf: %s)", this, conference_->getConfId().c_str());
     591             : 
     592          59 :     if (videoMixer_) {
     593          59 :         if (sender_)
     594          45 :             videoMixer_->detach(sender_.get());
     595             : 
     596          59 :         if (receiveThread_) {
     597          50 :             auto activeStream = videoMixer_->verifyActive(streamId_);
     598          50 :             videoMixer_->detachVideo(receiveThread_.get());
     599          50 :             receiveThread_->startSink();
     600          50 :             if (activeStream)
     601           0 :                 videoMixer_->setActiveStream(streamId_);
     602             :         }
     603             : 
     604          59 :         videoMixer_.reset();
     605             :     }
     606             : 
     607          59 :     conference_ = nullptr;
     608         117 : }
     609             : 
     610             : bool
     611         393 : VideoRtpSession::check_RCTP_Info_RR(RTCPInfo& rtcpi)
     612             : {
     613         393 :     auto rtcpInfoVect = socketPair_->getRtcpRR();
     614         393 :     unsigned totalLost = 0;
     615         393 :     unsigned totalJitter = 0;
     616         393 :     unsigned nbDropNotNull = 0;
     617         393 :     auto vectSize = rtcpInfoVect.size();
     618             : 
     619         393 :     if (vectSize != 0) {
     620          20 :         for (const auto& it : rtcpInfoVect) {
     621          10 :             if (it.fraction_lost != 0) // Exclude null drop
     622           0 :                 nbDropNotNull++;
     623          10 :             totalLost += it.fraction_lost;
     624          10 :             totalJitter += ntohl(it.jitter);
     625             :         }
     626          10 :         rtcpi.packetLoss = nbDropNotNull ? (float) (100 * totalLost) / (256.0 * nbDropNotNull) : 0;
     627             :         // Jitter is expressed in timestamp unit -> convert to milliseconds
     628             :         // https://stackoverflow.com/questions/51956520/convert-jitter-from-rtp-timestamp-unit-to-millisseconds
     629          10 :         rtcpi.jitter = (totalJitter / vectSize / 90000.0f) * 1000;
     630          10 :         rtcpi.nb_sample = vectSize;
     631          10 :         rtcpi.latency = socketPair_->getLastLatency();
     632          10 :         return true;
     633             :     }
     634         383 :     return false;
     635         393 : }
     636             : 
     637             : bool
     638         393 : VideoRtpSession::check_RCTP_Info_REMB(uint64_t* br)
     639             : {
     640         393 :     auto rtcpInfoVect = socketPair_->getRtcpREMB();
     641             : 
     642         393 :     if (!rtcpInfoVect.empty()) {
     643         163 :         auto pkt = rtcpInfoVect.back();
     644         163 :         auto temp = cc->parseREMB(pkt);
     645         163 :         *br = (temp >> 10) | ((temp << 6) & 0xff00) | ((temp << 16) & 0x30000);
     646         163 :         return true;
     647             :     }
     648         230 :     return false;
     649         393 : }
     650             : 
     651             : void
     652         393 : VideoRtpSession::adaptQualityAndBitrate()
     653             : {
     654         393 :     setupVideoBitrateInfo();
     655             : 
     656             :     uint64_t br;
     657         393 :     if (check_RCTP_Info_REMB(&br)) {
     658         163 :         delayProcessing(br);
     659             :     }
     660             : 
     661         393 :     RTCPInfo rtcpi {};
     662         393 :     if (check_RCTP_Info_RR(rtcpi)) {
     663          10 :         dropProcessing(&rtcpi);
     664             :     }
     665         393 : }
     666             : 
     667             : void
     668          10 : VideoRtpSession::dropProcessing(RTCPInfo* rtcpi)
     669             : {
     670             :     // If bitrate has changed, let time to receive fresh RTCP packets
     671          10 :     auto now = clock::now();
     672          10 :     auto restartTimer = now - lastMediaRestart_;
     673          10 :     if (restartTimer < DELAY_AFTER_RESTART) {
     674           0 :         return;
     675             :     }
     676             : #ifndef __ANDROID__
     677             :     // Do nothing if jitter is more than 1 second
     678          10 :     if (rtcpi->jitter > 1000) {
     679           0 :         return;
     680             :     }
     681             : #endif
     682          10 :     auto pondLoss = getPonderateLoss(rtcpi->packetLoss);
     683          10 :     auto oldBitrate = videoBitrateInfo_.videoBitrateCurrent;
     684          10 :     int newBitrate = oldBitrate;
     685             : 
     686             :     // Fill histoLoss and histoJitter_ with samples
     687          10 :     if (restartTimer < DELAY_AFTER_RESTART + std::chrono::seconds(1)) {
     688           0 :         return;
     689             :     } else {
     690             :         // If ponderate drops are inferior to 10% that mean drop are not from congestion but from
     691             :         // network...
     692             :         // ... we can increase
     693          10 :         if (pondLoss >= 5.0f && rtcpi->packetLoss > 0.0f) {
     694           0 :             newBitrate *= 1.0f - rtcpi->packetLoss / 150.0f;
     695           0 :             histoLoss_.clear();
     696           0 :             lastMediaRestart_ = now;
     697           0 :             JAMI_DBG(
     698             :                 "[BandwidthAdapt] Detected transmission bandwidth overuse, decrease bitrate from "
     699             :                 "%u Kbps to %d Kbps, ratio %f (ponderate loss: %f%%, packet loss rate: %f%%)",
     700             :                 oldBitrate,
     701             :                 newBitrate,
     702             :                 (float) newBitrate / oldBitrate,
     703             :                 pondLoss,
     704             :                 rtcpi->packetLoss);
     705             :         }
     706             :     }
     707             : 
     708          10 :     setNewBitrate(newBitrate);
     709             : }
     710             : 
     711             : void
     712         163 : VideoRtpSession::delayProcessing(int br)
     713             : {
     714         163 :     int newBitrate = videoBitrateInfo_.videoBitrateCurrent;
     715         163 :     if (br == 0x6803)
     716           0 :         newBitrate *= 0.85f;
     717         163 :     else if (br == 0x7378) {
     718         163 :         auto now = clock::now();
     719         163 :         auto msSinceLastDecrease = std::chrono::duration_cast<std::chrono::milliseconds>(
     720         163 :             now - lastBitrateDecrease);
     721         163 :         auto increaseCoefficient = std::min(msSinceLastDecrease.count() / 600000.0f + 1.0f, 1.05f);
     722         163 :         newBitrate *= increaseCoefficient;
     723             :     } else
     724           0 :         return;
     725             : 
     726         163 :     setNewBitrate(newBitrate);
     727             : }
     728             : 
     729             : void
     730         173 : VideoRtpSession::setNewBitrate(unsigned int newBR)
     731             : {
     732         173 :     newBR = std::max(newBR, videoBitrateInfo_.videoBitrateMin);
     733         173 :     newBR = std::min(newBR, videoBitrateInfo_.videoBitrateMax);
     734             : 
     735         173 :     if (newBR < videoBitrateInfo_.videoBitrateCurrent)
     736           0 :         lastBitrateDecrease = clock::now();
     737             : 
     738         173 :     if (videoBitrateInfo_.videoBitrateCurrent != newBR) {
     739          32 :         videoBitrateInfo_.videoBitrateCurrent = newBR;
     740          32 :         storeVideoBitrateInfo();
     741             : 
     742             : #if __ANDROID__
     743             :         if (auto input_device = std::dynamic_pointer_cast<VideoInput>(videoLocal_))
     744             :             emitSignal<libjami::VideoSignal::SetBitrate>(input_device->getConfig().name, (int) newBR);
     745             : #endif
     746             : 
     747          32 :         if (sender_) {
     748          32 :             auto ret = sender_->setBitrate(newBR);
     749          32 :             if (ret == -1)
     750           0 :                 JAMI_ERR("Fail to access the encoder");
     751          32 :             else if (ret == 0)
     752           0 :                 restartSender();
     753             :         } else {
     754           0 :             JAMI_ERR("Fail to access the sender");
     755             :         }
     756             :     }
     757         173 : }
     758             : 
     759             : void
     760         858 : VideoRtpSession::setupVideoBitrateInfo()
     761             : {
     762         858 :     auto codecVideo = std::static_pointer_cast<jami::SystemVideoCodecInfo>(send_.codec);
     763         858 :     if (codecVideo) {
     764         543 :         videoBitrateInfo_ = {
     765         543 :             codecVideo->bitrate,
     766         543 :             codecVideo->minBitrate,
     767         543 :             codecVideo->maxBitrate,
     768         543 :             codecVideo->quality,
     769         543 :             codecVideo->minQuality,
     770         543 :             codecVideo->maxQuality,
     771         543 :             videoBitrateInfo_.cptBitrateChecking,
     772         543 :             videoBitrateInfo_.maxBitrateChecking,
     773         543 :             videoBitrateInfo_.packetLostThreshold,
     774             :         };
     775             :     } else {
     776             :         videoBitrateInfo_
     777         315 :             = {0, 0, 0, 0, 0, 0, 0, MAX_ADAPTATIVE_BITRATE_ITERATION, PACKET_LOSS_THRESHOLD};
     778             :     }
     779         858 : }
     780             : 
     781             : void
     782         712 : VideoRtpSession::storeVideoBitrateInfo()
     783             : {
     784         712 :     if (auto codecVideo = std::static_pointer_cast<jami::SystemVideoCodecInfo>(send_.codec)) {
     785         470 :         codecVideo->bitrate = videoBitrateInfo_.videoBitrateCurrent;
     786         470 :         codecVideo->quality = videoBitrateInfo_.videoQualityCurrent;
     787         712 :     }
     788         712 : }
     789             : 
     790             : void
     791         393 : VideoRtpSession::processRtcpChecker()
     792             : {
     793         393 :     adaptQualityAndBitrate();
     794         393 :     socketPair_->waitForRTCP(std::chrono::seconds(rtcp_checking_interval));
     795         393 : }
     796             : 
     797             : void
     798          31 : VideoRtpSession::attachRemoteRecorder(const MediaStream& ms)
     799             : {
     800          31 :     std::lock_guard lock(mutex_);
     801          31 :     if (!recorder_ || !receiveThread_)
     802           0 :         return;
     803          31 :     if (auto ob = recorder_->addStream(ms)) {
     804          31 :         receiveThread_->attach(ob);
     805             :     }
     806          31 : }
     807             : 
     808             : void
     809           0 : VideoRtpSession::attachLocalRecorder(const MediaStream& ms)
     810             : {
     811           0 :     std::lock_guard lock(mutex_);
     812           0 :     if (!recorder_ || !videoLocal_ || !Manager::instance().videoPreferences.getRecordPreview())
     813           0 :         return;
     814           0 :     if (auto ob = recorder_->addStream(ms)) {
     815           0 :         videoLocal_->attach(ob);
     816             :     }
     817           0 : }
     818             : 
     819             : void
     820           7 : VideoRtpSession::initRecorder()
     821             : {
     822           7 :     if (!recorder_)
     823           0 :         return;
     824           7 :     if (receiveThread_) {
     825           5 :         receiveThread_->setRecorderCallback([w=weak_from_this()](const MediaStream& ms) {
     826           1 :             Manager::instance().ioContext()->post([w=std::move(w), ms]() {
     827           1 :                 if (auto shared = w.lock())
     828           1 :                     shared->attachRemoteRecorder(ms);
     829           1 :             });
     830           1 :         });
     831             :     }
     832           7 :     if (videoLocal_ && !send_.onHold) {
     833           3 :         videoLocal_->setRecorderCallback([w=weak_from_this()](const MediaStream& ms) {
     834           0 :             Manager::instance().ioContext()->post([w=std::move(w), ms]() {
     835           0 :                 if (auto shared = w.lock())
     836           0 :                     shared->attachLocalRecorder(ms);
     837           0 :             });
     838           0 :         });
     839             :     }
     840             : }
     841             : 
     842             : void
     843         320 : VideoRtpSession::deinitRecorder()
     844             : {
     845         320 :     if (!recorder_)
     846           0 :         return;
     847         320 :     if (receiveThread_) {
     848         135 :         auto ms = receiveThread_->getInfo();
     849         135 :         if (auto ob = recorder_->getStream(ms.name)) {
     850           0 :             receiveThread_->detach(ob);
     851           0 :             recorder_->removeStream(ms);
     852             :         }
     853         135 :     }
     854         320 :     if (videoLocal_) {
     855           3 :         auto ms = videoLocal_->getInfo();
     856           3 :         if (auto ob = recorder_->getStream(ms.name)) {
     857           0 :             videoLocal_->detach(ob);
     858           0 :             recorder_->removeStream(ms);
     859             :         }
     860           3 :     }
     861             : }
     862             : 
     863             : void
     864         150 : VideoRtpSession::setChangeOrientationCallback(std::function<void(int)> cb)
     865             : {
     866         150 :     changeOrientationCallback_ = std::move(cb);
     867         150 :     if (sender_)
     868           9 :         sender_->setChangeOrientationCallback(changeOrientationCallback_);
     869         150 : }
     870             : 
     871             : float
     872          10 : VideoRtpSession::getPonderateLoss(float lastLoss)
     873             : {
     874          10 :     float pond = 0.0f, pondLoss = 0.0f, totalPond = 0.0f;
     875          10 :     constexpr float coefficient_a = -1 / 100.0f;
     876          10 :     constexpr float coefficient_b = 100.0f;
     877             : 
     878          10 :     auto now = clock::now();
     879             : 
     880          10 :     histoLoss_.emplace_back(now, lastLoss);
     881             : 
     882          20 :     for (auto it = histoLoss_.begin(); it != histoLoss_.end();) {
     883          10 :         auto delay = std::chrono::duration_cast<std::chrono::milliseconds>(now - it->first);
     884             : 
     885             :         // 1ms      -> 100%
     886             :         // 2000ms   -> 80%
     887          10 :         if (delay <= EXPIRY_TIME_RTCP) {
     888          10 :             if (it->second == 0.0f)
     889          10 :                 pond = 20.0f; // Reduce weight of null drop
     890             :             else
     891           0 :                 pond = std::min(delay.count() * coefficient_a + coefficient_b, 100.0f);
     892          10 :             totalPond += pond;
     893          10 :             pondLoss += it->second * pond;
     894          10 :             ++it;
     895             :         } else
     896           0 :             it = histoLoss_.erase(it);
     897             :     }
     898          10 :     if (totalPond == 0)
     899           0 :         return 0.0f;
     900             : 
     901          10 :     return pondLoss / totalPond;
     902             : }
     903             : 
     904             : void
     905        5240 : VideoRtpSession::delayMonitor(int gradient, int deltaT)
     906             : {
     907        5240 :     float estimation = cc->kalmanFilter(gradient);
     908        5240 :     float thresh = cc->get_thresh();
     909             : 
     910        5240 :     cc->update_thresh(estimation, deltaT);
     911             : 
     912        5240 :     BandwidthUsage bwState = cc->get_bw_state(estimation, thresh);
     913        5240 :     auto now = clock::now();
     914             : 
     915        5240 :     if (bwState == BandwidthUsage::bwOverusing) {
     916           0 :         auto remb_timer_dec = now - last_REMB_dec_;
     917           0 :         if ((not remb_dec_cnt_) or (remb_timer_dec > DELAY_AFTER_REMB_DEC)) {
     918           0 :             last_REMB_dec_ = now;
     919           0 :             remb_dec_cnt_ = 0;
     920             :         }
     921             : 
     922             :         // Limit REMB decrease to MAX_REMB_DEC every DELAY_AFTER_REMB_DEC ms
     923           0 :         if (remb_dec_cnt_ < MAX_REMB_DEC && remb_timer_dec < DELAY_AFTER_REMB_DEC) {
     924           0 :             remb_dec_cnt_++;
     925           0 :             JAMI_WARN("[BandwidthAdapt] Detected reception bandwidth overuse");
     926           0 :             uint8_t* buf = nullptr;
     927           0 :             uint64_t br = 0x6803; // Decrease 3
     928           0 :             auto v = cc->createREMB(br);
     929           0 :             buf = &v[0];
     930           0 :             socketPair_->writeData(buf, v.size());
     931           0 :             last_REMB_inc_ = clock::now();
     932           0 :         }
     933        5240 :     } else if (bwState == BandwidthUsage::bwNormal) {
     934        3952 :         auto remb_timer_inc = now - last_REMB_inc_;
     935        3952 :         if (remb_timer_inc > DELAY_AFTER_REMB_INC) {
     936         163 :             uint8_t* buf = nullptr;
     937         163 :             uint64_t br = 0x7378; // INcrease
     938         163 :             auto v = cc->createREMB(br);
     939         163 :             buf = &v[0];
     940         163 :             socketPair_->writeData(buf, v.size());
     941         163 :             last_REMB_inc_ = clock::now();
     942         163 :         }
     943             :     }
     944        5240 : }
     945             : } // namespace video
     946             : } // namespace jami

Generated by: LCOV version 1.14