mediasoup 3.28.0 → 3.29.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 (155) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/node/lib/PipeTransportTypes.d.ts +6 -1
  3. package/node/lib/PipeTransportTypes.d.ts.map +1 -1
  4. package/node/lib/PlainTransportTypes.d.ts +6 -1
  5. package/node/lib/PlainTransportTypes.d.ts.map +1 -1
  6. package/node/lib/Router.d.ts +4 -4
  7. package/node/lib/Router.d.ts.map +1 -1
  8. package/node/lib/Router.js +10 -7
  9. package/node/lib/RouterTypes.d.ts +7 -1
  10. package/node/lib/RouterTypes.d.ts.map +1 -1
  11. package/node/lib/Transport.d.ts +2 -1
  12. package/node/lib/Transport.d.ts.map +1 -1
  13. package/node/lib/Transport.js +17 -6
  14. package/node/lib/TransportTypes.d.ts +12 -2
  15. package/node/lib/TransportTypes.d.ts.map +1 -1
  16. package/node/lib/WebRtcTransportTypes.d.ts +6 -1
  17. package/node/lib/WebRtcTransportTypes.d.ts.map +1 -1
  18. package/node/lib/fbs/transport/options.d.ts +6 -2
  19. package/node/lib/fbs/transport/options.d.ts.map +1 -1
  20. package/node/lib/fbs/transport/options.js +19 -7
  21. package/node/lib/fbs/transport/sctp-zero-checksum.d.ts +6 -0
  22. package/node/lib/fbs/transport/sctp-zero-checksum.d.ts.map +1 -0
  23. package/node/lib/fbs/transport/sctp-zero-checksum.js +11 -0
  24. package/node/lib/fbs/transport/stats.d.ts +2 -8
  25. package/node/lib/fbs/transport/stats.d.ts.map +1 -1
  26. package/node/lib/fbs/transport/stats.js +5 -29
  27. package/node/lib/fbs/transport.d.ts +1 -0
  28. package/node/lib/fbs/transport.d.ts.map +1 -1
  29. package/node/lib/fbs/transport.js +4 -2
  30. package/npm-scripts.mjs +35 -44
  31. package/package.json +10 -6
  32. package/worker/fbs/transport.fbs +9 -2
  33. package/worker/fuzzer/src/RTC/RTP/FuzzerRetransmissionBuffer.cpp +14 -1
  34. package/worker/fuzzer/src/RTC/RTP/FuzzerRtpStreamSend.cpp +17 -4
  35. package/worker/include/RTC/BWE/AimdRateControl.hpp +9 -9
  36. package/worker/include/RTC/BWE/BweTypes.hpp +54 -17
  37. package/worker/include/RTC/BWE/DelayBasedBwe.hpp +3 -3
  38. package/worker/include/RTC/BWE/FeedbackAdapter.hpp +2 -2
  39. package/worker/include/RTC/BWE/LossBasedController.hpp +4 -4
  40. package/worker/include/RTC/BWE/OveruseDetector.hpp +5 -4
  41. package/worker/include/RTC/BWE/ProbeBitrateEstimator.hpp +1 -1
  42. package/worker/include/RTC/BWE/ProbeController.hpp +6 -6
  43. package/worker/include/RTC/BWE/ProbePacketGenerator.hpp +2 -6
  44. package/worker/include/RTC/BWE/ProbingScheduler.hpp +3 -7
  45. package/worker/include/RTC/BWE/ReceiverTransportCongestionController.hpp +148 -0
  46. package/worker/include/RTC/BWE/RembGenerator.hpp +4 -3
  47. package/worker/include/RTC/BWE/RemoteBitrateEstimatorAbsSendTime.hpp +1 -1
  48. package/worker/include/RTC/BWE/SenderTransportCongestionController.hpp +343 -0
  49. package/worker/include/RTC/BWE/TargetRateController.hpp +4 -1
  50. package/worker/include/RTC/BWE/TransportWideCcFeedbackGenerator.hpp +5 -4
  51. package/worker/include/RTC/BWE/TrendlineEstimator.hpp +3 -3
  52. package/worker/include/RTC/Consumer.hpp +8 -1
  53. package/worker/include/RTC/RTP/RetransmissionBuffer.hpp +36 -10
  54. package/worker/include/RTC/RTP/RtpStream.hpp +21 -0
  55. package/worker/include/RTC/RTP/RtpStreamRecv.hpp +1 -0
  56. package/worker/include/RTC/RTP/RtpStreamSend.hpp +122 -9
  57. package/worker/include/RTC/SCTP/packet/parameters/ZeroChecksumAcceptableParameter.hpp +12 -3
  58. package/worker/include/RTC/TransportCongestionControlClient.hpp +0 -5
  59. package/worker/include/RTC/TransportCongestionControlServer.hpp +0 -5
  60. package/worker/include/Utils.hpp +9 -2
  61. package/worker/meson.build +4 -0
  62. package/worker/src/RTC/BWE/AimdRateControl.cpp +22 -22
  63. package/worker/src/RTC/BWE/DelayBasedBwe.cpp +16 -20
  64. package/worker/src/RTC/BWE/FeedbackAdapter.cpp +1 -1
  65. package/worker/src/RTC/BWE/OveruseDetector.cpp +8 -8
  66. package/worker/src/RTC/BWE/ProbeBitrateEstimator.cpp +2 -2
  67. package/worker/src/RTC/BWE/ProbeController.cpp +1 -1
  68. package/worker/src/RTC/BWE/ProbePacketGenerator.cpp +1 -6
  69. package/worker/src/RTC/BWE/ProbingScheduler.cpp +2 -7
  70. package/worker/src/RTC/BWE/ReceiverTransportCongestionController.cpp +207 -0
  71. package/worker/src/RTC/BWE/RembGenerator.cpp +1 -1
  72. package/worker/src/RTC/BWE/RemoteBitrateEstimatorAbsSendTime.cpp +1 -1
  73. package/worker/src/RTC/BWE/SenderTransportCongestionController.cpp +599 -0
  74. package/worker/src/RTC/BWE/TransportWideCcFeedbackGenerator.cpp +2 -3
  75. package/worker/src/RTC/BWE/TrendlineEstimator.cpp +5 -5
  76. package/worker/src/RTC/Consumer.cpp +3 -2
  77. package/worker/src/RTC/DataConsumer.cpp +2 -2
  78. package/worker/src/RTC/RTP/RetransmissionBuffer.cpp +21 -8
  79. package/worker/src/RTC/RTP/RtpStream.cpp +0 -3
  80. package/worker/src/RTC/RTP/RtpStreamRecv.cpp +57 -71
  81. package/worker/src/RTC/RTP/RtpStreamSend.cpp +428 -244
  82. package/worker/src/RTC/RTP/RtxStream.cpp +14 -19
  83. package/worker/src/RTC/SCTP/association/Association.cpp +3 -2
  84. package/worker/src/RTC/SCTP/association/StateCookie.cpp +26 -6
  85. package/worker/src/RTC/SCTP/packet/parameters/ZeroChecksumAcceptableParameter.cpp +4 -3
  86. package/worker/src/RTC/SCTP/rx/ReassemblyQueue.cpp +2 -1
  87. package/worker/src/RTC/SCTP/tx/RetransmissionQueue.cpp +4 -2
  88. package/worker/src/RTC/Transport.cpp +49 -20
  89. package/worker/src/RTC/TransportCongestionControlClient.cpp +0 -66
  90. package/worker/src/RTC/TransportCongestionControlServer.cpp +0 -55
  91. package/worker/src/Utils/Crypto.cpp +114 -98
  92. package/worker/subprojects/catch2.wrap +6 -6
  93. package/worker/subprojects/openssl.wrap +11 -12
  94. package/worker/subprojects/packagefiles/ankerl-unordered-dense/meson.build +1 -1
  95. package/worker/subprojects/unordered-dense.wrap +4 -4
  96. package/worker/test/src/RTC/BWE/TestDelayBasedBwe.cpp +5 -5
  97. package/worker/test/src/RTC/BWE/TestFeedbackAdapter.cpp +26 -23
  98. package/worker/test/src/RTC/BWE/TestInterArrivalDelta.cpp +8 -8
  99. package/worker/test/src/RTC/BWE/TestOveruseDetector.cpp +4 -4
  100. package/worker/test/src/RTC/BWE/TestProbeController.cpp +35 -32
  101. package/worker/test/src/RTC/BWE/TestProbePacketGenerator.cpp +2 -17
  102. package/worker/test/src/RTC/BWE/TestProbingScheduler.cpp +1 -25
  103. package/worker/test/src/RTC/BWE/TestReceiverTransportCongestionController.cpp +346 -0
  104. package/worker/test/src/RTC/BWE/TestRembGenerator.cpp +1 -1
  105. package/worker/test/src/RTC/BWE/TestRemoteBitrateEstimatorAbsSendTime.cpp +9 -9
  106. package/worker/test/src/RTC/BWE/TestSenderTransportCongestionController.cpp +510 -0
  107. package/worker/test/src/RTC/BWE/TestTransportWideCcFeedbackGenerator.cpp +16 -14
  108. package/worker/test/src/RTC/BWE/TestTrendlineEstimator.cpp +6 -6
  109. package/worker/test/src/RTC/BWE/helpers/RemoteBitrateEstimatorAbsSendTimeTestHelper.cpp +1 -2
  110. package/worker/test/src/RTC/RTCP/TestBye.cpp +1 -1
  111. package/worker/test/src/RTC/RTCP/TestFeedbackPsAfb.cpp +1 -1
  112. package/worker/test/src/RTC/RTCP/TestFeedbackPsFir.cpp +1 -1
  113. package/worker/test/src/RTC/RTCP/TestFeedbackPsLei.cpp +1 -1
  114. package/worker/test/src/RTC/RTCP/TestFeedbackPsPli.cpp +1 -1
  115. package/worker/test/src/RTC/RTCP/TestFeedbackPsRemb.cpp +1 -1
  116. package/worker/test/src/RTC/RTCP/TestFeedbackPsRpsi.cpp +1 -1
  117. package/worker/test/src/RTC/RTCP/TestFeedbackPsSli.cpp +1 -1
  118. package/worker/test/src/RTC/RTCP/TestFeedbackPsTst.cpp +1 -1
  119. package/worker/test/src/RTC/RTCP/TestFeedbackPsVbcm.cpp +1 -1
  120. package/worker/test/src/RTC/RTCP/TestFeedbackRtpEcn.cpp +1 -1
  121. package/worker/test/src/RTC/RTCP/TestFeedbackRtpNack.cpp +1 -1
  122. package/worker/test/src/RTC/RTCP/TestFeedbackRtpSrReq.cpp +1 -1
  123. package/worker/test/src/RTC/RTCP/TestFeedbackRtpTllei.cpp +1 -1
  124. package/worker/test/src/RTC/RTCP/TestFeedbackRtpTmmb.cpp +1 -1
  125. package/worker/test/src/RTC/RTCP/TestFeedbackRtpTransport.cpp +1 -1
  126. package/worker/test/src/RTC/RTCP/TestReceiverReport.cpp +1 -1
  127. package/worker/test/src/RTC/RTCP/TestSenderReport.cpp +1 -1
  128. package/worker/test/src/RTC/RTP/TestRetransmissionBuffer.cpp +60 -28
  129. package/worker/test/src/RTC/RTP/TestRtpStreamRecv.cpp +1 -1
  130. package/worker/test/src/RTC/RTP/TestRtpStreamSend.cpp +746 -24
  131. package/worker/test/src/RTC/RTP/TestSharedPacket.cpp +1 -1
  132. package/worker/test/src/RTC/SCTP/association/TestAssociation.cpp +42 -0
  133. package/worker/test/src/RTC/SCTP/association/TestNegotiatedCapabilities.cpp +54 -5
  134. package/worker/test/src/RTC/SCTP/association/TestStateCookie.cpp +80 -15
  135. package/worker/test/src/RTC/SCTP/association/TestStreamResetHandler.cpp +410 -3
  136. package/worker/test/src/RTC/SCTP/association/TestTransmissionControlBlock.cpp +1 -1
  137. package/worker/test/src/RTC/SCTP/packet/parameters/TestZeroChecksumAcceptableParameter.cpp +37 -5
  138. package/worker/test/src/RTC/SCTP/rx/TestDataTracker.cpp +5 -4
  139. package/worker/test/src/RTC/SCTP/rx/TestInterleavedReassemblyStreams.cpp +1 -1
  140. package/worker/test/src/RTC/SCTP/rx/TestReassemblyQueue.cpp +1 -1
  141. package/worker/test/src/RTC/SCTP/rx/TestTraditionalReassemblyStreams.cpp +1 -1
  142. package/worker/test/src/RTC/SCTP/tx/TestOutstandingData.cpp +1 -1
  143. package/worker/test/src/RTC/SCTP/tx/TestRetransmissionQueue.cpp +11 -10
  144. package/worker/test/src/RTC/SCTP/tx/TestRetransmissionTimeout.cpp +1 -1
  145. package/worker/test/src/RTC/TestNackGenerator.cpp +1 -1
  146. package/worker/test/src/RTC/TestPortManager.cpp +2 -2
  147. package/worker/test/src/RTC/TestRateCalculator.cpp +1 -1
  148. package/worker/test/src/RTC/TestRemoteCaptureTimeEstimator.cpp +21 -15
  149. package/worker/test/src/RTC/TestRemoteClockOffsetEstimator.cpp +13 -13
  150. package/worker/test/src/RTC/TestRtpEncodingParameters.cpp +1 -1
  151. package/worker/test/src/RTC/TestSubchannelsCodec.cpp +2 -2
  152. package/worker/test/src/RTC/TestTransportCongestionControlServer.cpp +1 -1
  153. package/worker/test/src/RTC/TestTransportTuple.cpp +3 -2
  154. package/worker/test/src/Utils/TestCrypto.cpp +71 -2
  155. package/worker/test/src/Utils/TestTime.cpp +2 -2
