mediasoup 3.24.2 → 3.25.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 (53) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/node/lib/Transport.d.ts.map +1 -1
  3. package/node/lib/Transport.js +7 -0
  4. package/node/lib/ortc.d.ts.map +1 -1
  5. package/node/lib/ortc.js +10 -0
  6. package/node/lib/test/test-PipeTransport.js +35 -30
  7. package/package.json +1 -1
  8. package/worker/include/DepLibUV.hpp +33 -4
  9. package/worker/include/RTC/Consumer.hpp +4 -1
  10. package/worker/include/RTC/Producer.hpp +34 -5
  11. package/worker/include/RTC/RTCP/Packet.hpp +7 -1
  12. package/worker/include/RTC/RTP/Packet.hpp +18 -0
  13. package/worker/include/RTC/RTP/RtpStream.hpp +76 -14
  14. package/worker/include/RTC/RTP/RtpStreamRecv.hpp +191 -7
  15. package/worker/include/RTC/RTP/RtpStreamSend.hpp +56 -10
  16. package/worker/include/RTC/RTP/RtxStream.hpp +34 -2
  17. package/worker/include/RTC/RemoteCaptureTimeEstimator.hpp +92 -0
  18. package/worker/include/RTC/RemoteClockOffsetEstimator.hpp +87 -0
  19. package/worker/include/RTC/Router.hpp +2 -5
  20. package/worker/include/RTC/SimulcastProducerStreamManager.hpp +3 -0
  21. package/worker/include/RTC/Transport.hpp +15 -7
  22. package/worker/include/Shared.hpp +5 -0
  23. package/worker/include/SharedInterface.hpp +6 -0
  24. package/worker/include/Utils.hpp +44 -3
  25. package/worker/meson.build +4 -0
  26. package/worker/mocks/include/MockShared.hpp +7 -0
  27. package/worker/src/DepLibUV.cpp +17 -0
  28. package/worker/src/RTC/Consumer.cpp +8 -5
  29. package/worker/src/RTC/Producer.cpp +85 -16
  30. package/worker/src/RTC/RTP/Packet.cpp +9 -1
  31. package/worker/src/RTC/RTP/RtpStream.cpp +14 -10
  32. package/worker/src/RTC/RTP/RtpStreamRecv.cpp +150 -23
  33. package/worker/src/RTC/RTP/RtpStreamSend.cpp +34 -19
  34. package/worker/src/RTC/RTP/RtxStream.cpp +12 -6
  35. package/worker/src/RTC/RemoteCaptureTimeEstimator.cpp +77 -0
  36. package/worker/src/RTC/RemoteClockOffsetEstimator.cpp +106 -0
  37. package/worker/src/RTC/Router.cpp +10 -8
  38. package/worker/src/RTC/RtpDictionaries/RtcpParameters.cpp +13 -3
  39. package/worker/src/RTC/SCTP/packet/chunks/SackChunk.cpp +6 -2
  40. package/worker/src/RTC/SimulcastProducerStreamManager.cpp +82 -40
  41. package/worker/src/RTC/Transport.cpp +96 -4
  42. package/worker/test/src/RTC/RTP/TestPacket.cpp +6 -0
  43. package/worker/test/src/RTC/RTP/TestRtpStreamRecv.cpp +2 -2
  44. package/worker/test/src/RTC/RTP/TestRtpStreamSend.cpp +119 -0
  45. package/worker/test/src/RTC/TestConsumer.cpp +4 -3
  46. package/worker/test/src/RTC/TestPortManager.cpp +1 -1
  47. package/worker/test/src/RTC/TestRemoteCaptureTimeEstimator.cpp +164 -0
  48. package/worker/test/src/RTC/TestRemoteClockOffsetEstimator.cpp +241 -0
  49. package/worker/test/src/RTC/TestSimpleProducerStreamManager.cpp +2 -2
  50. package/worker/test/src/RTC/TestSimulcastProducerStreamManager.cpp +340 -15
  51. package/worker/test/src/RTC/TestSvcProducerStreamManager.cpp +2 -2
  52. package/worker/test/src/RTC/TestTransportTuple.cpp +1 -1
  53. package/worker/test/src/Utils/TestTime.cpp +49 -0
