mediasoup 3.24.2 → 3.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/node/lib/Consumer.js +1 -1
- package/node/lib/Transport.d.ts.map +1 -1
- package/node/lib/Transport.js +7 -0
- package/node/lib/ortc.d.ts.map +1 -1
- package/node/lib/ortc.js +10 -0
- package/node/lib/test/test-PipeTransport.js +35 -30
- package/package.json +6 -6
- package/worker/include/DepLibUV.hpp +33 -4
- package/worker/include/RTC/Consumer.hpp +4 -1
- package/worker/include/RTC/Producer.hpp +34 -5
- package/worker/include/RTC/RTCP/Packet.hpp +7 -1
- package/worker/include/RTC/RTP/Packet.hpp +18 -0
- package/worker/include/RTC/RTP/RtpStream.hpp +76 -14
- package/worker/include/RTC/RTP/RtpStreamRecv.hpp +191 -7
- package/worker/include/RTC/RTP/RtpStreamSend.hpp +56 -10
- package/worker/include/RTC/RTP/RtxStream.hpp +34 -2
- package/worker/include/RTC/RemoteCaptureTimeEstimator.hpp +92 -0
- package/worker/include/RTC/RemoteClockOffsetEstimator.hpp +87 -0
- package/worker/include/RTC/Router.hpp +2 -5
- package/worker/include/RTC/SimulcastProducerStreamManager.hpp +3 -0
- package/worker/include/RTC/Transport.hpp +15 -7
- package/worker/include/Shared.hpp +5 -0
- package/worker/include/SharedInterface.hpp +6 -0
- package/worker/include/Utils.hpp +44 -3
- package/worker/meson.build +4 -0
- package/worker/mocks/include/MockShared.hpp +7 -0
- package/worker/src/DepLibUV.cpp +17 -0
- package/worker/src/RTC/Consumer.cpp +8 -5
- package/worker/src/RTC/Producer.cpp +85 -16
- package/worker/src/RTC/RTP/Packet.cpp +9 -1
- package/worker/src/RTC/RTP/RtpStream.cpp +14 -10
- package/worker/src/RTC/RTP/RtpStreamRecv.cpp +150 -23
- package/worker/src/RTC/RTP/RtpStreamSend.cpp +34 -19
- package/worker/src/RTC/RTP/RtxStream.cpp +12 -6
- package/worker/src/RTC/RemoteCaptureTimeEstimator.cpp +77 -0
- package/worker/src/RTC/RemoteClockOffsetEstimator.cpp +106 -0
- package/worker/src/RTC/Router.cpp +10 -8
- package/worker/src/RTC/RtpDictionaries/RtcpParameters.cpp +13 -3
- package/worker/src/RTC/SCTP/packet/chunks/SackChunk.cpp +6 -2
- package/worker/src/RTC/SimulcastProducerStreamManager.cpp +115 -56
- package/worker/src/RTC/SvcProducerStreamManager.cpp +22 -17
- package/worker/src/RTC/Transport.cpp +96 -4
- package/worker/test/src/RTC/RTP/TestPacket.cpp +6 -0
- package/worker/test/src/RTC/RTP/TestRtpStreamRecv.cpp +2 -2
- package/worker/test/src/RTC/RTP/TestRtpStreamSend.cpp +119 -0
- package/worker/test/src/RTC/TestConsumer.cpp +4 -3
- package/worker/test/src/RTC/TestPortManager.cpp +1 -1
- package/worker/test/src/RTC/TestRemoteCaptureTimeEstimator.cpp +164 -0
- package/worker/test/src/RTC/TestRemoteClockOffsetEstimator.cpp +241 -0
- package/worker/test/src/RTC/TestSimpleProducerStreamManager.cpp +2 -2
- package/worker/test/src/RTC/TestSimulcastProducerStreamManager.cpp +340 -15
- package/worker/test/src/RTC/TestSvcProducerStreamManager.cpp +2 -2
- package/worker/test/src/RTC/TestTransportTuple.cpp +1 -1
- package/worker/test/src/Utils/TestTime.cpp +49 -0
|
@@ -299,6 +299,40 @@ namespace RTC
|
|
|
299
299
|
// Calculate jitter.
|
|
300
300
|
CalculateJitter(packet->GetTimestamp());
|
|
301
301
|
|
|
302
|
+
// Store the capture instant if the packet carries it.
|
|
303
|
+
{
|
|
304
|
+
uint64_t absCaptureTimestamp{ 0 };
|
|
305
|
+
int64_t estimatedCaptureClockOffset{ 0 };
|
|
306
|
+
|
|
307
|
+
// NOTE: A zeroed capture timestamp gives no capture instant to store.
|
|
308
|
+
if (packet->ReadAbsCaptureTime(absCaptureTimestamp, estimatedCaptureClockOffset) && absCaptureTimestamp != 0)
|
|
309
|
+
{
|
|
310
|
+
Utils::Time::Ntp ntp{}; // NOLINT(cppcoreguidelines-pro-type-member-init)
|
|
311
|
+
|
|
312
|
+
ntp.seconds = static_cast<uint32_t>(absCaptureTimestamp >> 32);
|
|
313
|
+
ntp.fractions = static_cast<uint32_t>(absCaptureTimestamp);
|
|
314
|
+
|
|
315
|
+
// The capture timestamp belongs to the clock of the capture system, which is
|
|
316
|
+
// not the clock of the sender of this stream when the media comes from
|
|
317
|
+
// somewhere else. The capture clock offset gives the instant back in the clock
|
|
318
|
+
// of the sender, which is the one this stream is estimated against:
|
|
319
|
+
//
|
|
320
|
+
// capture NTP clock = sender NTP clock + capture clock offset
|
|
321
|
+
const auto captureMs = static_cast<int64_t>(Utils::Time::Ntp2TimeMs(ntp)) -
|
|
322
|
+
Utils::Time::Q32x32ToTimeMs(estimatedCaptureClockOffset);
|
|
323
|
+
|
|
324
|
+
// NOTE: A capture instant that is not positive means a capture clock offset
|
|
325
|
+
// that cannot be true, so there is nothing to store.
|
|
326
|
+
if (captureMs > 0)
|
|
327
|
+
{
|
|
328
|
+
this->lastAbsCaptureTime = AbsCaptureTime{
|
|
329
|
+
.ts = packet->GetTimestamp(),
|
|
330
|
+
.ntpMs = static_cast<uint64_t>(captureMs),
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
302
336
|
// Increase transmission counter.
|
|
303
337
|
this->transmissionCounter.Update(packet);
|
|
304
338
|
|
|
@@ -452,18 +486,15 @@ namespace RTC
|
|
|
452
486
|
{
|
|
453
487
|
MS_TRACE();
|
|
454
488
|
|
|
455
|
-
|
|
489
|
+
// Ask the listener for the worst remote fraction lost.
|
|
490
|
+
const uint8_t worstRemoteFractionLost =
|
|
491
|
+
this->params.useInBandFec ? static_cast<RTP::RtpStreamRecv::Listener*>(this->listener)
|
|
492
|
+
->OnRtpStreamNeedWorstRemoteFractionLost(this)
|
|
493
|
+
: 0;
|
|
456
494
|
|
|
457
|
-
if (
|
|
495
|
+
if (worstRemoteFractionLost > 0)
|
|
458
496
|
{
|
|
459
|
-
|
|
460
|
-
static_cast<RTP::RtpStreamRecv::Listener*>(this->listener)
|
|
461
|
-
->OnRtpStreamNeedWorstRemoteFractionLost(this, worstRemoteFractionLost);
|
|
462
|
-
|
|
463
|
-
if (worstRemoteFractionLost > 0)
|
|
464
|
-
{
|
|
465
|
-
MS_DEBUG_TAG(rtcp, "using worst remote fraction lost:%" PRIu8, worstRemoteFractionLost);
|
|
466
|
-
}
|
|
497
|
+
MS_DEBUG_TAG(rtcp, "using worst remote fraction lost:%" PRIu8, worstRemoteFractionLost);
|
|
467
498
|
}
|
|
468
499
|
|
|
469
500
|
auto* report = new RTC::RTCP::ReceiverReport();
|
|
@@ -528,17 +559,19 @@ namespace RTC
|
|
|
528
559
|
report->SetLastSeq(static_cast<uint32_t>(this->maxSeq) + this->cycles);
|
|
529
560
|
report->SetJitter(this->jitter);
|
|
530
561
|
|
|
531
|
-
if (this->
|
|
562
|
+
if (this->lastSenderReportTiming.has_value())
|
|
532
563
|
{
|
|
564
|
+
const auto& senderReportTiming = this->lastSenderReportTiming.value();
|
|
533
565
|
// Get delay in milliseconds.
|
|
534
|
-
auto delayMs =
|
|
566
|
+
auto delayMs =
|
|
567
|
+
static_cast<uint32_t>(this->shared->GetTimeMs() - senderReportTiming.receivedMs);
|
|
535
568
|
// Express delay in units of 1/65536 seconds.
|
|
536
569
|
uint32_t dlsr = (delayMs / 1000) << 16;
|
|
537
570
|
|
|
538
571
|
dlsr |= uint32_t{ (delayMs % 1000) * 65536 / 1000 };
|
|
539
572
|
|
|
540
573
|
report->SetDelaySinceLastSenderReport(dlsr);
|
|
541
|
-
report->SetLastSenderReport(
|
|
574
|
+
report->SetLastSenderReport(senderReportTiming.compactNtp);
|
|
542
575
|
}
|
|
543
576
|
else
|
|
544
577
|
{
|
|
@@ -565,18 +598,31 @@ namespace RTC
|
|
|
565
598
|
{
|
|
566
599
|
MS_TRACE();
|
|
567
600
|
|
|
568
|
-
this->lastSrReceived = this->shared->GetTimeMs();
|
|
569
|
-
this->lastSrTimestamp = report->GetNtpSec() << 16;
|
|
570
|
-
this->lastSrTimestamp += report->GetNtpFrac() >> 16;
|
|
571
|
-
|
|
572
601
|
// Update info about last Sender Report.
|
|
573
|
-
|
|
602
|
+
//
|
|
603
|
+
// NOTE: A sender with no wall clock may report a zeroed NTP timestamp, which
|
|
604
|
+
// gives nothing to store.
|
|
605
|
+
if (report->GetNtpSec() != 0 || report->GetNtpFrac() != 0)
|
|
606
|
+
{
|
|
607
|
+
uint32_t compactNtp = report->GetNtpSec() << 16;
|
|
608
|
+
|
|
609
|
+
compactNtp += report->GetNtpFrac() >> 16;
|
|
574
610
|
|
|
575
|
-
|
|
576
|
-
|
|
611
|
+
this->lastSenderReportTiming = SenderReportTiming{
|
|
612
|
+
.compactNtp = compactNtp,
|
|
613
|
+
.receivedMs = this->shared->GetTimeMs(),
|
|
614
|
+
};
|
|
577
615
|
|
|
578
|
-
|
|
579
|
-
|
|
616
|
+
Utils::Time::Ntp ntp{}; // NOLINT(cppcoreguidelines-pro-type-member-init)
|
|
617
|
+
|
|
618
|
+
ntp.seconds = report->GetNtpSec();
|
|
619
|
+
ntp.fractions = report->GetNtpFrac();
|
|
620
|
+
|
|
621
|
+
this->lastSenderReportMapping = RTP::RtpStream::SenderReportMapping{
|
|
622
|
+
.ntpMs = Utils::Time::Ntp2TimeMs(ntp),
|
|
623
|
+
.ts = report->GetRtpTs(),
|
|
624
|
+
};
|
|
625
|
+
}
|
|
580
626
|
|
|
581
627
|
// Update the score with the current RR.
|
|
582
628
|
UpdateScore();
|
|
@@ -600,7 +646,7 @@ namespace RTC
|
|
|
600
646
|
|
|
601
647
|
// Get the NTP representation of the current timestamp.
|
|
602
648
|
const uint64_t nowMs = this->shared->GetTimeMs();
|
|
603
|
-
auto ntp = Utils::Time::TimeMs2Ntp(nowMs);
|
|
649
|
+
auto ntp = Utils::Time::TimeMs2Ntp(nowMs + this->shared->GetNtpOffsetMs());
|
|
604
650
|
|
|
605
651
|
// Get the compact NTP representation of the current timestamp.
|
|
606
652
|
uint32_t compactNtp = (ntp.seconds & 0x0000FFFF) << 16;
|
|
@@ -634,6 +680,78 @@ namespace RTC
|
|
|
634
680
|
}
|
|
635
681
|
}
|
|
636
682
|
|
|
683
|
+
std::optional<uint64_t> RtpStreamRecv::GetRemoteCaptureMsFromAbsCaptureTime(uint32_t ts) const
|
|
684
|
+
{
|
|
685
|
+
MS_TRACE();
|
|
686
|
+
|
|
687
|
+
if (!this->lastAbsCaptureTime.has_value())
|
|
688
|
+
{
|
|
689
|
+
return std::nullopt;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
const auto& absCaptureTime = this->lastAbsCaptureTime.value();
|
|
693
|
+
|
|
694
|
+
return InterpolateRemoteCaptureMs(
|
|
695
|
+
absCaptureTime.ntpMs, absCaptureTime.ts, ts, RtpStreamRecv::MaxAbsCaptureTimeInterpolationMs);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
std::optional<uint64_t> RtpStreamRecv::GetRemoteCaptureMsFromSenderReport(uint32_t ts) const
|
|
699
|
+
{
|
|
700
|
+
MS_TRACE();
|
|
701
|
+
|
|
702
|
+
if (!this->lastSenderReportMapping.has_value())
|
|
703
|
+
{
|
|
704
|
+
return std::nullopt;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
const auto& senderReportMapping = this->lastSenderReportMapping.value();
|
|
708
|
+
|
|
709
|
+
return InterpolateRemoteCaptureMs(
|
|
710
|
+
senderReportMapping.ntpMs,
|
|
711
|
+
senderReportMapping.ts,
|
|
712
|
+
ts,
|
|
713
|
+
RtpStreamRecv::MaxSenderReportInterpolationMs);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
std::optional<uint64_t> RtpStreamRecv::InterpolateRemoteCaptureMs(
|
|
717
|
+
uint64_t referenceNtpMs, uint32_t referenceTs, uint32_t ts, uint64_t maxDistanceMs) const
|
|
718
|
+
{
|
|
719
|
+
MS_TRACE();
|
|
720
|
+
|
|
721
|
+
const auto clockRate = GetClockRate();
|
|
722
|
+
|
|
723
|
+
if (clockRate == 0)
|
|
724
|
+
{
|
|
725
|
+
return std::nullopt;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// Distance in RTP timestamp units, taking wrap around into account.
|
|
729
|
+
const auto distanceTs = static_cast<int64_t>(static_cast<int32_t>(ts - referenceTs));
|
|
730
|
+
const int64_t distanceMs = (distanceTs * 1000) / static_cast<int64_t>(clockRate);
|
|
731
|
+
// NOTE: The negation is safe since `distanceMs` comes from a 32 bits
|
|
732
|
+
// distance scaled down by the clock rate.
|
|
733
|
+
const auto absDistanceMs = static_cast<uint64_t>(distanceMs < 0 ? -distanceMs : distanceMs);
|
|
734
|
+
|
|
735
|
+
if (absDistanceMs > maxDistanceMs)
|
|
736
|
+
{
|
|
737
|
+
return std::nullopt;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const int64_t captureMs = static_cast<int64_t>(referenceNtpMs) + distanceMs;
|
|
741
|
+
|
|
742
|
+
// The reference does not map into a valid capture instant, so the remote
|
|
743
|
+
// endpoint is reporting nonsense.
|
|
744
|
+
if (captureMs < 0)
|
|
745
|
+
{
|
|
746
|
+
MS_WARN_2TAGS(
|
|
747
|
+
rtp, rtcp, "invalid interpolated capture instant [distanceMs:%" PRIi64 "]", distanceMs);
|
|
748
|
+
|
|
749
|
+
return std::nullopt;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
return static_cast<uint64_t>(captureMs);
|
|
753
|
+
}
|
|
754
|
+
|
|
637
755
|
void RtpStreamRecv::RequestKeyFrame()
|
|
638
756
|
{
|
|
639
757
|
MS_TRACE();
|
|
@@ -788,6 +906,15 @@ namespace RTC
|
|
|
788
906
|
return;
|
|
789
907
|
}
|
|
790
908
|
|
|
909
|
+
// We expected packets but received none of them, so there is nothing to
|
|
910
|
+
// compute (and ratios below would divide by zero).
|
|
911
|
+
if (received == 0)
|
|
912
|
+
{
|
|
913
|
+
RTP::RtpStream::UpdateScore(0);
|
|
914
|
+
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
|
|
791
918
|
lost = std::min(lost, received);
|
|
792
919
|
|
|
793
920
|
if (repaired > lost)
|
|
@@ -21,11 +21,6 @@ namespace RTC
|
|
|
21
21
|
MaxRequestedPackets + 1);
|
|
22
22
|
static constexpr uint32_t DefaultRtt{ 100u };
|
|
23
23
|
|
|
24
|
-
/* Class Static. */
|
|
25
|
-
|
|
26
|
-
const uint32_t RtpStreamSend::MaxRetransmissionDelayForVideoMs{ 2000u };
|
|
27
|
-
const uint32_t RtpStreamSend::MaxRetransmissionDelayForAudioMs{ 1000u };
|
|
28
|
-
|
|
29
24
|
/* Instance methods. */
|
|
30
25
|
|
|
31
26
|
RtpStreamSend::RtpStreamSend(
|
|
@@ -283,7 +278,7 @@ namespace RTC
|
|
|
283
278
|
|
|
284
279
|
// Get the NTP representation of the current timestamp.
|
|
285
280
|
const uint64_t nowMs = this->shared->GetTimeMs();
|
|
286
|
-
auto ntp = Utils::Time::TimeMs2Ntp(nowMs);
|
|
281
|
+
auto ntp = Utils::Time::TimeMs2Ntp(nowMs + this->shared->GetNtpOffsetMs());
|
|
287
282
|
|
|
288
283
|
// Get the compact NTP representation of the current timestamp.
|
|
289
284
|
uint32_t compactNtp = (ntp.seconds & 0x0000FFFF) << 16;
|
|
@@ -322,9 +317,14 @@ namespace RTC
|
|
|
322
317
|
{
|
|
323
318
|
MS_TRACE();
|
|
324
319
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
320
|
+
uint32_t compactNtp = report->GetNtpSec() << 16;
|
|
321
|
+
|
|
322
|
+
compactNtp += report->GetNtpFrac() >> 16;
|
|
323
|
+
|
|
324
|
+
this->lastReceiverReferenceTime = ReceiverReferenceTime{
|
|
325
|
+
.compactNtp = compactNtp,
|
|
326
|
+
.receivedMs = this->shared->GetTimeMs(),
|
|
327
|
+
};
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
RTC::RTCP::SenderReport* RtpStreamSend::GetRtcpSenderReport(uint64_t nowMs)
|
|
@@ -336,23 +336,37 @@ namespace RTC
|
|
|
336
336
|
return nullptr;
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
-
|
|
339
|
+
// A stream that stopped sending cannot tell where its RTP timeline is now, and
|
|
340
|
+
// extrapolating would claim RTP timestamps of packets never sent.
|
|
341
|
+
if (nowMs - this->maxPacketMs > RtpStreamSend::MaxSenderReportReferenceAgeMs)
|
|
342
|
+
{
|
|
343
|
+
return nullptr;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
auto ntp = Utils::Time::TimeMs2Ntp(nowMs + this->shared->GetNtpOffsetMs());
|
|
340
347
|
auto* report = new RTC::RTCP::SenderReport();
|
|
341
348
|
|
|
342
|
-
// Calculate TS difference between now and
|
|
343
|
-
|
|
344
|
-
|
|
349
|
+
// Calculate TS difference between now and the instant at which the media in the
|
|
350
|
+
// packet holding the highest RTP timestamp was captured, falling back to the
|
|
351
|
+
// instant that packet was seen while the capture instant cannot be told.
|
|
352
|
+
const uint64_t referenceMs = this->maxPacketCaptureMs.value_or(this->maxPacketMs);
|
|
353
|
+
// NOTE: The capture instant is an estimation, so it may land ahead of now.
|
|
354
|
+
const uint64_t diffMs = nowMs > referenceMs ? nowMs - referenceMs : 0;
|
|
355
|
+
const uint64_t diffTs = diffMs * GetClockRate() / 1000;
|
|
356
|
+
const auto rtpTs = static_cast<uint32_t>(this->maxPacketTs + diffTs);
|
|
345
357
|
|
|
346
358
|
report->SetSsrc(GetSsrc());
|
|
347
359
|
report->SetPacketCount(this->transmissionCounter.GetPacketCount());
|
|
348
360
|
report->SetOctetCount(this->transmissionCounter.GetBytes());
|
|
349
361
|
report->SetNtpSec(ntp.seconds);
|
|
350
362
|
report->SetNtpFrac(ntp.fractions);
|
|
351
|
-
report->SetRtpTs(
|
|
363
|
+
report->SetRtpTs(rtpTs);
|
|
352
364
|
|
|
353
365
|
// Update info about last Sender Report.
|
|
354
|
-
this->
|
|
355
|
-
|
|
366
|
+
this->lastSenderReportMapping = RTP::RtpStream::SenderReportMapping{
|
|
367
|
+
.ntpMs = nowMs,
|
|
368
|
+
.ts = rtpTs,
|
|
369
|
+
};
|
|
356
370
|
|
|
357
371
|
return report;
|
|
358
372
|
}
|
|
@@ -361,13 +375,14 @@ namespace RTC
|
|
|
361
375
|
{
|
|
362
376
|
MS_TRACE();
|
|
363
377
|
|
|
364
|
-
if (this->
|
|
378
|
+
if (!this->lastReceiverReferenceTime.has_value())
|
|
365
379
|
{
|
|
366
380
|
return nullptr;
|
|
367
381
|
}
|
|
368
382
|
|
|
383
|
+
const auto& receiverReferenceTime = this->lastReceiverReferenceTime.value();
|
|
369
384
|
// Get delay in milliseconds.
|
|
370
|
-
auto delayMs = static_cast<uint32_t>(nowMs -
|
|
385
|
+
auto delayMs = static_cast<uint32_t>(nowMs - receiverReferenceTime.receivedMs);
|
|
371
386
|
// Express delay in units of 1/65536 seconds.
|
|
372
387
|
uint32_t dlrr = (delayMs / 1000) << 16;
|
|
373
388
|
|
|
@@ -377,7 +392,7 @@ namespace RTC
|
|
|
377
392
|
|
|
378
393
|
ssrcInfo->SetSsrc(GetSsrc());
|
|
379
394
|
ssrcInfo->SetDelaySinceLastReceiverReport(dlrr);
|
|
380
|
-
ssrcInfo->SetLastReceiverReport(
|
|
395
|
+
ssrcInfo->SetLastReceiverReport(receiverReferenceTime.compactNtp);
|
|
381
396
|
|
|
382
397
|
return ssrcInfo;
|
|
383
398
|
}
|
|
@@ -138,17 +138,18 @@ namespace RTC
|
|
|
138
138
|
// NOTE: Do not calculate any jitter.
|
|
139
139
|
report->SetJitter(0);
|
|
140
140
|
|
|
141
|
-
if (this->
|
|
141
|
+
if (this->lastSenderReportTiming.has_value())
|
|
142
142
|
{
|
|
143
|
+
const auto& senderReportTiming = this->lastSenderReportTiming.value();
|
|
143
144
|
// Get delay in milliseconds.
|
|
144
|
-
const uint32_t delayMs = this->shared->GetTimeMs() -
|
|
145
|
+
const uint32_t delayMs = this->shared->GetTimeMs() - senderReportTiming.receivedMs;
|
|
145
146
|
// Express delay in units of 1/65536 seconds.
|
|
146
147
|
uint32_t dlsr = (delayMs / 1000) << 16;
|
|
147
148
|
|
|
148
149
|
dlsr |= uint32_t{ (delayMs % 1000) * 65536 / 1000 };
|
|
149
150
|
|
|
150
151
|
report->SetDelaySinceLastSenderReport(dlsr);
|
|
151
|
-
report->SetLastSenderReport(
|
|
152
|
+
report->SetLastSenderReport(senderReportTiming.compactNtp);
|
|
152
153
|
}
|
|
153
154
|
else
|
|
154
155
|
{
|
|
@@ -163,9 +164,14 @@ namespace RTC
|
|
|
163
164
|
{
|
|
164
165
|
MS_TRACE();
|
|
165
166
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
uint32_t compactNtp = report->GetNtpSec() << 16;
|
|
168
|
+
|
|
169
|
+
compactNtp += report->GetNtpFrac() >> 16;
|
|
170
|
+
|
|
171
|
+
this->lastSenderReportTiming = SenderReportTiming{
|
|
172
|
+
.compactNtp = compactNtp,
|
|
173
|
+
.receivedMs = this->shared->GetTimeMs(),
|
|
174
|
+
};
|
|
169
175
|
}
|
|
170
176
|
|
|
171
177
|
bool RtxStream::UpdateSeq(const RTP::Packet* packet)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#define MS_CLASS "RTC::RemoteCaptureTimeEstimator"
|
|
2
|
+
// #define MS_LOG_DEV_LEVEL 3
|
|
3
|
+
|
|
4
|
+
#include "RTC/RemoteCaptureTimeEstimator.hpp"
|
|
5
|
+
#include "Logger.hpp"
|
|
6
|
+
|
|
7
|
+
namespace RTC
|
|
8
|
+
{
|
|
9
|
+
/* Instance methods. */
|
|
10
|
+
|
|
11
|
+
void RemoteCaptureTimeEstimator::UpdateSource(bool absCaptureTimeNegotiated)
|
|
12
|
+
{
|
|
13
|
+
MS_TRACE();
|
|
14
|
+
|
|
15
|
+
// Once the Sender Report source has been chosen there is no way back.
|
|
16
|
+
if (this->source == RemoteCaptureTimeEstimator::Source::SENDER_REPORT)
|
|
17
|
+
{
|
|
18
|
+
if (absCaptureTimeNegotiated)
|
|
19
|
+
{
|
|
20
|
+
MS_WARN_2TAGS(rtp, rtcp, "cannot move capture instant source back to abs-capture-time");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
this->source = absCaptureTimeNegotiated ? RemoteCaptureTimeEstimator::Source::ABS_CAPTURE_TIME
|
|
27
|
+
: RemoteCaptureTimeEstimator::Source::SENDER_REPORT;
|
|
28
|
+
|
|
29
|
+
MS_DEBUG_2TAGS(
|
|
30
|
+
rtp,
|
|
31
|
+
rtcp,
|
|
32
|
+
"capture instant source set to %s",
|
|
33
|
+
this->source == RemoteCaptureTimeEstimator::Source::ABS_CAPTURE_TIME ? "abs-capture-time"
|
|
34
|
+
: "Sender Report");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
void RemoteCaptureTimeEstimator::SenderReportReceived(const RTC::RTP::RtpStreamRecv* rtpStream)
|
|
38
|
+
{
|
|
39
|
+
MS_TRACE();
|
|
40
|
+
|
|
41
|
+
const auto senderReportMapping = rtpStream->GetSenderReportMapping();
|
|
42
|
+
const auto senderReportReceivedMs = rtpStream->GetSenderReportReceivedMs();
|
|
43
|
+
|
|
44
|
+
// Both are stored together, so either both are set or none of them is.
|
|
45
|
+
if (!senderReportMapping.has_value() || !senderReportReceivedMs.has_value())
|
|
46
|
+
{
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this->clockOffsetEstimator.AddSenderReport(
|
|
51
|
+
senderReportMapping.value().ntpMs,
|
|
52
|
+
senderReportReceivedMs.value(),
|
|
53
|
+
static_cast<uint32_t>(rtpStream->GetRtt()));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
std::optional<uint64_t> RemoteCaptureTimeEstimator::GetLocalCaptureMs(
|
|
57
|
+
const RTC::RTP::RtpStreamRecv* rtpStream, uint32_t ts) const
|
|
58
|
+
{
|
|
59
|
+
MS_TRACE();
|
|
60
|
+
|
|
61
|
+
if (!this->source.has_value())
|
|
62
|
+
{
|
|
63
|
+
return std::nullopt;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const auto remoteCaptureMs = this->source == RemoteCaptureTimeEstimator::Source::ABS_CAPTURE_TIME
|
|
67
|
+
? rtpStream->GetRemoteCaptureMsFromAbsCaptureTime(ts)
|
|
68
|
+
: rtpStream->GetRemoteCaptureMsFromSenderReport(ts);
|
|
69
|
+
|
|
70
|
+
if (!remoteCaptureMs.has_value())
|
|
71
|
+
{
|
|
72
|
+
return std::nullopt;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return this->clockOffsetEstimator.RemoteMsToLocalMs(remoteCaptureMs.value());
|
|
76
|
+
}
|
|
77
|
+
} // namespace RTC
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#define MS_CLASS "RTC::RemoteClockOffsetEstimator"
|
|
2
|
+
// #define MS_LOG_DEV_LEVEL 3
|
|
3
|
+
|
|
4
|
+
#include "RTC/RemoteClockOffsetEstimator.hpp"
|
|
5
|
+
#include "Logger.hpp"
|
|
6
|
+
|
|
7
|
+
namespace RTC
|
|
8
|
+
{
|
|
9
|
+
/* Instance methods. */
|
|
10
|
+
|
|
11
|
+
RemoteClockOffsetEstimator::RemoteClockOffsetEstimator()
|
|
12
|
+
{
|
|
13
|
+
MS_TRACE();
|
|
14
|
+
|
|
15
|
+
this->samples.reserve(RemoteClockOffsetEstimator::WindowSize);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
void RemoteClockOffsetEstimator::AddSenderReport(
|
|
19
|
+
uint64_t remoteNtpMs, uint64_t localArrivalMs, uint32_t rttMs)
|
|
20
|
+
{
|
|
21
|
+
MS_TRACE();
|
|
22
|
+
|
|
23
|
+
// Ignore Sender Reports with no NTP timestamp.
|
|
24
|
+
if (remoteNtpMs == 0)
|
|
25
|
+
{
|
|
26
|
+
MS_DEBUG_DEV("ignoring Sender Report with no NTP timestamp");
|
|
27
|
+
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Ignore a Sender Report belonging to a compound packet already accounted
|
|
32
|
+
// for. Otherwise a single delayed compound packet would contribute as many
|
|
33
|
+
// samples as streams it reports about, and hence bias the median.
|
|
34
|
+
if (localArrivalMs == this->lastLocalArrivalMs)
|
|
35
|
+
{
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this->lastLocalArrivalMs = localArrivalMs;
|
|
40
|
+
|
|
41
|
+
// The sample holds the clock offset plus the one way delay of this Sender
|
|
42
|
+
// Report. Assume a symmetric path and remove half of the RTT.
|
|
43
|
+
const int64_t sample = static_cast<int64_t>(localArrivalMs) -
|
|
44
|
+
static_cast<int64_t>(remoteNtpMs) - (static_cast<int64_t>(rttMs) / 2);
|
|
45
|
+
|
|
46
|
+
if (this->samples.size() == RemoteClockOffsetEstimator::WindowSize)
|
|
47
|
+
{
|
|
48
|
+
this->samples.erase(this->samples.begin());
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this->samples.push_back(sample);
|
|
52
|
+
|
|
53
|
+
UpdateOffsetMs();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
std::optional<uint64_t> RemoteClockOffsetEstimator::RemoteMsToLocalMs(uint64_t remoteMs) const
|
|
57
|
+
{
|
|
58
|
+
MS_TRACE();
|
|
59
|
+
|
|
60
|
+
if (!this->offsetMs.has_value())
|
|
61
|
+
{
|
|
62
|
+
return std::nullopt;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const int64_t localMs = static_cast<int64_t>(remoteMs) + this->offsetMs.value();
|
|
66
|
+
|
|
67
|
+
// The given time does not map into our clock, so the input is bogus.
|
|
68
|
+
if (localMs < 0)
|
|
69
|
+
{
|
|
70
|
+
MS_WARN_2TAGS(
|
|
71
|
+
rtp, rtcp, "remote time does not map into our clock [remoteMs:%" PRIu64 "]", remoteMs);
|
|
72
|
+
|
|
73
|
+
return std::nullopt;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return static_cast<uint64_t>(localMs);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
void RemoteClockOffsetEstimator::Reset()
|
|
80
|
+
{
|
|
81
|
+
MS_TRACE();
|
|
82
|
+
|
|
83
|
+
this->samples.clear();
|
|
84
|
+
this->lastLocalArrivalMs = 0;
|
|
85
|
+
this->offsetMs.reset();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
void RemoteClockOffsetEstimator::UpdateOffsetMs()
|
|
89
|
+
{
|
|
90
|
+
MS_TRACE();
|
|
91
|
+
|
|
92
|
+
if (this->samples.size() < RemoteClockOffsetEstimator::MinSampleCount)
|
|
93
|
+
{
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Take the median of the window. While the window is not full its size may
|
|
98
|
+
// be even, in which case the upper of the two middle samples is taken.
|
|
99
|
+
std::vector<int64_t> sortedSamples(this->samples);
|
|
100
|
+
const auto middle = sortedSamples.begin() + (sortedSamples.size() / 2);
|
|
101
|
+
|
|
102
|
+
std::nth_element(sortedSamples.begin(), middle, sortedSamples.end());
|
|
103
|
+
|
|
104
|
+
this->offsetMs = *middle;
|
|
105
|
+
}
|
|
106
|
+
} // namespace RTC
|
|
@@ -701,20 +701,22 @@ namespace RTC
|
|
|
701
701
|
}
|
|
702
702
|
}
|
|
703
703
|
|
|
704
|
-
|
|
705
|
-
RTC::Transport* /*transport*/,
|
|
706
|
-
RTC::Producer* producer,
|
|
707
|
-
uint32_t mappedSsrc,
|
|
708
|
-
uint8_t& worstRemoteFractionLost)
|
|
704
|
+
uint8_t Router::OnTransportNeedWorstRemoteFractionLost(
|
|
705
|
+
RTC::Transport* /*transport*/, RTC::Producer* producer, uint32_t mappedSsrc)
|
|
709
706
|
{
|
|
710
707
|
MS_TRACE();
|
|
711
708
|
|
|
712
|
-
auto& consumers = this->mapProducerConsumers.at(producer);
|
|
709
|
+
const auto& consumers = this->mapProducerConsumers.at(producer);
|
|
713
710
|
|
|
714
|
-
|
|
711
|
+
uint8_t worstRemoteFractionLost{ 0 };
|
|
712
|
+
|
|
713
|
+
for (const auto* consumer : consumers)
|
|
715
714
|
{
|
|
716
|
-
|
|
715
|
+
worstRemoteFractionLost =
|
|
716
|
+
std::max(consumer->GetWorstRemoteFractionLost(mappedSsrc), worstRemoteFractionLost);
|
|
717
717
|
}
|
|
718
|
+
|
|
719
|
+
return worstRemoteFractionLost;
|
|
718
720
|
}
|
|
719
721
|
|
|
720
722
|
void Router::OnTransportNewConsumer(
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// #define MS_LOG_DEV_LEVEL 3
|
|
3
3
|
|
|
4
4
|
#include "Logger.hpp"
|
|
5
|
+
#include "MediaSoupErrors.hpp"
|
|
5
6
|
#include "RTC/RtpDictionaries.hpp"
|
|
6
7
|
|
|
7
8
|
namespace RTC
|
|
@@ -12,10 +13,19 @@ namespace RTC
|
|
|
12
13
|
{
|
|
13
14
|
MS_TRACE();
|
|
14
15
|
|
|
15
|
-
// cname is
|
|
16
|
-
|
|
16
|
+
// cname is mandatory. It is what identifies the streams that come from a same
|
|
17
|
+
// sender, and hence share its clock, so media of a sender that comes with no CNAME
|
|
18
|
+
// cannot be synchronized against the rest of the media of that same sender.
|
|
19
|
+
if (!flatbuffers::IsFieldPresent(data, FBS::RtpParameters::RtcpParameters::VT_CNAME))
|
|
17
20
|
{
|
|
18
|
-
|
|
21
|
+
MS_THROW_TYPE_ERROR("missing cname");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this->cname = data->cname()->str();
|
|
25
|
+
|
|
26
|
+
if (this->cname.empty())
|
|
27
|
+
{
|
|
28
|
+
MS_THROW_TYPE_ERROR("empty cname");
|
|
19
29
|
}
|
|
20
30
|
|
|
21
31
|
// reducedSize is optional, default value is true.
|
|
@@ -204,14 +204,18 @@ namespace RTC
|
|
|
204
204
|
const uint16_t start = GetAckBlockStartAt(idx);
|
|
205
205
|
const uint16_t end = GetAckBlockEndAt(idx);
|
|
206
206
|
|
|
207
|
-
|
|
207
|
+
// NOTE: A block with `end == start` is a legal single TSN block, so it
|
|
208
|
+
// must be kept. dcSCTP's `ChunkValidators::Clean()` uses `end > start`
|
|
209
|
+
// here, which contradicts its own `ChunkValidators::Validate()` (it
|
|
210
|
+
// accepts `end == start`) and needlessly discards valid ack information.
|
|
211
|
+
if (end >= start)
|
|
208
212
|
{
|
|
209
213
|
gapAckBlocks.emplace_back(start, end);
|
|
210
214
|
}
|
|
211
215
|
}
|
|
212
216
|
|
|
213
217
|
// Not more than at most one remaining? Exit early.
|
|
214
|
-
if (gapAckBlocks.size()
|
|
218
|
+
if (gapAckBlocks.size() <= 1)
|
|
215
219
|
{
|
|
216
220
|
return gapAckBlocks;
|
|
217
221
|
}
|