@@ -3,11 +3,10 @@
3
3
 
4
4
  #include "RTC/RTP/RtpStreamSend.hpp"
5
5
  #include "Logger.hpp"
6
- #include "RTC/RTCP/FeedbackPsFir.hpp"
7
- #include "RTC/RTCP/FeedbackPsPli.hpp"
8
6
  #include "RTC/RtpDictionaries.hpp"
9
7
  #include "Utils.hpp"
10
- #include <vector>
8
+ #include <cmath> // std::pow(), std::round()
9
+ #include <limits>
11
10
 
12
11
  namespace RTC
13
12
  {
@@ -16,12 +15,12 @@ namespace RTC
16
15
  /* Static. */
17
16
 
18
17
  // Limit max number of items in the retransmission buffer.
19
- static constexpr size_t RetransmissionBufferMaxItems{ 2500 };
20
- // 17: 16 bit mask + the initial sequence number.
21
- static constexpr size_t MaxRequestedPackets{ 17 };
22
- static thread_local std::vector<RTP::RetransmissionBuffer::Item*> RetransmissionContainer(
23
- MaxRequestedPackets + 1);
18
+ static constexpr size_t RetransmissionBufferMaxItems{ 3000 };
24
19
  static constexpr int64_t DefaultRttMs{ 100 };
20
+ // Interval between consecutive iterations of pending retransmissions.
21
+ static constexpr int64_t RetransmissionIntervalMs{ 10 };
22
+ // Maximum number of packets retransmitted in each iteration.
23
+ static constexpr size_t MaxRetransmittedPacketsPerIteration{ 2 };
25
24
 
26
25
  /* Instance methods. */
27
26
 
@@ -32,7 +31,9 @@ namespace RTC
32
31
  std::string& mid)
