mediasoup 3.27.0 → 3.28.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 (213) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/node/lib/Consumer.d.ts.map +1 -1
  3. package/node/lib/Consumer.js +4 -51
  4. package/node/lib/Producer.d.ts.map +1 -1
  5. package/node/lib/Producer.js +4 -8
  6. package/node/lib/ProducerTypes.d.ts +4 -2
  7. package/node/lib/ProducerTypes.d.ts.map +1 -1
  8. package/node/lib/Router.d.ts.map +1 -1
  9. package/node/lib/Router.js +4 -0
  10. package/node/lib/Transport.d.ts.map +1 -1
  11. package/node/lib/Transport.js +7 -10
  12. package/node/lib/fbs/rtp-parameters/rtp-header-extension-uri.d.ts +3 -4
  13. package/node/lib/fbs/rtp-parameters/rtp-header-extension-uri.d.ts.map +1 -1
  14. package/node/lib/fbs/rtp-parameters/rtp-header-extension-uri.js +3 -4
  15. package/node/lib/ortc.d.ts +0 -2
  16. package/node/lib/ortc.d.ts.map +1 -1
  17. package/node/lib/ortc.js +11 -21
  18. package/node/lib/rtpMappingFbsUtils.d.ts +6 -0
  19. package/node/lib/rtpMappingFbsUtils.d.ts.map +1 -0
  20. package/node/lib/rtpMappingFbsUtils.js +65 -0
  21. package/node/lib/rtpParametersFbsUtils.d.ts.map +1 -1
  22. package/node/lib/rtpParametersFbsUtils.js +0 -6
  23. package/node/lib/rtpParametersTypes.d.ts +1 -1
  24. package/node/lib/rtpParametersTypes.d.ts.map +1 -1
  25. package/node/lib/rtpStreamFbsUtils.d.ts +4 -0
  26. package/node/lib/rtpStreamFbsUtils.d.ts.map +1 -0
  27. package/node/lib/rtpStreamFbsUtils.js +51 -0
  28. package/node/lib/supportedRtpCapabilities.d.ts.map +1 -1
  29. package/node/lib/supportedRtpCapabilities.js +6 -13
  30. package/node/lib/test/test-Consumer.js +1 -13
  31. package/node/lib/test/test-DataConsumer.js +9 -0
  32. package/node/lib/test/test-DataProducer.js +38 -0
  33. package/node/lib/test/test-PipeTransport.js +27 -29
  34. package/node/lib/test/test-Producer.js +97 -0
  35. package/node/lib/test/test-ortc.js +34 -14
  36. package/package.json +10 -10
  37. package/worker/fbs/FBS/meson.build +25 -0
  38. package/worker/fbs/meson.build +15 -18
  39. package/worker/fbs/rtpParameters.fbs +0 -1
  40. package/worker/fuzzer/src/FuzzerUtils.cpp +5 -3
  41. package/worker/fuzzer/src/RTC/RTP/FuzzerPacket.cpp +16 -2
  42. package/worker/include/DepLibUV.hpp +13 -0
  43. package/worker/include/RTC/BWE/AlrDetector.hpp +97 -0
  44. package/worker/include/RTC/BWE/BitrateProber.hpp +217 -0
  45. package/worker/include/RTC/BWE/BitrateUtils.hpp +39 -0
  46. package/worker/include/RTC/BWE/BweTypes.hpp +171 -3
  47. package/worker/include/RTC/BWE/FeedbackAdapter.hpp +0 -14
  48. package/worker/include/RTC/BWE/InterArrival.hpp +117 -0
  49. package/worker/include/RTC/BWE/IntervalBudget.hpp +81 -0
  50. package/worker/include/RTC/BWE/LossBasedController.hpp +625 -0
  51. package/worker/include/RTC/BWE/OveruseDetector.hpp +71 -0
  52. package/worker/include/RTC/BWE/OveruseEstimator.hpp +115 -0
  53. package/worker/include/RTC/BWE/ProbeBitrateEstimator.hpp +84 -0
  54. package/worker/include/RTC/BWE/ProbeController.hpp +379 -0
  55. package/worker/include/RTC/BWE/ProbePacketGenerator.hpp +130 -0
  56. package/worker/include/RTC/BWE/ProbingScheduler.hpp +187 -0
  57. package/worker/include/RTC/BWE/RembGenerator.hpp +96 -0
  58. package/worker/include/RTC/BWE/RemoteBitrateEstimatorAbsSendTime.hpp +237 -0
  59. package/worker/include/RTC/BWE/RobustThroughputEstimator.hpp +26 -0
  60. package/worker/include/RTC/BWE/SendPacketHistory.hpp +161 -25
  61. package/worker/include/RTC/BWE/TargetRateController.hpp +306 -0
  62. package/worker/include/RTC/BWE/TransportWideCcFeedbackGenerator.hpp +146 -0
  63. package/worker/include/RTC/BWE/TrendlineEstimator.hpp +73 -16
  64. package/worker/include/RTC/Consts.hpp +70 -0
  65. package/worker/include/RTC/DataConsumer.hpp +3 -5
  66. package/worker/include/RTC/DirectTransport.hpp +4 -5
  67. package/worker/include/RTC/PipeTransport.hpp +4 -5
  68. package/worker/include/RTC/PlainTransport.hpp +4 -5
  69. package/worker/include/RTC/RTCP/FeedbackPsRemb.hpp +6 -7
  70. package/worker/include/RTC/RTCP/FeedbackRtpTransport.hpp +3 -1
  71. package/worker/include/RTC/RTP/Codecs/Tools.hpp +1 -1
  72. package/worker/include/RTC/RTP/HeaderExtensionIds.hpp +0 -1
  73. package/worker/include/RTC/RTP/Packet.hpp +16 -4
  74. package/worker/include/RTC/RTP/ProbationGenerator.hpp +0 -8
  75. package/worker/include/RTC/RTP/RtpStream.hpp +12 -9
  76. package/worker/include/RTC/RTP/RtpStreamRecv.hpp +22 -4
  77. package/worker/include/RTC/RTP/RtpStreamSend.hpp +4 -2
  78. package/worker/include/RTC/RTP/RtxStream.hpp +10 -4
  79. package/worker/include/RTC/RateCalculator.hpp +103 -24
  80. package/worker/include/RTC/RemoteCaptureTimeEstimator.hpp +33 -3
  81. package/worker/include/RTC/{RtcLogger.hpp → RtcLogger/RtpPacket.hpp} +5 -3
  82. package/worker/include/RTC/RtcLogger/SendBurst.hpp +88 -0
  83. package/worker/include/RTC/RtpDictionaries.hpp +3 -4
  84. package/worker/include/RTC/SCTP/association/Association.hpp +1 -1
  85. package/worker/include/RTC/SCTP/association/StreamResetHandler.hpp +36 -0
  86. package/worker/include/RTC/SCTP/public/AssociationMetrics.hpp +0 -1
  87. package/worker/include/RTC/SCTP/public/SctpTypes.hpp +9 -9
  88. package/worker/include/RTC/SCTP/rx/DataTracker.hpp +1 -1
  89. package/worker/include/RTC/SCTP/rx/ReassemblyQueue.hpp +11 -1
  90. package/worker/include/RTC/SctpDictionaries.hpp +2 -2
  91. package/worker/include/RTC/TcpConnection.hpp +2 -1
  92. package/worker/include/RTC/Transport.hpp +23 -8
  93. package/worker/include/RTC/TransportTuple.hpp +61 -6
  94. package/worker/include/RTC/WebRtcTransport.hpp +4 -5
  95. package/worker/include/Shared.hpp +5 -0
  96. package/worker/include/SharedInterface.hpp +10 -0
  97. package/worker/include/Utils.hpp +72 -3
  98. package/worker/include/common.hpp +12 -4
  99. package/worker/include/handles/SendCallbacks.hpp +34 -0
  100. package/worker/include/handles/TcpConnectionHandle.hpp +4 -12
  101. package/worker/include/handles/UdpSocketHandle.hpp +4 -8
  102. package/worker/meson.build +45 -1
  103. package/worker/meson_options.txt +1 -0
  104. package/worker/mocks/include/MockShared.hpp +16 -2
  105. package/worker/mocks/include/handles/MockBackoffTimerHandle.hpp +1 -1
  106. package/worker/scripts/package-lock.json +6 -6
  107. package/worker/src/RTC/BWE/AimdRateControl.cpp +4 -5
  108. package/worker/src/RTC/BWE/AlrDetector.cpp +80 -0
  109. package/worker/src/RTC/BWE/BitrateProber.cpp +256 -0
  110. package/worker/src/RTC/BWE/BitrateUtils.cpp +54 -0
  111. package/worker/src/RTC/BWE/DelayBasedBwe.cpp +2 -2
  112. package/worker/src/RTC/BWE/FeedbackAdapter.cpp +20 -37
  113. package/worker/src/RTC/BWE/InterArrival.cpp +216 -0
  114. package/worker/src/RTC/BWE/IntervalBudget.cpp +93 -0
  115. package/worker/src/RTC/BWE/LossBasedController.cpp +1194 -0
  116. package/worker/src/RTC/BWE/OveruseDetector.cpp +128 -0
  117. package/worker/src/RTC/BWE/OveruseEstimator.cpp +166 -0
  118. package/worker/src/RTC/BWE/ProbeBitrateEstimator.cpp +222 -0
  119. package/worker/src/RTC/BWE/ProbeController.cpp +645 -0
  120. package/worker/src/RTC/BWE/ProbePacketGenerator.cpp +140 -0
  121. package/worker/src/RTC/BWE/ProbingScheduler.cpp +196 -0
  122. package/worker/src/RTC/BWE/RembGenerator.cpp +169 -0
  123. package/worker/src/RTC/BWE/RemoteBitrateEstimatorAbsSendTime.cpp +497 -0
  124. package/worker/src/RTC/BWE/RobustThroughputEstimator.cpp +47 -0
  125. package/worker/src/RTC/BWE/SendPacketHistory.cpp +173 -50
  126. package/worker/src/RTC/BWE/TargetRateController.cpp +433 -0
  127. package/worker/src/RTC/BWE/TransportWideCcFeedbackGenerator.cpp +377 -0
  128. package/worker/src/RTC/BWE/TrendlineEstimator.cpp +102 -5
  129. package/worker/src/RTC/Consumer.cpp +7 -5
  130. package/worker/src/RTC/DataConsumer.cpp +8 -10
  131. package/worker/src/RTC/DirectTransport.cpp +14 -8
  132. package/worker/src/RTC/DtlsTransport.cpp +8 -8
  133. package/worker/src/RTC/ICE/IceCandidate.cpp +4 -4
  134. package/worker/src/RTC/ICE/IceServer.cpp +1 -1
  135. package/worker/src/RTC/PipeTransport.cpp +13 -8
  136. package/worker/src/RTC/PlainTransport.cpp +13 -8
  137. package/worker/src/RTC/Producer.cpp +14 -31
  138. package/worker/src/RTC/RTCP/Bye.cpp +1 -1
  139. package/worker/src/RTC/RTCP/FeedbackPsRemb.cpp +18 -0
  140. package/worker/src/RTC/RTCP/FeedbackPsRpsi.cpp +2 -2
  141. package/worker/src/RTC/RTCP/FeedbackPsVbcm.cpp +2 -1
  142. package/worker/src/RTC/RTCP/FeedbackRtpTransport.cpp +2 -2
  143. package/worker/src/RTC/RTCP/Sdes.cpp +1 -1
  144. package/worker/src/RTC/RTP/Codecs/DependencyDescriptor.cpp +2 -1
  145. package/worker/src/RTC/RTP/Packet.cpp +26 -11
  146. package/worker/src/RTC/RTP/ProbationGenerator.cpp +5 -6
  147. package/worker/src/RTC/RTP/RtpStream.cpp +1 -1
  148. package/worker/src/RTC/RTP/RtpStreamRecv.cpp +69 -48
  149. package/worker/src/RTC/RTP/RtpStreamSend.cpp +31 -31
  150. package/worker/src/RTC/RTP/RtxStream.cpp +22 -7
  151. package/worker/src/RTC/RateCalculator.cpp +133 -28
  152. package/worker/src/RTC/RemoteCaptureTimeEstimator.cpp +56 -1
  153. package/worker/src/RTC/{RtcLogger.cpp → RtcLogger/RtpPacket.cpp} +2 -2
  154. package/worker/src/RTC/RtcLogger/SendBurst.cpp +124 -0
  155. package/worker/src/RTC/RtpDictionaries/RtpHeaderExtensionUri.cpp +2 -12
  156. package/worker/src/RTC/RtpDictionaries/RtpParameters.cpp +1 -1
  157. package/worker/src/RTC/SCTP/association/Association.cpp +16 -2
  158. package/worker/src/RTC/SCTP/association/StreamResetHandler.cpp +106 -1
  159. package/worker/src/RTC/SCTP/public/AssociationMetrics.cpp +1 -1
  160. package/worker/src/RTC/SCTP/rx/InterleavedReassemblyStreams.cpp +3 -3
  161. package/worker/src/RTC/SCTP/rx/ReassemblyQueue.cpp +2 -1
  162. package/worker/src/RTC/SCTP/rx/TraditionalReassemblyStreams.cpp +4 -4
  163. package/worker/src/RTC/SCTP/tx/RetransmissionTimeout.cpp +4 -4
  164. package/worker/src/RTC/SctpDictionaries/SctpStreamParameters.cpp +6 -10
  165. package/worker/src/RTC/SimulcastProducerStreamManager.cpp +2 -2
  166. package/worker/src/RTC/SrtpSession.cpp +2 -2
  167. package/worker/src/RTC/SvcProducerStreamManager.cpp +2 -2
  168. package/worker/src/RTC/TcpConnection.cpp +2 -2
  169. package/worker/src/RTC/Transport.cpp +112 -75
  170. package/worker/src/RTC/TransportTuple.cpp +4 -4
  171. package/worker/src/RTC/WebRtcServer.cpp +4 -3
  172. package/worker/src/RTC/WebRtcTransport.cpp +20 -13
  173. package/worker/src/Utils/BitStream.cpp +24 -0
  174. package/worker/src/handles/BackoffTimerHandle.cpp +1 -1
  175. package/worker/src/handles/TcpConnectionHandle.cpp +13 -23
  176. package/worker/src/handles/UdpSocketHandle.cpp +14 -21
  177. package/worker/tasks.py +43 -18
  178. package/worker/test/include/RTC/BWE/helpers/LinkSimulator.hpp +68 -14
  179. package/worker/test/include/RTC/BWE/helpers/RemoteBitrateEstimatorAbsSendTimeTestHelper.hpp +125 -0
  180. package/worker/test/src/RTC/BWE/TestAlrDetector.cpp +110 -0
  181. package/worker/test/src/RTC/BWE/TestBitrateProber.cpp +418 -0
  182. package/worker/test/src/RTC/BWE/TestBitrateUtils.cpp +61 -0
  183. package/worker/test/src/RTC/BWE/TestDelayBasedBwe.cpp +4 -4
  184. package/worker/test/src/RTC/BWE/TestFeedbackAdapter.cpp +53 -37
  185. package/worker/test/src/RTC/BWE/TestInterArrival.cpp +643 -0
  186. package/worker/test/src/RTC/BWE/TestIntervalBudget.cpp +130 -0
  187. package/worker/test/src/RTC/BWE/TestLossBasedController.cpp +1199 -0
  188. package/worker/test/src/RTC/BWE/TestOveruseDetector.cpp +519 -0
  189. package/worker/test/src/RTC/BWE/TestProbeBitrateEstimator.cpp +266 -0
  190. package/worker/test/src/RTC/BWE/TestProbeController.cpp +1682 -0
  191. package/worker/test/src/RTC/BWE/TestProbePacketGenerator.cpp +265 -0
  192. package/worker/test/src/RTC/BWE/TestProbingScheduler.cpp +260 -0
  193. package/worker/test/src/RTC/BWE/TestRembGenerator.cpp +285 -0
  194. package/worker/test/src/RTC/BWE/TestRemoteBitrateEstimatorAbsSendTime.cpp +414 -0
  195. package/worker/test/src/RTC/BWE/TestSendPacketHistory.cpp +296 -77
  196. package/worker/test/src/RTC/BWE/TestTargetRateController.cpp +337 -0
  197. package/worker/test/src/RTC/BWE/TestTransportWideCcFeedbackGenerator.cpp +578 -0
  198. package/worker/test/src/RTC/BWE/TestTrendlineEstimator.cpp +45 -0
  199. package/worker/test/src/RTC/BWE/helpers/LinkSimulator.cpp +133 -29
  200. package/worker/test/src/RTC/BWE/helpers/RemoteBitrateEstimatorAbsSendTimeTestHelper.cpp +625 -0
  201. package/worker/test/src/RTC/RTCP/TestFeedbackPsRemb.cpp +19 -0
  202. package/worker/test/src/RTC/RTP/TestPacket.cpp +58 -4
  203. package/worker/test/src/RTC/RTP/TestProbationGenerator.cpp +3 -2
  204. package/worker/test/src/RTC/RTP/TestRtpStreamRecv.cpp +50 -0
  205. package/worker/test/src/RTC/RTP/TestRtpStreamSend.cpp +59 -0
  206. package/worker/test/src/RTC/SCTP/association/TestStreamResetHandler.cpp +111 -0
  207. package/worker/test/src/RTC/TestRateCalculator.cpp +322 -60
  208. package/worker/test/src/RTC/TestRemoteCaptureTimeEstimator.cpp +95 -7
  209. package/worker/test/src/RTC/TestSimpleProducerStreamManager.cpp +34 -16
  210. package/worker/test/src/RTC/TestSimulcastProducerStreamManager.cpp +31 -13
  211. package/worker/test/src/RTC/TestSvcProducerStreamManager.cpp +37 -19
  212. package/worker/test/src/RTC/TestTransportCongestionControlServer.cpp +10 -10
  213. package/worker/test/src/Utils/TestTime.cpp +120 -21
