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.
Files changed (55) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/node/lib/Consumer.js +1 -1
  3. package/node/lib/Transport.d.ts.map +1 -1
  4. package/node/lib/Transport.js +7 -0
  5. package/node/lib/ortc.d.ts.map +1 -1
  6. package/node/lib/ortc.js +10 -0
  7. package/node/lib/test/test-PipeTransport.js +35 -30
  8. package/package.json +6 -6
  9. package/worker/include/DepLibUV.hpp +33 -4
  10. package/worker/include/RTC/Consumer.hpp +4 -1
  11. package/worker/include/RTC/Producer.hpp +34 -5
  12. package/worker/include/RTC/RTCP/Packet.hpp +7 -1
  13. package/worker/include/RTC/RTP/Packet.hpp +18 -0
  14. package/worker/include/RTC/RTP/RtpStream.hpp +76 -14
  15. package/worker/include/RTC/RTP/RtpStreamRecv.hpp +191 -7
  16. package/worker/include/RTC/RTP/RtpStreamSend.hpp +56 -10
  17. package/worker/include/RTC/RTP/RtxStream.hpp +34 -2
  18. package/worker/include/RTC/RemoteCaptureTimeEstimator.hpp +92 -0
  19. package/worker/include/RTC/RemoteClockOffsetEstimator.hpp +87 -0
  20. package/worker/include/RTC/Router.hpp +2 -5
  21. package/worker/include/RTC/SimulcastProducerStreamManager.hpp +3 -0
  22. package/worker/include/RTC/Transport.hpp +15 -7
  23. package/worker/include/Shared.hpp +5 -0
  24. package/worker/include/SharedInterface.hpp +6 -0
  25. package/worker/include/Utils.hpp +44 -3
  26. package/worker/meson.build +4 -0
  27. package/worker/mocks/include/MockShared.hpp +7 -0
  28. package/worker/src/DepLibUV.cpp +17 -0
  29. package/worker/src/RTC/Consumer.cpp +8 -5
  30. package/worker/src/RTC/Producer.cpp +85 -16
  31. package/worker/src/RTC/RTP/Packet.cpp +9 -1
  32. package/worker/src/RTC/RTP/RtpStream.cpp +14 -10
  33. package/worker/src/RTC/RTP/RtpStreamRecv.cpp +150 -23
  34. package/worker/src/RTC/RTP/RtpStreamSend.cpp +34 -19
  35. package/worker/src/RTC/RTP/RtxStream.cpp +12 -6
  36. package/worker/src/RTC/RemoteCaptureTimeEstimator.cpp +77 -0
  37. package/worker/src/RTC/RemoteClockOffsetEstimator.cpp +106 -0
  38. package/worker/src/RTC/Router.cpp +10 -8
  39. package/worker/src/RTC/RtpDictionaries/RtcpParameters.cpp +13 -3
  40. package/worker/src/RTC/SCTP/packet/chunks/SackChunk.cpp +6 -2
  41. package/worker/src/RTC/SimulcastProducerStreamManager.cpp +115 -56
  42. package/worker/src/RTC/SvcProducerStreamManager.cpp +22 -17
  43. package/worker/src/RTC/Transport.cpp +96 -4
  44. package/worker/test/src/RTC/RTP/TestPacket.cpp +6 -0
  45. package/worker/test/src/RTC/RTP/TestRtpStreamRecv.cpp +2 -2
  46. package/worker/test/src/RTC/RTP/TestRtpStreamSend.cpp +119 -0
  47. package/worker/test/src/RTC/TestConsumer.cpp +4 -3
  48. package/worker/test/src/RTC/TestPortManager.cpp +1 -1
  49. package/worker/test/src/RTC/TestRemoteCaptureTimeEstimator.cpp +164 -0
  50. package/worker/test/src/RTC/TestRemoteClockOffsetEstimator.cpp +241 -0
  51. package/worker/test/src/RTC/TestSimpleProducerStreamManager.cpp +2 -2
  52. package/worker/test/src/RTC/TestSimulcastProducerStreamManager.cpp +340 -15
  53. package/worker/test/src/RTC/TestSvcProducerStreamManager.cpp +2 -2
  54. package/worker/test/src/RTC/TestTransportTuple.cpp +1 -1
  55. package/worker/test/src/Utils/TestTime.cpp +49 -0