@@ -164,19 +164,32 @@ namespace RTC
164
164
  {
165
165
  MS_TRACE();
166
166
 
167
- // Just interested if this is the first Sender Report for a RTP stream.
168
- if (!first)
167
+ if (first)
168
+ {
169
+ MS_DEBUG_TAG(simulcast, "first SenderReport [ssrc:%" PRIu32 "]", rtpStream->GetSsrc());
170
+ }
171
+
172
+ // If the capture instant of our RTP timestamp reference stream cannot be told
173
+ // yet, do nothing since we know we won't be able to switch.
174
+ auto* producerTsReferenceRtpStream = GetProducerTsReferenceRtpStream();
175
+
176
+ if (!producerTsReferenceRtpStream || !producerTsReferenceRtpStream->GetCaptureMapping().has_value())
169
177
  {
170
178
  return;
171
179
  }
172
180
 
173
- MS_DEBUG_TAG(simulcast, "first SenderReport [ssrc:%" PRIu32 "]", rtpStream->GetSsrc());
181
+ // Other than the first Sender Report of each stream, the only one worth checking
182
+ // layers upon is the one that makes the capture instant of the RTP timestamp
183
+ // reference stream known, since other spatial layers may become switchable then.
184
+ // NOTE: Doing it on every single Sender Report would ask the Transport to
185
+ // redistribute its outgoing bitrate at the pace of the RTCP of the sender and we
186
+ // don't want that.
187
+ const bool tsReferenceCaptureMappingIsNew =
188
+ this->tsReferenceSpatialLayerWithCaptureMapping != this->tsReferenceSpatialLayer;
174
189
 
175
- // If our RTP timestamp reference stream does not yet have SR, do nothing
176
- // since we know we won't be able to switch.
177
- auto* producerTsReferenceRtpStream = GetProducerTsReferenceRtpStream();
190
+ this->tsReferenceSpatialLayerWithCaptureMapping = this->tsReferenceSpatialLayer;
178
191
 
179
- if (!producerTsReferenceRtpStream || !producerTsReferenceRtpStream->GetSenderReportNtpMs())
192
+ if (!first && !tsReferenceCaptureMappingIsNew)
180
193
  {
181
194
  return;
182
195
  }
@@ -589,43 +602,71 @@ namespace RTC
589
602
  {
590
603
  tsOffset = 0u;
591
604
  }
592
- // If this is not the RTP stream we use as TS reference, do NTP based RTP
593
- // TS synchronization.
605
+ // If this is not the RTP stream we use as TS reference, synchronize its RTP
606
+ // timestamps based on the capture instant of the media.
594
607
  else
595
608
  {
596
- auto* producerTsReferenceRtpStream = GetProducerTsReferenceRtpStream();
597
- auto* producerTargetRtpStream = GetProducerTargetRtpStream();
598
-
599
- // NOTE: If we are here is because we have Sender Reports for both the
600
- // TS reference stream and the target one.
601
- MS_ASSERT(
602
- producerTsReferenceRtpStream->GetSenderReportNtpMs(),
603
- "no Sender Report for TS reference RTP stream");
604
- MS_ASSERT(
605
- producerTargetRtpStream->GetSenderReportNtpMs(), "no Sender Report for current RTP stream");
606
-
607
- // Calculate NTP and TS stuff.
608
- auto ntpMs1 = producerTsReferenceRtpStream->GetSenderReportNtpMs();
609
- auto ts1 = producerTsReferenceRtpStream->GetSenderReportTs();
610
- auto ntpMs2 = producerTargetRtpStream->GetSenderReportNtpMs();
611
- auto ts2 = producerTargetRtpStream->GetSenderReportTs();
612
- int64_t diffMs;
613
-
614
- if (ntpMs2 >= ntpMs1)
609
+ const auto* producerTsReferenceRtpStream = GetProducerTsReferenceRtpStream();
610
+ // NOTE: The stream of the spatial layer of this packet, which is not always
611
+ // the one of the target spatial layer. A resync may be pending for the
612
+ // current spatial layer while the target one is a different one.
613
+ const auto* producerRtpStream = this->producerRtpStreams.at(spatialLayer);
614
+
615
+ const auto tsReferenceCaptureMapping = producerTsReferenceRtpStream->GetCaptureMapping();
616
+ const auto captureMapping = producerRtpStream->GetCaptureMapping();
617
+
618
+ // Without the capture instant of the TS reference stream there is nothing to
619
+ // align this one to, so take this spatial layer as the new TS reference and
620
+ // let its RTP timestamps go untouched, which is what would have been done had
621
+ // it been chosen as TS reference in the first place.
622
+ if (!tsReferenceCaptureMapping.has_value())
615
623
  {
616
- diffMs = ntpMs2 - ntpMs1;
624
+ MS_DEBUG_TAG(
625
+ simulcast,
626
+ "cannot tell the capture instant of the TS reference stream, using spatial layer "
627
+ "%" PRIi16 " as RTP timestamp reference",
628
+ spatialLayer);
629
+
630
+ this->tsReferenceSpatialLayer = spatialLayer;
617
631
  }
618
- else
632
+ // The capture instant of this stream cannot be told yet, so there is no way to
633
+ // align it and no reason to give up a TS reference stream that is good. Discard
634
+ // the packet and wait, since the Sender Report that is missing is on its way.
635
+ else if (!captureMapping.has_value())
619
636
  {
620
- diffMs = -1 * (ntpMs1 - ntpMs2);
621
- }
637
+ MS_DEBUG_TAG(
638
+ simulcast,
639
+ "cannot tell yet the capture instant of spatial layer %" PRIi16
640
+ ", discarding packet [ssrc:%" PRIu32 ", seq:%" PRIu16 "]",
641
+ spatialLayer,
642
+ packet->GetSsrc(),
643
+ packet->GetSequenceNumber());
644
+
645
+ // Ask for another key frame since this one is being discarded.
646
+ if (packet->IsKeyFrame())
647
+ {
648
+ RequestKeyFrame();
649
+ }
622
650
 
623
- const int64_t diffTs = diffMs * clockRate / 1000;
624
- const uint32_t newTs2 = ts2 - diffTs;
651
+ result.type = RtpPacketProcessResult::Type::SILENT_DROP;
625
652
 
626
- // Apply offset. This is the difference that later must be removed from
627
- // the sending RTP packet.
628
- tsOffset = newTs2 - ts1;
653
+ return result;
654
+ }
655
+ else
656
+ {
657
+ // Calculate capture instant and TS stuff.
658
+ const auto captureMs1 = tsReferenceCaptureMapping.value().captureMs;
659
+ const auto ts1 = tsReferenceCaptureMapping.value().ts;
660
+ const auto captureMs2 = captureMapping.value().captureMs;
661
+ const auto ts2 = captureMapping.value().ts;
662
+ const int64_t diffMs = static_cast<int64_t>(captureMs2) - static_cast<int64_t>(captureMs1);
663
+ const int64_t diffTs = diffMs * clockRate / 1000;
664
+ const uint32_t newTs2 = ts2 - diffTs;
665
+
666
+ // Apply offset. This is the difference that later must be removed from
667
+ // the sending RTP packet.
668
+ tsOffset = newTs2 - ts1;
669
+ }
629
670
  }
