diff --git a/src/media/media_codec.h b/src/media/media_codec.h index 256448757797520b691ca40214e0aa5b5ee4e8f9..489ee7e6a9f49b7e5c9ea8d0ea2d04a4ed9463a7 100644 --- a/src/media/media_codec.h +++ b/src/media/media_codec.h @@ -60,7 +60,7 @@ struct SystemCodecInfo { static constexpr unsigned DEFAULT_CODEC_QUALITY {30}; #ifdef ENABLE_VIDEO - static constexpr unsigned DEFAULT_H264_MIN_QUALITY {35}; + static constexpr unsigned DEFAULT_H264_MIN_QUALITY {40}; static constexpr unsigned DEFAULT_H264_MAX_QUALITY {20}; static constexpr unsigned DEFAULT_VP8_MIN_QUALITY {50}; static constexpr unsigned DEFAULT_VP8_MAX_QUALITY {20}; @@ -70,8 +70,8 @@ struct SystemCodecInfo // indicates that the codec does not use quality factor static constexpr unsigned DEFAULT_NO_QUALITY {0}; - static constexpr unsigned DEFAULT_MIN_BITRATE {250}; - static constexpr unsigned DEFAULT_MAX_BITRATE {3000}; + static constexpr unsigned DEFAULT_MIN_BITRATE {220}; + static constexpr unsigned DEFAULT_MAX_BITRATE {6000}; SystemCodecInfo(unsigned avcodecId, const std::string& name, const std::string& libName, MediaType mediaType, diff --git a/src/media/media_encoder.cpp b/src/media/media_encoder.cpp index bedecd9099b1dee391a4c9e922f3500992bc3788..e6722717a96c379709e41c6ad500e85754d04b1e 100644 --- a/src/media/media_encoder.cpp +++ b/src/media/media_encoder.cpp @@ -52,6 +52,9 @@ extern "C" { namespace jami { +constexpr double LOGREG_PARAM_A {114.40432}; +constexpr double LOGREG_PARAM_B {-6.049181}; + MediaEncoder::MediaEncoder() : outputCtx_(avformat_alloc_context()) {} @@ -218,9 +221,9 @@ MediaEncoder::initStream(const SystemCodecInfo& systemCodecInfo, AVBufferRef* fr } #endif - auto maxBitrate = 1000 * std::atoi(libav_utils::getDictValue(options_, "max_rate")); - auto bufSize = 2 * maxBitrate; // as recommended (TODO: make it customizable) - auto crf = std::atoi(libav_utils::getDictValue(options_, "crf")); + uint64_t maxBitrate = 1000 * std::atoi(libav_utils::getDictValue(options_, "max_rate")); + uint8_t crf = (uint8_t) std::round(LOGREG_PARAM_A + log(pow(maxBitrate, LOGREG_PARAM_B))); // CRF = A + B*ln(maxBitrate) + uint64_t bufSize = 2 * maxBitrate; /* let x264 preset override our encoder settings */ if (systemCodecInfo.avcodecId == AV_CODEC_ID_H264) { @@ -245,7 +248,7 @@ MediaEncoder::initStream(const SystemCodecInfo& systemCodecInfo, AVBufferRef* fr if (crf == SystemCodecInfo::DEFAULT_NO_QUALITY) crf = 30; // good value for H264-720p@30 JAMI_DBG("H264 encoder setup: crf=%u, maxrate=%u, bufsize=%u", crf, maxBitrate, bufSize); - + libav_utils::setDictValue(&options_, "crf", std::to_string(crf)); av_opt_set_int(encoderCtx, "crf", crf, AV_OPT_SEARCH_CHILDREN); encoderCtx->rc_buffer_size = bufSize; encoderCtx->rc_max_rate = maxBitrate; @@ -651,7 +654,7 @@ MediaEncoder::extractProfileLevelID(const std::string ¶meters, // From RFC3984: // If no profile-level-id is present, the Baseline Profile without // additional constraints at Level 1 MUST be implied. - ctx->profile = FF_PROFILE_H264_BASELINE; + ctx->profile = FF_PROFILE_H264_CONSTRAINED_BASELINE; ctx->level = 0x0d; // ctx->level = 0x0d; // => 13 aka 1.3 if (parameters.empty()) diff --git a/src/media/socket_pair.cpp b/src/media/socket_pair.cpp index 2fe7fb068033a2b5861a4a9911d1b561e562565d..31065c8a0ec8a5ce9c0b2abaabe57237426789dd 100644 --- a/src/media/socket_pair.cpp +++ b/src/media/socket_pair.cpp @@ -59,6 +59,24 @@ extern "C" { #include <fcntl.h> #endif + +// Swap 2 byte, 16 bit values: +#define Swap2Bytes(val) \ + ( (((val) >> 8) & 0x00FF) | (((val) << 8) & 0xFF00) ) + +// Swap 4 byte, 32 bit values: +#define Swap4Bytes(val) \ + ( (((val) >> 24) & 0x000000FF) | (((val) >> 8) & 0x0000FF00) | \ + (((val) << 8) & 0x00FF0000) | (((val) << 24) & 0xFF000000) ) + +// Swap 8 byte, 64 bit values: +#define Swap8Bytes(val) \ + ( (((val) >> 56) & 0x00000000000000FF) | (((val) >> 40) & 0x000000000000FF00) | \ + (((val) >> 24) & 0x0000000000FF0000) | (((val) >> 8) & 0x00000000FF000000) | \ + (((val) << 8) & 0x000000FF00000000) | (((val) << 24) & 0x0000FF0000000000) | \ + (((val) << 40) & 0x00FF000000000000) | (((val) << 56) & 0xFF00000000000000) ) + + namespace jami { static constexpr int NET_POLL_TIMEOUT = 100; /* poll() timeout in ms */ @@ -195,6 +213,15 @@ SocketPair::~SocketPair() closeSockets(); } +bool +SocketPair::waitForRTCP(std::chrono::seconds interval) +{ + std::unique_lock<std::mutex> lock(rtcpInfo_mutex_); + return cvRtcpPacketReadyToRead_.wait_for(lock, interval, [this]{ + return interrupted_ or not listRtcpHeader_.empty(); + }); +} + void SocketPair::saveRtcpPacket(uint8_t* buf, size_t len) { @@ -212,19 +239,15 @@ SocketPair::saveRtcpPacket(uint8_t* buf, size_t len) } listRtcpHeader_.push_back(*header); + + cvRtcpPacketReadyToRead_.notify_one(); } -std::vector<rtcpRRHeader> +std::list<rtcpRRHeader> SocketPair::getRtcpInfo() { - decltype(listRtcpHeader_) l; - { - std::lock_guard<std::mutex> lock(rtcpInfo_mutex_); - if (listRtcpHeader_.empty()) - return {}; - l = std::move(listRtcpHeader_); - } - return {std::make_move_iterator(l.begin()), std::make_move_iterator(l.end())}; + std::lock_guard<std::mutex> lock(rtcpInfo_mutex_); + return std::move(listRtcpHeader_); } void @@ -406,6 +429,14 @@ SocketPair::readCallback(uint8_t* buf, int buf_size) int len = 0; bool fromRTCP = false; + auto header = reinterpret_cast<rtcpRRHeader*>(buf); + if(header->pt == 201) //201 = RR PT + { + lastDLSR_ = Swap4Bytes(header->dlsr); + //JAMI_WARN("Read RR, lastDLSR : %d", lastDLSR_); + lastRR_time = std::chrono::steady_clock::now(); + } + // Priority to RTCP as its less invasive in bandwidth if (datatype & static_cast<int>(DataType::RTCP)) { len = readRtcpData(buf, buf_size); @@ -478,6 +509,8 @@ SocketPair::writeCallback(uint8_t* buf, int buf_size) { int ret; bool isRTCP = RTP_PT_IS_RTCP(buf[1]); + unsigned int ts_LSB, ts_MSB; + double currentSRTS, currentLatency; // Encrypt? if (not isRTCP and srtpContext_ and srtpContext_->srtp_out.aes) { @@ -505,15 +538,46 @@ SocketPair::writeCallback(uint8_t* buf, int buf_size) ret = writeData(buf, buf_size); } while (ret < 0 and errno == EAGAIN); + if(buf[1] == 200) //Sender Report + { + auto header = reinterpret_cast<rtcpSRHeader*>(buf); + ts_LSB = Swap4Bytes(header->timestampLSB); + ts_MSB = Swap4Bytes(header->timestampMSB); + + currentSRTS = ts_MSB + (ts_LSB / pow(2,32)); + + if(lastSRTS_ != 0 && lastDLSR_ != 0) + { + if (histoLatency_.size() >= MAX_LIST_SIZE) + histoLatency_.pop_front(); + + currentLatency = (currentSRTS - lastSRTS_) / 2; + //JAMI_WARN("Current Latency : %f from sender %X", currentLatency, header->ssrc); + histoLatency_.push_back(currentLatency); + } + + lastSRTS_ = currentSRTS; + + // JAMI_WARN("SENDING NEW RTCP SR !! "); + + } + else if(buf[1] == 201) //Receiver Report + { + //auto header = reinterpret_cast<rtcpRRHeader*>(buf); + //JAMI_WARN("SENDING NEW RTCP RR !! "); + + } + return ret < 0 ? -errno : ret; } -bool -SocketPair::rtcpPacketLossDetected() const +double +SocketPair::getLastLatency() { - // set to false on checking packet loss to avoid burst of keyframe requests - bool b = true; - return rtcpPacketLoss_.compare_exchange_strong(b, false); + if(not histoLatency_.empty()) + return histoLatency_.back(); + else + return -1; } } // namespace jami diff --git a/src/media/socket_pair.h b/src/media/socket_pair.h index f761c270e41ba4067a257020a44282677a100239..e620202e9fd6027875d37b870c740d3363d433ad 100644 --- a/src/media/socket_pair.h +++ b/src/media/socket_pair.h @@ -66,12 +66,37 @@ typedef struct { uint32_t pt:8; /* payload type */ uint32_t len:16; /* length of RTCP packet */ uint32_t ssrc; /* synchronization source identifier of packet send */ - uint32_t ssrc_1; /* synchronization source identifier of first source */ - uint32_t fraction_lost; /* 8 bits of fraction, 24 bits of total packets lost */ - uint32_t last_seq; /*last sequence number */ - uint32_t jitter; /*jitter */ + uint32_t id; /* synchronization source identifier of first source */ + uint32_t fraction_lost:8; /* 8 bits of fraction, 24 bits of total packets lost */ + uint32_t cum_lost_packet:24; /* cumulative number packet lost */ + uint32_t ext_high; /* Extended highest sequence number received */ + uint32_t jitter; /* jitter */ + uint32_t lsr; /* last SR timestamp */ + uint32_t dlsr; /* Delay since last SR timestamp */ } rtcpRRHeader; +typedef struct { +#ifdef WORDS_BIGENDIAN + uint32_t version:2; /* protocol version */ + uint32_t p:1; /* padding flag */ + uint32_t rc:5; /* reception report count must be 201 for report */ + +#else + uint32_t rc:5; /* reception report count must be 201 for report */ + uint32_t p:1; /* padding flag */ + uint32_t version:2; /* protocol version */ +#endif + uint32_t pt:8; /* payload type */ + uint32_t len:16; /* length of RTCP packet */ + uint32_t ssrc; /* synchronization source identifier of packet send */ + uint32_t timestampMSB; /* timestamp MSB */ + uint32_t timestampLSB; /* timestamp LSB */ + uint32_t timestampRTP; /* RTP timestamp */ + uint32_t spc; /* Sender's packet count */ + uint32_t soc; /* Sender's octet count */ +} rtcpSRHeader; + + class SocketPair { public: SocketPair(const char* uri, int localPort); @@ -106,8 +131,10 @@ class SocketPair { const char* in_suite, const char* in_params); void stopSendOp(bool state = true); - std::vector<rtcpRRHeader> getRtcpInfo(); - bool rtcpPacketLossDetected() const; + std::list<rtcpRRHeader> getRtcpInfo(); + + bool waitForRTCP(std::chrono::seconds interval); + double getLastLatency(); private: NON_COPYABLE(SocketPair); @@ -139,12 +166,17 @@ class SocketPair { std::list<rtcpRRHeader> listRtcpHeader_; std::mutex rtcpInfo_mutex_; - static constexpr unsigned MAX_LIST_SIZE {20}; + std::condition_variable cvRtcpPacketReadyToRead_; + static constexpr unsigned MAX_LIST_SIZE {10}; mutable std::atomic_bool rtcpPacketLoss_ {false}; -}; + double lastSRTS_; + uint32_t lastDLSR_; + std::list<double> histoLatency_; + std::chrono::steady_clock::time_point lastRR_time; +}; } // namespace jami diff --git a/src/media/video/video_receive_thread.cpp b/src/media/video/video_receive_thread.cpp index 1df7177708603c85e65653f15b2fe12997ea733d..f7354e3f35ca958658b67c9d3d6f518082d65989 100644 --- a/src/media/video/video_receive_thread.cpp +++ b/src/media/video/video_receive_thread.cpp @@ -40,6 +40,8 @@ namespace jami { namespace video { using std::string; +constexpr auto MS_BETWEEN_2_KEYFRAME_REQUEST = std::chrono::milliseconds(500); + VideoReceiveThread::VideoReceiveThread(const std::string& id, const std::string &sdp, uint16_t mtu) : @@ -199,7 +201,14 @@ bool VideoReceiveThread::decodeFrame() case MediaDecoder::Status::DecodeError: JAMI_WARN("video decoding failure"); if (requestKeyFrameCallback_) - requestKeyFrameCallback_(); + { + auto keyFrameCheckTimer = std::chrono::steady_clock::now() - lastKeyFrameTime_; + if (keyFrameCheckTimer >= MS_BETWEEN_2_KEYFRAME_REQUEST) + { + lastKeyFrameTime_ = std::chrono::steady_clock::now(); + requestKeyFrameCallback_(); + } + } break; case MediaDecoder::Status::ReadError: diff --git a/src/media/video/video_receive_thread.h b/src/media/video/video_receive_thread.h index 436ab5496d9988d5419d59ae3167bf253777c78d..413c529857bc45ed9d61a4ee22ea95d4c1cb5221 100644 --- a/src/media/video/video_receive_thread.h +++ b/src/media/video/video_receive_thread.h @@ -90,6 +90,7 @@ private: uint16_t mtu_; int rotation_; std::shared_ptr<AVBufferRef> displayMatrix_; + std::chrono::steady_clock::time_point lastKeyFrameTime_; std::function<void(void)> requestKeyFrameCallback_; void openDecoder(); diff --git a/src/media/video/video_rtp_session.cpp b/src/media/video/video_rtp_session.cpp index d0f362a9b28492bd3c6329ac2ef41c8c59792fa4..28d951e4d241bae03320a45832c0b357b6e72fc8 100644 --- a/src/media/video/video_rtp_session.cpp +++ b/src/media/video/video_rtp_session.cpp @@ -45,21 +45,16 @@ namespace jami { namespace video { using std::string; -// how long (in seconds) to wait before rechecking for packet loss -static constexpr auto RTCP_PACKET_LOSS_INTERVAL = std::chrono::milliseconds(1000); +constexpr auto DELAY_AFTER_RESTART = std::chrono::seconds(2); +constexpr auto EXPIRY_TIME_RTCP = std::chrono::milliseconds(2000); VideoRtpSession::VideoRtpSession(const string &callID, const DeviceParams& localVideoParams) : RtpSession(callID), localVideoParams_(localVideoParams) - , lastRTCPCheck_(std::chrono::system_clock::now()) - , lastLongRTCPCheck_(std::chrono::system_clock::now()) , videoBitrateInfo_ {} , rtcpCheckerThread_([] { return true; }, [this]{ processRtcpChecker(); }, []{}) - , packetLossThread_([] { return true; }, - [this]{ processPacketLoss(); }, - []{}) { setupVideoBitrateInfo(); // reset bitrate } @@ -133,6 +128,7 @@ void VideoRtpSession::startSender() JAMI_ERR("%s", e.what()); send_.enabled = false; } + lastMediaRestart_ = clock::now(); auto codecVideo = std::static_pointer_cast<jami::AccountVideoCodecInfo>(send_.codec); auto autoQuality = codecVideo->isAutoQualityEnabled; if (autoQuality and not rtcpCheckerThread_.isRunning()) @@ -168,13 +164,11 @@ void VideoRtpSession::startReceiver() receiveThread_->setRequestKeyFrameCallback(requestKeyFrameCallback_); receiveThread_->addIOContext(*socketPair_); receiveThread_->startLoop(); - packetLossThread_.start(); } else { JAMI_DBG("Video receiving disabled"); if (receiveThread_) receiveThread_->detach(videoMixer_.get()); receiveThread_.reset(); - packetLossThread_.join(); } } @@ -214,8 +208,6 @@ void VideoRtpSession::start(std::unique_ptr<IceSocket> rtp_sock, void VideoRtpSession::stop() { std::lock_guard<std::recursive_mutex> lock(mutex_); - rtcpCheckerThread_.join(); - packetLossThread_.join(); if (videoLocal_) videoLocal_->detach(sender_.get()); @@ -228,6 +220,8 @@ void VideoRtpSession::stop() if (socketPair_) socketPair_->interrupt(); + rtcpCheckerThread_.join(); + // reset default video quality if exist if (videoBitrateInfo_.videoQualityCurrent != SystemCodecInfo::DEFAULT_NO_QUALITY) videoBitrateInfo_.videoQualityCurrent = SystemCodecInfo::DEFAULT_CODEC_QUALITY; @@ -336,23 +330,29 @@ void VideoRtpSession::exitConference() conference_ = nullptr; } -float -VideoRtpSession::checkPeerPacketLoss() +bool +VideoRtpSession::checkMediumRCTPInfo(RTCPInfo& rtcpi) { auto rtcpInfoVect = socketPair_->getRtcpInfo(); unsigned totalLost = 0; - unsigned fract = 0; + unsigned totalJitter = 0; + unsigned nbDropNotNull = 0; auto vectSize = rtcpInfoVect.size(); - for (const auto& it : rtcpInfoVect) { - fract = (ntohl(it.fraction_lost) & 0xff000000) >> 24; - totalLost += fract; + if (vectSize != 0) { + for (const auto& it : rtcpInfoVect) { + if(it.fraction_lost != 0) // Exclude null drop + nbDropNotNull++; + totalLost += it.fraction_lost; + totalJitter += ntohl(it.jitter); + } + rtcpi.packetLoss = nbDropNotNull ? (float)( 100 * totalLost) / (256.0 * nbDropNotNull) : 0; + rtcpi.jitter = totalJitter / vectSize / 16; // millisecond + rtcpi.nb_sample = vectSize; + rtcpi.latency = socketPair_->getLastLatency(); + return true; } - - if (vectSize != 0) - return (float)( 100 * totalLost) / (256.0 * vectSize); - else - return NO_PACKET_LOSS_CALCULATED; + return false; } unsigned @@ -392,117 +392,54 @@ VideoRtpSession::getLowerBitrate() void VideoRtpSession::adaptQualityAndBitrate() { - bool needToCheckQuality = false; - bool mediaRestartNeeded = false; - float packetLostRate = 0.0; - - auto rtcpCheckTimer = std::chrono::duration_cast<std::chrono::seconds> (std::chrono::system_clock::now() - lastRTCPCheck_); - auto rtcpLongCheckTimer = std::chrono::duration_cast<std::chrono::seconds> (std::chrono::system_clock::now() - lastLongRTCPCheck_); + setupVideoBitrateInfo(); - if (rtcpCheckTimer.count() >= RTCP_CHECKING_INTERVAL) { - needToCheckQuality = true; - lastRTCPCheck_ = std::chrono::system_clock::now(); + RTCPInfo rtcpi {}; + if (not checkMediumRCTPInfo(rtcpi)) { + JAMI_DBG("[AutoAdapt] Sample not ready"); + return; } - if (rtcpLongCheckTimer.count() >= RTCP_LONG_CHECKING_INTERVAL) { - needToCheckQuality = true; - hasReachMaxQuality_ = false; - lastLongRTCPCheck_ = std::chrono::system_clock::now(); - // we force iterative bitrate adaptation - videoBitrateInfo_.cptBitrateChecking = 0; + auto now = clock::now(); + auto restartTimer = now - lastMediaRestart_; + //Sleep 3 seconds while the media restart + if (restartTimer < DELAY_AFTER_RESTART) { + //JAMI_DBG("[AutoAdapt] Waiting for delay %ld ms", std::chrono::duration_cast<std::chrono::milliseconds>(restartTimer)); + return; } + if (rtcpi.jitter > 5000) { + JAMI_DBG("[AutoAdapt] Jitter too high"); + return; + } - if (needToCheckQuality) { - - videoBitrateInfo_.cptBitrateChecking++; - - // packetLostRate is not already available. Do nothing - if ((packetLostRate = checkPeerPacketLoss()) == NO_PACKET_LOSS_CALCULATED) { - // we force iterative bitrate adaptation - videoBitrateInfo_.cptBitrateChecking = 0; - - // too much packet lost : decrease quality and bitrate - } else if (packetLostRate >= videoBitrateInfo_.packetLostThreshold) { - - // calculate new quality by dichotomie - videoBitrateInfo_.videoQualityCurrent = getLowerQuality(); - - // calculate new bitrate by dichotomie - videoBitrateInfo_.videoBitrateCurrent = getLowerBitrate(); - - // boundaries low - if (videoBitrateInfo_.videoQualityCurrent > videoBitrateInfo_.videoQualityMin) - videoBitrateInfo_.videoQualityCurrent = videoBitrateInfo_.videoQualityMin; - - if (videoBitrateInfo_.videoBitrateCurrent < videoBitrateInfo_.videoBitrateMin) - videoBitrateInfo_.videoBitrateCurrent = videoBitrateInfo_.videoBitrateMin; - - - // we force iterative bitrate and quality adaptation - videoBitrateInfo_.cptBitrateChecking = 0; - - // asynchronous A/V media restart - // we give priority to quality - if (((videoBitrateInfo_.videoQualityCurrent != SystemCodecInfo::DEFAULT_NO_QUALITY) && - (videoBitrateInfo_.videoQualityCurrent != (histoQuality_.empty() ? 0 : histoQuality_.back()))) || - ((videoBitrateInfo_.videoQualityCurrent == SystemCodecInfo::DEFAULT_NO_QUALITY) && - (videoBitrateInfo_.videoBitrateCurrent != (histoBitrate_.empty() ? 0 : histoBitrate_.back())))) { - mediaRestartNeeded = true; - hasReachMaxQuality_ = true; - } - - - // no packet lost: increase quality and bitrate - } else if ((videoBitrateInfo_.cptBitrateChecking <= videoBitrateInfo_.maxBitrateChecking) - and not hasReachMaxQuality_) { - - // calculate new quality by dichotomie - videoBitrateInfo_.videoQualityCurrent = - (videoBitrateInfo_.videoQualityCurrent + videoBitrateInfo_.videoQualityMax) / 2; - - // calculate new bitrate by dichotomie - videoBitrateInfo_.videoBitrateCurrent = - ( videoBitrateInfo_.videoBitrateCurrent + videoBitrateInfo_.videoBitrateMax) / 2; - - // boundaries high - if (videoBitrateInfo_.videoQualityCurrent < videoBitrateInfo_.videoQualityMax) - videoBitrateInfo_.videoQualityCurrent = videoBitrateInfo_.videoQualityMax; - - if (videoBitrateInfo_.videoBitrateCurrent > videoBitrateInfo_.videoBitrateMax) - videoBitrateInfo_.videoBitrateCurrent = videoBitrateInfo_.videoBitrateMax; - - // asynchronous A/V media restart - // we give priority to quality - if (((videoBitrateInfo_.videoQualityCurrent != SystemCodecInfo::DEFAULT_NO_QUALITY) && - (videoBitrateInfo_.videoQualityCurrent != (histoQuality_.empty() ? 0 : histoQuality_.back()))) || - ((videoBitrateInfo_.videoQualityCurrent == SystemCodecInfo::DEFAULT_NO_QUALITY) && - (videoBitrateInfo_.videoBitrateCurrent != (histoBitrate_.empty() ? 0 : histoBitrate_.back())))) - mediaRestartNeeded = true; - - if (videoBitrateInfo_.cptBitrateChecking == videoBitrateInfo_.maxBitrateChecking) - lastLongRTCPCheck_ = std::chrono::system_clock::now(); + auto oldBitrate = videoBitrateInfo_.videoBitrateCurrent; - } else { - // nothing we reach maximal tries - } + //Take action only when two successive drop superior to 1% are catched... + //and when jitter is less than 5 seconds + auto pondLoss = getPonderateLoss(rtcpi.packetLoss); + //JAMI_DBG("[AutoAdapt] Ponderate packet loss rate: %f%, Last packet loss rate: %f%, Medium Jitter: %dms" , pondLoss, rtcpi.packetLoss, rtcpi.jitter); + if(pondLoss >= 2.0f) + { + videoBitrateInfo_.videoBitrateCurrent = videoBitrateInfo_.videoBitrateCurrent / ((rtcpi.packetLoss / 20)+1); + JAMI_WARN("[AutoAdapt] packet loss rate: %f%%, decrease bitrate from %d Kbps to %d Kbps", rtcpi.packetLoss, oldBitrate, videoBitrateInfo_.videoBitrateCurrent); } - if (mediaRestartNeeded) { + videoBitrateInfo_.videoBitrateCurrent = std::max(videoBitrateInfo_.videoBitrateCurrent, videoBitrateInfo_.videoBitrateMin); + videoBitrateInfo_.videoBitrateCurrent = std::min(videoBitrateInfo_.videoBitrateCurrent, videoBitrateInfo_.videoBitrateMax); + + if(oldBitrate != videoBitrateInfo_.videoBitrateCurrent) { storeVideoBitrateInfo(); - const auto& cid = callID_; + JAMI_DBG("[AutoAdapt] Restart media sender"); - JAMI_WARN("[%u/%u] packetLostRate=%f -> change quality to %d bitrate to %d", - videoBitrateInfo_.cptBitrateChecking, - videoBitrateInfo_.maxBitrateChecking, - packetLostRate, - videoBitrateInfo_.videoQualityCurrent, - videoBitrateInfo_.videoBitrateCurrent); + const auto& cid = callID_; runOnMainThread([cid]{ if (auto call = Manager::instance().callFactory.getCall(cid)) call->restartMediaSender(); }); + + lastMediaRestart_ = now; } } @@ -550,7 +487,6 @@ VideoRtpSession::storeVideoBitrateInfo() { histoQuality_.push_back(videoBitrateInfo_.videoQualityCurrent); histoBitrate_.push_back(videoBitrateInfo_.videoBitrateCurrent); - } } @@ -558,17 +494,7 @@ void VideoRtpSession::processRtcpChecker() { adaptQualityAndBitrate(); - rtcpCheckerThread_.wait_for(std::chrono::seconds(RTCP_CHECKING_INTERVAL)); -} - -void -VideoRtpSession::processPacketLoss() -{ - if (packetLossThread_.wait_for(RTCP_PACKET_LOSS_INTERVAL, - [this]{return socketPair_->rtcpPacketLossDetected();})) { - if (requestKeyFrameCallback_) - requestKeyFrameCallback_(); - } + socketPair_->waitForRTCP(std::chrono::seconds(rtcp_checking_interval)); } void @@ -607,4 +533,39 @@ VideoRtpSession::setChangeOrientationCallback(std::function<void(int)> cb) changeOrientationCallback_ = std::move(cb); } +float +VideoRtpSession::getPonderateLoss(float lastLoss) +{ + float pond = 0.0f, pondLoss = 0.0f, totalPond = 0.0f; + constexpr float coefficient_a = -1/2000.0f; + constexpr float coefficient_b = 1.0f; + + auto now = clock::now(); + + histoLoss_.emplace_back(now, lastLoss); + + //for(auto& it : histoLoss_) + for (auto it = histoLoss_.begin(); it != histoLoss_.end();) { + auto delay = now - it->first; + + //JAMI_WARN("now - it.first: %ld", std::chrono::duration_cast<std::chrono::milliseconds>(delay)); + + // 1ms -> 100% + // 2000ms -> 1 + if(delay <= EXPIRY_TIME_RTCP) + { + pond = std::min(delay.count() * coefficient_a + coefficient_b, 1.0f); + totalPond += pond; + pondLoss += it->second * pond; + ++it; + } + else + it = histoLoss_.erase(it); + } + return pondLoss / totalPond; +} + + }} // namespace jami::video + + diff --git a/src/media/video/video_rtp_session.h b/src/media/video/video_rtp_session.h index 2e589331f7ebd1bc12bd8d15ddd26410c84eb3f5..75ea08b28d709c182db9eb1697600ecde18696bd 100644 --- a/src/media/video/video_rtp_session.h +++ b/src/media/video/video_rtp_session.h @@ -41,6 +41,13 @@ class VideoMixer; class VideoSender; class VideoReceiveThread; +struct RTCPInfo { + float packetLoss; + unsigned int jitter; + unsigned int nb_sample; + float latency; +}; + struct VideoBitrateInfo { unsigned videoBitrateCurrent; unsigned videoBitrateMin; @@ -99,13 +106,12 @@ private: void setupVideoPipeline(); void startSender(); void startReceiver(); + using clock = std::chrono::steady_clock; + using time_point = clock::time_point; std::string input_; DeviceParams localVideoParams_; - std::chrono::time_point<std::chrono::system_clock> lastRTCPCheck_; - std::chrono::time_point<std::chrono::system_clock> lastLongRTCPCheck_; - std::unique_ptr<VideoSender> sender_; std::unique_ptr<VideoReceiveThread> receiveThread_; Conference* conference_ {nullptr}; @@ -115,20 +121,17 @@ private: std::function<void (void)> requestKeyFrameCallback_; - float checkPeerPacketLoss(); + bool checkMediumRCTPInfo(RTCPInfo&); unsigned getLowerQuality(); unsigned getLowerBitrate(); void adaptQualityAndBitrate(); void storeVideoBitrateInfo(); void setupVideoBitrateInfo(); void checkReceiver(); + float getPonderateLoss(float lastLoss); - // interval in seconds between RTCP checkings - const unsigned RTCP_CHECKING_INTERVAL {4}; - // long interval in seconds between RTCP checkings - const unsigned RTCP_LONG_CHECKING_INTERVAL {30}; // no packet loss can be calculated as no data in input - static constexpr float NO_PACKET_LOSS_CALCULATED {-1.0}; + static constexpr float NO_INFO_CALCULATED {-1.0}; // bitrate and quality info struct VideoBitrateInfo videoBitrateInfo_; // previous quality and bitrate used if quality or bitrate need to be decreased @@ -147,10 +150,14 @@ private: InterruptedThreadLoop rtcpCheckerThread_; void processRtcpChecker(); - InterruptedThreadLoop packetLossThread_; - void processPacketLoss(); - std::function<void(int)> changeOrientationCallback_; + + // interval in seconds between RTCP checkings + std::chrono::seconds rtcp_checking_interval {4}; + + time_point lastMediaRestart_ {time_point::min()}; + + std::list< std::pair<time_point, float> > histoLoss_; }; }} // namespace jami::video