33
32
  : RTP::RtpStream::RtpStream(listener, shared, params, 10),
34
33
  mid(mid),
35
- transmissionCounter(shared, /*ignorePaddingOnlyPackets*/ true)
34
+ transmissionCounter(shared, /*ignorePaddingOnlyPackets*/ true),
35
+ retransmissionTimer(
36
+ params.useNack ? shared->CreateTimer(this, "rtp-stream-send-retransmissions") : nullptr)
36
37
  {
37
38
  MS_TRACE();
38
39
 
@@ -112,6 +113,8 @@ namespace RTC
112
113
  return ReceivePacketResult::DISCARDED;
113
114
  }
114
115
 
116
+ UpdateUnsentSeqNumbers(packet->GetSequenceNumber());
117
+
115
118
  bool stored{ false };
116
119
 
117
120
  // If NACK is enabled, store the packet into the buffer.
@@ -132,7 +135,7 @@ namespace RTC
132
135
  return ReceivePacketResult::DISCARDED;
133
136
  }
134
137
 
135
- stored = this->retransmissionBuffer->Insert(packet, sharedPacket);
138
+ stored = this->retransmissionBuffer->Insert(packet, sharedPacket, this->shared->GetTimeMs());
136
139
  }
137
140
 
138
141
  // Increase transmission counter.
@@ -150,101 +153,61 @@ namespace RTC
150
153
 
151
154
  for (auto it = nackPacket->Begin(); it != nackPacket->End(); ++it)
152
155
  {
153
- const RTC::RTCP::FeedbackRtpNackItem* item = *it;
154
-
155
- this->nackPacketCount += item->CountRequestedPackets();
156
+ const RTC::RTCP::FeedbackRtpNackItem* nackItem = *it;
156
157
 
157
- FillRetransmissionContainer(item->GetPacketId(), item->GetLostPacketBitmask());
158
+ this->nackPacketCount += nackItem->CountRequestedPackets();
158
159
 
159
- for (auto* item : RetransmissionContainer)
160
+ if (!this->retransmissionBuffer)
160
161
  {
161
- if (!item)
162
- {
163
- break;
164
- }
165
-
166
- MS_ASSERT(
167
- item->sharedPacket.HasPacket(),
168
- "item in retransmission container doesn't contain a packet [ssrc:%" PRIu32
169
- ", seq:%" PRIu16 ", timestamp:%" PRIu32 "]",
170
- item->ssrc,
171
- item->sequenceNumber,
172
- item->timestamp);
173
-
174
- auto* packet = item->sharedPacket.GetPacket();
175
-
176
- // Keep the values of the original packet received by the Consumer.
177
- auto origSsrc = packet->GetSsrc();
178
- auto origSeq = packet->GetSequenceNumber();
179
- auto origTimestamp = packet->GetTimestamp();
180
- auto origMarker = packet->HasMarker();
181
- std::string origMid;
182
-
183
- // Put correct info into the packet.
184
- packet->SetSsrc(item->ssrc);
185
- packet->SetSequenceNumber(item->sequenceNumber);
186
- packet->SetTimestamp(item->timestamp);
187
- packet->SetMarker(item->marker);
188
-
189
- if (item->encoder != nullptr)
190
- {
191
- packet->EncodePayload(item->encoder.get());
192
- }
162
+ continue;
163
+ }
193
164
 
194
- // Update MID RTP extension value.
195
- if (!this->mid.empty())
196
- {
197
- packet->ReadMid(origMid);
198
- packet->UpdateMid(this->mid);
199
- }
165
+ // Queue the requested packets in the order they are requested: first the
166
+ // one in the packet id and then those in the bitmask. A packet already
167
+ // pending is not queued again since it will be retransmitted anyway.
168
+ uint16_t seq = nackItem->GetPacketId();
169
+ uint16_t bitmask = nackItem->GetLostPacketBitmask();
170
+ bool requested{ true };
200
171
 
201
- // If we use RTX, encode it.
202
- if (HasRtx())
172
+ while (requested || bitmask != 0)
173
+ {
174
+ if (requested)
203
175
  {
204
- // Increment RTX seq.
205
- this->rtxSeq++;
176
+ const bool inserted = this->pendingRetransmissionsSet.insert(seq).second;
206
177
 
207
- packet->RtxEncode(this->params.rtxPayloadType, this->params.rtxSsrc, this->rtxSeq);
178
+ if (inserted)
179
+ {
180
+ this->pendingRetransmissionsQueue.push_back(seq);
181
+ }
208
182
  }
209
183
 
210
- // Retransmit the packet.
211
- static_cast<RTP::RtpStreamSend::Listener*>(this->listener)
212
- ->OnRtpStreamRetransmitRtpPacket(this, packet);
213
-
214
- // Mark the packet as retransmitted.
215
- RTP::RtpStream::PacketRetransmitted(packet);
184
+ requested = (bitmask & 1) != 0;
185
+ bitmask >>= 1;
186
+ seq++;
187
+ }
188
+ }
216
189
 