630
671
 
631
672
  // When switching to a new stream it may happen that the timestamp of this
@@ -857,8 +898,9 @@ namespace RTC
857
898
 
858
899
  // If we don't have yet a RTP timestamp reference, set it now.
859
900
  if (
860
- newTargetSpatialLayer != -1 && (this->tsReferenceSpatialLayer == -1 ||
861
- !GetProducerTsReferenceRtpStream()->GetSenderReportNtpMs()))
901
+ newTargetSpatialLayer != -1 &&
902
+ (this->tsReferenceSpatialLayer == -1 ||
903
+ !GetProducerTsReferenceRtpStream()->GetCaptureMapping().has_value()))
862
904
  {
863
905
  MS_DEBUG_TAG(
864
906
  simulcast, "using spatial layer %" PRIi16 " as RTP timestamp reference", newTargetSpatialLayer);
@@ -1050,7 +1092,7 @@ namespace RTC
1050
1092
 
1051
1093
  return (
1052
1094
  this->tsReferenceSpatialLayer == -1 || spatialLayer == this->tsReferenceSpatialLayer ||
1053
- this->producerRtpStreams.at(spatialLayer)->GetSenderReportNtpMs());
1095
+ this->producerRtpStreams.at(spatialLayer)->GetCaptureMapping().has_value());
1054
1096
  }
1055
1097
 
1056
1098
  RTC::RTP::RtpStreamRecv* SimulcastProducerStreamManager::GetProducerTsReferenceRtpStream() const
@@ -640,6 +640,15 @@ namespace RTC
640
640
  // Insert into the map.
641
641
  this->mapProducers[producerId] = producer;
642
642
 
643
+ // Take this Producer into account for the capture time estimation of its
644
+ // sender.
645
+ {
646
+ const auto& cname = producer->GetRtpParameters().rtcp.cname;
647
+
648
+ this->mapCnameRemoteCaptureTimeEstimator[cname].UpdateSource(
649
+ producer->GetRtpHeaderExtensionIds().absCaptureTime != 0);
650
+ }
651
+
643
652
  MS_DEBUG_DEV("Producer created [producerId:%s]", producerId.c_str());
644
653
 
645
654
  // Take the transport related RTP header extensions of the Producer and
@@ -680,6 +689,14 @@ namespace RTC
680
689
  producerRtpHeaderExtensionIds.dependencyDescriptor;
681
690
  }
682
691
 
692
+ // NOTE: Not transport related, but needed here so that received packets carry
693
+ // its id and `RtpStreamRecv` can read the extension off them.
694
+ if (producerRtpHeaderExtensionIds.absCaptureTime != 0)
695
+ {
696
+ this->recvRtpHeaderExtensionIds.absCaptureTime =
697
+ producerRtpHeaderExtensionIds.absCaptureTime;
698
+ }
699
+
683
700
  // Create status response.
684
701
  auto responseOffset = FBS::Transport::CreateProduceResponse(
685
702
  request->GetBufferBuilder(), FBS::RtpParameters::Type(producer->GetType()));