@@ -8,12 +8,23 @@
8
8
 
9
9
  namespace RTC
10
10
  {
11
+ /* Static. */
12
+
13
+ // Factor of the window size within which the latest sample still counts as
14
+ // recent.
15
+ static constexpr double RecentSampleMarginFactor{ 1.5 };
16
+
17
+ /* Instance methods. */
18
+
11
19
  RateCalculator::RateCalculator(int64_t windowSizeMs, float scale, uint16_t windowItems)
12
20
  {
13
21
  MS_TRACE();
14
22
 
15
23
  // Clamp the given values so every derived value is safe to use.
16
- this->windowSizeMs = std::max<int64_t>(windowSizeMs, 1);
24
+ // NOTE: The upper bound is what keeps one window and a half, which is the
25
+ // margin computed at the end, within an int64_t.
26
+ this->windowSizeMs =
27
+ std::clamp<int64_t>(windowSizeMs, 1, std::numeric_limits<int64_t>::max() / 2);
17
28
 
18
29
  const int64_t items = std::max<int64_t>(windowItems, 1);
19
30
 
@@ -28,36 +39,73 @@ namespace RTC
28
39
  this->buffer.resize(
29
40
  static_cast<size_t>((this->windowSizeMs + this->itemSizeMs - 1) / this->itemSizeMs));
30
41
 
31
- this->rateScale = static_cast<double>(scale) / static_cast<double>(this->windowSizeMs);
42
+ // NOTE: A negative scale would give negative rates, which whoever reads them
43
+ // takes as a count of bits.
44
+ this->scale = std::max(static_cast<double>(scale), 0.0);
45
+
46
+ this->recentSampleMarginMs =
47
+ static_cast<int64_t>(RecentSampleMarginFactor * static_cast<double>(this->windowSizeMs));
32
48
  }
33
49
 
34
50
  void RateCalculator::Update(size_t size, int64_t nowMs)
35
51
  {
36
52
  MS_TRACE();
37
53
 
38
- // Ignore data older than the window. Should never happen.
54
+ // Whether the window still held data when this sample arrived, and the sample
55
+ // before it is recent enough for both to be taken as the same stretch of
56
+ // traffic.
57
+ // NOTE: Must be told before sliding the window, since sliding it is what
58
+ // expires that data.
59
+ const bool lastSampleIsRecent =
60
+ this->totalSamples != 0 && this->lastSampleTimeMs.has_value() &&
61
+ this->lastSampleTimeMs.value() > nowMs - this->recentSampleMarginMs;
62
+
63
+ // A sample whose instant lies before the whole window is still data that
64
+ // went through, so it is taken as if it had been given at the instant the
65
+ // newest item starts at. Dropping it would leave the meter reporting less
66
+ // than what flowed with nobody able to tell, while placing it at the wrong
67
+ // instant is an error bounded by the window.
39
68
  if (!SlideWindow(nowMs))
40
69
  {
41
- MS_WARN_DEV("given nowMs is older than the current window, ignoring data");
70
+ MS_WARN_DEV("given nowMs is older than the current window, taking the data as newer");
71
+
72
+ nowMs = this->newestItemStartTimeMs;
73
+ }
42
74
 
43
- return;
75
+ // The very first sample starts the measured period, and so does one that
76
+ // finds the window empty after long enough without traffic. A stream that
77
+ // just sends less often than the window keeps the period it had, so that it
78
+ // is measured over the window instead of restarting at every sample and
79
+ // hence never being measured at all.
80
+ if (!this->firstSampleTimeMs.has_value() || (this->totalSamples == 0 && !lastSampleIsRecent))
81
+ {
82
+ this->firstSampleTimeMs = nowMs;
83
+ this->firstSampleCount = size;
44
84
  }
45
85
 
46
- this->buffer[this->newestItemIndex] += size;
86
+ Item& item = this->buffer[this->newestItemIndex];
87
+
88
+ item.count += size;
89
+ item.samples++;
90
+
47
91
  this->totalCount += size;
92
+ this->totalSamples++;
48
93
  this->bytes += size;
94
+ this->lastSampleTimeMs = nowMs;
49
95
  }
50
96
 
51
- int64_t RateCalculator::GetRate(int64_t nowMs)
97
+ std::optional<int64_t> RateCalculator::GetRate(int64_t nowMs)
52
98
  {
53
99
  MS_TRACE();
54
100
 
55
- // If both keys match, the memoized rate is still exact. `lastRate` is a pure
56
- // function of `totalCount`, so the value is right, and no expiration can be
57
- // pending: SlideWindow() already ran for this very `nowMs` and
58
- // `newestItemStartTimeMs` only moves forward afterwards, while the initial and
59
- // post Reset() state has an empty ring anyway.
60
- if (nowMs == this->lastTimeMs && this->totalCount == this->lastTotalCount)
101
+ // If every key matches, the memoized rate is still exact. `lastRate` is a
102
+ // pure function of the state the keys cover, so the value is right, and no
103
+ // expiration can be pending: SlideWindow() already ran for this very `nowMs`
104
+ // and `newestItemStartTimeMs` only moves forward afterwards, while the
105
+ // initial and post Reset() state has an empty ring anyway.
106
+ if (
107
+ nowMs == this->lastTimeMs && this->totalCount == this->lastTotalCount &&
108
+ this->totalSamples == this->lastTotalSamples)
61
109
  {
62
110
  MS_DEBUG_DEV("nothing changed since the latest call, early return");
63
111
 
@@ -66,13 +114,61 @@ namespace RTC
66
114
 
67
115
  SlideWindow(nowMs);
68
116
 
69
- const double rate = std::trunc((static_cast<double>(this->totalCount) * this->rateScale) + 0.5);
70
-
71
117
  // NOTE: Must be read after SlideWindow(), which may have expired data.
72
- this->lastTotalCount = this->totalCount;
73
- this->lastTimeMs = nowMs;
74
- this->lastRate =
75
- static_cast<int64_t>(std::min(rate, static_cast<double>(std::numeric_limits<int64_t>::max())));
118
+ this->lastTotalCount = this->totalCount;
119
+ this->lastTotalSamples = this->totalSamples;
120
+ this->lastTimeMs = nowMs;
121
+ this->lastRate = std::nullopt;
122
+
123
+ // Either no sample has ever been taken, or none of them is still within the
124
+ // window.
125
+ if (!this->firstSampleTimeMs.has_value() || this->totalSamples == 0)
126
+ {
127
+ return this->lastRate;
128
+ }
129
+
130
+ // Period the data within the window actually spans. The instant the first
131
+ // sample was taken at already counts as a millisecond, hence the increment,
132
+ // which is also what keeps this from ever being zero. And once the data
133
+ // reaches back beyond the window, the period is the window itself.
134
+ const int64_t periodMs =
135
+ std::clamp<int64_t>(nowMs - this->firstSampleTimeMs.value() + 1, 1, this->windowSizeMs);
136
+
137
+ // A single millisecond gives no duration to divide by, and a single sample
138
+ // says how much data was taken at an instant but nothing about how fast it
139
+ // is flowing, so neither is a measurement until the window has filled.
140
+ if (periodMs <= 1 || (this->totalSamples <= 1 && periodMs < this->windowSizeMs))
141
+ {
142
+ return this->lastRate;
143
+ }
144
+
145
+ // While the period is anchored to a sample, that sample sits at its very
146
+ // start, so the period does not cover the time that sample took to arrive and
147
+ // its data is not part of what flowed during it. Counting it would report a
148
+ // rate too high by as much as the ratio of samples to gaps between them,
149
+ // which is double with two samples and a tenth with eleven.
150
+ //
151
+ // NOTE: Once the period is the whole window it is no longer anchored to any
152
+ // sample, all of them fall inside it, and every one of them counts. And that
153
+ // is also the point at which the anchoring sample has left the window, so
154
+ // there is nothing of it left to leave out.
155
+ const uint64_t count =
156
+ periodMs < this->windowSizeMs ? this->totalCount - this->firstSampleCount : this->totalCount;
157
+
158
+ const double rate =
159
+ std::trunc(((static_cast<double>(count) * this->scale) / static_cast<double>(periodMs)) + 0.5);
160
+
161
+ // A rate that does not fit is no rate at all, which is better than the
162
+ // garbage that converting it would give.
163
+ // NOTE: The comparison is not a strict one because converting the maximum of
164
+ // int64_t to double rounds it up, so a rate equal to that value is already
165
+ // out of range.
166
+ if (rate >= static_cast<double>(std::numeric_limits<int64_t>::max()))
167
+ {
168
+ return this->lastRate;
169
+ }
170
+
171
+ this->lastRate = static_cast<int64_t>(rate);
76
172
 
77
173
  return this->lastRate;
78
174
  }
@@ -81,14 +177,19 @@ namespace RTC
81
177
  {
82
178
  MS_TRACE();
83
179
 
84
- std::ranges::fill(this->buffer, 0);
180
+ std::ranges::fill(this->buffer, Item{});
85
181
 
182
+ this->firstSampleTimeMs.reset();
183
+ this->firstSampleCount = 0;
184
+ this->lastSampleTimeMs.reset();
86
185
  this->newestItemIndex = 0;
87
186
  this->newestItemStartTimeMs = 0;
88
187
  this->totalCount = 0;
89
- this->lastRate = 0;
188
+ this->totalSamples = 0;
189
+ this->lastRate = std::nullopt;
90
190
  this->lastTimeMs = 0;
91
191
  this->lastTotalCount = 0;
192
+ this->lastTotalSamples = 0;
92
193
  }
93
194
 
94
195
  /**
@@ -127,13 +228,14 @@ namespace RTC
127
228
  {
128
229
  MS_DEBUG_DEV("a whole window elapsed, resetting every item");
129
230
 
130
- // NOTE: totalCount is the sum of every item, so a zero total means that
131
- // the ring is already zeroed.
132
- if (this->totalCount != 0)
231
+ // NOTE: totalSamples is the sum of the samples of every item, so a zero
232
+ // total means that the ring is already empty.
233
+ if (this->totalSamples != 0)
133
234
  {
134
- std::ranges::fill(this->buffer, 0);
235
+ std::ranges::fill(this->buffer, Item{});
135
236
 
136
- this->totalCount = 0;
237
+ this->totalCount = 0;
238
+ this->totalSamples = 0;
137
239
  }
138
240
 
139
241
  this->newestItemIndex = 0;
@@ -151,9 +253,12 @@ namespace RTC
151
253
  this->newestItemIndex = 0;
152
254
  }
153
255
 
154
- this->totalCount -= this->buffer[this->newestItemIndex];
256
+ Item& item = this->buffer[this->newestItemIndex];
257
+
258
+ this->totalCount -= item.count;
259
+ this->totalSamples -= item.samples;
155
260
 
156
- this->buffer[this->newestItemIndex] = 0;
261
+ item = Item{};
157
262
  }
158
263
 
159
264
  // Advance by whole items rather than jumping to `nowMs`. The window is
@@ -3,9 +3,18 @@
3
3
 
4
4
  #include "RTC/RemoteCaptureTimeEstimator.hpp"
5
5
  #include "Logger.hpp"
6
+ #include "Utils.hpp"
6
7
 
7
8
  namespace RTC
8
9
  {
10
+ /* Static. */
11
+
12
+ // How long a sender that negotiated 'abs-capture-time' may go without sending it
13
+ // before the capture instant is read from its Sender Reports instead. It only has
14
+ // to outlast the first packets of a sender that does send it, since the extension
15
+ // travels on some of them rather than on every one.
16
+ static constexpr int64_t AbsCaptureTimeTimeoutMs{ 5 * 1000 };
17
+
9
18
  /* Instance methods. */
10
19
 
11
20
  void RemoteCaptureTimeEstimator::UpdateSource(bool absCaptureTimeNegotiated)
@@ -34,6 +43,50 @@ namespace RTC
34
43
  : "Sender Report");
35
44
  }
