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
|