@@ -80,6 +80,9 @@ namespace RTC
80
80
  int16_t spatialLayerToSync{ -1 };
81
81
  // Timestamp synchronization.
82
82
  int16_t tsReferenceSpatialLayer{ -1 };
83
+ // Spatial layer that was the RTP timestamp reference the last time its capture
84
+ // instant was known upon a received Sender Report.
85
+ int16_t tsReferenceSpatialLayerWithCaptureMapping{ -1 };
83
86
  uint32_t tsOffset{ 0u };
84
87
  bool keyFrameForTsOffsetRequested{ false };
85
88
  // Old-packet filtering after spatial switch.
@@ -17,6 +17,7 @@
17
17
  #include "RTC/RTP/HeaderExtensionIds.hpp"
18
18
  #include "RTC/RTP/Packet.hpp"
19
19
  #include "RTC/RateCalculator.hpp"
20
+ #include "RTC/RemoteCaptureTimeEstimator.hpp"
20
21
  #include "RTC/RtpListener.hpp"
21
22
  #include "RTC/SCTP/public/AssociationInterface.hpp"
22
23
  #include "RTC/SCTP/public/AssociationListenerInterface.hpp"
@@ -83,11 +84,12 @@ namespace RTC
83
84
  bool first) = 0;
84
85
  virtual void OnTransportProducerRtpPacketReceived(
85
86
  RTC::Transport* transport, RTC::Producer* producer, RTC::RTP::Packet* packet) = 0;
86
- virtual void OnTransportNeedWorstRemoteFractionLost(
87
- RTC::Transport* transport,
88
- RTC::Producer* producer,
89
- uint32_t mappedSsrc,
90
- uint8_t& worstRemoteFractionLost) = 0;
87
+ /**
88
+ * Obtains the worst remote fraction lost of all the Consumers consuming the
89
+ * given mapped SSRC of the given Producer.
90
+ */
91
+ virtual uint8_t OnTransportNeedWorstRemoteFractionLost(
92
+ RTC::Transport* transport, RTC::Producer* producer, uint32_t mappedSsrc) = 0;
91
93
  virtual void OnTransportNewConsumer(
92
94
  RTC::Transport* transport, RTC::Consumer* consumer, const std::string& producerId) = 0;
93
95
  virtual void OnTransportConsumerClosed(RTC::Transport* transport, RTC::Consumer* consumer) = 0;
@@ -264,8 +266,10 @@ namespace RTC
264
266
  RTC::Producer* producer, RTC::RTP::RtpStreamRecv* rtpStream, bool first) override;
265
267
  void OnProducerRtpPacketReceived(RTC::Producer* producer, RTC::RTP::Packet* packet) override;
266
268
  void OnProducerSendRtcpPacket(RTC::Producer* producer, RTC::RTCP::Packet* packet) override;
267
- void OnProducerNeedWorstRemoteFractionLost(
268
- RTC::Producer* producer, uint32_t mappedSsrc, uint8_t& worstRemoteFractionLost) override;
269
+ uint8_t OnProducerNeedWorstRemoteFractionLost(RTC::Producer* producer, uint32_t mappedSsrc) override;
270
+ std::optional<uint64_t> OnProducerNeedLocalCaptureMs(
271
+ RTC::Producer* producer, const RTC::RTP::RtpStreamRecv* rtpStream, uint32_t ts) override;
272
+ std::optional<int64_t> OnProducerNeedRemoteClockOffsetMs(const RTC::Producer* producer) override;
269
273
 
270
274
  /* Pure virtual methods inherited from RTC::Consumer::Listener. */
271
275
  public:
@@ -367,6 +371,10 @@ namespace RTC
367
371
  ankerl::unordered_dense::map<uint16_t, RTC::DataConsumer*> mapSctpStreamIdDataConsumers;
368
372
  ankerl::unordered_dense::map<uint32_t, RTC::Consumer*> mapSsrcConsumer;
369
373
  ankerl::unordered_dense::map<uint32_t, RTC::Consumer*> mapRtxSsrcConsumer;
374
+ // Capture time estimator of each sender publishing to this Transport, keyed by
375
+ // its CNAME.
376
+ ankerl::unordered_dense::map<std::string, RTC::RemoteCaptureTimeEstimator>
377
+ mapCnameRemoteCaptureTimeEstimator;
370
378
  TimerHandleInterface* rtcpTimer{ nullptr };
371
379
  // Allocated by this.
372
380
  std::unique_ptr<RTC::SCTP::AssociationInterface> sctpAssociation{ nullptr };
@@ -56,6 +56,11 @@ public:
56
56
  return DepLibUV::GetTimeUsInt64();
57
57
  }
58
58
 
59
+ uint64_t GetNtpOffsetMs() override
60
+ {
61
+ return DepLibUV::GetNtpOffsetMs();
62
+ }
63
+
59
64
  private:
60
65
  std::unique_ptr<Channel::ChannelMessageRegistrator> channelMessageRegistrator;
61
66
  std::unique_ptr<Channel::ChannelNotifier> channelNotifier;
@@ -72,6 +72,12 @@ public:
72
72
  * @todo Remove once not needed.
73
73
  */
74
74
  virtual int64_t GetTimeUsInt64() = 0;
75
+
76
+ /**
77
+ * Distance from the clock above to the NTP epoch (ms), which is what has to be added
78
+ * to it to obtain the NTP timestamps of the RTCP we generate.
79
+ */
80
+ virtual uint64_t GetNtpOffsetMs() = 0;
75
81
  };
76
82
 
77
83
  #endif
@@ -500,9 +500,11 @@ namespace Utils
500
500
 
501
501
  class Time
