mediasoup 3.20.3 → 3.20.5

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 (25) hide show
  1. package/package.json +2 -2
  2. package/worker/include/RTC/SCTP/association/Association.hpp +16 -2
  3. package/worker/include/RTC/SCTP/association/TransmissionControlBlock.hpp +16 -1
  4. package/worker/include/RTC/SCTP/association/TransmissionControlBlockContextInterface.hpp +19 -2
  5. package/worker/include/RTC/SCTP/public/SctpTypes.hpp +2 -0
  6. package/worker/include/RTC/SCTP/rx/ReassemblyQueue.hpp +7 -0
  7. package/worker/include/RTC/SCTP/tx/RetransmissionErrorCounter.hpp +3 -4
  8. package/worker/include/RTC/SeqManager.hpp +2 -2
  9. package/worker/meson.build +3 -0
  10. package/worker/mocks/include/RTC/SCTP/association/MockAssociationListener.hpp +68 -7
  11. package/worker/mocks/include/handles/MockBackoffTimerHandle.hpp +10 -4
  12. package/worker/mocks/src/handles/MockBackoffTimerHandle.cpp +7 -0
  13. package/worker/src/RTC/SCTP/association/Association.cpp +56 -14
  14. package/worker/src/RTC/SCTP/association/HeartbeatHandler.cpp +33 -13
  15. package/worker/src/RTC/SCTP/association/StreamResetHandler.cpp +19 -11
  16. package/worker/src/RTC/SCTP/association/TransmissionControlBlock.cpp +34 -11
  17. package/worker/src/RTC/SCTP/rx/DataTracker.cpp +4 -1
  18. package/worker/src/RTC/SCTP/rx/ReassemblyQueue.cpp +17 -8
  19. package/worker/src/RTC/SeqManager.cpp +42 -29
  20. package/worker/src/RTC/Transport.cpp +1 -1
  21. package/worker/test/src/RTC/SCTP/association/TestAssociation.cpp +1755 -0
  22. package/worker/test/src/RTC/SCTP/association/TestPacketSender.cpp +121 -0
  23. package/worker/test/src/RTC/SCTP/association/TestStreamResetHandler.cpp +369 -0
  24. package/worker/test/src/RTC/SCTP/association/TestTransmissionControlBlock.cpp +11 -0
  25. package/worker/test/src/RTC/SCTP/rx/TestReassemblyQueue.cpp +24 -0
@@ -21,6 +21,7 @@ namespace RTC
21
21
  /* Instance methods. */
22
22
 
23
23
  TransmissionControlBlock::TransmissionControlBlock(
24
+ TransmissionControlBlockContextInterface::Listener* listener,
24
25
  AssociationListenerDeferrer& associationListenerDeferrer,
25
26
  const SctpOptions& sctpOptions,
26
27
  SharedInterface* shared,
@@ -35,7 +36,8 @@ namespace RTC
35
36
  const NegotiatedCapabilities& negotiatedCapabilities,
36
37
  size_t maxPacketLength,
37
38
  std::function<bool()> isAssociationEstablished)
38
- : associationListenerDeferrer(associationListenerDeferrer),
39
+ : listener(listener),
40
+ associationListenerDeferrer(associationListenerDeferrer),
39
41
  sctpOptions(sctpOptions),
40
42
  shared(shared),
41
43
  packetSender(packetSender),
@@ -407,10 +409,19 @@ namespace RTC
407
409
  }
408
410
  }
409
411
 
410
- void TransmissionControlBlock::OnT3RtxTimer(uint64_t& /*baseTimeoutMs*/, bool& /*stop*/)
412
+ void TransmissionControlBlock::OnT3RtxTimer(uint64_t& /*baseTimeoutMs*/, bool& stop)
411
413
  {
412
414
  MS_TRACE();
413
415
 
416
+ const auto maxRestarts = this->t3RtxTimer->GetMaxRestarts();
417
+
418
+ MS_DEBUG_TAG(
419
+ sctp,
420
+ "%s timer has expired [expirations:%zu, maxRestarts:%s]",
421
+ this->t3RtxTimer->GetLabel().c_str(),
422
+ this->t3RtxTimer->GetExpirationCount(),
423
+ maxRestarts ? std::to_string(maxRestarts.value()).c_str() : "Infinite");
424
+
414
425
  // This is a top-level timer entry point (invoked by libuv outside any other
415
426
  // SCTP API call), so it must establish the deferrer scope itself, just like
416
427
  // Association does in its own timer handlers.
@@ -432,6 +443,15 @@ namespace RTC
432
443
 
433
444
  SendBufferedPackets(nowMs);
434
445
  }
446
+ else
447
+ {
448
+ // `IncrementTxErrorCounter()` has closed (and destroyed) this TCB and
449
+ // its timers. Signal the firing timer to stop and don't touch any
450
+ // member afterwards.
451
+ stop = true;
452
+
453
+ return;
454
+ }
435
455
  }