36
45
 
46
+ void RemoteCaptureTimeEstimator::MayFallBackToSenderReport(
47
+ const RTC::RTP::RtpStreamRecv* rtpStream, int64_t nowUs)
48
+ {
49
+ MS_TRACE();
50
+
51
+ if (this->source != RemoteCaptureTimeEstimator::Source::ABS_CAPTURE_TIME)
52
+ {
53
+ return;
54
+ }
55
+
56
+ // Some stream of this sender has carried the extension at some point, so it is
57
+ // being sent and whatever stream has none of it is just early.
58
+ if (this->absCaptureTimeReceived)
59
+ {
60
+ return;
61
+ }
62
+
63
+ if (rtpStream->HasAbsCaptureTime())
64
+ {
65
+ this->absCaptureTimeReceived = true;
66
+
67
+ return;
68
+ }
69
+
70
+ if (!this->firstPacketAtUs.has_value())
71
+ {
72
+ this->firstPacketAtUs = nowUs;
73
+
74
+ return;
75
+ }
76
+
77
+ if (Utils::Time::TimeUsToMs(nowUs - this->firstPacketAtUs.value()) < AbsCaptureTimeTimeoutMs)
78
+ {
79
+ return;
80
+ }
81
+
82
+ MS_WARN_2TAGS(
83
+ rtp,
84
+ rtcp,
85
+ "abs-capture-time negotiated but never received, reading the capture instant from Sender Reports instead");
86
+
87
+ this->source = RemoteCaptureTimeEstimator::Source::SENDER_REPORT;
88
+ }
89
+
37
90
  void RemoteCaptureTimeEstimator::SenderReportReceived(const RTC::RTP::RtpStreamRecv* rtpStream)