502
502
  {
503
- private:
503
+ public:
504
504
  // Seconds from Jan 1, 1900 to Jan 1, 1970.
505
- static constexpr uint32_t UnixNtpOffset{ 0x83AA7E80 };
505
+ static constexpr uint32_t UnixNtpOffsetSec{ 0x83AA7E80 };
506
+
507
+ private:
506
508
  // NTP fractional unit.
507
509
  static constexpr uint64_t NtpFractionalUnit{ 1LL << 32 };
508
510
 
@@ -517,7 +519,11 @@ namespace Utils
517
519
  {
518
520
  Time::Ntp ntp{}; // NOLINT(cppcoreguidelines-pro-type-member-init)
519
521
 
520
- ntp.seconds = ms / 1000;
522
+ // NOTE: The truncation is intended. Seconds are 32 bits wide in NTP, so they wrap
523
+ // every 2^32 seconds since Jan 1, 1900, being Feb 7, 2036 the next time it
524
+ // happens. That is the NTP era and receivers deal with it by doing modular
525
+ // arithmetic, so there is nothing to protect against here.
526
+ ntp.seconds = static_cast<uint32_t>(ms / 1000);
521
527
  ntp.fractions =
522
528
  static_cast<uint32_t>((static_cast<double>(ms % 1000) / 1000) * NtpFractionalUnit);
523
529
 
@@ -536,6 +542,41 @@ namespace Utils
536
542
  {
537
543
  return static_cast<uint32_t>(((ms << 18) + 500) / 1000) & 0x00FFFFFF;
538
544
  }
545
+
546
+ /**
547
+ * Convert milliseconds into signed Q32.32 fixed point seconds, which is how the
548
+ * `abs-capture-time` RTP header extension encodes its clock offset, being the
549
+ * capture timestamp itself unsigned instead (UQ32.32).
550
+ *
551
+ * @returns No value if the given instant does not fit in the format, whose seconds
552
+ * are 32 bits wide.
553
+ *
554
+ * @see https://datatracker.ietf.org/doc/html/draft-ietf-avtcore-abs-capture-time-00
555
+ */
556
+ static std::optional<int64_t> TimeMs2Q32x32(int64_t ms)
557
+ {
558
+ // The seconds of the format are 32 bits wide, so from here on it does not fit.
559
+ static constexpr int64_t OutOfRangeMs{ (1LL << 31) * 1000 };
560
+
561
+ if (ms >= OutOfRangeMs || ms <= -OutOfRangeMs)
562
+ {
563
+ return std::nullopt;
564
+ }
565
+
566
+ return static_cast<int64_t>(
567
+ std::round(static_cast<double>(ms) * (static_cast<double>(NtpFractionalUnit) / 1000)));
568
+ }
569
+
570
+ /**
571
+ * Convert signed Q32.32 fixed point seconds into milliseconds.
572
+ *
573
+ * @see https://datatracker.ietf.org/doc/html/draft-ietf-avtcore-abs-capture-time-00
574
+ */
575
+ static int64_t Q32x32ToTimeMs(int64_t q32x32)
576
+ {
577
+ return static_cast<int64_t>(
578
+ std::round(static_cast<double>(q32x32) * (1000 / static_cast<double>(NtpFractionalUnit))));
579
+ }
539
580
  };
540
581
 
541
582
  class BitStream
@@ -124,6 +124,8 @@ common_sources = [
124
124
  'src/RTC/PortManager.cpp',
125
125
  'src/RTC/Producer.cpp',
126
126
  'src/RTC/RateCalculator.cpp',
127
+ 'src/RTC/RemoteCaptureTimeEstimator.cpp',
128
+ 'src/RTC/RemoteClockOffsetEstimator.cpp',
127
129
  'src/RTC/Router.cpp',
128
130
  'src/RTC/RtcLogger.cpp',
129
131
  'src/RTC/RtpListener.cpp',
@@ -429,6 +431,8 @@ test_sources = [
429
431
  'test/src/RTC/TestNackGenerator.cpp',
430
432
  'test/src/RTC/TestPortManager.cpp',
431
433
  'test/src/RTC/TestRateCalculator.cpp',
434
+ 'test/src/RTC/TestRemoteCaptureTimeEstimator.cpp',
435
+ 'test/src/RTC/TestRemoteClockOffsetEstimator.cpp',
432
436
  'test/src/RTC/TestRtpEncodingParameters.cpp',
433
437
  'test/src/RTC/TestSeqManager.cpp',
434
438
  'test/src/RTC/TestSubchannelsCodec.cpp',
@@ -60,6 +60,13 @@ namespace mocks
60
60
  return static_cast<int64_t>(GetTimeUs());
61
61
  }
62
62
 
63
+ // NOTE: The NTP epoch is made to be the very clock given by argument, so that tests
64
+ // can reason about a single set of values.
65
+ uint64_t GetNtpOffsetMs() override
66
+ {
67
+ return 0;
68
+ }
69
+
63
70
  // Methods for testing.
64
71
  public:
65
72
  MockBackoffTimerHandle* GetBackoffTimer(const std::string_view label) const
@@ -3,10 +3,12 @@
3
3
 
4
4
  #include "DepLibUV.hpp"
5
5
  #include "Logger.hpp"
6
+ #include "Utils.hpp"
6
7
 
7
8
  /* Class variables. */
8
9
 
9
10
  thread_local uv_loop_t* DepLibUV::loop{ nullptr };
11
+ thread_local uint64_t DepLibUV::ntpOffsetMs{ 0 };
10
12
 
11
13
  /* Static methods for UV callbacks. */
12
14
 
@@ -48,6 +50,21 @@ void DepLibUV::ClassInit()
48
50
  {
49
51
  MS_ABORT("libuv loop initialization failed");
50
52
  }
53
+
54
+ // Take the distance from our own monotonic clock to the NTP epoch just once, so that
55
+ // the NTP timestamps we generate never step when the system clock is adjusted.
56
+ uv_timeval64_t timeval{}; // NOLINT(cppcoreguidelines-pro-type-member-init)
57
+
58
+ if (uv_gettimeofday(std::addressof(timeval)) != 0)
59
+ {
60
+ MS_ABORT("uv_gettimeofday() failed");
61
+ }
62
+
63
+ const auto unixSec = static_cast<uint64_t>(timeval.tv_sec);
64
+ const auto unixMs = (unixSec * 1000) + (static_cast<uint64_t>(timeval.tv_usec) / 1000);
65
+ const auto ntpMs = unixMs + (static_cast<uint64_t>(Utils::Time::UnixNtpOffsetSec) * 1000);
66
+
67
+ DepLibUV::ntpOffsetMs = ntpMs - DepLibUV::GetTimeMs();
51
68
  }
52
69
 
53
70
  void DepLibUV::ClassDestroy()
@@ -1414,22 +1414,25 @@ namespace RTC
1414
1414
  return true;
1415
1415
  }