436
456
  }
437
457
 
@@ -439,6 +459,18 @@ namespace RTC
439
459
  {
440
460
  MS_TRACE();
441
461
 
462
+ #if MS_LOG_DEV_LEVEL == 3
463
+ const auto maxRestarts = this->delayedAckTimer->GetMaxRestarts();
464
+ #endif
465
+
466
+ // NOTE: This timer expires very frequently (whenever received data is
467
+ // pending to be acked), so it's logged at dev level to avoid being noisy.
468
+ MS_DEBUG_DEV(
469
+ "%s timer has expired [expirations:%zu, maxRestarts:%s]",
470
+ this->delayedAckTimer->GetLabel().c_str(),
471
+ this->delayedAckTimer->GetExpirationCount(),
472
+ maxRestarts ? std::to_string(maxRestarts.value()).c_str() : "Infinite");
473
+
442
474
  // This is a top-level timer entry point (invoked by libuv outside any other
443
475
  // SCTP API call), so it must establish the deferrer scope itself, just like
444
476
  // Association does in its own timer handlers.
@@ -454,15 +486,6 @@ namespace RTC
454
486
  {
455
487
  MS_TRACE();
456
488
 
457
- const auto maxRestarts = backoffTimer->GetMaxRestarts();
458
-
459
- MS_DEBUG_TAG(
460
- sctp,
461
- "%s timer has expired [expìrations:%zu/%s]",
462
- backoffTimer->GetLabel().c_str(),
463
- backoffTimer->GetExpirationCount(),
464
- maxRestarts ? std::to_string(maxRestarts.value()).c_str() : "Infinite");
465
-
466
489
  if (backoffTimer == this->t3RtxTimer.get())
467
490
  {
468
491
  OnT3RtxTimer(baseTimeoutMs, stop);
@@ -268,7 +268,9 @@ namespace RTC
268
268
  {
269
269
  MS_TRACE();
270
270
 
271
- UpdateAckState(AckState::IMMEDIATE, "force immediate SACK");
271
+ // NOTE: Assign directly instead of going through UpdateAckState() to avoid
272
+ // its side effect of stopping the delayed-ack timer.
273
+ this->ackState = AckState::IMMEDIATE;
272
274
  }
273
275
 
274
276
  bool DataTracker::WillIncreaseCumAckTsn(uint32_t tsn) const
@@ -359,6 +361,7 @@ namespace RTC
359
361
  {
360
362
  this->delayedAckTimer->Start();
361
363
  }
364
+
362
365
  this->ackState = newAckState;
363
366
  }
364
367
  }
@@ -140,12 +140,16 @@ namespace RTC
140
140
  {
141
141
  MS_DEBUG_DEV("forward TSN to %" PRIu32 ", deferring", tsn.Wrap());
142
142
 
143
+ this->queuedBytes += ForwardTsnCost(skippedStreams.size());
144
+
143
145
  this->deferredResetStreams->deferredActions.emplace_back(
144
146
  [this,
145
147
  newCumulativeTsn,
146
148
  skippedStreams2 = std::vector<AnyForwardTsnChunk::SkippedStream>(
147
149
  skippedStreams.begin(), skippedStreams.end())]
148
150
  {
151
+ this->queuedBytes -= ForwardTsnCost(skippedStreams2.size());
152
+
149
153
  HandleForwardTsn(newCumulativeTsn, skippedStreams2);
150
154
  });
151
155
 
@@ -216,15 +220,13 @@ namespace RTC
216
220
 
217
221
  if (!this->deferredResetStreams.has_value())
218
222
  {
219
- return;
220
- }
221
-
222
- MS_DEBUG_DEV(
223
- "entering deferred reset [senderLastAssignedTsn:%" PRIu32 "]", senderLastAssignedTsn);
223
+ MS_DEBUG_DEV(
224
+ "entering deferred reset [senderLastAssignedTsn:%" PRIu32 "]", senderLastAssignedTsn);
224
225
 
225
- this->deferredResetStreams = std::make_optional<DeferredResetStreams>(
226
- this->tsnUnwrapper.Unwrap(senderLastAssignedTsn),
227
- std::set<uint16_t>(streamIds.begin(), streamIds.end()));
226
+ this->deferredResetStreams = std::make_optional<DeferredResetStreams>(
227
+ this->tsnUnwrapper.Unwrap(senderLastAssignedTsn),
228
+ std::set<uint16_t>(streamIds.begin(), streamIds.end()));
229
+ }
228
230
 
229
231
  AssertIsConsistent();
230
232
  }
@@ -244,6 +246,13 @@ namespace RTC
244
246
  }
245
247
  }
246
248
 