38
91
  {
39
92
  MS_TRACE();
@@ -54,7 +107,7 @@ namespace RTC
54
107
  }
55
108
 
56
109
  std::optional<int64_t> RemoteCaptureTimeEstimator::GetLocalCaptureAtUs(
57
- const RTC::RTP::RtpStreamRecv* rtpStream, uint32_t ts) const
110
+ const RTC::RTP::RtpStreamRecv* rtpStream, uint32_t ts, int64_t nowUs)
58
111
  {
59
112
  MS_TRACE();
60
113
 
@@ -63,6 +116,8 @@ namespace RTC
63
116
  return std::nullopt;
64
117
  }
65
118
 
119
+ MayFallBackToSenderReport(rtpStream, nowUs);
120
+
66
121
  const auto remoteCaptureAtUs = this->source == RemoteCaptureTimeEstimator::Source::ABS_CAPTURE_TIME
67
122
  ? rtpStream->GetRemoteCaptureAtUsFromAbsCaptureTime(ts)
68
123
  : rtpStream->GetRemoteCaptureAtUsFromSenderReport(ts);
@@ -1,7 +1,7 @@
1
- #define MS_CLASS "RTC::RtcLogger"
1
+ #define MS_CLASS "RTC::RtcLogger::RtpPacket"
2
2
  // #define MS_LOG_DEV_LEVEL 3