1416
1416
 
1417
- void Consumer::NeedWorstRemoteFractionLost(uint32_t /*mappedSsrc*/, uint8_t& worstRemoteFractionLost)
1417
+ uint8_t Consumer::GetWorstRemoteFractionLost(uint32_t /*mappedSsrc*/) const
1418
1418
  {
1419
1419
  MS_TRACE();
1420
1420
 
1421
1421
  if (!IsActive())
1422
1422
  {
1423
- return;
1423
+ return 0;
1424
1424
  }
1425
1425
 
1426
- for (auto* rtpStream : this->rtpStreams)
1426
+ uint8_t worstRemoteFractionLost{ 0 };
1427
+
1428
+ for (const auto* rtpStream : this->rtpStreams)
1427
1429
  {
1428
- auto fractionLost = rtpStream->GetFractionLost();
1430
+ const auto fractionLost = rtpStream->GetFractionLost();
1429
1431
 
1430
- // If our fraction lost is worse than the given one, update it.
1431
1432
  worstRemoteFractionLost = std::max(fractionLost, worstRemoteFractionLost);
1432
1433
  }
1434
+
1435
+ return worstRemoteFractionLost;
1433
1436
  }
1434
1437
 
1435
1438
  void Consumer::ReceiveNack(RTC::RTCP::FeedbackRtpNackPacket* nackPacket)
@@ -580,6 +580,9 @@ namespace RTC
580
580
 
581
581
  ReceiveRtpPacketResult result;
582
582
  bool isRtx{ false };
583
+ // Highest RTP timestamp of the stream before this packet is processed, so that it
584
+ // can be told afterwards whether this packet made it change.
585
+ const auto previousMaxPacketTs = rtpStream->GetMaxPacketTs();
583
586
 
584
587
  // Media packet.
585
588
  if (packet->GetSsrc() == rtpStream->GetSsrc())
@@ -675,7 +678,10 @@ namespace RTC
675
678
  }
676
679
 
677
680
  // Post-process the packet.
678
- PostProcessRtpPacket(packet);
681
+ PostProcessRtpPacket(
682
+ packet,
683
+ rtpStream,
684
+ /*maxPacketTsChanged*/ rtpStream->GetMaxPacketTs() != previousMaxPacketTs);
679
685
 
680
686
  this->listener->OnProducerRtpPacketReceived(this, packet);
681
687
 
@@ -691,7 +697,7 @@ namespace RTC
691
697
  if (it != this->mapSsrcRtpStream.end())