249
+ size_t ReassemblyQueue::ForwardTsnCost(size_t numStreams)
250
+ {
251
+ MS_TRACE();
252
+
253
+ return (1 + numStreams) * 4;
254
+ }
255
+
247
256
  void ReassemblyQueue::AddReassembledMessage(std::span<const Types::UnwrappedTsn> tsns, Message message)
248
257
  {
249
258
  MS_TRACE();
@@ -63,9 +63,15 @@ namespace RTC
63
63
  {
64
64
  this->maxInput = input;
65
65
  this->maxDropped = input;
66
- // Insert input in the last position.
67
- // Explicitly insert at the end, which is more performant.
68
- this->dropped.insert(this->dropped.end(), input);
66
+ // Insert input in sorted order, if not present.
67
+ const SeqLowerThan seqLowerThan;
68
+ const auto it =
69
+ std::lower_bound(this->dropped.begin(), this->dropped.end(), input, seqLowerThan);
70
+
71
+ if (it == this->dropped.end() || *it != input)
72
+ {
73
+ this->dropped.insert(it, input);
74
+ }
69
75
 
70
76
  ClearDropped();
71
77
  }
@@ -74,7 +80,15 @@ namespace RTC
74
80
  // Allows for properly accounting for out of order drops until an input is forwarded.
75
81
  else if (this->maxInput == this->maxDropped && SeqManager<T, N>::IsSeqHigherThan(input, this->maxForwarded))
76
82
  {
77
- this->dropped.insert(input);
83
+ // Insert input in sorted order, if not present.
84
+ const SeqLowerThan seqLowerThan;
85
+ const auto it =
86
+ std::lower_bound(this->dropped.begin(), this->dropped.end(), input, seqLowerThan);
87
+
88
+ if (it == this->dropped.end() || *it != input)
89
+ {
90
+ this->dropped.insert(it, input);
91
+ }
78
92
 
79
93
  ClearDropped();
80
94
  }
@@ -112,22 +126,24 @@ namespace RTC
112
126
  {
113
127
  goto done;
114
128
  }
115
- // This input was dropped.
116
- else if (this->dropped.find(input) != this->dropped.end())
117
- {
118
- MS_DEBUG_DEV("trying to send a dropped input");
119
-
120
- return false;
121
- }
122
- // There are dropped inputs, calculate 'base' for this input.
123
129
  else
124
130
  {
125
- auto droppedCount = this->dropped.size();
131
+ const SeqLowerThan seqLowerThan;
132
+ const auto it =
133
+ std::lower_bound(this->dropped.begin(), this->dropped.end(), input, seqLowerThan);
126
134
 
127
- // Get the first dropped input which is higher than or equal 'input'.
128
- auto it = this->dropped.lower_bound(input);
135
+ if (it != this->dropped.end() && *it == input)
136
+ {
137
+ MS_DEBUG_DEV("trying to send a dropped input");
138
+
139
+ return false;
140
+ }
141
+
142
+ // There are dropped inputs, calculate 'base' for this input.
143
+ auto droppedCount = std::distance(
144
+ this->dropped.begin(),
145
+ std::lower_bound(this->dropped.begin(), this->dropped.end(), input, SeqLowerThan()));
129
146
 
130
- droppedCount -= std::distance(it, this->dropped.end());
131
147
  base = (this->base - droppedCount) & SeqManager::MaxValue;
132
148
  }
133
149
 
@@ -191,19 +207,16 @@ namespace RTC
191
207
 
192
208
  const size_t previousDroppedSize = this->dropped.size();
193
209
 
194
- for (auto it = this->dropped.begin(); it != this->dropped.end();)
195
- {
196
- auto value = *it;
197
-
198
- if (SeqManager<T, N>::IsSeqHigherThan(value, this->maxInput))
199
- {
200
- it = this->dropped.erase(it);
201
- }
202
- else
203
- {
204
- break;
205
- }
206
- }
210
+ // Cleanup dropped values.
211
+ this->dropped.erase(
212
+ this->dropped.begin(),
213
+ std::find_if(
214
+ this->dropped.begin(),
215
+ this->dropped.end(),
216
+ [this](T value)
217
+ {
218
+ return !SeqManager<T, N>::IsSeqHigherThan(value, this->maxInput);
219
+ }));
207
220
 
208
221
  // Adapt base.
209
222
  this->base = (this->base - (previousDroppedSize - this->dropped.size())) & SeqManager::MaxValue;
@@ -91,7 +91,7 @@ namespace RTC
91
91
  };
92
92
 
93
93
  this->sctpAssociation = std::make_unique<RTC::SCTP::Association>(
94
- sctpOptions, this, this->shared, options->isDataChannel());
94
+ sctpOptions, this, this->shared, options->isDataChannel(), /*mayConnectOnReceivedSctpData*/ true);
95
95
  }
96
96
 
97
97
  // Create the RTCP timer.