33#include <dhtnet/ice_socket.h>
34#include <asio/post.hpp>
35#include <asio/io_context.hpp>
53 const string& streamId,
55 const std::shared_ptr<MediaRecorder>&
rec)
58 , videoBitrateInfo_ {}
59 , rtcpCheckerThread_([] {
return true; }, [
this] { processRtcpChecker(); }, [] {})
60 , cc(std::make_unique<CongestionControl>())
63 setupVideoBitrateInfo();
64 JAMI_LOG(
"[{:p}] Video RTP session created for call {} (recorder {:p})",
74 JAMI_LOG(
"[{:p}] Video RTP session destroyed", fmt::ptr(
this));
80 return videoBitrateInfo_;
96 setupVideoBitrateInfo();
102 cbKeyFrameRequest_ = std::move(
cb);
106VideoRtpSession::startSender()
108 std::lock_guard lock(
mutex_);
110 JAMI_DBG(
"[%p] Start video RTP sender: input [%s] - muted [%s]",
113 send_.hold ?
"YES" :
"NO");
117 JAMI_WARN(
"[%p] Transport not set yet",
this);
124 videoLocal_->detach(sender_.get());
126 videoMixer_->detach(sender_.get());
127 JAMI_WARN(
"[%p] Restarting video sender",
this);
130 if (
not conference_) {
133 videoLocal_->setRecorderCallback([w =
weak_from_this()](
const MediaStream& ms) {
135 if (
auto shared = w.lock())
136 shared->attachLocalRecorder(ms);
139 auto newParams = videoLocal_->getParams();
144 JAMI_ERR(
"[%p] No valid new video parameters",
this);
147 }
catch (
const std::exception&
e) {
148 JAMI_ERR(
"Exception during retrieving video parameters: %s",
e.what());
156#if (defined(__ANDROID__) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS))
157 videoLocal_->setupSink(localVideoParams_.
width, localVideoParams_.
height);
174 && localVideoParams_.
format !=
"lavfi");
182 MediaStream ms = !videoMixer_
183 ? MediaStream(
"video sender",
185 1 /
static_cast<rational<int>
>(localVideoParams_.
framerate),
186 localVideoParams_.
width == 0 ? 1080
188 localVideoParams_.height == 0
193 : videoMixer_->getStream(
"Video Sender");
196 if (changeOrientationCallback_)
197 sender_->setChangeOrientationCallback(changeOrientationCallback_);
199 socketPair_->setPacketLossCallback([
this]() { cbKeyFrameRequest_(); });
201 }
catch (
const MediaEncoderException&
e) {
205 lastMediaRestart_ = clock::now();
206 last_REMB_inc_ = clock::now();
207 last_REMB_dec_ = clock::now();
209 rtcpCheckerThread_.
start();
211 rtcpCheckerThread_.
join();
221 std::lock_guard lock(
mutex_);
232 setupVideoPipeline();
240 JAMI_DBG(
"[%p] Stop video RTP sender: input [%s] - muted [%s]",
243 send_.hold ?
"YES" :
"NO");
247 videoLocal_->detach(sender_.get());
249 videoMixer_->detach(sender_.get());
263VideoRtpSession::startReceiver()
267 JAMI_DBG(
"[%p] Starting receiver",
this);
271 JAMI_WARN(
"[%p] Already has a receiver, restarting",
this);
281 receiveThread_->startLoop();
282 receiveThread_->setRequestKeyFrameCallback([
this]() { cbKeyFrameRequest_(); });
283 receiveThread_->setRotation(rotation_.load());
284 if (videoMixer_
and conference_) {
293 receiveThread_->setRecorderCallback([w =
weak_from_this()](
const MediaStream& ms) {
295 if (
auto shared = w.lock())
296 shared->attachRemoteRecorder(ms);
300 JAMI_DBG(
"[%p] Video receiver disabled",
this);
301 if (videoMixer_
and conference_) {
305 if (receiveThread_) {
308 receiveThread_->detach(videoMixer_.get());
310 videoMixer_->setActiveStream(
audioId_);
329 JAMI_DBG(
"[%p] Stopping receiver",
this);
331 if (
not receiveThread_)
339 receiveThread_->detach(videoMixer_.get());
341 videoMixer_->setActiveStream(
audioId);
355 auto ms = receiveThread_->getInfo();
357 receiveThread_->detach(
ob);
362 receiveThread_->stopLoop();
363 receiveThread_->stopSink();
369 std::lock_guard lock(
mutex_);
391 last_REMB_inc_ = clock::now();
392 last_REMB_dec_ = clock::now();
402 }
catch (
const std::runtime_error&
e) {
403 JAMI_ERR(
"[%p] Socket creation failed: %s",
this,
e.what());
418 setupVideoPipeline();
425 std::lock_guard lock(
mutex_);
433 rtcpCheckerThread_.
join();
440 storeVideoBitrateInfo();
449 std::lock_guard lock(
mutex_);
454 JAMI_DBG(
"[%p] Local already %s",
this,
mute ?
"muted" :
"un-muted");
460 auto ms = videoLocal_->getInfo();
462 videoLocal_->detach(
ob);
475 JAMI_DBG(
"[%p] Remote already %s",
this,
mute ?
"muted" :
"un-muted");
480 if (receiveThread_) {
481 auto ms = receiveThread_->getInfo();
483 receiveThread_->detach(
ob);
499 std::lock_guard lock(
mutex_);
505 sender_->forceKeyFrame();
512 rotation_.store(rotation);
514 receiveThread_->setRotation(rotation);
518VideoRtpSession::setupVideoPipeline()
522 JAMI_DBG(
"[%p] Setup video pipeline on local capture device",
this);
523 videoLocal_->attach(sender_.get());
539 videoLocal_->detach(sender_.get());
541 videoMixer_->attach(sender_.get());
547 if (receiveThread_) {
548 receiveThread_->stopSink();
560 std::lock_guard lock(
mutex_);
581 std::lock_guard lock(
mutex_);
586 JAMI_DEBUG(
"[conf:{}] Exiting conference", conference_->getConfId());
590 videoMixer_->detach(sender_.get());
592 if (receiveThread_) {
594 videoMixer_->detachVideo(receiveThread_.get());
595 receiveThread_->startSink();
603 conference_ =
nullptr;
617 if (
it.fraction_lost != 0)
625 rtcpi.jitter =
static_cast<unsigned int>(
635VideoRtpSession::check_RCTP_Info_REMB(
uint64_t*
br)
641 auto temp = cc->parseREMB(
pkt);
642 *
br = (
temp >> 10) | ((
temp << 6) & 0xff00) | ((
temp << 16) & 0x30000);
649VideoRtpSession::adaptQualityAndBitrate()
651 setupVideoBitrateInfo();
654 if (check_RCTP_Info_REMB(&
br)) {
655 delayProcessing(
static_cast<int>(
br));
659 if (check_RCTP_Info_RR(
rtcpi)) {
660 dropProcessing(&
rtcpi);
665VideoRtpSession::dropProcessing(RTCPInfo*
rtcpi)
668 auto now = clock::now();
675 if (
rtcpi->jitter > 1000) {
693 lastMediaRestart_ =
now;
694 JAMI_DBG(
"[BandwidthAdapt] Detected transmission bandwidth overuse, decrease bitrate from "
695 "%u Kbps to %d Kbps, ratio %f (ponderate loss: %f%%, packet loss rate: %f%%)",
708VideoRtpSession::delayProcessing(
int br)
713 else if (
br == 0x7378) {
714 auto now = clock::now();
715 auto msSinceLastDecrease = std::chrono::duration_cast<std::chrono::milliseconds>(
now - lastBitrateDecrease);
725VideoRtpSession::setNewBitrate(
unsigned int newBR)
731 lastBitrateDecrease = clock::now();
735 storeVideoBitrateInfo();
738 if (
auto input_device = std::dynamic_pointer_cast<VideoInput>(videoLocal_))
743 auto ret = sender_->setBitrate(
newBR);
745 JAMI_ERR(
"Fail to access the encoder");
749 JAMI_ERR(
"Fail to access the sender");
755VideoRtpSession::setupVideoBitrateInfo()
759 videoBitrateInfo_ = {
771 videoBitrateInfo_ = {0, 0, 0, 0, 0, 0, 0, MAX_ADAPTATIVE_BITRATE_ITERATION, PACKET_LOSS_THRESHOLD};
776VideoRtpSession::storeVideoBitrateInfo()
785VideoRtpSession::processRtcpChecker()
787 adaptQualityAndBitrate();
788 socketPair_->waitForRTCP(std::chrono::seconds(rtcp_checking_interval));
792VideoRtpSession::attachRemoteRecorder(
const MediaStream& ms)
794 std::lock_guard lock(
mutex_);
798 receiveThread_->attach(
ob);
803VideoRtpSession::attachLocalRecorder(
const MediaStream& ms)
805 std::lock_guard lock(
mutex_);
809 videoLocal_->attach(
ob);
818 if (receiveThread_) {
821 if (
auto shared = w.lock())
822 shared->attachRemoteRecorder(ms);
829 if (
auto shared = w.lock())
830 shared->attachLocalRecorder(ms);
841 if (receiveThread_) {
842 auto ms = receiveThread_->getInfo();
844 receiveThread_->detach(
ob);
849 auto ms = videoLocal_->getInfo();
851 videoLocal_->detach(
ob);
860 changeOrientationCallback_ = std::move(
cb);
862 sender_->setChangeOrientationCallback(changeOrientationCallback_);
866VideoRtpSession::getPonderateLoss(
float lastLoss)
872 auto now = clock::now();
876 for (
auto it = histoLoss_.begin();
it != histoLoss_.end();) {
877 auto delay = std::chrono::duration_cast<std::chrono::milliseconds>(
now -
it->first);
882 if (
it->second == 0.0f)
890 it = histoLoss_.erase(
it);
902 float thresh = cc->get_thresh();
907 auto now = clock::now();
912 last_REMB_dec_ =
now;
919 JAMI_WARN(
"[BandwidthAdapt] Detected reception bandwidth overuse");
922 auto v = cc->createREMB(
br);
925 last_REMB_inc_ = clock::now();
932 auto v = cc->createREMB(
br);
935 last_REMB_inc_ = clock::now();
std::string getSrtpKeyInfo() const
std::string getCryptoSuite() const
static LIBJAMI_TEST_EXPORT Manager & instance()
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_DEBUG(formatstr,...)
#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)
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