692
698
  {
693
699
  auto* rtpStream = it->second;
694
- const bool first = rtpStream->GetSenderReportNtpMs() == 0;
700
+ const bool first = !rtpStream->GetSenderReportMapping().has_value();
695
701
 
696
702
  rtpStream->ReceiveRtcpSenderReport(report);
697
703
 
@@ -765,7 +771,8 @@ namespace RTC
765
771
  // Add a receiver reference time report if no present in the packet.
766
772
  if (!packet->HasReceiverReferenceTime())
767
773
  {
768
- auto ntp = Utils::Time::TimeMs2Ntp(nowMs);
774
+ auto ntp = Utils::Time::TimeMs2Ntp(nowMs + this->shared->GetNtpOffsetMs());
775
+
769
776
  receiverReferenceTimeReport = new RTC::RTCP::ReceiverReferenceTime();
770
777
 
771
778
  receiverReferenceTimeReport->SetNtpSec(ntp.seconds);
@@ -1223,19 +1230,64 @@ namespace RTC
1223
1230
  }
1224
1231
 
1225
1232
  // Proxy http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time.
1233
+ //
1234
+ // The capture timestamp is left untouched since it belongs to the clock of the
1235
+ // capture system, but the capture clock offset has to be rewritten. It tells the
1236
+ // offset between the clock of the capture system and the clock of whoever sends
1237
+ // the stream, and by forwarding the stream we become that sender:
1238
+ //
1239
+ // capture NTP clock = sender NTP clock + capture clock offset
1240
+ //
1241
+ // @see https://datatracker.ietf.org/doc/html/draft-ietf-avtcore-abs-capture-time-00
1226
1242
  extenValue = packet->GetExtensionValue(this->rtpHeaderExtensionIds.absCaptureTime, extenLen);
1227
1243
 
1228
- if (extenValue)
1244
+ // NOTE: The extension value is 8 or 16 bytes long depending on whether it holds
1245
+ // the capture clock offset or not.
1246
+ if (extenValue && (extenLen == 8u || extenLen == 16u))
1229
1247
  {
1230
- std::memcpy(bufferPtr, extenValue, extenLen);
1248
+ // Offset between our own clock and the clock of the sender of this stream, in
1249
+ // the format the extension uses.
1250
+ //
1251
+ // NOTE: While it cannot be told yet, and it never can if the sender sends no
1252
+ // RTCP at all, both clocks are taken to be the same. Our own clock is real NTP
1253
+ // and so is the one of any sender that fills this extension, so no offset is
1254
+ // the most likely case rather than a wild guess.
1255
+ const auto remoteClockOffsetMs = this->listener->OnProducerNeedRemoteClockOffsetMs(this);
1256
+ const auto remoteClockOffsetQ32x32 =
1257
+ Utils::Time::TimeMs2Q32x32(remoteClockOffsetMs.value_or(0));
1258
+
1259
+ // NOTE: An offset that does not fit in the extension means a sender whose clock
1260
+ // is decades away from ours, and there is no value to write that would not be a
1261
+ // lie, so the extension is not forwarded.
1262
+ if (remoteClockOffsetQ32x32.has_value())
1263
+ {
1264
+ const auto absCaptureTimestamp = Utils::Byte::Get8Bytes(extenValue, 0);
1265
+ // Offset between the clock of the capture system and the clock of the sender
1266
+ // of this stream, as told by that sender. There is none when the extension
1267
+ // comes in its shortened form, which is what a sender that captures its own
1268
+ // media sends.
1269
+ const int64_t incomingCaptureClockOffset =
1270
+ extenLen == 16u ? static_cast<int64_t>(Utils::Byte::Get8Bytes(extenValue, 8)) : 0;
1271
+ // NOTE: The estimator gives our own clock minus the one of the sender, so
1272
+ // subtracting it adds the offset of the sender against ours.
1273
+ const int64_t captureClockOffset =
1274
+ incomingCaptureClockOffset - remoteClockOffsetQ32x32.value();
1275
+
1276
+ // The extension is always forwarded in its extended form, since the capture
1277
+ // clock offset is always written.
1278
+ extenLen = 16u;
1279
+
1280
+ Utils::Byte::Set8Bytes(bufferPtr, 0, absCaptureTimestamp);
1281
+ Utils::Byte::Set8Bytes(bufferPtr, 8, static_cast<uint64_t>(captureClockOffset));
1231
1282
 
1232
- extensions.emplace_back(
1233
- /*type*/ RTC::RtpHeaderExtensionUri::Type::ABS_CAPTURE_TIME,
1234
- /*id*/ static_cast<uint8_t>(RTC::RtpHeaderExtensionUri::Type::ABS_CAPTURE_TIME),
1235
- /*len*/ extenLen,
1236
- /*value*/ bufferPtr);
1283
+ extensions.emplace_back(
1284
+ /*type*/ RTC::RtpHeaderExtensionUri::Type::ABS_CAPTURE_TIME,
1285
+ /*id*/ static_cast<uint8_t>(RTC::RtpHeaderExtensionUri::Type::ABS_CAPTURE_TIME),
1286
+ /*len*/ extenLen,
1287
+ /*value*/ bufferPtr);
1237
1288
 
1238
- bufferPtr += extenLen;
1289
+ bufferPtr += extenLen;
1290
+ }
1239
1291
  }
1240
1292
 
1241
1293
  // Proxy http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
@@ -1409,10 +1461,28 @@ namespace RTC
1409
1461
  return true;
1410
1462
  }