217
- // Mark the packet as repaired (only if this is the first retransmission).
218
- if (item->sentTimes == 1)
219
- {
220
- RTP::RtpStream::PacketRepaired(packet);
221
- }
190
+ // If NACK is not supported, exit.
191
+ if (!this->retransmissionBuffer)
192
+ {
193
+ MS_WARN_TAG(rtx, "NACK not supported");
222
194
 
223
- // If we use RTX, restore it.
224
- if (HasRtx())
225
- {
226
- // Restore the packet.
227
- packet->RtxDecode(RtpStream::GetPayloadType(), item->ssrc);
228
- }
195
+ return;
196
+ }
229
197
 
230
- // Restore MID.
231
- if (!this->mid.empty())
232
- {
233
- packet->UpdateMid(origMid);
234
- }
198
+ // If a retransmission iteration is already scheduled, nothing else to do.
199
+ if (this->retransmissionTimer->IsActive())
200
+ {
201
+ return;
202
+ }
235
203
 
236
- // Restore payload.
237
- if (item->encoder != nullptr)
238
- {
239
- packet->RestorePayload();
240
- }
204
+ // Otherwise retransmit the first pending packets right away and schedule
205
+ // the next iteration if there are retransmissions left.
206
+ RetransmitPendingPackets();
241
207
 
242
- // Restore RTP header fields.
243
- packet->SetSsrc(origSsrc);
244
- packet->SetSequenceNumber(origSeq);
245
- packet->SetTimestamp(origTimestamp);
246
- packet->SetMarker(origMarker);
247
- }
208
+ if (!this->pendingRetransmissionsQueue.empty())
209
+ {
210
+ this->retransmissionTimer->Start(RetransmissionIntervalMs);
248
211
  }
249
212
  }
250
213
 
@@ -272,7 +235,8 @@ namespace RTC
272
235
  }
273
236
  }
274
237
 
275
- void RtpStreamSend::ReceiveRtcpReceiverReport(RTC::RTCP::ReceiverReport* report, int64_t receivedAtUs)
238
+ std::optional<RtpStreamSend::Loss> RtpStreamSend::ReceiveRtcpReceiverReport(
239
+ RTC::RTCP::ReceiverReport* report, int64_t receivedAtUs)
276
240
  {
277
241
  MS_TRACE();
278
242
 
@@ -301,12 +265,91 @@ namespace RTC
301
265
  static_cast<float>(Utils::Time::CompactNtpRttToTimeUs(compactNtp - dlsr - lastSr)) / 1000;
302
266
  }
303
267
 
304
- this->packetsLost = report->GetTotalLost();
305
- this->fractionLost = report->GetFractionLost();
306
- this->jitter = static_cast<float>(report->GetJitter());
268
+ this->jitter = static_cast<float>(report->GetJitter());
269
+
270
+ // Work out how much of what it reports as lost really was lost on the way
271
+ // to it, which is the only part this link is answerable for.
272
+ const auto loss = UpdateSendLoss(report);
307
273
 
308
274
  // Update the score with the received RR.