3
3
 
4
- #include "RTC/RtcLogger.hpp"
4
+ #include "RTC/RtcLogger/RtpPacket.hpp"
5
5
  #include "Logger.hpp"
6
6
  #include <sstream>
7
7
 
@@ -0,0 +1,124 @@
1
+ #define MS_CLASS "RTC::RtcLogger::SendBurst"
2
+ // #define MS_LOG_DEV_LEVEL 3
3
+
4
+ #include "RTC/RtcLogger/SendBurst.hpp"
5
+ #include "Logger.hpp"
6
+ #include <sstream>
7
+
8
+ namespace RTC
9
+ {
10
+ namespace RtcLogger
11
+ {
12
+ /* Instance methods. */
13
+
14
+ void SendBurst::Sent(uint64_t loopTimeMs, size_t length, bool isRetransmission, bool isProbation)
15
+ {
16
+ MS_TRACE();
17
+
18
+ if (loopTimeMs != this->loopTimeMs)
19
+ {
20
+ CloseBurst();
21
+
22
+ this->loopTimeMs = loopTimeMs;
23
+ }
24
+
25
+ this->packets++;
26
+ this->bytes += length;
27
+
28
+ if (isRetransmission)
29
+ {
30
+ this->retransmissions++;
31
+ }
32
+
33
+ if (isProbation)
34
+ {
35
+ this->probations++;
36
+ }
37
+ }
38
+
39
+ void SendBurst::Log()
40
+ {
41
+ MS_TRACE();
42
+
43
+ // The iteration this runs in is not the one any burst belongs to, so whatever
44
+ // is in progress is already complete.
45
+ CloseBurst();
46
+
47
+ // Nothing has been sent since the last time, and a transport that sends
48
+ // nothing has nothing to say.
49
+ if (this->maxPackets == 0)
50
+ {
51
+ return;
52
+ }
53
+
54
+ std::stringstream ss;
55
+
56
+ ss << "{";
57
+ ss << R"("transportId": ")" << this->transportId << "\"";
58
+ ss << ", \"maxPackets\": " << this->maxPackets;
59
+ ss << ", \"maxBytes\": " << this->maxBytes;
60
+ ss << ", \"bursts\": [";
61
+
62
+ bool first{ true };
63
+
64
+ for (const auto& kv : this->bursts)
65
+ {
66
+ const auto packets = kv.first;
67
+ const auto& counters = kv.second;
68
+
69
+ if (!first)
70
+ {
71
+ ss << ", ";
72
+ }
73
+
74
+ first = false;
75
+
76
+ ss << "{\"packets\": " << packets;
77
+ ss << ", \"count\": " << counters.count;
78
+ ss << ", \"retransmissions\": " << counters.retransmissions;
79
+ ss << ", \"probations\": " << counters.probations;
80
+ ss << "}";
81
+ }
82
+
83
+ ss << "]}";
84
+
85
+ MS_DUMP_CLEAN(0, "%s", ss.str().c_str());
86
+
87
+ Clear();
88
+ }
89
+
90
+ void SendBurst::CloseBurst()
91
+ {
92
+ MS_TRACE();
93
+
94
+ if (this->packets == 0)
95
+ {
96
+ return;
97
+ }
98
+
99
+ auto& counters = this->bursts[this->packets];
100
+
101
+ counters.count++;
102
+ counters.retransmissions += this->retransmissions;
103
+ counters.probations += this->probations;
104
+
105
+ this->maxPackets = std::max(this->maxPackets, this->packets);
106
+ this->maxBytes = std::max(this->maxBytes, this->bytes);
107
+
108
+ this->packets = 0;
109
+ this->retransmissions = 0;
110
+ this->probations = 0;
111
+ this->bytes = 0;
112
+ }
113
+
114
+ void SendBurst::Clear()
115
+ {
116
+ MS_TRACE();
117
+
118
+ this->bursts.clear();
119
+
120
+ this->maxPackets = 0;
121
+ this->maxBytes = 0;
122
+ }
123
+ } // namespace RtcLogger
124
+ } // namespace RTC
@@ -50,11 +50,6 @@ namespace RTC
50
50
  return RtpHeaderExtensionUri::Type::VIDEO_ORIENTATION;