@@ -1245,6 +1262,26 @@ namespace RTC
1245
1262
  // Remove it from the map.
1246
1263
  this->mapProducers.erase(producer->id);
1247
1264
 
1265
+ // Remove the capture time estimator of its sender once its last Producer
1266
+ // is gone.
1267
+ {
1268
+ const auto& cname = producer->GetRtpParameters().rtcp.cname;
1269
+
1270
+ const bool cnameStillInUse = std::ranges::any_of(
1271
+ this->mapProducers,
1272
+ [&cname](const auto& kv)
1273
+ {
1274
+ const auto* otherProducer = kv.second;
1275
+
1276
+ return otherProducer->GetRtpParameters().rtcp.cname == cname;
1277
+ });
1278
+
1279
+ if (!cnameStillInUse)
1280
+ {
1281
+ this->mapCnameRemoteCaptureTimeEstimator.erase(cname);
1282
+ }
1283
+ }
1284
+
1248
1285
  // Tell the child class to clear associated SSRCs.
1249
1286
  for (const auto& kv : producer->GetRtpStreams())
1250
1287
  {
@@ -2538,6 +2575,17 @@ namespace RTC
2538
2575
  {
2539
2576
  MS_TRACE();
2540
2577
 
2578
+ // Feed the capture time estimator of the sender of this Producer.
2579
+ const auto it =
2580
+ this->mapCnameRemoteCaptureTimeEstimator.find(producer->GetRtpParameters().rtcp.cname);
2581
+
2582
+ if (it != this->mapCnameRemoteCaptureTimeEstimator.end())
2583
+ {
2584
+ auto& remoteCaptureTimeEstimator = it->second;
2585
+
2586
+ remoteCaptureTimeEstimator.SenderReportReceived(rtpStream);
2587
+ }
2588
+
2541
2589
  this->listener->OnTransportProducerRtcpSenderReport(this, producer, rtpStream, first);
2542
2590
  }
2543
2591
 
@@ -2555,13 +2603,57 @@ namespace RTC
2555
2603
  SendRtcpPacket(packet);
2556
2604
  }
2557
2605
 
2558
- void Transport::OnProducerNeedWorstRemoteFractionLost(
2559
- RTC::Producer* producer, uint32_t mappedSsrc, uint8_t& worstRemoteFractionLost)
2606
+ uint8_t Transport::OnProducerNeedWorstRemoteFractionLost(RTC::Producer* producer, uint32_t mappedSsrc)
2607
+ {
2608
+ MS_TRACE();
2609
+
2610
+ return this->listener->OnTransportNeedWorstRemoteFractionLost(this, producer, mappedSsrc);
2611
+ }
2612
+
2613
+ std::optional<uint64_t> Transport::OnProducerNeedLocalCaptureMs(
2614
+ RTC::Producer* producer, const RTC::RTP::RtpStreamRecv* rtpStream, uint32_t ts)
2615
+ {
2616
+ MS_TRACE();
2617
+
2618
+ const auto it =
2619
+ this->mapCnameRemoteCaptureTimeEstimator.find(producer->GetRtpParameters().rtcp.cname);
2620
+
2621
+ if (it == this->mapCnameRemoteCaptureTimeEstimator.end())
2622
+ {
2623
+ return std::nullopt;
2624
+ }
2625
+
2626
+ const auto& remoteCaptureTimeEstimator = it->second;
2627
+
2628
+ return remoteCaptureTimeEstimator.GetLocalCaptureMs(rtpStream, ts);
2629
+ }
2630
+
2631
+ std::optional<int64_t> Transport::OnProducerNeedRemoteClockOffsetMs(const RTC::Producer* producer)
2560
2632
  {
2561
2633
  MS_TRACE();
2562
2634
 
2563
- this->listener->OnTransportNeedWorstRemoteFractionLost(
2564
- this, producer, mappedSsrc, worstRemoteFractionLost);
2635
+ const auto it =
2636
+ this->mapCnameRemoteCaptureTimeEstimator.find(producer->GetRtpParameters().rtcp.cname);
2637
+
2638
+ if (it == this->mapCnameRemoteCaptureTimeEstimator.end())
2639
+ {
2640
+ return std::nullopt;
2641
+ }
2642
+
2643
+ const auto& remoteCaptureTimeEstimator = it->second;
2644
+ const auto clockOffsetMs = remoteCaptureTimeEstimator.GetClockOffsetMs();
2645
+
2646
+ if (!clockOffsetMs.has_value())
2647
+ {
2648
+ return std::nullopt;
2649
+ }
2650
+
2651
+ // NOTE: The estimator gives the offset against our own monotonic clock, while what
2652
+ // a receiver reconstructs out of the Sender Reports we send is the clock we
2653
+ // announce, so the distance to the NTP epoch has to be taken into account. Both
2654
+ // terms are huge and their sum is small, so they are added as milliseconds before
2655
+ // anything scales them up.
2656
+ return clockOffsetMs.value() + static_cast<int64_t>(this->shared->GetNtpOffsetMs());
2565
2657
  }