309
- UpdateScore(report);
275
+ UpdateScore(loss);
276
+
277
+ return loss;
278
+ }
279
+
280
+ std::optional<RtpStreamSend::Loss> RtpStreamSend::UpdateSendLoss(RTC::RTCP::ReceiverReport* report)
281
+ {
282
+ MS_TRACE();
283
+
284
+ // Not a single packet has been sent, so there is no stretch of the stream
285
+ // this report can be measuring.
286
+ if (!this->lastRrSeq.has_value())
287
+ {
288
+ return std::nullopt;
289
+ }
290
+
291
+ // The remote endpoint counts its cycles from the first packet it saw, so
292
+ // the extended value it reports is not in our numbering. Only the 16 bits
293
+ // that travelled on the wire are, and this places them where they belong
294
+ // without disturbing what the unwrapper knows.
295
+ const int64_t rrSeq =
296
+ this->seqUnwrapper.PeekUnwrap(static_cast<uint16_t>(report->GetLastSeq())).GetValue();
297
+ // NOTE: It is signed and may go down, since a duplicate counts as a packet
298
+ // received twice.
299
+ const int32_t totalLost = std::max<int32_t>(report->GetTotalLost(), 0);
300
+
301
+ // A report that goes backwards is one that got reordered. Taking it would
302
+ // move the mark back and count a stretch of the stream twice.
303
+ if (rrSeq <= this->lastRrSeq.value())
304
+ {
305
+ return std::nullopt;
306
+ }
307
+
308
+ // What was never sent within the interval is what the remote endpoint
309
+ // counts as lost without this link having lost it.
310
+ const auto beginSeq = this->unsentSeqs.upper_bound(this->lastRrSeq.value());
311
+ const auto endSeq = this->unsentSeqs.upper_bound(rrSeq);
312
+ const auto unsentSeqCount = static_cast<int64_t>(std::distance(beginSeq, endSeq));
313
+
314
+ // Everything up to what this report has seen is settled.
315
+ this->unsentSeqs.erase(this->unsentSeqs.begin(), endSeq);
316
+
317
+ // The numbering was reset and the remote endpoint went on counting over the
318
+ // whole session, so there is nothing this report can be compared against. It
319
+ // only leaves the marks for the next one.
320
+ if (!this->lastRrTotalLost.has_value())
321
+ {
322
+ this->lastRrSeq = rrSeq;
323
+ this->lastRrTotalLost = totalLost;
324
+
325
+ return std::nullopt;
326
+ }
327
+
328
+ const int64_t expectedPackets = (rrSeq - this->lastRrSeq.value()) - unsentSeqCount;
329
+ // A hole already counted by a previous report and filled in since makes
330
+ // the remote endpoint report fewer lost packets than before, so the
331
+ // difference may well come out negative.
332
+ const int64_t lostPackets =
333
+ int64_t{ totalLost } - this->lastRrTotalLost.value() - unsentSeqCount;
334
+ // What is reported cannot go backwards, so it takes the count held between
335
+ // nothing lost and everything expected lost.
336
+ const int64_t reportedLostPackets = std::clamp<int64_t>(lostPackets, 0, expectedPackets);
337
+
338
+ this->lastRrSeq = rrSeq;
339
+ this->lastRrTotalLost = totalLost;
340
+
341
+ // NOTE: It is a count over the whole life of the stream, so it is held below
342
+ // the maximum of its type rather than let overflow.
343
+ this->packetsLost = static_cast<int32_t>(std::min<int64_t>(
344
+ int64_t{ this->packetsLost } + reportedLostPackets, std::numeric_limits<int32_t>::max()));
345
+ // NOTE: The fraction is in 1/256 units, hence the shift, which is the
346
+ // scale a Receiver Report uses for its own.
347
+ this->fractionLost =
348
+ expectedPackets > 0
349
+ ? static_cast<uint8_t>(std::min<int64_t>((reportedLostPackets << 8) / expectedPackets, 255))
350
+ : 0;
351
+
352
+ return Loss{ .lostPackets = lostPackets, .expectedPackets = expectedPackets };
310
353
  }
311
354
 
312
355
  void RtpStreamSend::ReceiveRtcpXrReceiverReferenceTime(
@@ -423,8 +466,22 @@ namespace RTC
423
466
  this->retransmissionBuffer->Clear();
424
467
  }
425
468
 
469
+ // Discard pending retransmissions.
470
+ this->pendingRetransmissionsQueue.clear();
471
+ this->pendingRetransmissionsSet.clear();
472
+
473
+ if (this->retransmissionTimer)
474
+ {
475
+ this->retransmissionTimer->Stop();
476
+ }
477
+
426
478
  // Reset jitter.
427
479
  this->jitter = 0;
480
+
481
+ // Nothing is being measured while the stream is paused, and the numbering
482
+ // goes on where it was once it resumes, so the marks the next Receiver
483
+ // Report is measured against are left alone.
484
+ this->fractionLost = 0;
428
485
  }
429
486
 
430
487
  void RtpStreamSend::Resume()
@@ -432,6 +489,58 @@ namespace RTC
432
489
  MS_TRACE();
433
490
  }
434
491
 
492
+ void RtpStreamSend::UpdateUnsentSeqNumbers(uint16_t seq)
493
+ {
494
+ MS_TRACE();
495
+
496
+ const int64_t unwrappedSeq = this->seqUnwrapper.Unwrap(seq).GetValue();
497
+
498
+ // Nothing was sent before this one, so it leaves nothing behind. It is also
499
+ // where the interval of the first Receiver Report starts, so that the first
500
+ // one measures from here rather than from nowhere.
501
+ if (!this->highestSentSeq.has_value())
502
+ {
503
+ this->highestSentSeq = unwrappedSeq;
504
+ this->lastRrSeq = unwrappedSeq - 1;
505
+
506
+ return;
507
+ }
508
+
509
+ // It arrived late and fills in one that was taken for never sent.
510
+ if (unwrappedSeq <= this->highestSentSeq.value())
511
+ {
512
+ this->unsentSeqs.erase(unwrappedSeq);
513
+
514
+ return;
515
+ }
516
+
517
+ // Whatever it skipped was never handed to this stream and never will be:
518
+ // it is what the uplink of the Producer lost and nobody could forward.
519
+ //
520
+ // NOTE: Only the most recent ones are taken, since anything older than that
521
+ // is what the bound below would drop right away anyway, and a numbering
522
+ // that jumps far ahead must not cost a walk over half the sequence number
523
+ // space.
524
+ const int64_t firstUnsentSeq = std::max<int64_t>(
525
+ this->highestSentSeq.value() + 1,
526
+ unwrappedSeq - static_cast<int64_t>(RtpStreamSend::MaxUnsentSeqNumbers));
527
+
528
+ for (int64_t unsentSeq{ firstUnsentSeq }; unsentSeq < unwrappedSeq; ++unsentSeq)
529
+ {
530
+ this->unsentSeqs.insert(unsentSeq);
531
+ }
532
+
533
+ this->highestSentSeq = unwrappedSeq;
534
+
535
+ // Only a Receiver Report drains this, so a remote endpoint that stops
536
+ // reporting must not make it grow without end. The oldest ones go, which
537
+ // are the ones the next report is least likely to name.
538
+ while (this->unsentSeqs.size() > RtpStreamSend::MaxUnsentSeqNumbers)
539
+ {
540
+ this->unsentSeqs.erase(this->unsentSeqs.begin());
541
+ }
542
+ }
543
+
435
544
  int64_t RtpStreamSend::GetBitrate(
436
545
  int64_t /*nowMs*/, uint8_t /*spatialLayer*/, uint8_t /*temporalLayer*/)
437
546
  {
@@ -455,221 +564,245 @@ namespace RTC
455
564
  MS_ABORT("invalid method call");
456
565
  }
457
566
 
458
- // This method looks for the requested RTP packets and inserts them into the
459
- // RetransmissionContainer vector (and sets to null the next position).
567
+ // This method takes pending retransmissions from the front of the queue and
568
+ // retransmits their packets until MaxRetransmittedPacketsPerIteration packets
569
+ // have been retransmitted or the queue is empty. Pending retransmissions whose
570
+ // packet is not retransmitted don't count.
460
571
  //
