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.
Files changed (55) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/node/lib/Consumer.js +1 -1
  3. package/node/lib/Transport.d.ts.map +1 -1
  4. package/node/lib/Transport.js +7 -0
  5. package/node/lib/ortc.d.ts.map +1 -1
  6. package/node/lib/ortc.js +10 -0
  7. package/node/lib/test/test-PipeTransport.js +35 -30
  8. package/package.json +6 -6
  9. package/worker/include/DepLibUV.hpp +33 -4
  10. package/worker/include/RTC/Consumer.hpp +4 -1
  11. package/worker/include/RTC/Producer.hpp +34 -5
  12. package/worker/include/RTC/RTCP/Packet.hpp +7 -1
  13. package/worker/include/RTC/RTP/Packet.hpp +18 -0
  14. package/worker/include/RTC/RTP/RtpStream.hpp +76 -14
  15. package/worker/include/RTC/RTP/RtpStreamRecv.hpp +191 -7
  16. package/worker/include/RTC/RTP/RtpStreamSend.hpp +56 -10
  17. package/worker/include/RTC/RTP/RtxStream.hpp +34 -2
  18. package/worker/include/RTC/RemoteCaptureTimeEstimator.hpp +92 -0
  19. package/worker/include/RTC/RemoteClockOffsetEstimator.hpp +87 -0
  20. package/worker/include/RTC/Router.hpp +2 -5
  21. package/worker/include/RTC/SimulcastProducerStreamManager.hpp +3 -0
  22. package/worker/include/RTC/Transport.hpp +15 -7
  23. package/worker/include/Shared.hpp +5 -0
  24. package/worker/include/SharedInterface.hpp +6 -0
  25. package/worker/include/Utils.hpp +44 -3
  26. package/worker/meson.build +4 -0
  27. package/worker/mocks/include/MockShared.hpp +7 -0
  28. package/worker/src/DepLibUV.cpp +17 -0
  29. package/worker/src/RTC/Consumer.cpp +8 -5
  30. package/worker/src/RTC/Producer.cpp +85 -16
  31. package/worker/src/RTC/RTP/Packet.cpp +9 -1
  32. package/worker/src/RTC/RTP/RtpStream.cpp +14 -10
  33. package/worker/src/RTC/RTP/RtpStreamRecv.cpp +150 -23
  34. package/worker/src/RTC/RTP/RtpStreamSend.cpp +34 -19
  35. package/worker/src/RTC/RTP/RtxStream.cpp +12 -6
  36. package/worker/src/RTC/RemoteCaptureTimeEstimator.cpp +77 -0
  37. package/worker/src/RTC/RemoteClockOffsetEstimator.cpp +106 -0
  38. package/worker/src/RTC/Router.cpp +10 -8
  39. package/worker/src/RTC/RtpDictionaries/RtcpParameters.cpp +13 -3
  40. package/worker/src/RTC/SCTP/packet/chunks/SackChunk.cpp +6 -2
  41. package/worker/src/RTC/SimulcastProducerStreamManager.cpp +115 -56
  42. package/worker/src/RTC/SvcProducerStreamManager.cpp +22 -17
  43. package/worker/src/RTC/Transport.cpp +96 -4
  44. package/worker/test/src/RTC/RTP/TestPacket.cpp +6 -0
  45. package/worker/test/src/RTC/RTP/TestRtpStreamRecv.cpp +2 -2
  46. package/worker/test/src/RTC/RTP/TestRtpStreamSend.cpp +119 -0
  47. package/worker/test/src/RTC/TestConsumer.cpp +4 -3
  48. package/worker/test/src/RTC/TestPortManager.cpp +1 -1
  49. package/worker/test/src/RTC/TestRemoteCaptureTimeEstimator.cpp +164 -0
  50. package/worker/test/src/RTC/TestRemoteClockOffsetEstimator.cpp +241 -0
  51. package/worker/test/src/RTC/TestSimpleProducerStreamManager.cpp +2 -2
  52. package/worker/test/src/RTC/TestSimulcastProducerStreamManager.cpp +340 -15
  53. package/worker/test/src/RTC/TestSvcProducerStreamManager.cpp +2 -2
  54. package/worker/test/src/RTC/TestTransportTuple.cpp +1 -1
  55. 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
- uint8_t worstRemoteFractionLost{ 0 };
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 (this->params.useInBandFec)
495
+ if (worstRemoteFractionLost > 0)
458
496
  {
459
- // Notify the listener so we'll get the worst remote fraction lost.
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->lastSrReceived != 0)
562
+ if (this->lastSenderReportTiming.has_value())
532
563
  {
564
+ const auto& senderReportTiming = this->lastSenderReportTiming.value();
533
565
  // Get delay in milliseconds.
534
- auto delayMs = static_cast<uint32_t>(this->shared->GetTimeMs() - this->lastSrReceived);
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(this->lastSrTimestamp);
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
- Utils::Time::Ntp ntp{}; // NOLINT(cppcoreguidelines-pro-type-member-init)
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
- ntp.seconds = report->GetNtpSec();
576
- ntp.fractions = report->GetNtpFrac();
611
+ this->lastSenderReportTiming = SenderReportTiming{
612
+ .compactNtp = compactNtp,
613
+ .receivedMs = this->shared->GetTimeMs(),
614
+ };
577
615
 
578
- this->lastSenderReportNtpMs = Utils::Time::Ntp2TimeMs(ntp);
579
- this->lastSenderReportTs = report->GetRtpTs();
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
- this->lastRrReceivedMs = this->shared->GetTimeMs();
326
- this->lastRrTimestamp = report->GetNtpSec() << 16;
327
- this->lastRrTimestamp += report->GetNtpFrac() >> 16;
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
- auto ntp = Utils::Time::TimeMs2Ntp(nowMs);
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 maxPacketMs.
343
- auto diffMs = nowMs - this->maxPacketMs;
344
- auto diffTs = diffMs * GetClockRate() / 1000;
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(this->maxPacketTs + diffTs);
363
+ report->SetRtpTs(rtpTs);
352
364
 
353
365
  // Update info about last Sender Report.
354
- this->lastSenderReportNtpMs = nowMs;
355
- this->lastSenderReportTs = this->maxPacketTs + diffTs;
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->lastRrReceivedMs == 0u)
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 - this->lastRrReceivedMs);
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(this->lastRrTimestamp);
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->lastSrReceived != 0)
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() - this->lastSrReceived;
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(this->lastSrTimestamp);
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
- this->lastSrReceived = this->shared->GetTimeMs();
167
- this->lastSrTimestamp = report->GetNtpSec() << 16;
168
- this->lastSrTimestamp += report->GetNtpFrac() >> 16;
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
- void Router::OnTransportNeedWorstRemoteFractionLost(
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
- for (auto* consumer : consumers)
711
+ uint8_t worstRemoteFractionLost{ 0 };
712
+
713
+ for (const auto* consumer : consumers)
715
714
  {
716
- consumer->NeedWorstRemoteFractionLost(mappedSsrc, worstRemoteFractionLost);
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 optional.
16
- if (flatbuffers::IsFieldPresent(data, FBS::RtpParameters::RtcpParameters::VT_CNAME))
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
- this->cname = data->cname()->str();
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
- if (end > start)
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() == 1)
218
+ if (gapAckBlocks.size() <= 1)
215
219
  {
216
220
  return gapAckBlocks;
217
221
  }