2566
2658
 
2567
2659
  void Transport::OnConsumerSendRtpPacket(RTC::Consumer* consumer, RTC::RTP::Packet* packet)
@@ -1059,12 +1059,16 @@ SCENARIO("RTP Packet", "[serializable][rtp][packet]")
1059
1059
  /*paddingLength*/ 0);
1060
1060
 
1061
1061
  REQUIRE(packet->IsPaddedTo4Bytes() == true);
1062
+ REQUIRE(packet->GetCaptureMs() == std::nullopt);
1062
1063
 
1063
1064
  packet->SetPayloadType(100);
1064
1065
  packet->SetMarker(true);
1065
1066
  packet->SetSequenceNumber(12345);
1066
1067
  packet->SetTimestamp(987654321);
1067
1068
  packet->SetSsrc(1234567890);
1069
+ packet->SetCaptureMs(99998888);
1070
+
1071
+ REQUIRE(packet->GetCaptureMs() == 99998888);
1068
1072
 
1069
1073
  std::vector<RTC::RTP::Packet::Extension> extensions;
1070
1074
 
@@ -1204,6 +1208,7 @@ SCENARIO("RTP Packet", "[serializable][rtp][packet]")
1204
1208
  REQUIRE(extensionLen == 3);
1205
1209
 
1206
1210
  REQUIRE(packet->IsPaddedTo4Bytes() == true);
1211
+ REQUIRE(packet->GetCaptureMs() == 99998888);
1207
1212
 
1208
1213
  /* Clone it. */
1209
1214
 
@@ -1252,6 +1257,7 @@ SCENARIO("RTP Packet", "[serializable][rtp][packet]")
1252
1257
  REQUIRE(extensionLen == 3);
1253
1258
 
1254
1259
  REQUIRE(packet->IsPaddedTo4Bytes() == true);
1260
+ REQUIRE(packet->GetCaptureMs() == 99998888);
1255
1261
 
1256
1262
  /* Set payload. */
1257
1263
 
@@ -106,9 +106,9 @@ SCENARIO("RtpStreamRecv", "[rtp][rtpstream][rtpstreamrecv]")
106
106
  }
107
107
  }
108
108
 
109
- void OnRtpStreamNeedWorstRemoteFractionLost(
110
- RTC::RTP::RtpStreamRecv* /*rtpStream*/, uint8_t& /*worstRemoteFractionLost*/) override
109
+ uint8_t OnRtpStreamNeedWorstRemoteFractionLost(RTC::RTP::RtpStreamRecv* /*rtpStream*/) override
111
110
  {
111
+ return 0;
112
112
  }
113
113
 
114
114
  public:
@@ -1,5 +1,6 @@
1
1
  #include "common.hpp"
2
2
  #include "RTC/RTCP/FeedbackRtpNack.hpp"
3
+ #include "RTC/RTCP/SenderReport.hpp"
3
4
  #include "RTC/RTP/Codecs/AV1.hpp"
4
5
  #include "RTC/RTP/Codecs/PayloadDescriptorHandler.hpp"
5
6
  #include "RTC/RTP/Codecs/VP8.hpp"
@@ -1125,6 +1126,124 @@ SCENARIO("RtpStreamSend", "[rtp][rtcp][nack][rtpstream][rtpstreamsend]")
1125
1126
  REQUIRE(result == RTC::RTP::RtpStreamSend::ReceivePacketResult::DISCARDED);
1126
1127
  }
1127
1128
 