461
- // If RTX is used the stored packet will be RTX encoded now (if not already
462
- // encoded in a previous resend).
463
- //
464
- // NOTE: This method doesn't verify whether requested stored packet is too
465
- // old, why? Because, if we verified it, we would do it by comparing its
466
- // timestamp with the newest one in the retransmission buffer. However we
467
- // already clean old packets upon receipt of any new packet (see Insert()
468
- // method in RetransmissionBuffer class).
469
- void RtpStreamSend::FillRetransmissionContainer(uint16_t seq, uint16_t bitmask)
572
+ // If RTX is used the stored packet will be RTX encoded now.
573
+ void RtpStreamSend::RetransmitPendingPackets()
470
574
  {
471
575
  MS_TRACE();
472
576
 
473
- // Ensure the container's first element is 0.
474
- RetransmissionContainer[0] = nullptr;
577
+ const int64_t nowMs = this->shared->GetTimeMs();
578
+ const int64_t rttMs = (this->rttMs > 0.0f ? static_cast<int64_t>(this->rttMs) : DefaultRttMs);
579
+
580
+ size_t numRetransmittedPackets{ 0 };
475
581
 
476
- // If NACK is not supported, exit.
477
- if (!this->retransmissionBuffer)
582
+ while (!this->pendingRetransmissionsQueue.empty() &&
583
+ numRetransmittedPackets < MaxRetransmittedPacketsPerIteration)
478
584
  {
479
- MS_WARN_TAG(rtx, "NACK not supported");
585
+ const uint16_t seq = this->pendingRetransmissionsQueue.front();
480
586
 
481
- return;
482
- }
587
+ this->pendingRetransmissionsQueue.pop_front();
588
+ this->pendingRetransmissionsSet.erase(seq);
483
589
 
484
- // Look for each requested packet.
485
- const int64_t nowMs = this->shared->GetTimeMs();
486
- const int64_t rttMs = (this->rttMs > 0.0f ? static_cast<int64_t>(this->rttMs) : DefaultRttMs);
487
- uint16_t currentSeq = seq;
488
- bool requested{ true };
489
- size_t containerIdx{ 0 };
490
-
491
- // Variables for debugging.
492
- const uint16_t origBitmask = bitmask;
493
- uint16_t sentBitmask{ 0b0000000000000000 };
494
- bool isFirstPacket{ true };
495
- bool firstPacketSent{ false };
496
- uint8_t bitmaskCounter{ 0 };
497
-
498
- while (requested || bitmask != 0)
499
- {
500
- bool sent = false;
590
+ RTP::RetransmissionBuffer::Item* const item = this->retransmissionBuffer->Get(seq);
501
591
 
502
- if (requested)
592
+ // Packet not found.
593
+ if (!item)
503
594
  {
504
- auto* item = this->retransmissionBuffer->Get(currentSeq);
595
+ MS_DEBUG_DEV("ignoring retransmission for a packet not stored [seq:%" PRIu16 "]", seq);
505
596
 
506
- // Packet not found.
507
- if (!item)
508
- {
509
- // Do nothing.
510
- }
511
- // Don't resent the packet if it was resent in the last RTT ms.
512
- else if (item->resentAtMs != 0 && nowMs - item->resentAtMs <= rttMs)
513
- {
514
- MS_DEBUG_TAG(
515
- rtx,
516
- "ignoring retransmission for a packet already resent in the last RTT ms "
517
- "[seq:%" PRIu16 ", rtt:%" PRIi64 " ms]",
518
- item->sequenceNumber,
519
- rttMs);
520
- }
521
- // Stored packet is valid for retransmission. Resend it.
522
- else
523
- {
524
- // Save when this packet was resent.
525
- item->resentAtMs = nowMs;
597
+ continue;
598
+ }
599
+ // Don't resend the packet if it was stored too long ago.
600
+ else if (this->retransmissionBuffer->IsTooOld(item, nowMs))
601
+ {
602
+ MS_DEBUG_DEV(
603
+ "ignoring retransmission for a packet stored too long ago [seq:%" PRIu16
604
+ ", stored:%" PRIi64 " ms ago]",
605
+ seq,
606
+ nowMs - item->storedAtMs);
607
+
608
+ continue;
609
+ }
610
+ // Don't resend the packet if it was resent in the last RTT ms.
611
+ else if (item->resentAtMs != 0 && nowMs - item->resentAtMs <= rttMs)
612
+ {
613
+ MS_DEBUG_TAG(
614
+ rtx,
615
+ "ignoring retransmission for a packet already resent in the last RTT ms "
616
+ "[seq:%" PRIu16 ", rtt:%" PRIi64 " ms]",
617
+ item->sequenceNumber,
618
+ rttMs);
526
619
 
527
- // Increase the number of times this packet was sent.
528
- item->sentTimes++;
620
+ continue;
621
+ }
529
622
 
530
- // Store the item in the container and then increment its index.
531
- RetransmissionContainer[containerIdx++] = item;
623
+ // Save when this packet was resent.
624
+ item->resentAtMs = nowMs;
532
625
 
533
- sent = true;
626
+ // Increase the number of times this packet was sent.
627
+ item->sentTimes++;
534
628
 
535
- if (isFirstPacket)
536
- {
537
- firstPacketSent = true;
538
- }
539
- }
540
- }
629
+ MS_ASSERT(
630
+ item->sharedPacket.HasPacket(),
631
+ "stored item doesn't contain a packet [ssrc:%" PRIu32 ", seq:%" PRIu16
632
+ ", timestamp:%" PRIu32 "]",
633
+ item->ssrc,
634
+ item->sequenceNumber,
635
+ item->timestamp);
636
+
637
+ auto* const packet = item->sharedPacket.GetPacket();
638
+
639
+ // Keep the values of the original packet received by the Consumer.
640
+ const auto origSsrc = packet->GetSsrc();
641
+ const auto origSeq = packet->GetSequenceNumber();
642
+ const auto origTimestamp = packet->GetTimestamp();
643
+ const auto origMarker = packet->HasMarker();
541
644
 
542
- requested = (bitmask & 1) != 0;
543
- bitmask >>= 1;
544
- currentSeq++;
645
+ std::string origMid;
545
646
 
546
- if (!isFirstPacket)
647
+ // Put correct info into the packet.
648
+ packet->SetSsrc(item->ssrc);
649
+ packet->SetSequenceNumber(item->sequenceNumber);
650
+ packet->SetTimestamp(item->timestamp);
651
+ packet->SetMarker(item->marker);
652
+
653
+ if (item->encoder != nullptr)
547
654
  {
548
- sentBitmask |= (sent ? 1 : 0) << bitmaskCounter;
549
- bitmaskCounter++;
655
+ packet->EncodePayload(item->encoder.get());
550
656
  }
551
- else
657
+
658
+ // Update MID RTP extension value.
659
+ if (!this->mid.empty())
552
660
  {
553
- isFirstPacket = false;
661
+ packet->ReadMid(origMid);
662
+ packet->UpdateMid(this->mid);
554
663
  }
555
- }
556
664
 
