39#include <dhtnet/ice_socket.h>
41#include <asio/io_context.hpp>
61 const string& streamId,
63 const std::shared_ptr<MediaRecorder>&
rec)
66 , videoBitrateInfo_ {}
67 , rtcpCheckerThread_([] {
return true; }, [
this] { processRtcpChecker(); }, [] {})
68 , cc(std::make_unique<CongestionControl>())
71 setupVideoBitrateInfo();
72 JAMI_LOG(
"[{:p}] Video RTP session created for call {} (recorder {:p})", fmt::ptr(
this), callId_, fmt::ptr(recorder_));
79 JAMI_LOG(
"[{:p}] Video RTP session destroyed", fmt::ptr(
this));
85 return videoBitrateInfo_;
101 setupVideoBitrateInfo();
107 cbKeyFrameRequest_ = std::move(
cb);
111VideoRtpSession::startSender()
113 std::lock_guard lock(
mutex_);
115 JAMI_DBG(
"[%p] Start video RTP sender: input [%s] - muted [%s]",
118 send_.onHold ?
"YES" :
"NO");
122 JAMI_WARN(
"[%p] Transport not set yet",
this);
129 videoLocal_->detach(sender_.get());
131 videoMixer_->detach(sender_.get());
132 JAMI_WARN(
"[%p] Restarting video sender",
this);
135 if (
not conference_) {
138 videoLocal_->setRecorderCallback(
141 if (
auto shared = w.lock())
142 shared->attachLocalRecorder(ms);
145 auto newParams = videoLocal_->getParams();
151 JAMI_ERR(
"[%p] No valid new video parameters",
this);
154 }
catch (
const std::exception&
e) {
155 JAMI_ERR(
"Exception during retrieving video parameters: %s",
e.what());
163#if (defined(__ANDROID__) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS))
164 videoLocal_->setupSink(localVideoParams_.
width, localVideoParams_.
height);
190 ? MediaStream(
"video sender",
192 1 /
static_cast<rational<int>
>(localVideoParams_.
framerate),
193 localVideoParams_.
width == 0 ? 1080u : localVideoParams_.width,
194 localVideoParams_.height == 0 ? 720u : localVideoParams_.height,
197 : videoMixer_->getStream(
"Video Sender");
198 sender_.reset(
new VideoSender(
200 if (changeOrientationCallback_)
201 sender_->setChangeOrientationCallback(changeOrientationCallback_);
203 socketPair_->setPacketLossCallback([
this]() { cbKeyFrameRequest_(); });
205 }
catch (
const MediaEncoderException&
e) {
209 lastMediaRestart_ = clock::now();
210 last_REMB_inc_ = clock::now();
211 last_REMB_dec_ = clock::now();
213 rtcpCheckerThread_.
start();
215 rtcpCheckerThread_.
join();
225 std::lock_guard lock(
mutex_);
236 setupVideoPipeline();
244 JAMI_DBG(
"[%p] Stop video RTP sender: input [%s] - muted [%s]",
247 send_.onHold ?
"YES" :
"NO");
251 videoLocal_->detach(sender_.get());
253 videoMixer_->detach(sender_.get());
267VideoRtpSession::startReceiver()
271 JAMI_DBG(
"[%p] Starting receiver",
this);
275 JAMI_WARN(
"[%p] Already has a receiver, restarting",
this);
276 receiveThread_.reset(
286 receiveThread_->startLoop();
287 receiveThread_->setRequestKeyFrameCallback([
this]() { cbKeyFrameRequest_(); });
288 receiveThread_->setRotation(rotation_.load());
289 if (videoMixer_
and conference_) {
298 receiveThread_->setRecorderCallback([w=
weak_from_this()](
const MediaStream& ms) {
300 if (
auto shared = w.lock())
301 shared->attachRemoteRecorder(ms);
305 JAMI_DBG(
"[%p] Video receiver disabled",
this);
306 if (receiveThread_
and videoMixer_
and conference_) {
312 receiveThread_->detach(videoMixer_.get());
314 videoMixer_->setActiveStream(
audioId_);
326 JAMI_DBG(
"[%p] Stopping receiver",
this);
328 if (
not receiveThread_)
336 receiveThread_->detach(videoMixer_.get());
338 videoMixer_->setActiveStream(
audioId);
352 auto ms = receiveThread_->getInfo();
354 receiveThread_->detach(
ob);
359 receiveThread_->stopLoop();
360 receiveThread_->stopSink();
366 std::lock_guard lock(
mutex_);
388 last_REMB_inc_ = clock::now();
389 last_REMB_dec_ = clock::now();
400 }
catch (
const std::runtime_error&
e) {
401 JAMI_ERR(
"[%p] Socket creation failed: %s",
this,
e.what());
416 setupVideoPipeline();
423 std::lock_guard lock(
mutex_);
431 rtcpCheckerThread_.
join();
438 storeVideoBitrateInfo();
447 std::lock_guard lock(
mutex_);
452 JAMI_DBG(
"[%p] Local already %s",
this,
mute ?
"muted" :
"un-muted");
458 auto ms = videoLocal_->getInfo();
460 videoLocal_->detach(
ob);
473 JAMI_DBG(
"[%p] Remote already %s",
this,
mute ?
"muted" :
"un-muted");
478 if (receiveThread_) {
479 auto ms = receiveThread_->getInfo();
481 receiveThread_->detach(
ob);
497 std::lock_guard lock(
mutex_);
503 sender_->forceKeyFrame();
510 rotation_.store(rotation);
512 receiveThread_->setRotation(rotation);
516VideoRtpSession::setupVideoPipeline()
520 JAMI_DBG(
"[%p] Setup video pipeline on local capture device",
this);
521 videoLocal_->attach(sender_.get());
532 JAMI_DBG(
"[%p] Setup video sender pipeline on conference %s for call %s",
540 videoLocal_->detach(sender_.get());
542 videoMixer_->attach(sender_.get());
547 JAMI_DBG(
"[%p] Setup video receiver pipeline on conference %s for call %s",
551 if (receiveThread_) {
552 receiveThread_->stopSink();
564 std::lock_guard lock(
mutex_);
585 std::lock_guard lock(
mutex_);
590 JAMI_DBG(
"[%p] exitConference (conf: %s)",
this, conference_->getConfId().c_str());
594 videoMixer_->detach(sender_.get());
596 if (receiveThread_) {
598 videoMixer_->detachVideo(receiveThread_.get());
599 receiveThread_->startSink();
607 conference_ =
nullptr;
621 if (
it.fraction_lost != 0)
638VideoRtpSession::check_RCTP_Info_REMB(
uint64_t*
br)
644 auto temp = cc->parseREMB(
pkt);
645 *
br = (
temp >> 10) | ((
temp << 6) & 0xff00) | ((
temp << 16) & 0x30000);
652VideoRtpSession::adaptQualityAndBitrate()
654 setupVideoBitrateInfo();
657 if (check_RCTP_Info_REMB(&
br)) {
662 if (check_RCTP_Info_RR(
rtcpi)) {
663 dropProcessing(&
rtcpi);
668VideoRtpSession::dropProcessing(RTCPInfo*
rtcpi)
671 auto now = clock::now();
678 if (
rtcpi->jitter > 1000) {
696 lastMediaRestart_ =
now;
698 "[BandwidthAdapt] Detected transmission bandwidth overuse, decrease bitrate from "
699 "%u Kbps to %d Kbps, ratio %f (ponderate loss: %f%%, packet loss rate: %f%%)",
712VideoRtpSession::delayProcessing(
int br)
717 else if (
br == 0x7378) {
718 auto now = clock::now();
720 now - lastBitrateDecrease);
730VideoRtpSession::setNewBitrate(
unsigned int newBR)
736 lastBitrateDecrease = clock::now();
740 storeVideoBitrateInfo();
743 if (
auto input_device = std::dynamic_pointer_cast<VideoInput>(videoLocal_))
748 auto ret = sender_->setBitrate(
newBR);
750 JAMI_ERR(
"Fail to access the encoder");
754 JAMI_ERR(
"Fail to access the sender");
760VideoRtpSession::setupVideoBitrateInfo()
764 videoBitrateInfo_ = {
777 = {0, 0, 0, 0, 0, 0, 0, MAX_ADAPTATIVE_BITRATE_ITERATION, PACKET_LOSS_THRESHOLD};
782VideoRtpSession::storeVideoBitrateInfo()
791VideoRtpSession::processRtcpChecker()
793 adaptQualityAndBitrate();
794 socketPair_->waitForRTCP(std::chrono::seconds(rtcp_checking_interval));
798VideoRtpSession::attachRemoteRecorder(
const MediaStream& ms)
800 std::lock_guard lock(
mutex_);
804 receiveThread_->attach(
ob);
809VideoRtpSession::attachLocalRecorder(
const MediaStream& ms)
811 std::lock_guard lock(
mutex_);
815 videoLocal_->attach(
ob);
824 if (receiveThread_) {
827 if (
auto shared = w.lock())
828 shared->attachRemoteRecorder(ms);
835 if (
auto shared = w.lock())
836 shared->attachLocalRecorder(ms);
847 if (receiveThread_) {
848 auto ms = receiveThread_->getInfo();
850 receiveThread_->detach(
ob);
855 auto ms = videoLocal_->getInfo();
857 videoLocal_->detach(
ob);
866 changeOrientationCallback_ = std::move(
cb);
868 sender_->setChangeOrientationCallback(changeOrientationCallback_);
872VideoRtpSession::getPonderateLoss(
float lastLoss)
878 auto now = clock::now();
882 for (
auto it = histoLoss_.begin();
it != histoLoss_.end();) {
883 auto delay = std::chrono::duration_cast<std::chrono::milliseconds>(
now -
it->first);
888 if (
it->second == 0.0f)
896 it = histoLoss_.erase(
it);
908 float thresh = cc->get_thresh();
913 auto now = clock::now();
918 last_REMB_dec_ =
now;
925 JAMI_WARN(
"[BandwidthAdapt] Detected reception bandwidth overuse");
928 auto v = cc->createREMB(
br);
931 last_REMB_inc_ = clock::now();
938 auto v = cc->createREMB(
br);
941 last_REMB_inc_ = clock::now();
std::string getSrtpKeyInfo() const
std::string getCryptoSuite() const
static LIBJAMI_TEST_EXPORT Manager & instance()
std::shared_ptr< asio::io_context > ioContext() const
std::string getRemoteRtpUri() const
std::recursive_mutex mutex_
const std::string callId_
virtual void updateMedia(const MediaDescription &send, const MediaDescription &receive)
MediaDescription receive_
const std::string streamId_
std::function< void(MediaType, bool)> onSuccessfulSetup_
std::unique_ptr< SocketPair > socketPair_
std::shared_ptr< MediaRecorder > recorder_
bool isRunning() const noexcept
void setRotation(int rotation)
Set video orientation.
void initRecorder() override
const VideoBitrateInfo & getVideoBitrateInfo()
void deinitRecorder() override
void setMuted(bool mute, Direction dir=Direction::SEND) override
void enterConference(Conference &conference)
void updateMedia(const MediaDescription &send, const MediaDescription &receive) override
Setup internal VideoBitrateInfo structure from media descriptors.
void setRequestKeyFrameCallback(std::function< void(void)> cb)
void restartSender() override
void setChangeOrientationCallback(std::function< void(int)> cb)
VideoRtpSession(const std::string &callId, const std::string &streamId, const DeviceParams &localVideoParams, const std::shared_ptr< MediaRecorder > &rec)
void start(std::unique_ptr< dhtnet::IceSocket > rtp_sock, std::unique_ptr< dhtnet::IceSocket > rtcp_sock) override
#define JAMI_LOG(formatstr,...)
constexpr auto DELAY_AFTER_REMB_DEC
constexpr auto DELAY_AFTER_REMB_INC
constexpr auto DELAY_AFTER_RESTART
constexpr auto EXPIRY_TIME_RTCP
static constexpr unsigned MAX_REMB_DEC
void emitSignal(Args... args)
static constexpr auto NEWPARAMS_TIMEOUT
void string_replace(std::string &str, const std::string &from, const std::string &to)
Specific VoIPLink for SIP (SIP core for incoming and outgoing events).
DeviceParams Parameters used by MediaDecoder and MediaEncoder to open a LibAV device/stream.
rational< double > framerate
static constexpr unsigned DEFAULT_CODEC_QUALITY
static constexpr unsigned DEFAULT_MAX_BITRATE
static constexpr unsigned DEFAULT_NO_QUALITY
static constexpr unsigned DEFAULT_VIDEO_BITRATE
unsigned cptBitrateChecking
float packetLostThreshold
unsigned videoBitrateCurrent
unsigned maxBitrateChecking
unsigned videoQualityCurrent