1129
+ SECTION("Sender Report RTP timestamp is based on the capture instant when known")
1130
+ {
1131
+ // Instant reported by the mocked clock, and hence the one at which packets are seen.
1132
+ constexpr uint64_t PacketMs{ 1000 };
1133
+ constexpr uint32_t PacketTs{ 1533790901 };
1134
+
1135
+ TestRtpStreamListener testRtpStreamListener;
1136
+
1137
+ RTC::RTP::RtpStream::Params params;
1138
+
1139
+ params.ssrc = 1111;
1140
+ params.clockRate = 90000;
1141
+ params.mimeType.type = RTC::RtpCodecMimeType::Type::VIDEO;
1142
+
1143
+ std::string mid;
1144
+
1145
+ // Without capture instant, the instant at which the packet was seen is used.
1146
+ {
1147
+ RTC::RTP::RtpStreamSend stream(
1148
+ std::addressof(testRtpStreamListener), std::addressof(shared), params, mid);
1149
+ auto packet(createRtpPacket(rtpBuffer1, sizeof(rtpBuffer1), 21006, PacketTs));
1150
+
1151
+ sendRtpPacket(
1152
+ {
1153
+ { std::addressof(stream), params.ssrc }
1154
+ },
1155
+ packet.get());
1156
+
1157
+ const std::unique_ptr<RTC::RTCP::SenderReport> report(
1158
+ stream.GetRtcpSenderReport(PacketMs + 1000));
1159
+
1160
+ REQUIRE(report);
1161
+ REQUIRE(report->GetRtpTs() == PacketTs + params.clockRate);
1162
+ }
1163
+
1164
+ // With capture instant, the extra half second since it is accounted for.
1165
+ {
1166
+ RTC::RTP::RtpStreamSend stream(
1167
+ std::addressof(testRtpStreamListener), std::addressof(shared), params, mid);
1168
+ auto packet(createRtpPacket(rtpBuffer1, sizeof(rtpBuffer1), 21006, PacketTs));
1169
+
1170
+ packet->SetCaptureMs(PacketMs - 500);
1171
+
1172
+ sendRtpPacket(
1173
+ {
1174
+ { std::addressof(stream), params.ssrc }
1175
+ },
1176
+ packet.get());
1177
+
1178
+ const std::unique_ptr<RTC::RTCP::SenderReport> report(
1179
+ stream.GetRtcpSenderReport(PacketMs + 1000));
1180
+
1181
+ REQUIRE(report);
1182
+ REQUIRE(report->GetRtpTs() == PacketTs + ((1500 * params.clockRate) / 1000));
1183
+ }
1184
+
1185
+ // A capture instant ahead of now, which the estimation may yield, does not make the
1186
+ // difference wrap around.
1187
+ {
1188
+ RTC::RTP::RtpStreamSend stream(
1189
+ std::addressof(testRtpStreamListener), std::addressof(shared), params, mid);
1190
+ auto packet(createRtpPacket(rtpBuffer1, sizeof(rtpBuffer1), 21006, PacketTs));
1191
+
1192
+ packet->SetCaptureMs(PacketMs + 3000);
1193
+
1194
+ sendRtpPacket(
1195
+ {
1196
+ { std::addressof(stream), params.ssrc }
1197
+ },
1198
+ packet.get());
1199
+
1200
+ const std::unique_ptr<RTC::RTCP::SenderReport> report(
1201
+ stream.GetRtcpSenderReport(PacketMs + 1000));
1202
+
1203
+ REQUIRE(report);
1204
+ REQUIRE(report->GetRtpTs() == PacketTs);
1205
+ }
1206
+ }
1207
+
1208
+ SECTION("no Sender Report is generated once the stream has stopped sending")
1209
+ {
1210
+ // Instant reported by the mocked clock, and hence the one at which packets are seen.
1211
+ constexpr uint64_t PacketMs{ 1000 };
1212
+ constexpr uint32_t PacketTs{ 1533790901 };
1213
+
1214
+ TestRtpStreamListener testRtpStreamListener;
1215
+
1216
+ RTC::RTP::RtpStream::Params params;
1217
+
1218
+ params.ssrc = 1111;
1219
+ params.clockRate = 90000;
1220
+ params.mimeType.type = RTC::RtpCodecMimeType::Type::VIDEO;
1221
+
1222
+ std::string mid;
1223
+
1224
+ RTC::RTP::RtpStreamSend stream(
1225
+ std::addressof(testRtpStreamListener), std::addressof(shared), params, mid);
1226
+ auto packet(createRtpPacket(rtpBuffer1, sizeof(rtpBuffer1), 21006, PacketTs));
1227
+
1228
+ sendRtpPacket(
1229
+ {
1230
+ { std::addressof(stream), params.ssrc }
1231
+ },
1232
+ packet.get());
1233
+
1234
+ // Right at the limit the Sender Report is still generated.
1235
+ const std::unique_ptr<RTC::RTCP::SenderReport> report(
1236
+ stream.GetRtcpSenderReport(PacketMs + RTC::RTP::RtpStreamSend::MaxSenderReportReferenceAgeMs));
1237
+
1238
+ REQUIRE(report);
1239
+
1240
+ // Past the limit it is not.
1241
+ const std::unique_ptr<RTC::RTCP::SenderReport> staleReport(stream.GetRtcpSenderReport(
1242
+ PacketMs + RTC::RTP::RtpStreamSend::MaxSenderReportReferenceAgeMs + 1));
1243
+
1244
+ REQUIRE_FALSE(staleReport);
1245
+ }
1246
+
1128
1247
  #ifdef PERFORMANCE_TEST
1129
1248
  SECTION("Performance")
1130
1249
  {
@@ -33,9 +33,9 @@ namespace
33
33
  {
34
34
  }
35
35
 
36
- void OnRtpStreamNeedWorstRemoteFractionLost(
37
- RTC::RTP::RtpStreamRecv* /*rtpStream*/, uint8_t& /*worstRemoteFractionLost*/) override
36
+ uint8_t OnRtpStreamNeedWorstRemoteFractionLost(RTC::RTP::RtpStreamRecv* /*rtpStream*/) override
38
37
  {
38
+ return 0;
39
39
  }
40
40
  };