557
- // If not all the requested packets was sent, log it.
558
- if (!firstPacketSent || origBitmask != sentBitmask)
559
- {
560
- MS_WARN_DEV(
561
- "could not resend all packets [seq:%" PRIu16
562
- ", first:%s, "
563
- "bitmask:" MS_UINT16_TO_BINARY_PATTERN ", sent bitmask:" MS_UINT16_TO_BINARY_PATTERN "]",
564
- seq,
565
- firstPacketSent ? "yes" : "no",
566
- MS_UINT16_TO_BINARY(origBitmask),
567
- MS_UINT16_TO_BINARY(sentBitmask));
568
- }
569
- else
570
- {
571
- MS_DEBUG_DEV(
572
- "all packets resent [seq:%" PRIu16 ", bitmask:" MS_UINT16_TO_BINARY_PATTERN "]",
573
- seq,
574
- MS_UINT16_TO_BINARY(origBitmask));
575
- }
665
+ // If we use RTX, encode it.
666
+ if (HasRtx())
667
+ {
668
+ // Increment RTX seq.
669
+ this->rtxSeq++;
576
670
 
577
- // Set the next container element to null.
578
- RetransmissionContainer[containerIdx] = nullptr;
579
- }
671
+ packet->RtxEncode(this->params.rtxPayloadType, this->params.rtxSsrc, this->rtxSeq);
672
+ }
580
673
 
581
- void RtpStreamSend::UpdateScore(RTC::RTCP::ReceiverReport* report)
582
- {
583
- MS_TRACE();
674
+ // Retransmit the packet.
675
+ static_cast<RTP::RtpStreamSend::Listener*>(this->listener)
676
+ ->OnRtpStreamRetransmitRtpPacket(this, packet);
584
677
 
585
- // Calculate number of packets sent in this interval.
586
- const auto totalSent = this->transmissionCounter.GetPacketCount();
587
- const auto sent = totalSent - this->sentPriorScore;
678
+ // Mark the packet as retransmitted.
679
+ RTP::RtpStream::PacketRetransmitted(packet);
588
680
 
589
- this->sentPriorScore = totalSent;
681
+ // Mark the packet as repaired (only if this is the first retransmission).
682
+ if (item->sentTimes == 1)
683
+ {
684
+ RTP::RtpStream::PacketRepaired(packet);
685
+ }
590
686
 
591
- // Calculate number of packets lost in this interval.
592
- const int32_t totalLost = report->GetTotalLost() > 0 ? report->GetTotalLost() : 0;
687
+ // If we use RTX, restore it.
688
+ if (HasRtx())
689
+ {
690
+ // Restore the packet.
691
+ packet->RtxDecode(RtpStream::GetPayloadType(), item->ssrc);
692
+ }
593
693
 
594
- uint64_t lost;
694
+ // Restore MID.
695
+ if (!this->mid.empty())
696
+ {
697
+ packet->UpdateMid(origMid);
698
+ }
595
699
 
596
- if (totalLost < this->lostPriorScore)
597
- {
598
- lost = 0;
599
- }
600
- else
601
- {
602
- lost = totalLost - this->lostPriorScore;
603
- }
700
+ // Restore payload.
701
+ if (item->encoder != nullptr)
702
+ {
703
+ packet->RestorePayload();
704
+ }
604
705
 
605
- this->lostPriorScore = totalLost;
706
+ // Restore RTP header fields.
707
+ packet->SetSsrc(origSsrc);
708
+ packet->SetSequenceNumber(origSeq);
709
+ packet->SetTimestamp(origTimestamp);
710
+ packet->SetMarker(origMarker);
606
711
 
607
- // Calculate number of packets repaired in this interval.
608
- const auto totalRepaired = this->packetsRepaired;
712
+ numRetransmittedPackets++;
713
+ }
714
+ }
609
715
 
610
- auto repaired = totalRepaired - this->repairedPriorScore;
716
+ void RtpStreamSend::UpdateScore(const std::optional<Loss>& loss)
717
+ {
718
+ MS_TRACE();
611
719
 
612
- this->repairedPriorScore = totalRepaired;
720
+ // Calculate the packets of this interval, which is what each counter has
721
+ // grown by since the previous score update.
722
+ const auto totalSentPackets = this->transmissionCounter.GetPacketCount();
723
+ const auto totalRepairedPackets = this->packetsRepaired;
724
+ const auto totalRetransmittedPackets = this->packetsRetransmitted;
725
+ const auto sentPackets = totalSentPackets - this->sentPriorScore;
726
+ const auto retransmittedPackets = totalRetransmittedPackets - this->retransmittedPriorScore;
613
727
 
614
- // Calculate number of packets retransmitted in this interval.
615
- const auto totatRetransmitted = this->packetsRetransmitted;
616
- const auto retransmitted = totatRetransmitted - this->retransmittedPriorScore;
728
+ auto repairedPackets = totalRepairedPackets - this->repairedPriorScore;
617
729
 
618
- this->retransmittedPriorScore = totatRetransmitted;
730
+ this->sentPriorScore = totalSentPackets;
731
+ this->repairedPriorScore = totalRepairedPackets;
732
+ this->retransmittedPriorScore = totalRetransmittedPackets;
733
+
734
+ // The Receiver Report measured no stretch of the stream, so there is nothing
735
+ // to say about this interval. The counters above have been taken anyway, so
736
+ // that the next one is measured over what it really covers.
737
+ if (!loss.has_value())
738
+ {
739
+ return;
740
+ }
619
741
 
620
742
  // We didn't send any packet.
621
- if (sent == 0)
743
+ if (sentPackets == 0)
622
744
  {
623
745
  RTP::RtpStream::UpdateScore(10);
624
746
 
625
747
  return;
626
748
  }
627
749
 
628
- lost = std::min(lost, sent);
629
- repaired = std::min(repaired, lost);
750
+ // Number of packets lost in this interval, which is what this link really
751
+ // lost and not what the remote endpoint reported, since that one also
752
+ // counts the sequence numbers that were never sent.
753
+ //
754
+ // NOTE: A report that gives back what a previous one was overcharged leaves
755
+ // this interval with nothing lost.
756
+ auto lostPackets = static_cast<uint64_t>(std::max<int64_t>(loss.value().lostPackets, 0));
757
+
758
+ lostPackets = std::min(lostPackets, sentPackets);
759
+ repairedPackets = std::min(repairedPackets, lostPackets);
630
760
 
631
761
  #if MS_LOG_DEV_LEVEL == 3
632
762
  MS_DEBUG_TAG(
633
763
  score,
634
- "[totalSent:%" PRIu64 ", totalLost:%" PRIi32 ", totalRepaired:%" PRIu64,
635
- totalSent,
636
- totalLost,
637
- totalRepaired);
764
+ "[totalSentPackets:%" PRIu64 ", totalLostPackets:%" PRIi32 ", totalRepairedPackets:%" PRIu64,
765
+ totalSentPackets,
766
+ this->packetsLost,
767
+ totalRepairedPackets);
638
768
 