51
51
  }
52
52
 
53
- case FBS::RtpParameters::RtpHeaderExtensionUri::TimeOffset:
54
- {
55
- return RtpHeaderExtensionUri::Type::TIME_OFFSET;
56
- }
57
-
58
53
  case FBS::RtpParameters::RtpHeaderExtensionUri::PlayoutDelay:
59
54
  {
60
55
  return RtpHeaderExtensionUri::Type::PLAYOUT_DELAY;
@@ -75,7 +70,7 @@ namespace RTC
75
70
  return RtpHeaderExtensionUri::Type::MEDIASOUP_PACKET_ID;
76
71
  }
77
72
 
78
- NO_DEFAULT_GCC();
73
+ NO_DEFAULT();
79
74
  }
80
75
  }
81
76
 
@@ -126,11 +121,6 @@ namespace RTC
126
121
  return FBS::RtpParameters::RtpHeaderExtensionUri::VideoOrientation;
127
122
  }
128
123
 
129
- case RtpHeaderExtensionUri::Type::TIME_OFFSET:
130
- {
131
- return FBS::RtpParameters::RtpHeaderExtensionUri::TimeOffset;
132
- }
133
-
134
124
  case RtpHeaderExtensionUri::Type::ABS_CAPTURE_TIME:
135
125
  {
136
126
  return FBS::RtpParameters::RtpHeaderExtensionUri::AbsCaptureTime;
@@ -146,7 +136,7 @@ namespace RTC
146
136
  return FBS::RtpParameters::RtpHeaderExtensionUri::MediasoupPacketId;
147
137
  }
