mediasoup 3.24.1 → 3.24.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  ### NEXT
4
4
 
5
+ ### 3.24.2
6
+
7
+ - Worker: Verify `DataConsumer` subchannels before cloning the message ([PR #1880](https://github.com/versatica/mediasoup/pull/1880)).
8
+
5
9
  ### 3.24.1
6
10
 
7
11
  - Worker: Don't check `ignoredSubchannel` in piped `DataConsumers` ([PR #1879](https://github.com/versatica/mediasoup/pull/1879)).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mediasoup",
3
- "version": "3.24.1",
3
+ "version": "3.24.2",
4
4
  "description": "Cutting Edge WebRTC Video Conferencing",
5
5
  "contributors": [
6
6
  "Iñaki Baz Castillo <ibc@aliax.net> (https://inakibaz.me)",
@@ -121,8 +121,8 @@
121
121
  "@types/debug": "^4.1.13",
122
122
  "@types/ini": "^4.1.1",
123
123
  "@types/jest": "^30.0.0",
124
- "@types/node": "^26.1.2",
125
- "eslint": "^10.8.0",
124
+ "@types/node": "^26.2.0",
125
+ "eslint": "^10.8.1",
126
126
  "eslint-config-prettier": "^10.1.8",
127
127
  "eslint-plugin-jest": "^29.16.0",
128
128
  "eslint-plugin-prettier": "^5.5.6",
@@ -93,6 +93,18 @@ namespace RTC
93
93
  void SctpBufferedAmountLow(uint32_t bufferedAmount) const;
94
94
  void SctpSendBufferFull() const;
95
95
  void DataProducerClosed();
96
+ /**
97
+ * Verifies if given subchannels would allow a message to be delivered to
98
+ * the DataConsumer endpoint or not.
99
+ *
100
+ * @remarks
101
+ * - This method must be called before invoking `SendMessage()` if subchannels
102
+ * are given to `SendMessage()`.
103
+ */
104
+ bool VerifySubchannels(
105
+ const std::vector<uint16_t>& subchannels,
106
+ std::optional<uint16_t> requiredSubchannel,
107
+ std::optional<uint16_t> ignoredSubchannel) const;
96
108
  bool SendMessage(
97
109
  RTC::SCTP::Message message,
98
110
  std::vector<uint16_t>& subchannels,
@@ -482,101 +482,93 @@ namespace RTC
482
482
  this->listener->OnDataConsumerDataProducerClosed(this);
483
483
  }
484
484
 
485
- bool DataConsumer::SendMessage(
486
- RTC::SCTP::Message message,
487
- std::vector<uint16_t>& subchannels,
485
+ bool DataConsumer::VerifySubchannels(
486
+ const std::vector<uint16_t>& subchannels,
488
487
  std::optional<uint16_t> requiredSubchannel,
489
- std::optional<uint16_t> ignoredSubchannel,
490
- const onQueuedCallback* cb)
488
+ std::optional<uint16_t> ignoredSubchannel) const
491
489
  {
492
490
  MS_TRACE();
493
491
 
494
- if (!IsActive())
492
+ // Only verify subchannels if this is not a piped DataConsumer or if this
493
+ // is a piped DataConsumer and it's subscribed to at least one subchannel.
494
+ const bool verifySubchannels = !this->pipe || !this->subchannels.empty();
495
+
496
+ if (!verifySubchannels)
495
497
  {
496
- if (cb)
497
- {
498
- (*cb)(false, false);
499
- delete cb;
500
- }
498
+ return true;
499
+ }
501
500
 
501
+ // If a required subchannel is given, verify that this data consumer is
502
+ // subscribed to it.
503
+ if (requiredSubchannel.has_value() && !this->subchannels.contains(requiredSubchannel.value()))
504
+ {
502
505
  return false;
503
506
  }
504
507
 
505
- // Only verify subchannels if this is not a piped DataConsumer or if this
506
- // is a piped DataConsumer and it's subscribed to at least one subchannel.
507
- const bool verifySubchannels = !this->pipe || !this->subchannels.empty();
508
+ // If an ignored subchannel is given, verify that this data consumer is
509
+ // not subscribed to it, otherwise don't send this message to it.
510
+ // NOTE: We don't check this in piped DataConsumers on purpose. Otherwise
511
+ // a message generated by peer X and forwarded to all peers in another
512
+ // host wouldn't pass this filter in the local PipeTransport.
513
+ if (!this->pipe && ignoredSubchannel.has_value() && this->subchannels.contains(ignoredSubchannel.value()))
514
+ {
515
+ return false;
516
+ }
508
517
 
509
- if (verifySubchannels)
518
+ // If subchannels are given, verify that this data consumer is subscribed
519
+ // to at least one of them.
520
+ if (!subchannels.empty())
510
521
  {
511
- // If a required subchannel is given, verify that this data consumer is
512
- // subscribed to it.
513
- if (requiredSubchannel.has_value() && !this->subchannels.contains(requiredSubchannel.value()))
522
+ bool subchannelMatched{ false };
523
+
524
+ for (const auto subchannel : subchannels)
514
525
  {
515
- if (cb)
526
+ if (this->subchannels.contains(subchannel))
516
527
  {
517
- (*cb)(false, false);
518
- delete cb;
519
- }
528
+ subchannelMatched = true;
520
529
 
521
- return false;
530
+ break;
531
+ }
522
532
  }
523
533
 
524
- // If an ignored subchannel is given, verify that this data consumer is
525
- // not subscribed to it, otherwise don't send this message to it.
526
- // NOTE: We don't check this in piped DataConsumers on purpose. Otherwise
527
- // a message generated by peer X and forwarded to all peers in another
528
- // host wouldn't pass this filter in the local PipeTransport.
529
- if (
530
- !this->pipe && ignoredSubchannel.has_value() &&
531
- this->subchannels.contains(ignoredSubchannel.value()))
534
+ if (!subchannelMatched)
532
535
  {
533
- if (cb)
534
- {
535
- (*cb)(false, false);
536
- delete cb;
537
- }
538
-
539
536
  return false;
540
537
  }
538
+ }
541
539
 
542
- // If subchannels are given, verify that this data consumer is subscribed
543
- // to at least one of them.
544
- if (!subchannels.empty())
545
- {
546
- bool subchannelMatched{ false };
547
-
548
- for (const auto subchannel : subchannels)
549
- {
550
- if (this->subchannels.contains(subchannel))
551
- {
552
- subchannelMatched = true;
553
-
554
- break;
555
- }
556
- }
540
+ return true;
541
+ }
557
542
 
558
- if (!subchannelMatched)
559
- {
560
- if (cb)
561
- {
562
- (*cb)(false, false);
563
- delete cb;
564
- }
543
+ bool DataConsumer::SendMessage(
544
+ RTC::SCTP::Message message,
545
+ std::vector<uint16_t>& subchannels,
546
+ std::optional<uint16_t> requiredSubchannel,
547
+ std::optional<uint16_t> ignoredSubchannel,
548
+ const onQueuedCallback* cb)
549
+ {
550
+ MS_TRACE();
565
551
 
566
- return false;
567
- }
552
+ if (!IsActive())
553
+ {
554
+ if (cb)
555
+ {
556
+ (*cb)(false, false);
557
+ delete cb;
568
558
  }
559
+
560
+ return false;
569
561
  }
570
562
 
571
- // If this is a piped DataConsumer, encode given subchannels and required
572
- // subchannel at the beginning of the message payload so the receiving
573
- // PipeTransport can decode them and apply them to its own DataConsumers.
563
+ // If this is a piped DataConsumer, encode given subchannels at the beginning
564
+ // of the message payload so the receiving PipeTransport can decode them and
565
+ // apply them to its own DataConsumers.
574
566
  if (this->pipe)
575
567
  {
576
- // If subchannels were verified, encode just those this DataConsumer is
577
- // subscribed to. Otherwise the receiving Router would deliver the message to
568
+ // Encode just those subchannels this DataConsumer is subscribed to.
569
+ // Otherwise the receiving Router would deliver the message to
578
570
  // DataConsumers subscribed to subchannels that this pipe does not carry.
579
- const bool reduceSubchannels = verifySubchannels && !subchannels.empty();
571
+ const bool reduceSubchannels = !this->subchannels.empty();
580
572
 
581
573
  std::vector<uint16_t> reducedSubchannels;
582
574
 
@@ -917,44 +917,57 @@ namespace RTC
917
917
 
918
918
  auto& dataConsumers = this->mapDataProducerDataConsumers.at(dataProducer);
919
919
 
920
- if (!dataConsumers.empty())
920
+ const auto getStreamId = [](const RTC::DataConsumer* dataConsumer) -> uint16_t
921
921
  {
922
- const auto numDataConsumers = dataConsumers.size();
922
+ return dataConsumer->GetType() == DataConsumer::Type::SCTP
923
+ ? dataConsumer->GetSctpStreamParameters().streamId
924
+ : 0;
925
+ };
923
926
 
924
- for (auto* dataConsumer : dataConsumers)
927
+ // NOTE: We don't send the message to a matching DataConsumer right away.
928
+ // Instead we hold it as pending until we know whether there is another
929
+ // matching DataConsumer after it. This way just the last matching one needs
930
+ // no message clone since it can take ownership of the original message.
931
+ RTC::DataConsumer* pendingDataConsumer{ nullptr };
932
+
933
+ for (auto* dataConsumer : dataConsumers)
934
+ {
935
+ // Verify subchannels first to avoid cloning the message needlessly.
936
+ if (!dataConsumer->IsActive() || !dataConsumer->VerifySubchannels(subchannels, requiredSubchannel, ignoredSubchannel))
925
937
  {
926
- const uint16_t streamId =
927
- (dataConsumer->GetType() == DataConsumer::Type::SCTP
928
- ? dataConsumer->GetSctpStreamParameters().streamId
929
- : 0);
938
+ continue;
939
+ }
930
940
 
931
- if (numDataConsumers == 1)
932
- {
933
- // We must update the message`s `streamId` for each destination
934
- // DataConsumer.
935
- // NOTE: clang-tidy doesn't understand that we are only doing this
936
- // once in the original `message`.
937
- // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move, bugprone-use-after-move, hicpp-invalid-access-moved)
938
- message.SetStreamId(streamId);
939
-
940
- dataConsumer->SendMessage(
941
- std::move(message), subchannels, requiredSubchannel, ignoredSubchannel);
942
- }
943
- // NOTE: Here we are cloning the message before passing it to each
944
- // DataConsumer because each DataConsumer will pass std::move(message)
945
- // internally to its SCTP association.
946
- else
947
- {
948
- auto clonedMessage = message.Clone();
941
+ // There is a matching DataConsumer after the pending one, so the pending
942
+ // one must be given a clone of the message.
943
+ // NOTE: Here we are cloning the message before passing it to the
944
+ // DataConsumer because each DataConsumer will pass std::move(message)
945
+ // internally to its SCTP association.
946
+ if (pendingDataConsumer)
947
+ {
948
+ auto clonedMessage = message.Clone();
949
949
 
950
- // We must update the message`s `streamId` for each destination
951
- // DataConsumer.
952
- clonedMessage.SetStreamId(streamId);
950
+ // We must update the message`s `streamId` for each destination
951
+ // DataConsumer.
952
+ clonedMessage.SetStreamId(getStreamId(pendingDataConsumer));
953
953
 
954
- dataConsumer->SendMessage(
955
- std::move(clonedMessage), subchannels, requiredSubchannel, ignoredSubchannel);
956
- }
954
+ pendingDataConsumer->SendMessage(
955
+ std::move(clonedMessage), subchannels, requiredSubchannel, ignoredSubchannel);
957
956
  }
957
+
958
+ pendingDataConsumer = dataConsumer;
959
+ }
960
+
961
+ // The last matching DataConsumer (if any) takes ownership of the original
962
+ // message.
963
+ if (pendingDataConsumer)
964
+ {
965
+ // We must update the message`s `streamId` for each destination
966
+ // DataConsumer.
967
+ message.SetStreamId(getStreamId(pendingDataConsumer));
968
+
969
+ pendingDataConsumer->SendMessage(
970
+ std::move(message), subchannels, requiredSubchannel, ignoredSubchannel);
958
971
  }
959
972
  }
960
973