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
|
@@ -164,19 +164,32 @@ namespace RTC
|
|
|
164
164
|
{
|
|
165
165
|
MS_TRACE();
|
|
166
166
|
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
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
|
-
|
|
176
|
-
// since we know we won't be able to switch.
|
|
177
|
-
auto* producerTsReferenceRtpStream = GetProducerTsReferenceRtpStream();
|
|
190
|
+
this->tsReferenceSpatialLayerWithCaptureMapping = this->tsReferenceSpatialLayer;
|
|
178
191
|
|
|
179
|
-
if (!
|
|
192
|
+
if (!first && !tsReferenceCaptureMappingIsNew)
|
|
180
193
|
{
|
|
181
194
|
return;
|
|
182
195
|
}
|
|
@@ -225,6 +238,11 @@ namespace RTC
|
|
|
225
238
|
uint32_t requiredBitrate{ 0u };
|
|
226
239
|
int16_t spatialLayer{ 0 };
|
|
227
240
|
int16_t temporalLayer{ 0 };
|
|
241
|
+
// Whether a usable spatial layer has been found, in which case we must not
|
|
242
|
+
// go above the preferred spatial layer. Same criteria as in
|
|
243
|
+
// RecalculateTargetLayers(), so both take the same decision no matter the
|
|
244
|
+
// bitrate of the layer.
|
|
245
|
+
bool usableSpatialLayerFound{ this->provisionalTargetLayers.spatial != -1 };
|
|
228
246
|
|
|
229
247
|
for (size_t sIdx{ 0u }; sIdx < this->producerRtpStreams.size(); ++sIdx)
|
|
230
248
|
{
|
|
@@ -249,8 +267,12 @@ namespace RTC
|
|
|
249
267
|
{
|
|
250
268
|
continue;
|
|
251
269
|
}
|
|
252
|
-
// If this is higher than preferred spatial layer, abort
|
|
253
|
-
|
|
270
|
+
// If this is higher than preferred spatial layer, abort unless no lower
|
|
271
|
+
// spatial layer is usable or this is the provisional one (so we may still
|
|
272
|
+
// increase its temporal layer).
|
|
273
|
+
else if (
|
|
274
|
+
spatialLayer > this->preferredLayers.spatial && usableSpatialLayerFound &&
|
|
275
|
+
spatialLayer != this->provisionalTargetLayers.spatial)
|
|
254
276
|
{
|
|
255
277
|
MS_DEBUG_DEV(
|
|
256
278
|
"avoid upgrading to spatial layer %" PRIi16
|
|
@@ -300,10 +322,19 @@ namespace RTC
|
|
|
300
322
|
continue;
|
|
301
323
|
}
|
|
302
324
|
|
|
325
|
+
// This spatial layer is usable even if it has no bitrate at all.
|
|
326
|
+
usableSpatialLayerFound = true;
|
|
327
|
+
|
|
303
328
|
temporalLayer = 0;
|
|
304
329
|
|
|
330
|
+
// Don't consider temporal layers above the preferred one, nor above the
|
|
331
|
+
// ones this stream has.
|
|
332
|
+
const auto maxTemporalLayer = std::min(
|
|
333
|
+
static_cast<int16_t>(producerRtpStream->GetTemporalLayers() - 1),
|
|
334
|
+
this->preferredLayers.temporal);
|
|
335
|
+
|
|
305
336
|
// Check bitrate of every temporal layer.
|
|
306
|
-
for (;
|
|
337
|
+
for (; temporalLayer <= maxTemporalLayer; ++temporalLayer)
|
|
307
338
|
{
|
|
308
339
|
// Ignore temporal layers lower than the one we already have (taking
|
|
309
340
|
// into account the spatial layer too).
|
|
@@ -589,43 +620,71 @@ namespace RTC
|
|
|
589
620
|
{
|
|
590
621
|
tsOffset = 0u;
|
|
591
622
|
}
|
|
592
|
-
// If this is not the RTP stream we use as TS reference,
|
|
593
|
-
//
|
|
623
|
+
// If this is not the RTP stream we use as TS reference, synchronize its RTP
|
|
624
|
+
// timestamps based on the capture instant of the media.
|
|
594
625
|
else
|
|
595
626
|
{
|
|
596
|
-
auto* producerTsReferenceRtpStream = GetProducerTsReferenceRtpStream();
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
//
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
//
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
auto ntpMs2 = producerTargetRtpStream->GetSenderReportNtpMs();
|
|
611
|
-
auto ts2 = producerTargetRtpStream->GetSenderReportTs();
|
|
612
|
-
int64_t diffMs;
|
|
613
|
-
|
|
614
|
-
if (ntpMs2 >= ntpMs1)
|
|
627
|
+
const auto* producerTsReferenceRtpStream = GetProducerTsReferenceRtpStream();
|
|
628
|
+
// NOTE: The stream of the spatial layer of this packet, which is not always
|
|
629
|
+
// the one of the target spatial layer. A resync may be pending for the
|
|
630
|
+
// current spatial layer while the target one is a different one.
|
|
631
|
+
const auto* producerRtpStream = this->producerRtpStreams.at(spatialLayer);
|
|
632
|
+
|
|
633
|
+
const auto tsReferenceCaptureMapping = producerTsReferenceRtpStream->GetCaptureMapping();
|
|
634
|
+
const auto captureMapping = producerRtpStream->GetCaptureMapping();
|
|
635
|
+
|
|
636
|
+
// Without the capture instant of the TS reference stream there is nothing to
|
|
637
|
+
// align this one to, so take this spatial layer as the new TS reference and
|
|
638
|
+
// let its RTP timestamps go untouched, which is what would have been done had
|
|
639
|
+
// it been chosen as TS reference in the first place.
|
|
640
|
+
if (!tsReferenceCaptureMapping.has_value())
|
|
615
641
|
{
|
|
616
|
-
|
|
642
|
+
MS_DEBUG_TAG(
|
|
643
|
+
simulcast,
|
|
644
|
+
"cannot tell the capture instant of the TS reference stream, using spatial layer "
|
|
645
|
+
"%" PRIi16 " as RTP timestamp reference",
|
|
646
|
+
spatialLayer);
|
|
647
|
+
|
|
648
|
+
this->tsReferenceSpatialLayer = spatialLayer;
|
|
617
649
|
}
|
|
618
|
-
|
|
650
|
+
// The capture instant of this stream cannot be told yet, so there is no way to
|
|
651
|
+
// align it and no reason to give up a TS reference stream that is good. Discard
|
|
652
|
+
// the packet and wait, since the Sender Report that is missing is on its way.
|
|
653
|
+
else if (!captureMapping.has_value())
|
|
619
654
|
{
|
|
620
|
-
|
|
621
|
-
|
|
655
|
+
MS_DEBUG_TAG(
|
|
656
|
+
simulcast,
|
|
657
|
+
"cannot tell yet the capture instant of spatial layer %" PRIi16
|
|
658
|
+
", discarding packet [ssrc:%" PRIu32 ", seq:%" PRIu16 "]",
|
|
659
|
+
spatialLayer,
|
|
660
|
+
packet->GetSsrc(),
|
|
661
|
+
packet->GetSequenceNumber());
|
|
662
|
+
|
|
663
|
+
// Ask for another key frame since this one is being discarded.
|
|
664
|
+
if (packet->IsKeyFrame())
|
|
665
|
+
{
|
|
666
|
+
RequestKeyFrame();
|
|
667
|
+
}
|
|
622
668
|
|
|
623
|
-
|
|
624
|
-
const uint32_t newTs2 = ts2 - diffTs;
|
|
669
|
+
result.type = RtpPacketProcessResult::Type::SILENT_DROP;
|
|
625
670
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
671
|
+
return result;
|
|
672
|
+
}
|
|
673
|
+
else
|
|
674
|
+
{
|
|
675
|
+
// Calculate capture instant and TS stuff.
|
|
676
|
+
const auto captureMs1 = tsReferenceCaptureMapping.value().captureMs;
|
|
677
|
+
const auto ts1 = tsReferenceCaptureMapping.value().ts;
|
|
678
|
+
const auto captureMs2 = captureMapping.value().captureMs;
|
|
679
|
+
const auto ts2 = captureMapping.value().ts;
|
|
680
|
+
const int64_t diffMs = static_cast<int64_t>(captureMs2) - static_cast<int64_t>(captureMs1);
|
|
681
|
+
const int64_t diffTs = diffMs * clockRate / 1000;
|
|
682
|
+
const uint32_t newTs2 = ts2 - diffTs;
|
|
683
|
+
|
|
684
|
+
// Apply offset. This is the difference that later must be removed from
|
|
685
|
+
// the sending RTP packet.
|
|
686
|
+
tsOffset = newTs2 - ts1;
|
|
687
|
+
}
|
|
629
688
|
}
|
|
630
689
|
|
|
631
690
|
// When switching to a new stream it may happen that the timestamp of this
|
|
@@ -857,8 +916,9 @@ namespace RTC
|
|
|
857
916
|
|
|
858
917
|
// If we don't have yet a RTP timestamp reference, set it now.
|
|
859
918
|
if (
|
|
860
|
-
newTargetSpatialLayer != -1 &&
|
|
861
|
-
|
|
919
|
+
newTargetSpatialLayer != -1 &&
|
|
920
|
+
(this->tsReferenceSpatialLayer == -1 ||
|
|
921
|
+
!GetProducerTsReferenceRtpStream()->GetCaptureMapping().has_value()))
|
|
862
922
|
{
|
|
863
923
|
MS_DEBUG_TAG(
|
|
864
924
|
simulcast, "using spatial layer %" PRIi16 " as RTP timestamp reference", newTargetSpatialLayer);
|
|
@@ -962,6 +1022,13 @@ namespace RTC
|
|
|
962
1022
|
continue;
|
|
963
1023
|
}
|
|
964
1024
|
|
|
1025
|
+
// Don't go above the preferred spatial layer if we already found a usable
|
|
1026
|
+
// lower one.
|
|
1027
|
+
if (spatialLayer > this->preferredLayers.spatial && newTargetLayers.spatial != -1)
|
|
1028
|
+
{
|
|
1029
|
+
break;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
965
1032
|
newTargetLayers.spatial = spatialLayer;
|
|
966
1033
|
|
|
967
1034
|
// If this is the preferred or higher spatial layer take it and exit.
|
|
@@ -973,19 +1040,11 @@ namespace RTC
|
|
|
973
1040
|
|
|
974
1041
|
if (newTargetLayers.spatial != -1)
|
|
975
1042
|
{
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
{
|
|
982
|
-
newTargetLayers.temporal =
|
|
983
|
-
static_cast<int16_t>(this->encodingContext->GetTemporalLayers() - 1);
|
|
984
|
-
}
|
|
985
|
-
else
|
|
986
|
-
{
|
|
987
|
-
newTargetLayers.temporal = 0;
|
|
988
|
-
}
|
|
1043
|
+
// Don't consider temporal layers above the preferred one, nor above the
|
|
1044
|
+
// ones this stream has.
|
|
1045
|
+
newTargetLayers.temporal = std::min(
|
|
1046
|
+
this->preferredLayers.temporal,
|
|
1047
|
+
static_cast<int16_t>(this->encodingContext->GetTemporalLayers() - 1));
|
|
989
1048
|
}
|
|
990
1049
|
|
|
991
1050
|
// Return true if any target layer changed.
|
|
@@ -1050,7 +1109,7 @@ namespace RTC
|
|
|
1050
1109
|
|
|
1051
1110
|
return (
|
|
1052
1111
|
this->tsReferenceSpatialLayer == -1 || spatialLayer == this->tsReferenceSpatialLayer ||
|
|
1053
|
-
this->producerRtpStreams.at(spatialLayer)->
|
|
1112
|
+
this->producerRtpStreams.at(spatialLayer)->GetCaptureMapping().has_value());
|
|
1054
1113
|
}
|
|
1055
1114
|
|
|
1056
1115
|
RTC::RTP::RtpStreamRecv* SimulcastProducerStreamManager::GetProducerTsReferenceRtpStream() const
|
|
@@ -190,9 +190,14 @@ namespace RTC
|
|
|
190
190
|
|
|
191
191
|
temporalLayer = 0;
|
|
192
192
|
|
|
193
|
+
// Don't consider temporal layers above the preferred one, nor above the
|
|
194
|
+
// ones this stream has.
|
|
195
|
+
const auto maxTemporalLayer = std::min(
|
|
196
|
+
static_cast<int16_t>(this->producerRtpStream->GetTemporalLayers() - 1),
|
|
197
|
+
this->preferredLayers.temporal);
|
|
198
|
+
|
|
193
199
|
// Check bitrate of every temporal layer.
|
|
194
|
-
for (;
|
|
195
|
-
++temporalLayer)
|
|
200
|
+
for (; temporalLayer <= maxTemporalLayer; ++temporalLayer)
|
|
196
201
|
{
|
|
197
202
|
// Ignore temporal layers lower than the one we already have (taking
|
|
198
203
|
// into account the spatial layer too).
|
|
@@ -247,8 +252,9 @@ namespace RTC
|
|
|
247
252
|
}
|
|
248
253
|
}
|
|
249
254
|
|
|
250
|
-
// If this is the preferred or higher spatial layer, take it and exit
|
|
251
|
-
|
|
255
|
+
// If this is the preferred or higher spatial layer, take it and exit,
|
|
256
|
+
// unless we have not found any usable spatial layer yet.
|
|
257
|
+
if (spatialLayer >= this->preferredLayers.spatial && this->provisionalTargetLayers.spatial != -1)
|
|
252
258
|
{
|
|
253
259
|
break;
|
|
254
260
|
}
|
|
@@ -589,6 +595,13 @@ namespace RTC
|
|
|
589
595
|
continue;
|
|
590
596
|
}
|
|
591
597
|
|
|
598
|
+
// Don't go above the preferred spatial layer if we already found a usable
|
|
599
|
+
// lower one.
|
|
600
|
+
if (spatialLayer > this->preferredLayers.spatial && newTargetLayers.spatial != -1)
|
|
601
|
+
{
|
|
602
|
+
break;
|
|
603
|
+
}
|
|
604
|
+
|
|
592
605
|
newTargetLayers.spatial = spatialLayer;
|
|
593
606
|
|
|
594
607
|
// If this is the preferred or higher spatial layer and has bitrate,
|
|
@@ -601,19 +614,11 @@ namespace RTC
|
|
|
601
614
|
|
|
602
615
|
if (newTargetLayers.spatial != -1)
|
|
603
616
|
{
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
{
|
|
610
|
-
newTargetLayers.temporal =
|
|
611
|
-
static_cast<int16_t>(this->encodingContext->GetTemporalLayers() - 1);
|
|
612
|
-
}
|
|
613
|
-
else
|
|
614
|
-
{
|
|
615
|
-
newTargetLayers.temporal = 0;
|
|
616
|
-
}
|
|
617
|
+
// Don't consider temporal layers above the preferred one, nor above the
|
|
618
|
+
// ones this stream has.
|
|
619
|
+
newTargetLayers.temporal = std::min(
|
|
620
|
+
this->preferredLayers.temporal,
|
|
621
|
+
static_cast<int16_t>(this->encodingContext->GetTemporalLayers() - 1));
|
|
617
622
|
}
|
|
618
623
|
|
|
619
624
|
done:
|
|
@@ -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
|
-
|
|
2559
|
-
|
|
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
|
-
|
|
2564
|
-
this
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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", "[
|
|
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)
|