148
138
 
149
- NO_DEFAULT_GCC();
139
+ NO_DEFAULT();
150
140
  }
151
141
  }
152
142
 
@@ -92,7 +92,7 @@ namespace RTC
92
92
  return FBS::RtpParameters::Type::PIPE;
93
93
  }
94
94
 
95
- NO_DEFAULT_GCC();
95
+ NO_DEFAULT();
96
96
  }
97
97
  }
98
98
 
@@ -107,7 +107,7 @@ namespace RTC
107
107
  MS_TRACE();
108
108
 
109
109
  const auto stateStringView = Association::StateToString(this->state);
110
- const auto associationStateStringView = Types::AssociationStateToString(GetAssociationState());
110
+ const auto associationStateStringView = Types::associationStateToString(GetAssociationState());
111
111
 
112
112
  MS_DUMP_CLEAN(indentation, "<SCTP::Association>");
113
113
 
@@ -187,7 +187,7 @@ namespace RTC
187
187
  return Types::AssociationState::SHUTTING_DOWN;
188
188
  }
189
189
 
190
- NO_DEFAULT_GCC();
190
+ NO_DEFAULT();
191
191
  }
192
192
  }
193
193
 
@@ -608,6 +608,20 @@ namespace RTC
608
608
 
609
609
  if (this->tcb)
610
610
  {
611
+ // The cumulative ack TSN may have advanced while processing the chunks
612
+ // in this packet, which may be what deferred reset processing (if any)
613
+ // was waiting for.
614
+ if (this->tcb->GetStreamResetHandler().MayLeaveDeferredReset())
615
+ {
616
+ // There may be chunks to deliver that were queued while waiting for
617
+ // the streams to reset.
618
+ //
619
+ // NOTE: Both this and the above are done before sending the SACK chunk
620
+ // below so that its advertised receiver window credit accounts for the
621
+ // released and delivered data.
622
+ MayDeliverMessages();
623
+ }
624
+
611
625
  this->tcb->GetDataTracker().ObservePacketEnd();
612
626
  this->tcb->MaySendSackChunk();
613
627
  }
@@ -143,6 +143,63 @@ namespace RTC
143
143
  }
144
144
  }
145
145
 