1411
1463
 
1412
- inline void Producer::PostProcessRtpPacket(RTC::RTP::Packet* packet)
1464
+ inline void Producer::PostProcessRtpPacket(
1465
+ RTC::RTP::Packet* packet, RTC::RTP::RtpStreamRecv* rtpStream, bool maxPacketTsChanged)
1413
1466
  {
1414
1467
  MS_TRACE();
1415
1468
 
1469
+ // Store the capture instant of the packet holding the highest RTP timestamp of its
1470
+ // stream, which is the one the Sender Reports we send are built upon.
1471
+ if (maxPacketTsChanged)
1472
+ {
1473
+ const auto captureMs =
1474
+ this->listener->OnProducerNeedLocalCaptureMs(this, rtpStream, packet->GetTimestamp());
1475
+
1476
+ if (captureMs.has_value())
1477
+ {
1478
+ packet->SetCaptureMs(captureMs.value());
1479
+
1480
+ // Also let the stream remember it, since this is the only place where the
1481
+ // capture instant of a received packet can be told.
1482
+ rtpStream->SetCaptureMapping(captureMs.value(), packet->GetTimestamp());
1483
+ }
1484
+ }
1485
+
1416
1486
  if (this->kind == RTC::Media::Kind::VIDEO)
1417
1487
  {
1418
1488
  bool camera{ false };
@@ -1697,15 +1767,14 @@ namespace RTC
1697
1767
  this->listener->OnProducerSendRtcpPacket(this, packet);
1698
1768
  }
1699
1769
 
1700
- inline void Producer::OnRtpStreamNeedWorstRemoteFractionLost(
1701
- RTC::RTP::RtpStreamRecv* rtpStream, uint8_t& worstRemoteFractionLost)
1770
+ inline uint8_t Producer::OnRtpStreamNeedWorstRemoteFractionLost(RTC::RTP::RtpStreamRecv* rtpStream)
1702
1771
  {
1703
1772
  MS_TRACE();
1704
1773
 
1705
- auto mappedSsrc = this->mapRtpStreamMappedSsrc.at(rtpStream);
1774
+ const auto mappedSsrc = this->mapRtpStreamMappedSsrc.at(rtpStream);
1706
1775
 
1707
1776
  // Notify the listener.
1708
- this->listener->OnProducerNeedWorstRemoteFractionLost(this, mappedSsrc, worstRemoteFractionLost);
1777
+ return this->listener->OnProducerNeedWorstRemoteFractionLost(this, mappedSsrc);
1709
1778
  }
1710
1779
 
1711
1780
  inline void Producer::OnKeyFrameNeeded(
@@ -391,6 +391,11 @@ namespace RTC
391
391
  MS_DUMP_CLEAN(indentation, " padding length: %" PRIu8, GetPaddingLength());
392
392
  MS_DUMP_CLEAN(indentation, " padded to 4 bytes: %s", IsPaddedTo4Bytes() ? "yes" : "no");
393
393
 
394
+ if (GetCaptureMs())
395
+ {
396
+ MS_DUMP_CLEAN(indentation, " capture time (ms):%" PRIu64, GetCaptureMs().value());
397
+ }
398
+
394
399
  if (this->payloadDescriptorHandler)
395
400
  {
396
401
  MS_DUMP_CLEAN(indentation + 1, "<PayloadDescriptorHandler>");
@@ -422,6 +427,9 @@ namespace RTC
422
427
  // Clone extension ids.
423
428
  clonedPacket->headerExtensionIds = this->headerExtensionIds;
424
429
 
430
+ // Clone capture time.
431
+ clonedPacket->captureMs = this->captureMs;
432
+
425
433
  // Assign the payload descriptor handler.
426
434
  clonedPacket->payloadDescriptorHandler = this->payloadDescriptorHandler;
427
435
 
@@ -1174,7 +1182,7 @@ namespace RTC
1174
1182
  // Extension value can be 8 or 16 bytes depending on whether it contains
1175
1183
  // estimated capture clock offset or not.
1176
1184
  //
1177
- // https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/abs-capture-time
1185
+ // @see https://datatracker.ietf.org/doc/html/draft-ietf-avtcore-abs-capture-time-00
1178
1186
  if (!extenValue || (extenLen != 8u && extenLen != 16u))
1179
1187
  {
1180
1188
  return false;
@@ -137,10 +137,11 @@ namespace RTC
137
137
  {
138
138
  InitSeq(seq);
139
139
 
140
- this->started = true;
141
- this->maxSeq = seq - 1;
142
- this->maxPacketTs = packet->GetTimestamp();
143
- this->maxPacketMs = this->shared->GetTimeMs();
140
+ this->started = true;
141
+ this->maxSeq = seq - 1;
142
+ this->maxPacketTs = packet->GetTimestamp();
143
+ this->maxPacketMs = this->shared->GetTimeMs();
144
+ this->maxPacketCaptureMs = packet->GetCaptureMs();
144
145
  }
145
146
 
146
147
  // If not a valid packet ignore it.
@@ -158,8 +159,9 @@ namespace RTC
158
159
  // Update highest seen RTP timestamp.
159
160
  if (Utils::Number::IsHigherThan<uint32_t>(packet->GetTimestamp(), this->maxPacketTs))
160
161
  {
161
- this->maxPacketTs = packet->GetTimestamp();
162
- this->maxPacketMs = this->shared->GetTimeMs();
162
+ this->maxPacketTs = packet->GetTimestamp();
163
+ this->maxPacketMs = this->shared->GetTimeMs();
164
+ this->maxPacketCaptureMs = packet->GetCaptureMs();
163
165
  }
164
166
 
165
167
  return true;
@@ -226,8 +228,9 @@ namespace RTC
226
228
  this->maxPacketTs,
227
229
  packet->GetTimestamp());
228
230
 
229
- this->maxPacketTs = packet->GetTimestamp();
230
- this->maxPacketMs = this->shared->GetTimeMs();
231
+ this->maxPacketTs = packet->GetTimestamp();
232
+ this->maxPacketMs = this->shared->GetTimeMs();
233
+ this->maxPacketCaptureMs = packet->GetCaptureMs();
231
234
  }
232
235
  }
233
236
  // Too old packet received (older than the allowed misorder).
@@ -248,8 +251,9 @@ namespace RTC
248
251
 
249
252
  InitSeq(seq);
250
253
 
251
- this->maxPacketTs = packet->GetTimestamp();
252
- this->maxPacketMs = this->shared->GetTimeMs();
254
+ this->maxPacketTs = packet->GetTimestamp();
255
+ this->maxPacketMs = this->shared->GetTimeMs();
256
+ this->maxPacketCaptureMs = packet->GetCaptureMs();
253
257
 
254
258
  // Notify the subclass about it.
255
259
  UserOnSequenceNumberReset();