639
769
  MS_DEBUG_TAG(
640
770
  score,
641
- "fixed values [sent:%" PRIu64 ", lost:%" PRIu64 ", repaired:%" PRIu64
642
- ", retransmitted:%" PRIu64,
643
- sent,
644
- lost,
645
- repaired,
646
- retransmitted);
771
+ "fixed values [sentPackets:%" PRIu64 ", lostPackets:%" PRIu64 ", repairedPackets:%" PRIu64
772
+ ", retransmittedPackets:%" PRIu64,
773
+ sentPackets,
774
+ lostPackets,
775
+ repairedPackets,
776
+ retransmittedPackets);
647
777
  #endif
648
778
 
649
- auto repairedRatio = static_cast<float>(repaired) / static_cast<float>(sent);
779
+ auto repairedRatio = static_cast<float>(repairedPackets) / static_cast<float>(sentPackets);
650
780
  auto repairedWeight = std::pow(1 / (repairedRatio + 1), 4);
651
781
 
652
- MS_ASSERT(retransmitted >= repaired, "repaired packets cannot be more than retransmitted ones");
782
+ MS_ASSERT(
783
+ retransmittedPackets >= repairedPackets,
784
+ "repaired packets cannot be more than retransmitted ones");
653
785
 
654
- if (retransmitted > 0)
786
+ if (retransmittedPackets > 0)
655
787
  {
656
- repairedWeight *= static_cast<float>(repaired) / retransmitted;
788
+ repairedWeight *= static_cast<float>(repairedPackets) / retransmittedPackets;
657
789
  }
658
790
 
659
- lost = static_cast<uint64_t>(lost - (repaired * repairedWeight));
791
+ lostPackets = static_cast<uint64_t>(lostPackets - (repairedPackets * repairedWeight));
660
792
 
661
- auto deliveredRatio = static_cast<float>(sent - lost) / static_cast<float>(sent);
662
- auto score = static_cast<uint8_t>(std::round(std::pow(deliveredRatio, 4) * 10));
793
+ auto deliveredRatio =
794
+ static_cast<float>(sentPackets - lostPackets) / static_cast<float>(sentPackets);
795
+ auto score = static_cast<uint8_t>(std::round(std::pow(deliveredRatio, 4) * 10));
663
796
 
664
797
  #if MS_LOG_DEV_LEVEL == 3
665
798
  MS_DEBUG_TAG(
666
799
  score,
667
- "[deliveredRatio:%f, repairedRatio:%f, repairedWeight:%f, new lost:%" PRIu64
800
+ "[deliveredRatio:%f, repairedRatio:%f, repairedWeight:%f, new lostPackets:%" PRIu64
668
801
  ", score:%" PRIu8 "]",
669
802
  deliveredRatio,
670
803
  repairedRatio,
671
804
  repairedWeight,
672
- lost,
805
+ lostPackets,
673
806
  score);
674
807
  #endif
675
808
 
@@ -686,6 +819,57 @@ namespace RTC
686
819
  {
687
820
  this->retransmissionBuffer->Clear();
688
821
  }
822
+
823
+ // Discard pending retransmissions, since their sequence numbers belong to
824
+ // the previous numbering.
825
+ this->pendingRetransmissionsQueue.clear();
826
+ this->pendingRetransmissionsSet.clear();
827
+
828
+ if (this->retransmissionTimer)
829
+ {
830
+ this->retransmissionTimer->Stop();
831
+ }
832
+
833
+ ResetSendLoss();
834
+ }
835
+
836
+ void RtpStreamSend::ResetSendLoss()
837
+ {
838
+ MS_TRACE();
839
+
840
+ // What was never sent belongs to a numbering that is not in use anymore,
841
+ // and so does the mark the next Receiver Report would be measured against.
842
+ this->seqUnwrapper.Reset();
843
+ this->unsentSeqs.clear();
844
+ this->highestSentSeq.reset();
845
+ this->lastRrSeq.reset();
846
+ // The remote endpoint counts what it has lost over the whole session and
847
+ // does not reset along with us, so the next Receiver Report cannot be a
848
+ // difference against anything and can only become the new mark.
849
+ this->lastRrTotalLost.reset();
850
+ // Nothing has been measured over this numbering, and holding on to what
851
+ // was measured over the previous one would keep reporting it for as long
852
+ // as the stream stays quiet.
853
+ //
854
+ // NOTE: The total of lost packets is not cleared, being a count over the
855
+ // whole life of the stream rather than a measure of its current state.
856
+ this->fractionLost = 0;
857
+ }
858
+
859
+ void RtpStreamSend::OnTimer(TimerHandleInterface* timer)
860
+ {
861
+ MS_TRACE();
862
+
863
+ if (timer == this->retransmissionTimer.get())
864
+ {
865
+ RetransmitPendingPackets();
866
+
867
+ // Schedule the next iteration if there are retransmissions left.
868
+ if (!this->pendingRetransmissionsQueue.empty())
869
+ {
870
+ this->retransmissionTimer->Start(RetransmissionIntervalMs);
871
+ }
872
+ }
689
873
  }
690
874
  } // namespace RTP
691
875
  } // namespace RTC