146
+ bool StreamResetHandler::MayLeaveDeferredReset()
147
+ {
148
+ MS_TRACE();
149
+
150
+ if (!this->deferredIncomingRequest.has_value())
151
+ {
152
+ return false;
153
+ }
154
+
155
+ // https://tools.ietf.org/html/rfc6525#section-5.2.2
156
+ //
157
+ // "In this mode, any data arriving with a TSN larger than the Sender's
158
+ // Last Assigned TSN for the affected stream(s) MUST be queued locally and
159
+ // held until the cumulative acknowledgment point reaches the Sender's Last
160
+ // Assigned TSN."
161
+ //
162
+ // So deferred reset processing must end as soon as the cumulative ack TSN
163
+ // reaches the sender's last assigned TSN. Otherwise the queued data would
164
+ // be held (and accounted for) until the peer decides to retransmit its
165
+ // request, which it may never do.
166
+ if (this->dataTracker->IsLaterThanCumulativeAckedTsn(this->deferredIncomingRequest->senderLastAssignedTsn))
167
+ {
168
+ return false;
169
+ }
170
+
171
+ MS_DEBUG_DEV(
172
+ "leaving deferred reset processing, sender last assigned tsn %" PRIu32 " reached",
173
+ this->deferredIncomingRequest->senderLastAssignedTsn);
174
+
175
+ // Remember the request being performed here. The peer was told "in
176
+ // progress" and will retry it with a new request sequence number, and that
177
+ // retry must not reset the very same streams a second time.
178
+ this->performedDeferredRequest = std::move(this->deferredIncomingRequest);
179
+
180
+ this->deferredIncomingRequest = std::nullopt;
181
+
182
+ // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
183
+ const auto& streamIds = this->performedDeferredRequest.value().streamIds;
184
+
185
+ // https://tools.ietf.org/html/rfc6525#section-5.2.2
186
+ //
187
+ // E3) "If no stream numbers are listed in the parameter, then all incoming
188
+ // streams MUST be reset to 0 as the next expected SSN. If specific stream
189
+ // numbers are listed, then only these specific streams MUST be reset to 0,
190
+ // and all other non-listed SSNs remain unchanged." E4: "Any queued TSNs
191
+ // (queued at step E2) MUST now be released and processed normally."
192
+ this->reassemblyQueue->ResetStreamsAndLeaveDeferredReset(streamIds);
193
+
194
+ this->associationListenerDeferrer.OnAssociationInboundStreamsReset(streamIds);
195
+
196
+ // The request has now been performed, so a retransmission of it must get
197
+ // the final response rather than "in progress" again.
198
+ this->lastProcessedReqResult = ReconfigurationResponseParameter::Result::SUCCESS_PERFORMED;
199
+
200
+ return true;
201
+ }
202
+
146
203
  bool StreamResetHandler::ValidateReceivedReConfigChunk(const ReConfigChunk* receivedReConfigChunk)
147
204
  {
148
205
  MS_TRACE();
@@ -284,6 +341,11 @@ namespace RTC
284
341
  validationResult == ReqSeqNbrValidationResult::RETRANSMISSION &&
285
342
  this->lastProcessedReqResult != ReconfigurationResponseParameter::Result::IN_PROGRESS)
286
343
  {
344
+ // The peer is getting the final result of this request, so there is no
345
+ // longer a need to remember that it may have been performed when leaving
346
+ // deferred reset processing.
347
+ this->performedDeferredRequest = std::nullopt;
348
+
287
349
  auto* reconfigurationResponseParameter =
288
350
  reConfigChunk->BuildParameterInPlace<ReconfigurationResponseParameter>();
289
351
 
@@ -301,7 +363,33 @@ namespace RTC
301
363
  // "In Progress" request. In all cases, re-evaluate the state.
302
364
  this->lastProcessedReqSeqNbr = requestSn;
303
365
 
304
- if (
366
+ // This very same request may have already been performed when the
367
+ // cumulative ack TSN reached its sender's last assigned TSN. The peer was
368
+ // told "in progress" and doesn't know it yet, so it retries it with a new
369
+ // request sequence number. Resetting those streams again would set their
370
+ // next expected SSN back to 0 after post reset data may have already been
371
+ // received, so just reply the final result.
372
+ const bool alreadyPerformed =
373
+ this->performedDeferredRequest.has_value() &&
374
+ this->performedDeferredRequest->senderLastAssignedTsn ==
375
+ receivedOutgoingSsnResetRequestParameter->GetSenderLastAssignedTsn() &&
376
+ this->performedDeferredRequest->streamIds ==
377
+ receivedOutgoingSsnResetRequestParameter->GetStreamIds();
378
+
379
+ this->performedDeferredRequest = std::nullopt;
380
+
381
+ if (alreadyPerformed)
382
+ {
383
+ // Set the result again rather than relying on the one stored when the
384
+ // reset was performed, since a received incoming stream reset request
385
+ // may have overwritten it in the meantime.
386
+ this->lastProcessedReqResult = ReconfigurationResponseParameter::Result::SUCCESS_PERFORMED;
387
+
388
+ MS_DEBUG_DEV(
389
+ "reset outgoing already performed when leaving deferred reset processing, sender last assigned tsn %" PRIu32,
390
+ receivedOutgoingSsnResetRequestParameter->GetSenderLastAssignedTsn());
391
+ }
392
+ else if (
305
393
  this->dataTracker->IsLaterThanCumulativeAckedTsn(
306
394
  receivedOutgoingSsnResetRequestParameter->GetSenderLastAssignedTsn()))
307
395
  {
@@ -314,6 +402,21 @@ namespace RTC
314
402
  receivedOutgoingSsnResetRequestParameter->GetSenderLastAssignedTsn(),
315
403
  receivedOutgoingSsnResetRequestParameter->GetStreamIds());
316
404
 
405
+ // Remember the request so that deferred reset processing can end as soon
406
+ // as the cumulative ack TSN reaches the sender's last assigned TSN,
407
+ // without depending on the peer retransmitting its request.
408
+ //
409
+ // NOTE: Just like ReassemblyQueue::EnterDeferredReset() does, a new
410
+ // request is ignored while already in deferred reset processing.
411
+ if (!this->deferredIncomingRequest.has_value())
412
+ {
413
+ this->deferredIncomingRequest = DeferredIncomingRequest{
414
+ .senderLastAssignedTsn =
415
+ receivedOutgoingSsnResetRequestParameter->GetSenderLastAssignedTsn(),
416
+ .streamIds = receivedOutgoingSsnResetRequestParameter->GetStreamIds(),
417
+ };
418
+ }
419
+
317
420
  // "If the endpoint enters 'deferred reset processing', it MUST put a
318
421
  // Re-configuration Response Parameter into a RE-CONFIG chunk indicating
319
422
  // 'In progress' and MUST send the RE-CONFIG chunk.
@@ -335,6 +438,8 @@ namespace RTC
335
438
  this->reassemblyQueue->ResetStreamsAndLeaveDeferredReset(
336
439
  receivedOutgoingSsnResetRequestParameter->GetStreamIds());
337
440
 
441
+ this->deferredIncomingRequest = std::nullopt;
442
+
338
443
  this->associationListenerDeferrer.OnAssociationInboundStreamsReset(
339
444
  receivedOutgoingSsnResetRequestParameter->GetStreamIds());
340
445