41
41
 
@@ -104,7 +104,8 @@ namespace
104
104
 
105
105
  encoding.ssrc = 1234567890;
106
106
 
107
- rtpParameters.mid = "mid";
107
+ rtpParameters.mid = "mid";
108
+ rtpParameters.rtcp.cname = "cname";
108
109
  rtpParameters.codecs.emplace_back(codec);
109
110
  rtpParameters.encodings.emplace_back(encoding);
110
111
  rtpParameters.headerExtensions = std::vector<RTC::RtpHeaderExtensionParameters>();
@@ -10,7 +10,7 @@
10
10
  // merged unrelated bindings. This scenario locks down the post-fix
11
11
  // behavior: distinct tuples produce distinct keys, equal tuples produce equal
12
12
  // keys.
13
- SCENARIO("PortManager", "[rtc][portmanager]")
13
+ SCENARIO("PortManager", "[portmanager]")
14
14
  {
15
15
  // Helper: build an IPv4 `sockaddr_storage` from a dotted-quad string + port=0.
16
16
  auto makeV4 = [](const char* dottedQuad)
@@ -0,0 +1,164 @@
1
+ #include "common.hpp"
2
+ #include "RTC/RTCP/SenderReport.hpp"
3
+ #include "RTC/RTP/RtpStreamRecv.hpp"
4
+ #include "RTC/RemoteCaptureTimeEstimator.hpp"
5
+ #include "mocks/include/MockShared.hpp"
6
+ #include <catch2/catch_test_macros.hpp>
7
+
8
+ SCENARIO("RemoteCaptureTimeEstimator", "[rtp][rtcp][remotecapturetimeestimator]")
9
+ {
10
+ // Wall clock of the remote sender when it generates its first Sender Report
11
+ // (seconds since 1900).
12
+ constexpr uint32_t RemoteBaseNtpSec{ 3976000000 };
13
+ // RTP timestamp that first Sender Report reports about.
14
+ constexpr uint32_t RemoteBaseTs{ 1000000 };
15
+ // Our own monotonic clock when that first Sender Report arrives.
16
+ constexpr uint64_t LocalBaseMs{ 1000000 };
17
+ constexpr uint32_t ClockRate{ 90000 };
18
+ constexpr uint32_t Ssrc{ 1111 };
19
+
20
+ class RtpStreamRecvListener : public RTC::RTP::RtpStreamRecv::Listener
21
+ {
22
+ public:
23
+ void OnRtpStreamScore(
24
+ RTC::RTP::RtpStream* /*rtpStream*/, uint8_t /*score*/, uint8_t /*previousScore*/) override
25
+ {
26
+ }
27
+
28
+ void OnRtpStreamSendRtcpPacket(
29
+ RTC::RTP::RtpStreamRecv* /*rtpStream*/, RTC::RTCP::Packet* /*packet*/) override
30
+ {
31
+ }
32
+
33
+ uint8_t OnRtpStreamNeedWorstRemoteFractionLost(RTC::RTP::RtpStreamRecv* /*rtpStream*/) override
34
+ {
35
+ return 0;
36
+ }
37
+ };
38
+
39
+ uint64_t nowMs{ LocalBaseMs };
40
+
41
+ mocks::MockShared shared(/*getTimeMs*/
42
+ [&nowMs]() -> uint64_t
43
+ {
44
+ return nowMs;
45
+ });
46
+
47
+ RTC::RTP::RtpStream::Params params;
48
+
49
+ params.ssrc = Ssrc;
50
+ params.clockRate = ClockRate;
51
+
52
+ RtpStreamRecvListener listener;
53
+ RTC::RTP::RtpStreamRecv rtpStream(
54
+ std::addressof(listener),
55
+ std::addressof(shared),
56
+ params,
57
+ /*sendNackDelayMs*/ 0,
58
+ /*useRtpInactivityCheck*/ false);
59
+
60
+ RTC::RemoteCaptureTimeEstimator estimator;
61
+
62
+ // Makes the Sender Report about `RemoteBaseTs` plus `idx` seconds of media reach
63
+ // us with no delay at all, and feeds it to the estimator.
64
+ auto receiveSenderReport = [&nowMs, &rtpStream, &estimator](uint32_t idx) -> void
65
+ {
66
+ nowMs = LocalBaseMs + (idx * 1000);
67
+
68
+ RTC::RTCP::SenderReport report;
69
+
70
+ report.SetSsrc(Ssrc);
71
+ report.SetNtpSec(RemoteBaseNtpSec + idx);
72
+ report.SetNtpFrac(0);
73
+ report.SetRtpTs(RemoteBaseTs + (idx * ClockRate));
74
+
75
+ rtpStream.ReceiveRtcpSenderReport(std::addressof(report));
76
+ estimator.SenderReportReceived(std::addressof(rtpStream));
77
+ };
78
+
79
+ SECTION("no source until the first Producer has been taken into account")
80
+ {
81
+ REQUIRE_FALSE(estimator.GetSource().has_value());
82
+ REQUIRE_FALSE(estimator.GetLocalCaptureMs(std::addressof(rtpStream), RemoteBaseTs).has_value());
83
+ }
84
+
85
+ SECTION("the first Producer chooses abs-capture-time when it negotiated it")
86
+ {
87
+ estimator.UpdateSource(/*absCaptureTimeNegotiated*/ true);
88
+
89
+ REQUIRE(estimator.GetSource() == RTC::RemoteCaptureTimeEstimator::Source::ABS_CAPTURE_TIME);
90
+ }
91
+
92
+ SECTION("the first Producer chooses Sender Report when it did not negotiate abs-capture-time")
93
+ {
94
+ estimator.UpdateSource(/*absCaptureTimeNegotiated*/ false);
95
+
96
+ REQUIRE(estimator.GetSource() == RTC::RemoteCaptureTimeEstimator::Source::SENDER_REPORT);
97
+ }
98
+
99
+ SECTION("a Producer without abs-capture-time moves the source to Sender Report")
100
+ {
101
+ estimator.UpdateSource(/*absCaptureTimeNegotiated*/ true);
102
+ estimator.UpdateSource(/*absCaptureTimeNegotiated*/ false);
103
+
104
+ REQUIRE(estimator.GetSource() == RTC::RemoteCaptureTimeEstimator::Source::SENDER_REPORT);
105
+ }
106
+
107
+ SECTION("the source never moves back to abs-capture-time")
108
+ {
109
+ estimator.UpdateSource(/*absCaptureTimeNegotiated*/ false);
110
+ estimator.UpdateSource(/*absCaptureTimeNegotiated*/ true);
111
+ estimator.UpdateSource(/*absCaptureTimeNegotiated*/ true);
112
+
113
+ REQUIRE(estimator.GetSource() == RTC::RemoteCaptureTimeEstimator::Source::SENDER_REPORT);
114
+ }
115
+
116
+ SECTION("the capture instant is translated into our clock")
117
+ {
118
+ estimator.UpdateSource(/*absCaptureTimeNegotiated*/ false);
119
+
120
+ // A single Sender Report is not enough for the clock offset to be estimated.
121
+ receiveSenderReport(0);
122
+
123
+ REQUIRE_FALSE(estimator.GetLocalCaptureMs(std::addressof(rtpStream), RemoteBaseTs).has_value());
124
+
125
+ for (uint32_t idx{ 1 }; idx < RTC::RemoteClockOffsetEstimator::MinSampleCount; ++idx)
126
+ {
127
+ receiveSenderReport(idx);
128
+ }
129
+
130
+ // The Sender Reports reached us with no delay, so the capture instant of the
131
+ // RTP timestamp of the last one is the very instant at which it arrived.
132
+ const auto lastIdx = RTC::RemoteClockOffsetEstimator::MinSampleCount - 1;
133
+ const auto lastTs = static_cast<uint32_t>(RemoteBaseTs + (lastIdx * ClockRate));
134
+
135
+ REQUIRE(estimator.GetLocalCaptureMs(std::addressof(rtpStream), lastTs).has_value());
136
+ REQUIRE(
137
+ // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
138
+ estimator.GetLocalCaptureMs(std::addressof(rtpStream), lastTs).value() ==
139
+ LocalBaseMs + (lastIdx * 1000));
140
+
141
+ // One second of media later maps one second later in our clock too.
142
+ const auto nextTs = static_cast<uint32_t>(RemoteBaseTs + ((lastIdx + 1) * ClockRate));
143
+
144
+ REQUIRE(estimator.GetLocalCaptureMs(std::addressof(rtpStream), nextTs).has_value());
145
+ REQUIRE(
146
+ // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
147
+ estimator.GetLocalCaptureMs(std::addressof(rtpStream), nextTs).value() ==
148
+ LocalBaseMs + ((lastIdx + 1) * 1000));
149
+ }
150
+
151
+ SECTION("no capture instant while the abs-capture-time source has nothing to offer")
152
+ {
153
+ estimator.UpdateSource(/*absCaptureTimeNegotiated*/ true);
154
+
155
+ for (uint32_t idx{ 0 }; idx < RTC::RemoteClockOffsetEstimator::MinSampleCount; ++idx)
156
+ {
157
+ receiveSenderReport(idx);
158
+ }
159
+
160
+ // Sender Reports have been received, but the source in use is not allowed to
161
+ // fall back to them.
162
+ REQUIRE_FALSE(estimator.GetLocalCaptureMs(std::addressof(rtpStream), RemoteBaseTs).has_value());
163
+ }
164
+ }