summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/call/rtp_demuxer_unittest.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /third_party/libwebrtc/call/rtp_demuxer_unittest.cc
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/call/rtp_demuxer_unittest.cc')
-rw-r--r--third_party/libwebrtc/call/rtp_demuxer_unittest.cc1287
1 files changed, 1287 insertions, 0 deletions
diff --git a/third_party/libwebrtc/call/rtp_demuxer_unittest.cc b/third_party/libwebrtc/call/rtp_demuxer_unittest.cc
new file mode 100644
index 0000000000..2b394d3bff
--- /dev/null
+++ b/third_party/libwebrtc/call/rtp_demuxer_unittest.cc
@@ -0,0 +1,1287 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "call/rtp_demuxer.h"
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "call/test/mock_rtp_packet_sink_interface.h"
+#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
+#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
+#include "modules/rtp_rtcp/source/rtp_packet_received.h"
+#include "rtc_base/arraysize.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/numerics/safe_conversions.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+
+using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::InSequence;
+using ::testing::NiceMock;
+
+class RtpDemuxerTest : public ::testing::Test {
+ protected:
+ ~RtpDemuxerTest() {
+ for (auto* sink : sinks_to_tear_down_) {
+ demuxer_.RemoveSink(sink);
+ }
+ }
+
+ // These are convenience methods for calling demuxer.AddSink with different
+ // parameters and will ensure that the sink is automatically removed when the
+ // test case finishes.
+
+ bool AddSink(const RtpDemuxerCriteria& criteria,
+ RtpPacketSinkInterface* sink) {
+ bool added = demuxer_.AddSink(criteria, sink);
+ if (added) {
+ sinks_to_tear_down_.insert(sink);
+ }
+ return added;
+ }
+
+ bool AddSinkOnlySsrc(uint32_t ssrc, RtpPacketSinkInterface* sink) {
+ RtpDemuxerCriteria criteria;
+ criteria.ssrcs().insert(ssrc);
+ return AddSink(criteria, sink);
+ }
+
+ bool AddSinkOnlyRsid(absl::string_view rsid, RtpPacketSinkInterface* sink) {
+ RtpDemuxerCriteria criteria(absl::string_view(), rsid);
+ return AddSink(criteria, sink);
+ }
+
+ bool AddSinkOnlyMid(absl::string_view mid, RtpPacketSinkInterface* sink) {
+ RtpDemuxerCriteria criteria(mid);
+ return AddSink(criteria, sink);
+ }
+
+ bool AddSinkBothMidRsid(absl::string_view mid,
+ absl::string_view rsid,
+ RtpPacketSinkInterface* sink) {
+ RtpDemuxerCriteria criteria(mid, rsid);
+ return AddSink(criteria, sink);
+ }
+
+ bool RemoveSink(RtpPacketSinkInterface* sink) {
+ sinks_to_tear_down_.erase(sink);
+ return demuxer_.RemoveSink(sink);
+ }
+
+ // The CreatePacket* methods are helpers for creating new RTP packets with
+ // various attributes set. Tests should use the helper that provides the
+ // minimum information needed to exercise the behavior under test. Tests also
+ // should not rely on any behavior which is not clearly described in the
+ // helper name/arguments. Any additional settings that are not covered by the
+ // helper should be set manually on the packet once it has been returned.
+ // For example, most tests in this file do not care about the RTP sequence
+ // number, but to ensure that the returned packets are valid the helpers will
+ // auto-increment the sequence number starting with 1. Tests that rely on
+ // specific sequence number behavior should call SetSequenceNumber manually on
+ // the returned packet.
+
+ // Intended for use only by other CreatePacket* helpers.
+ std::unique_ptr<RtpPacketReceived> CreatePacket(
+ uint32_t ssrc,
+ RtpPacketReceived::ExtensionManager* extension_manager) {
+ auto packet = std::make_unique<RtpPacketReceived>(extension_manager);
+ packet->SetSsrc(ssrc);
+ packet->SetSequenceNumber(next_sequence_number_++);
+ return packet;
+ }
+
+ std::unique_ptr<RtpPacketReceived> CreatePacketWithSsrc(uint32_t ssrc) {
+ return CreatePacket(ssrc, nullptr);
+ }
+
+ std::unique_ptr<RtpPacketReceived> CreatePacketWithSsrcMid(
+ uint32_t ssrc,
+ absl::string_view mid) {
+ RtpPacketReceived::ExtensionManager extension_manager;
+ extension_manager.Register<RtpMid>(11);
+
+ auto packet = CreatePacket(ssrc, &extension_manager);
+ packet->SetExtension<RtpMid>(mid);
+ return packet;
+ }
+
+ std::unique_ptr<RtpPacketReceived> CreatePacketWithSsrcRsid(
+ uint32_t ssrc,
+ absl::string_view rsid) {
+ RtpPacketReceived::ExtensionManager extension_manager;
+ extension_manager.Register<RtpStreamId>(6);
+
+ auto packet = CreatePacket(ssrc, &extension_manager);
+ packet->SetExtension<RtpStreamId>(rsid);
+ return packet;
+ }
+
+ std::unique_ptr<RtpPacketReceived> CreatePacketWithSsrcRrid(
+ uint32_t ssrc,
+ absl::string_view rrid) {
+ RtpPacketReceived::ExtensionManager extension_manager;
+ extension_manager.Register<RepairedRtpStreamId>(7);
+
+ auto packet = CreatePacket(ssrc, &extension_manager);
+ packet->SetExtension<RepairedRtpStreamId>(rrid);
+ return packet;
+ }
+
+ std::unique_ptr<RtpPacketReceived> CreatePacketWithSsrcMidRsid(
+ uint32_t ssrc,
+ absl::string_view mid,
+ absl::string_view rsid) {
+ RtpPacketReceived::ExtensionManager extension_manager;
+ extension_manager.Register<RtpMid>(11);
+ extension_manager.Register<RtpStreamId>(6);
+
+ auto packet = CreatePacket(ssrc, &extension_manager);
+ packet->SetExtension<RtpMid>(mid);
+ packet->SetExtension<RtpStreamId>(rsid);
+ return packet;
+ }
+
+ std::unique_ptr<RtpPacketReceived> CreatePacketWithSsrcRsidRrid(
+ uint32_t ssrc,
+ absl::string_view rsid,
+ absl::string_view rrid) {
+ RtpPacketReceived::ExtensionManager extension_manager;
+ extension_manager.Register<RtpStreamId>(6);
+ extension_manager.Register<RepairedRtpStreamId>(7);
+
+ auto packet = CreatePacket(ssrc, &extension_manager);
+ packet->SetExtension<RtpStreamId>(rsid);
+ packet->SetExtension<RepairedRtpStreamId>(rrid);
+ return packet;
+ }
+
+ RtpDemuxer demuxer_;
+ std::set<RtpPacketSinkInterface*> sinks_to_tear_down_;
+ uint16_t next_sequence_number_ = 1;
+};
+
+class RtpDemuxerDeathTest : public RtpDemuxerTest {};
+
+MATCHER_P(SamePacketAs, other, "") {
+ return arg.Ssrc() == other.Ssrc() &&
+ arg.SequenceNumber() == other.SequenceNumber();
+}
+
+TEST_F(RtpDemuxerTest, CanAddSinkBySsrc) {
+ MockRtpPacketSink sink;
+ constexpr uint32_t ssrc = 1;
+
+ EXPECT_TRUE(AddSinkOnlySsrc(ssrc, &sink));
+}
+
+TEST_F(RtpDemuxerTest, AllowAddSinkWithOverlappingPayloadTypesIfDifferentMid) {
+ const std::string mid1 = "v";
+ const std::string mid2 = "a";
+ constexpr uint8_t pt1 = 30;
+ constexpr uint8_t pt2 = 31;
+ constexpr uint8_t pt3 = 32;
+
+ RtpDemuxerCriteria pt1_pt2(mid1);
+ pt1_pt2.payload_types() = {pt1, pt2};
+ MockRtpPacketSink sink1;
+ AddSink(pt1_pt2, &sink1);
+
+ RtpDemuxerCriteria pt1_pt3(mid2);
+ pt1_pt3.payload_types() = {pt1, pt3};
+ MockRtpPacketSink sink2;
+ EXPECT_TRUE(AddSink(pt1_pt3, &sink2));
+}
+
+TEST_F(RtpDemuxerTest, RejectAddSinkForSameMidOnly) {
+ const std::string mid = "mid";
+
+ MockRtpPacketSink sink;
+ AddSinkOnlyMid(mid, &sink);
+ EXPECT_FALSE(AddSinkOnlyMid(mid, &sink));
+}
+
+TEST_F(RtpDemuxerTest, RejectAddSinkForSameMidRsid) {
+ const std::string mid = "v";
+ const std::string rsid = "1";
+
+ MockRtpPacketSink sink1;
+ AddSinkBothMidRsid(mid, rsid, &sink1);
+
+ MockRtpPacketSink sink2;
+ EXPECT_FALSE(AddSinkBothMidRsid(mid, rsid, &sink2));
+}
+
+TEST_F(RtpDemuxerTest, RejectAddSinkForConflictingMidAndMidRsid) {
+ const std::string mid = "v";
+ const std::string rsid = "1";
+
+ MockRtpPacketSink mid_sink;
+ AddSinkOnlyMid(mid, &mid_sink);
+
+ // This sink would never get any packets routed to it because the above sink
+ // would receive them all.
+ MockRtpPacketSink mid_rsid_sink;
+ EXPECT_FALSE(AddSinkBothMidRsid(mid, rsid, &mid_rsid_sink));
+}
+
+TEST_F(RtpDemuxerTest, RejectAddSinkForConflictingMidRsidAndMid) {
+ const std::string mid = "v";
+ const std::string rsid = "";
+
+ MockRtpPacketSink mid_rsid_sink;
+ AddSinkBothMidRsid(mid, rsid, &mid_rsid_sink);
+
+ // This sink would shadow the above sink.
+ MockRtpPacketSink mid_sink;
+ EXPECT_FALSE(AddSinkOnlyMid(mid, &mid_sink));
+}
+
+TEST_F(RtpDemuxerTest, AddSinkFailsIfCalledForTwoSinksWithSameSsrc) {
+ MockRtpPacketSink sink_a;
+ MockRtpPacketSink sink_b;
+ constexpr uint32_t ssrc = 1;
+ ASSERT_TRUE(AddSinkOnlySsrc(ssrc, &sink_a));
+
+ EXPECT_FALSE(AddSinkOnlySsrc(ssrc, &sink_b));
+}
+
+TEST_F(RtpDemuxerTest, AddSinkFailsIfCalledTwiceEvenIfSameSinkWithSameSsrc) {
+ MockRtpPacketSink sink;
+ constexpr uint32_t ssrc = 1;
+ ASSERT_TRUE(AddSinkOnlySsrc(ssrc, &sink));
+
+ EXPECT_FALSE(AddSinkOnlySsrc(ssrc, &sink));
+}
+
+// TODO(steveanton): Currently fails because payload type validation is not
+// complete in AddSink (see note in rtp_demuxer.cc).
+TEST_F(RtpDemuxerTest, DISABLED_RejectAddSinkForSamePayloadTypes) {
+ constexpr uint8_t pt1 = 30;
+ constexpr uint8_t pt2 = 31;
+
+ RtpDemuxerCriteria pt1_pt2;
+ pt1_pt2.payload_types() = {pt1, pt2};
+ MockRtpPacketSink sink1;
+ AddSink(pt1_pt2, &sink1);
+
+ RtpDemuxerCriteria pt2_pt1;
+ pt2_pt1.payload_types() = {pt2, pt1};
+ MockRtpPacketSink sink2;
+ EXPECT_FALSE(AddSink(pt2_pt1, &sink2));
+}
+
+// Routing Tests
+
+TEST_F(RtpDemuxerTest, OnRtpPacketCalledOnCorrectSinkBySsrc) {
+ constexpr uint32_t ssrcs[] = {101, 202, 303};
+ MockRtpPacketSink sinks[arraysize(ssrcs)];
+ for (size_t i = 0; i < arraysize(ssrcs); i++) {
+ AddSinkOnlySsrc(ssrcs[i], &sinks[i]);
+ }
+
+ for (size_t i = 0; i < arraysize(ssrcs); i++) {
+ auto packet = CreatePacketWithSsrc(ssrcs[i]);
+ EXPECT_CALL(sinks[i], OnRtpPacket(SamePacketAs(*packet))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet));
+ }
+}
+
+TEST_F(RtpDemuxerTest, OnRtpPacketCalledOnCorrectSinkByRsid) {
+ const std::string rsids[] = {"a", "b", "c"};
+ MockRtpPacketSink sinks[arraysize(rsids)];
+ for (size_t i = 0; i < arraysize(rsids); i++) {
+ AddSinkOnlyRsid(rsids[i], &sinks[i]);
+ }
+
+ for (size_t i = 0; i < arraysize(rsids); i++) {
+ auto packet =
+ CreatePacketWithSsrcRsid(rtc::checked_cast<uint32_t>(i), rsids[i]);
+ EXPECT_CALL(sinks[i], OnRtpPacket(SamePacketAs(*packet))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet));
+ }
+}
+
+TEST_F(RtpDemuxerTest, OnRtpPacketCalledOnCorrectSinkByMid) {
+ const std::string mids[] = {"a", "v", "s"};
+ MockRtpPacketSink sinks[arraysize(mids)];
+ for (size_t i = 0; i < arraysize(mids); i++) {
+ AddSinkOnlyMid(mids[i], &sinks[i]);
+ }
+
+ for (size_t i = 0; i < arraysize(mids); i++) {
+ auto packet =
+ CreatePacketWithSsrcMid(rtc::checked_cast<uint32_t>(i), mids[i]);
+ EXPECT_CALL(sinks[i], OnRtpPacket(SamePacketAs(*packet))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet));
+ }
+}
+
+TEST_F(RtpDemuxerTest, OnRtpPacketCalledOnCorrectSinkByMidAndRsid) {
+ const std::string mid = "v";
+ const std::string rsid = "1";
+ constexpr uint32_t ssrc = 10;
+
+ MockRtpPacketSink sink;
+ AddSinkBothMidRsid(mid, rsid, &sink);
+
+ auto packet = CreatePacketWithSsrcMidRsid(ssrc, mid, rsid);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet));
+}
+
+TEST_F(RtpDemuxerTest, OnRtpPacketCalledOnCorrectSinkByRepairedRsid) {
+ const std::string rrid = "1";
+ constexpr uint32_t ssrc = 10;
+
+ MockRtpPacketSink sink;
+ AddSinkOnlyRsid(rrid, &sink);
+
+ auto packet_with_rrid = CreatePacketWithSsrcRrid(ssrc, rrid);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_with_rrid))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_with_rrid));
+}
+
+TEST_F(RtpDemuxerTest, OnRtpPacketCalledOnCorrectSinkByPayloadType) {
+ constexpr uint32_t ssrc = 10;
+ constexpr uint8_t payload_type = 30;
+
+ MockRtpPacketSink sink;
+ RtpDemuxerCriteria criteria;
+ criteria.payload_types() = {payload_type};
+ AddSink(criteria, &sink);
+
+ auto packet = CreatePacketWithSsrc(ssrc);
+ packet->SetPayloadType(payload_type);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet));
+}
+
+TEST_F(RtpDemuxerTest, PacketsDeliveredInRightOrder) {
+ constexpr uint32_t ssrc = 101;
+ MockRtpPacketSink sink;
+ AddSinkOnlySsrc(ssrc, &sink);
+
+ std::unique_ptr<RtpPacketReceived> packets[5];
+ for (size_t i = 0; i < arraysize(packets); i++) {
+ packets[i] = CreatePacketWithSsrc(ssrc);
+ packets[i]->SetSequenceNumber(rtc::checked_cast<uint16_t>(i));
+ }
+
+ InSequence sequence;
+ for (const auto& packet : packets) {
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1);
+ }
+
+ for (const auto& packet : packets) {
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet));
+ }
+}
+
+TEST_F(RtpDemuxerTest, SinkMappedToMultipleSsrcs) {
+ constexpr uint32_t ssrcs[] = {404, 505, 606};
+ MockRtpPacketSink sink;
+ for (uint32_t ssrc : ssrcs) {
+ AddSinkOnlySsrc(ssrc, &sink);
+ }
+
+ // The sink which is associated with multiple SSRCs gets the callback
+ // triggered for each of those SSRCs.
+ for (uint32_t ssrc : ssrcs) {
+ auto packet = CreatePacketWithSsrc(ssrc);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet));
+ }
+}
+
+TEST_F(RtpDemuxerTest, NoCallbackOnSsrcSinkRemovedBeforeFirstPacket) {
+ constexpr uint32_t ssrc = 404;
+ MockRtpPacketSink sink;
+ AddSinkOnlySsrc(ssrc, &sink);
+
+ ASSERT_TRUE(RemoveSink(&sink));
+
+ // The removed sink does not get callbacks.
+ auto packet = CreatePacketWithSsrc(ssrc);
+ EXPECT_CALL(sink, OnRtpPacket(_)).Times(0); // Not called.
+ EXPECT_FALSE(demuxer_.OnRtpPacket(*packet));
+}
+
+TEST_F(RtpDemuxerTest, NoCallbackOnSsrcSinkRemovedAfterFirstPacket) {
+ constexpr uint32_t ssrc = 404;
+ NiceMock<MockRtpPacketSink> sink;
+ AddSinkOnlySsrc(ssrc, &sink);
+
+ InSequence sequence;
+ for (size_t i = 0; i < 10; i++) {
+ ASSERT_TRUE(demuxer_.OnRtpPacket(*CreatePacketWithSsrc(ssrc)));
+ }
+
+ ASSERT_TRUE(RemoveSink(&sink));
+
+ // The removed sink does not get callbacks.
+ auto packet = CreatePacketWithSsrc(ssrc);
+ EXPECT_CALL(sink, OnRtpPacket(_)).Times(0); // Not called.
+ EXPECT_FALSE(demuxer_.OnRtpPacket(*packet));
+}
+
+// An SSRC may only be mapped to a single sink. However, since configuration
+// of this associations might come from the network, we need to fail gracefully.
+TEST_F(RtpDemuxerTest, OnlyOneSinkPerSsrcGetsOnRtpPacketTriggered) {
+ MockRtpPacketSink sinks[3];
+ constexpr uint32_t ssrc = 404;
+ ASSERT_TRUE(AddSinkOnlySsrc(ssrc, &sinks[0]));
+ ASSERT_FALSE(AddSinkOnlySsrc(ssrc, &sinks[1]));
+ ASSERT_FALSE(AddSinkOnlySsrc(ssrc, &sinks[2]));
+
+ // The first sink associated with the SSRC remains active; other sinks
+ // were not really added, and so do not get OnRtpPacket() called.
+ auto packet = CreatePacketWithSsrc(ssrc);
+ EXPECT_CALL(sinks[0], OnRtpPacket(SamePacketAs(*packet))).Times(1);
+ EXPECT_CALL(sinks[1], OnRtpPacket(_)).Times(0);
+ EXPECT_CALL(sinks[2], OnRtpPacket(_)).Times(0);
+ ASSERT_TRUE(demuxer_.OnRtpPacket(*packet));
+}
+
+TEST_F(RtpDemuxerTest, NoRepeatedCallbackOnRepeatedAddSinkForSameSink) {
+ constexpr uint32_t ssrc = 111;
+ MockRtpPacketSink sink;
+
+ ASSERT_TRUE(AddSinkOnlySsrc(ssrc, &sink));
+ ASSERT_FALSE(AddSinkOnlySsrc(ssrc, &sink));
+
+ auto packet = CreatePacketWithSsrc(ssrc);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet));
+}
+
+TEST_F(RtpDemuxerTest, RemoveSinkReturnsFalseForNeverAddedSink) {
+ MockRtpPacketSink sink;
+ EXPECT_FALSE(RemoveSink(&sink));
+}
+
+TEST_F(RtpDemuxerTest, RemoveSinkReturnsTrueForPreviouslyAddedSsrcSink) {
+ constexpr uint32_t ssrc = 101;
+ MockRtpPacketSink sink;
+ AddSinkOnlySsrc(ssrc, &sink);
+
+ EXPECT_TRUE(RemoveSink(&sink));
+}
+
+TEST_F(RtpDemuxerTest,
+ RemoveSinkReturnsTrueForUnresolvedPreviouslyAddedRsidSink) {
+ const std::string rsid = "a";
+ MockRtpPacketSink sink;
+ AddSinkOnlyRsid(rsid, &sink);
+
+ EXPECT_TRUE(RemoveSink(&sink));
+}
+
+TEST_F(RtpDemuxerTest,
+ RemoveSinkReturnsTrueForResolvedPreviouslyAddedRsidSink) {
+ const std::string rsid = "a";
+ constexpr uint32_t ssrc = 101;
+ NiceMock<MockRtpPacketSink> sink;
+ AddSinkOnlyRsid(rsid, &sink);
+ ASSERT_TRUE(demuxer_.OnRtpPacket(*CreatePacketWithSsrcRsid(ssrc, rsid)));
+
+ EXPECT_TRUE(RemoveSink(&sink));
+}
+
+TEST_F(RtpDemuxerTest, RsidLearnedAndLaterPacketsDeliveredWithOnlySsrc) {
+ MockRtpPacketSink sink;
+ const std::string rsid = "a";
+ AddSinkOnlyRsid(rsid, &sink);
+
+ // Create a sequence of RTP packets, where only the first one actually
+ // mentions the RSID.
+ std::unique_ptr<RtpPacketReceived> packets[5];
+ constexpr uint32_t rsid_ssrc = 111;
+ packets[0] = CreatePacketWithSsrcRsid(rsid_ssrc, rsid);
+ for (size_t i = 1; i < arraysize(packets); i++) {
+ packets[i] = CreatePacketWithSsrc(rsid_ssrc);
+ }
+
+ // The first packet associates the RSID with the SSRC, thereby allowing the
+ // demuxer to correctly demux all of the packets.
+ InSequence sequence;
+ for (const auto& packet : packets) {
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1);
+ }
+ for (const auto& packet : packets) {
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet));
+ }
+}
+
+TEST_F(RtpDemuxerTest, NoCallbackOnRsidSinkRemovedBeforeFirstPacket) {
+ MockRtpPacketSink sink;
+ const std::string rsid = "a";
+ AddSinkOnlyRsid(rsid, &sink);
+
+ // Sink removed - it won't get triggers even if packets with its RSID arrive.
+ ASSERT_TRUE(RemoveSink(&sink));
+
+ constexpr uint32_t ssrc = 111;
+ auto packet = CreatePacketWithSsrcRsid(ssrc, rsid);
+ EXPECT_CALL(sink, OnRtpPacket(_)).Times(0); // Not called.
+ EXPECT_FALSE(demuxer_.OnRtpPacket(*packet));
+}
+
+TEST_F(RtpDemuxerTest, NoCallbackOnRsidSinkRemovedAfterFirstPacket) {
+ NiceMock<MockRtpPacketSink> sink;
+ const std::string rsid = "a";
+ AddSinkOnlyRsid(rsid, &sink);
+
+ InSequence sequence;
+ constexpr uint32_t ssrc = 111;
+ for (size_t i = 0; i < 10; i++) {
+ auto packet = CreatePacketWithSsrcRsid(ssrc, rsid);
+ ASSERT_TRUE(demuxer_.OnRtpPacket(*packet));
+ }
+
+ // Sink removed - it won't get triggers even if packets with its RSID arrive.
+ ASSERT_TRUE(RemoveSink(&sink));
+
+ auto packet = CreatePacketWithSsrcRsid(ssrc, rsid);
+ EXPECT_CALL(sink, OnRtpPacket(_)).Times(0); // Not called.
+ EXPECT_FALSE(demuxer_.OnRtpPacket(*packet));
+}
+
+TEST_F(RtpDemuxerTest, NoCallbackOnMidSinkRemovedBeforeFirstPacket) {
+ const std::string mid = "v";
+ constexpr uint32_t ssrc = 10;
+
+ MockRtpPacketSink sink;
+ AddSinkOnlyMid(mid, &sink);
+ RemoveSink(&sink);
+
+ auto packet = CreatePacketWithSsrcMid(ssrc, mid);
+ EXPECT_CALL(sink, OnRtpPacket(_)).Times(0);
+ EXPECT_FALSE(demuxer_.OnRtpPacket(*packet));
+}
+
+TEST_F(RtpDemuxerTest, NoCallbackOnMidSinkRemovedAfterFirstPacket) {
+ const std::string mid = "v";
+ constexpr uint32_t ssrc = 10;
+
+ NiceMock<MockRtpPacketSink> sink;
+ AddSinkOnlyMid(mid, &sink);
+
+ auto p1 = CreatePacketWithSsrcMid(ssrc, mid);
+ demuxer_.OnRtpPacket(*p1);
+
+ RemoveSink(&sink);
+
+ auto p2 = CreatePacketWithSsrcMid(ssrc, mid);
+ EXPECT_CALL(sink, OnRtpPacket(_)).Times(0);
+ EXPECT_FALSE(demuxer_.OnRtpPacket(*p2));
+}
+
+TEST_F(RtpDemuxerTest, NoCallbackOnMidRsidSinkRemovedAfterFirstPacket) {
+ const std::string mid = "v";
+ const std::string rsid = "1";
+ constexpr uint32_t ssrc = 10;
+
+ NiceMock<MockRtpPacketSink> sink;
+ AddSinkBothMidRsid(mid, rsid, &sink);
+
+ auto p1 = CreatePacketWithSsrcMidRsid(ssrc, mid, rsid);
+ demuxer_.OnRtpPacket(*p1);
+
+ RemoveSink(&sink);
+
+ auto p2 = CreatePacketWithSsrcMidRsid(ssrc, mid, rsid);
+ EXPECT_CALL(sink, OnRtpPacket(_)).Times(0);
+ EXPECT_FALSE(demuxer_.OnRtpPacket(*p2));
+}
+
+// The RSID to SSRC mapping should be one-to-one. If we end up receiving
+// two (or more) packets with the same SSRC, but different RSIDs, we guarantee
+// delivery to one of them but not both.
+TEST_F(RtpDemuxerTest, FirstSsrcAssociatedWithAnRsidIsNotForgotten) {
+ // Each sink has a distinct RSID.
+ MockRtpPacketSink sink_a;
+ const std::string rsid_a = "a";
+ AddSinkOnlyRsid(rsid_a, &sink_a);
+
+ MockRtpPacketSink sink_b;
+ const std::string rsid_b = "b";
+ AddSinkOnlyRsid(rsid_b, &sink_b);
+
+ InSequence sequence; // Verify that the order of delivery is unchanged.
+
+ constexpr uint32_t shared_ssrc = 100;
+
+ // First a packet with `rsid_a` is received, and `sink_a` is associated with
+ // its SSRC.
+ auto packet_a = CreatePacketWithSsrcRsid(shared_ssrc, rsid_a);
+ EXPECT_CALL(sink_a, OnRtpPacket(SamePacketAs(*packet_a))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_a));
+
+ // Second, a packet with `rsid_b` is received. We guarantee that `sink_b`
+ // receives it.
+ auto packet_b = CreatePacketWithSsrcRsid(shared_ssrc, rsid_b);
+ EXPECT_CALL(sink_a, OnRtpPacket(_)).Times(0);
+ EXPECT_CALL(sink_b, OnRtpPacket(SamePacketAs(*packet_b))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_b));
+
+ // Known edge-case; adding a new RSID association makes us re-examine all
+ // SSRCs. `sink_b` may or may not be associated with the SSRC now; we make
+ // no promises on that. However, since the RSID is specified and it cannot be
+ // found the packet should be dropped.
+ MockRtpPacketSink sink_c;
+ const std::string rsid_c = "c";
+ constexpr uint32_t some_other_ssrc = shared_ssrc + 1;
+ AddSinkOnlySsrc(some_other_ssrc, &sink_c);
+
+ auto packet_c = CreatePacketWithSsrcMid(shared_ssrc, rsid_c);
+ EXPECT_CALL(sink_a, OnRtpPacket(_)).Times(0);
+ EXPECT_CALL(sink_b, OnRtpPacket(_)).Times(0);
+ EXPECT_CALL(sink_c, OnRtpPacket(_)).Times(0);
+ EXPECT_FALSE(demuxer_.OnRtpPacket(*packet_c));
+}
+
+TEST_F(RtpDemuxerTest, MultipleRsidsOnSameSink) {
+ MockRtpPacketSink sink;
+ const std::string rsids[] = {"a", "b", "c"};
+
+ for (const std::string& rsid : rsids) {
+ AddSinkOnlyRsid(rsid, &sink);
+ }
+
+ InSequence sequence;
+ for (size_t i = 0; i < arraysize(rsids); i++) {
+ // Assign different SSRCs and sequence numbers to all packets.
+ const uint32_t ssrc = 1000 + static_cast<uint32_t>(i);
+ const uint16_t sequence_number = 50 + static_cast<uint16_t>(i);
+ auto packet = CreatePacketWithSsrcRsid(ssrc, rsids[i]);
+ packet->SetSequenceNumber(sequence_number);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet));
+ }
+}
+
+// RSIDs are given higher priority than SSRC because we believe senders are less
+// likely to mislabel packets with RSID than mislabel them with SSRCs.
+TEST_F(RtpDemuxerTest, SinkWithBothRsidAndSsrcAssociations) {
+ MockRtpPacketSink sink;
+ constexpr uint32_t standalone_ssrc = 10101;
+ constexpr uint32_t rsid_ssrc = 20202;
+ const std::string rsid = "1";
+
+ AddSinkOnlySsrc(standalone_ssrc, &sink);
+ AddSinkOnlyRsid(rsid, &sink);
+
+ InSequence sequence;
+
+ auto ssrc_packet = CreatePacketWithSsrc(standalone_ssrc);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*ssrc_packet))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*ssrc_packet));
+
+ auto rsid_packet = CreatePacketWithSsrcRsid(rsid_ssrc, rsid);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*rsid_packet))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*rsid_packet));
+}
+
+// Packets are always guaranteed to be routed to only one sink.
+TEST_F(RtpDemuxerTest, AssociatingByRsidAndBySsrcCannotTriggerDoubleCall) {
+ constexpr uint32_t ssrc = 10101;
+ const std::string rsid = "a";
+
+ MockRtpPacketSink sink;
+ AddSinkOnlySsrc(ssrc, &sink);
+ AddSinkOnlyRsid(rsid, &sink);
+
+ auto packet = CreatePacketWithSsrcRsid(ssrc, rsid);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet));
+}
+
+
+// If one sink is associated with SSRC x, and another sink with RSID y, then if
+// we receive a packet with both SSRC x and RSID y, route that to only the sink
+// for RSID y since we believe RSID tags to be more trustworthy than signaled
+// SSRCs.
+TEST_F(RtpDemuxerTest,
+ PacketFittingBothRsidSinkAndSsrcSinkGivenOnlyToRsidSink) {
+ constexpr uint32_t ssrc = 111;
+ MockRtpPacketSink ssrc_sink;
+ AddSinkOnlySsrc(ssrc, &ssrc_sink);
+
+ const std::string rsid = "a";
+ MockRtpPacketSink rsid_sink;
+ AddSinkOnlyRsid(rsid, &rsid_sink);
+
+ auto packet = CreatePacketWithSsrcRsid(ssrc, rsid);
+
+ EXPECT_CALL(ssrc_sink, OnRtpPacket(_)).Times(0);
+ EXPECT_CALL(rsid_sink, OnRtpPacket(SamePacketAs(*packet))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet));
+}
+
+// We're not expecting RSIDs to be resolved to SSRCs which were previously
+// mapped to sinks, and make no guarantees except for graceful handling.
+TEST_F(RtpDemuxerTest,
+ GracefullyHandleRsidBeingMappedToPrevouslyAssociatedSsrc) {
+ constexpr uint32_t ssrc = 111;
+ NiceMock<MockRtpPacketSink> ssrc_sink;
+ AddSinkOnlySsrc(ssrc, &ssrc_sink);
+
+ const std::string rsid = "a";
+ NiceMock<MockRtpPacketSink> rsid_sink;
+ AddSinkOnlyRsid(rsid, &rsid_sink);
+
+ // The SSRC was mapped to an SSRC sink, but was even active (packets flowed
+ // over it).
+ auto packet = CreatePacketWithSsrcRsid(ssrc, rsid);
+ demuxer_.OnRtpPacket(*packet);
+
+ // If the SSRC sink is ever removed, the RSID sink *might* receive indications
+ // of packets, and observers *might* be informed. Only graceful handling
+ // is guaranteed.
+ RemoveSink(&ssrc_sink);
+ EXPECT_CALL(rsid_sink, OnRtpPacket(SamePacketAs(*packet))).Times(AtLeast(0));
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet));
+}
+
+// Tests that when one MID sink is configured, packets that include the MID
+// extension will get routed to that sink and any packets that use the same
+// SSRC as one of those packets later will also get routed to the sink, even
+// if a new SSRC is introduced for the same MID.
+TEST_F(RtpDemuxerTest, RoutedByMidWhenSsrcAdded) {
+ const std::string mid = "v";
+ NiceMock<MockRtpPacketSink> sink;
+ AddSinkOnlyMid(mid, &sink);
+
+ constexpr uint32_t ssrc1 = 10;
+ constexpr uint32_t ssrc2 = 11;
+
+ auto packet_ssrc1_mid = CreatePacketWithSsrcMid(ssrc1, mid);
+ demuxer_.OnRtpPacket(*packet_ssrc1_mid);
+ auto packet_ssrc2_mid = CreatePacketWithSsrcMid(ssrc2, mid);
+ demuxer_.OnRtpPacket(*packet_ssrc2_mid);
+
+ auto packet_ssrc1_only = CreatePacketWithSsrc(ssrc1);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_ssrc1_only))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_ssrc1_only));
+
+ auto packet_ssrc2_only = CreatePacketWithSsrc(ssrc2);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_ssrc2_only))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_ssrc2_only));
+}
+
+TEST_F(RtpDemuxerTest, DontLearnMidSsrcBindingBeforeSinkAdded) {
+ const std::string mid = "v";
+ constexpr uint32_t ssrc = 10;
+
+ auto packet_ssrc_mid = CreatePacketWithSsrcMid(ssrc, mid);
+ ASSERT_FALSE(demuxer_.OnRtpPacket(*packet_ssrc_mid));
+
+ MockRtpPacketSink sink;
+ AddSinkOnlyMid(mid, &sink);
+
+ auto packet_ssrc_only = CreatePacketWithSsrc(ssrc);
+ EXPECT_CALL(sink, OnRtpPacket(_)).Times(0);
+ EXPECT_FALSE(demuxer_.OnRtpPacket(*packet_ssrc_only));
+}
+
+TEST_F(RtpDemuxerTest, DontForgetMidSsrcBindingWhenSinkRemoved) {
+ const std::string mid = "v";
+ constexpr uint32_t ssrc = 10;
+
+ NiceMock<MockRtpPacketSink> sink1;
+ AddSinkOnlyMid(mid, &sink1);
+
+ auto packet_with_mid = CreatePacketWithSsrcMid(ssrc, mid);
+ demuxer_.OnRtpPacket(*packet_with_mid);
+
+ RemoveSink(&sink1);
+
+ MockRtpPacketSink sink2;
+ AddSinkOnlyMid(mid, &sink2);
+
+ auto packet_with_ssrc = CreatePacketWithSsrc(ssrc);
+ EXPECT_CALL(sink2, OnRtpPacket(SamePacketAs(*packet_with_ssrc)));
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_with_ssrc));
+}
+
+// If a sink is added with only a MID, then any packet with that MID no matter
+// the RSID should be routed to that sink.
+TEST_F(RtpDemuxerTest, RoutedByMidWithAnyRsid) {
+ const std::string mid = "v";
+ const std::string rsid1 = "1";
+ const std::string rsid2 = "2";
+ constexpr uint32_t ssrc1 = 10;
+ constexpr uint32_t ssrc2 = 11;
+
+ MockRtpPacketSink sink;
+ AddSinkOnlyMid(mid, &sink);
+
+ InSequence sequence;
+
+ auto packet_ssrc1_rsid1 = CreatePacketWithSsrcMidRsid(ssrc1, mid, rsid1);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_ssrc1_rsid1))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_ssrc1_rsid1));
+
+ auto packet_ssrc2_rsid2 = CreatePacketWithSsrcMidRsid(ssrc2, mid, rsid2);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_ssrc2_rsid2))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_ssrc2_rsid2));
+}
+
+// These two tests verify that for a sink added with a MID, RSID pair, if the
+// MID and RSID are learned in separate packets (e.g., because the header
+// extensions are sent separately), then a later packet with just SSRC will get
+// routed to that sink.
+// The first test checks that the functionality works when MID is learned first.
+// The second test checks that the functionality works when RSID is learned
+// first.
+TEST_F(RtpDemuxerTest, LearnMidThenRsidSeparatelyAndRouteBySsrc) {
+ const std::string mid = "v";
+ const std::string rsid = "1";
+ constexpr uint32_t ssrc = 10;
+
+ NiceMock<MockRtpPacketSink> sink;
+ AddSinkBothMidRsid(mid, rsid, &sink);
+
+ auto packet_with_mid = CreatePacketWithSsrcMid(ssrc, mid);
+ ASSERT_FALSE(demuxer_.OnRtpPacket(*packet_with_mid));
+
+ auto packet_with_rsid = CreatePacketWithSsrcRsid(ssrc, rsid);
+ ASSERT_TRUE(demuxer_.OnRtpPacket(*packet_with_rsid));
+
+ auto packet_with_ssrc = CreatePacketWithSsrc(ssrc);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_with_ssrc))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_with_ssrc));
+}
+
+TEST_F(RtpDemuxerTest, LearnRsidThenMidSeparatelyAndRouteBySsrc) {
+ const std::string mid = "v";
+ const std::string rsid = "1";
+ constexpr uint32_t ssrc = 10;
+
+ NiceMock<MockRtpPacketSink> sink;
+ AddSinkBothMidRsid(mid, rsid, &sink);
+
+ auto packet_with_rsid = CreatePacketWithSsrcRsid(ssrc, rsid);
+ ASSERT_FALSE(demuxer_.OnRtpPacket(*packet_with_rsid));
+
+ auto packet_with_mid = CreatePacketWithSsrcMid(ssrc, mid);
+ ASSERT_TRUE(demuxer_.OnRtpPacket(*packet_with_mid));
+
+ auto packet_with_ssrc = CreatePacketWithSsrc(ssrc);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_with_ssrc))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_with_ssrc));
+}
+
+TEST_F(RtpDemuxerTest, DontLearnMidRsidBindingBeforeSinkAdded) {
+ const std::string mid = "v";
+ const std::string rsid = "1";
+ constexpr uint32_t ssrc = 10;
+
+ auto packet_with_both = CreatePacketWithSsrcMidRsid(ssrc, mid, rsid);
+ ASSERT_FALSE(demuxer_.OnRtpPacket(*packet_with_both));
+
+ MockRtpPacketSink sink;
+ AddSinkBothMidRsid(mid, rsid, &sink);
+
+ auto packet_with_ssrc = CreatePacketWithSsrc(ssrc);
+ EXPECT_CALL(sink, OnRtpPacket(_)).Times(0);
+ EXPECT_FALSE(demuxer_.OnRtpPacket(*packet_with_ssrc));
+}
+
+TEST_F(RtpDemuxerTest, DontForgetMidRsidBindingWhenSinkRemoved) {
+ const std::string mid = "v";
+ const std::string rsid = "1";
+ constexpr uint32_t ssrc = 10;
+
+ NiceMock<MockRtpPacketSink> sink1;
+ AddSinkBothMidRsid(mid, rsid, &sink1);
+
+ auto packet_with_both = CreatePacketWithSsrcMidRsid(ssrc, mid, rsid);
+ demuxer_.OnRtpPacket(*packet_with_both);
+
+ RemoveSink(&sink1);
+
+ MockRtpPacketSink sink2;
+ AddSinkBothMidRsid(mid, rsid, &sink2);
+
+ auto packet_with_ssrc = CreatePacketWithSsrc(ssrc);
+ EXPECT_CALL(sink2, OnRtpPacket(SamePacketAs(*packet_with_ssrc)));
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_with_ssrc));
+}
+
+TEST_F(RtpDemuxerTest, LearnMidRsidBindingAfterSinkAdded) {
+ const std::string mid = "v";
+ const std::string rsid = "1";
+ constexpr uint32_t ssrc = 10;
+
+ NiceMock<MockRtpPacketSink> sink;
+ AddSinkBothMidRsid(mid, rsid, &sink);
+
+ auto packet_with_both = CreatePacketWithSsrcMidRsid(ssrc, mid, rsid);
+ demuxer_.OnRtpPacket(*packet_with_both);
+
+ auto packet_with_ssrc = CreatePacketWithSsrc(ssrc);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_with_ssrc)));
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_with_ssrc));
+}
+
+TEST_F(RtpDemuxerTest, DropByPayloadTypeIfNoSink) {
+ constexpr uint8_t payload_type = 30;
+ constexpr uint32_t ssrc = 10;
+
+ auto packet = CreatePacketWithSsrc(ssrc);
+ packet->SetPayloadType(payload_type);
+ EXPECT_FALSE(demuxer_.OnRtpPacket(*packet));
+}
+
+// For legacy applications, it's possible for us to demux if the payload type is
+// unique. But if multiple sinks are registered with different MIDs and the same
+// payload types, then we cannot route a packet with just payload type because
+// it is ambiguous which sink it should be sent to.
+TEST_F(RtpDemuxerTest, DropByPayloadTypeIfAddedInMultipleSinks) {
+ const std::string mid1 = "v";
+ const std::string mid2 = "a";
+ constexpr uint8_t payload_type = 30;
+ constexpr uint32_t ssrc = 10;
+
+ RtpDemuxerCriteria mid1_pt(mid1);
+ mid1_pt.payload_types() = {payload_type};
+ MockRtpPacketSink sink1;
+ AddSink(mid1_pt, &sink1);
+
+ RtpDemuxerCriteria mid2_pt(mid2);
+ mid2_pt.payload_types() = {payload_type};
+ MockRtpPacketSink sink2;
+ AddSink(mid2_pt, &sink2);
+
+ auto packet = CreatePacketWithSsrc(ssrc);
+ packet->SetPayloadType(payload_type);
+
+ EXPECT_CALL(sink1, OnRtpPacket(_)).Times(0);
+ EXPECT_CALL(sink2, OnRtpPacket(_)).Times(0);
+ EXPECT_FALSE(demuxer_.OnRtpPacket(*packet));
+}
+
+// If two sinks are added with different MIDs but the same payload types, then
+// we cannot demux on the payload type only unless one of the sinks is removed.
+TEST_F(RtpDemuxerTest, RoutedByPayloadTypeIfAmbiguousSinkRemoved) {
+ const std::string mid1 = "v";
+ const std::string mid2 = "a";
+ constexpr uint8_t payload_type = 30;
+ constexpr uint32_t ssrc = 10;
+
+ RtpDemuxerCriteria mid1_pt(mid1);
+ mid1_pt.payload_types().insert(payload_type);
+ MockRtpPacketSink sink1;
+ AddSink(mid1_pt, &sink1);
+
+ RtpDemuxerCriteria mid2_pt(mid2);
+ mid2_pt.payload_types().insert(payload_type);
+ MockRtpPacketSink sink2;
+ AddSink(mid2_pt, &sink2);
+
+ RemoveSink(&sink1);
+
+ auto packet = CreatePacketWithSsrc(ssrc);
+ packet->SetPayloadType(payload_type);
+
+ EXPECT_CALL(sink1, OnRtpPacket(_)).Times(0);
+ EXPECT_CALL(sink2, OnRtpPacket(SamePacketAs(*packet))).Times(1);
+
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet));
+}
+
+TEST_F(RtpDemuxerTest, RoutedByPayloadTypeLatchesSsrc) {
+ constexpr uint8_t payload_type = 30;
+ constexpr uint32_t ssrc = 10;
+
+ RtpDemuxerCriteria pt;
+ pt.payload_types().insert(payload_type);
+ NiceMock<MockRtpPacketSink> sink;
+ AddSink(pt, &sink);
+
+ auto packet_with_pt = CreatePacketWithSsrc(ssrc);
+ packet_with_pt->SetPayloadType(payload_type);
+ ASSERT_TRUE(demuxer_.OnRtpPacket(*packet_with_pt));
+
+ auto packet_with_ssrc = CreatePacketWithSsrc(ssrc);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_with_ssrc))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_with_ssrc));
+}
+
+// RSIDs are scoped within MID, so if two sinks are registered with the same
+// RSIDs but different MIDs, then packets containing both extensions should be
+// routed to the correct one.
+TEST_F(RtpDemuxerTest, PacketWithSameRsidDifferentMidRoutedToProperSink) {
+ const std::string mid1 = "mid1";
+ const std::string mid2 = "mid2";
+ const std::string rsid = "rsid";
+ constexpr uint32_t ssrc1 = 10;
+ constexpr uint32_t ssrc2 = 11;
+
+ NiceMock<MockRtpPacketSink> mid1_sink;
+ AddSinkBothMidRsid(mid1, rsid, &mid1_sink);
+
+ MockRtpPacketSink mid2_sink;
+ AddSinkBothMidRsid(mid2, rsid, &mid2_sink);
+
+ auto packet_mid1 = CreatePacketWithSsrcMidRsid(ssrc1, mid1, rsid);
+ ASSERT_TRUE(demuxer_.OnRtpPacket(*packet_mid1));
+
+ auto packet_mid2 = CreatePacketWithSsrcMidRsid(ssrc2, mid2, rsid);
+ EXPECT_CALL(mid2_sink, OnRtpPacket(SamePacketAs(*packet_mid2))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_mid2));
+}
+
+// If a sink is first bound to a given SSRC by signaling but later a new sink is
+// bound to a given MID by a later signaling, then when a packet arrives with
+// both the SSRC and MID, then the signaled MID sink should take precedence.
+TEST_F(RtpDemuxerTest, SignaledMidShouldOverwriteSignaledSsrc) {
+ constexpr uint32_t ssrc = 11;
+ const std::string mid = "mid";
+
+ MockRtpPacketSink ssrc_sink;
+ AddSinkOnlySsrc(ssrc, &ssrc_sink);
+
+ MockRtpPacketSink mid_sink;
+ AddSinkOnlyMid(mid, &mid_sink);
+
+ auto p = CreatePacketWithSsrcMid(ssrc, mid);
+ EXPECT_CALL(ssrc_sink, OnRtpPacket(_)).Times(0);
+ EXPECT_CALL(mid_sink, OnRtpPacket(SamePacketAs(*p))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*p));
+}
+
+// Extends the previous test to also ensure that later packets that do not
+// specify MID are still routed to the MID sink rather than the overwritten SSRC
+// sink.
+TEST_F(RtpDemuxerTest, SignaledMidShouldOverwriteSignalledSsrcPersistent) {
+ constexpr uint32_t ssrc = 11;
+ const std::string mid = "mid";
+
+ MockRtpPacketSink ssrc_sink;
+ AddSinkOnlySsrc(ssrc, &ssrc_sink);
+
+ NiceMock<MockRtpPacketSink> mid_sink;
+ AddSinkOnlyMid(mid, &mid_sink);
+
+ EXPECT_CALL(ssrc_sink, OnRtpPacket(_)).Times(0);
+
+ auto packet_with_mid = CreatePacketWithSsrcMid(ssrc, mid);
+ demuxer_.OnRtpPacket(*packet_with_mid);
+
+ auto packet_without_mid = CreatePacketWithSsrc(ssrc);
+ EXPECT_CALL(mid_sink, OnRtpPacket(SamePacketAs(*packet_without_mid)))
+ .Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_without_mid));
+}
+
+TEST_F(RtpDemuxerTest, RouteByPayloadTypeMultipleMatch) {
+ constexpr uint32_t ssrc = 10;
+ constexpr uint8_t pt1 = 30;
+ constexpr uint8_t pt2 = 31;
+
+ MockRtpPacketSink sink;
+ RtpDemuxerCriteria criteria;
+ criteria.payload_types() = {pt1, pt2};
+ AddSink(criteria, &sink);
+
+ auto packet_with_pt1 = CreatePacketWithSsrc(ssrc);
+ packet_with_pt1->SetPayloadType(pt1);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_with_pt1)));
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_with_pt1));
+
+ auto packet_with_pt2 = CreatePacketWithSsrc(ssrc);
+ packet_with_pt2->SetPayloadType(pt2);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet_with_pt2)));
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_with_pt2));
+}
+
+TEST_F(RtpDemuxerTest, DontDemuxOnMidAloneIfAddedWithRsid) {
+ const std::string mid = "v";
+ const std::string rsid = "1";
+ constexpr uint32_t ssrc = 10;
+
+ MockRtpPacketSink sink;
+ AddSinkBothMidRsid(mid, rsid, &sink);
+
+ EXPECT_CALL(sink, OnRtpPacket(_)).Times(0);
+
+ auto packet = CreatePacketWithSsrcMid(ssrc, mid);
+ EXPECT_FALSE(demuxer_.OnRtpPacket(*packet));
+}
+
+TEST_F(RtpDemuxerTest, DemuxBySsrcEvenWithMidAndRsid) {
+ const std::string mid = "v";
+ const std::string rsid = "1";
+ constexpr uint32_t ssrc = 10;
+
+ RtpDemuxerCriteria criteria(mid, rsid);
+ criteria.ssrcs().insert(ssrc);
+ MockRtpPacketSink sink;
+ AddSink(criteria, &sink);
+
+ auto packet = CreatePacketWithSsrc(ssrc);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet));
+}
+
+// In slight deviation from the BUNDLE spec, if we match a sink according to
+// SSRC, then we do not verify payload type against the criteria and defer to
+// the sink to check that it is correct.
+TEST_F(RtpDemuxerTest, DoNotCheckPayloadTypeIfMatchedByOtherCriteria) {
+ constexpr uint32_t ssrc = 10;
+ constexpr uint8_t payload_type = 30;
+ constexpr uint8_t different_payload_type = payload_type + 1;
+
+ RtpDemuxerCriteria criteria;
+ criteria.ssrcs().insert(ssrc);
+ criteria.payload_types().insert(payload_type);
+ MockRtpPacketSink sink;
+ AddSink(criteria, &sink);
+
+ auto packet = CreatePacketWithSsrc(ssrc);
+ packet->SetPayloadType(different_payload_type);
+ EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet));
+}
+
+// If a repair packet includes an RSID it should be ignored and the packet
+// should be routed by its RRID.
+TEST_F(RtpDemuxerTest, PacketWithRsidAndRridRoutedByRrid) {
+ const std::string rsid = "1";
+ const std::string rrid = "1r";
+ constexpr uint32_t ssrc = 10;
+
+ MockRtpPacketSink sink_rsid;
+ AddSinkOnlyRsid(rsid, &sink_rsid);
+
+ MockRtpPacketSink sink_rrid;
+ AddSinkOnlyRsid(rrid, &sink_rrid);
+
+ auto packet = CreatePacketWithSsrcRsidRrid(ssrc, rsid, rrid);
+ EXPECT_CALL(sink_rsid, OnRtpPacket(_)).Times(0);
+ EXPECT_CALL(sink_rrid, OnRtpPacket(SamePacketAs(*packet))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet));
+}
+
+// Same test as above but checks that the latched SSRC routes to the RRID sink.
+TEST_F(RtpDemuxerTest, PacketWithRsidAndRridLatchesSsrcToRrid) {
+ const std::string rsid = "1";
+ const std::string rrid = "1r";
+ constexpr uint32_t ssrc = 10;
+
+ MockRtpPacketSink sink_rsid;
+ AddSinkOnlyRsid(rsid, &sink_rsid);
+
+ NiceMock<MockRtpPacketSink> sink_rrid;
+ AddSinkOnlyRsid(rrid, &sink_rrid);
+
+ auto packet_rsid_rrid = CreatePacketWithSsrcRsidRrid(ssrc, rsid, rrid);
+ demuxer_.OnRtpPacket(*packet_rsid_rrid);
+
+ auto packet_ssrc_only = CreatePacketWithSsrc(ssrc);
+ EXPECT_CALL(sink_rsid, OnRtpPacket(_)).Times(0);
+ EXPECT_CALL(sink_rrid, OnRtpPacket(SamePacketAs(*packet_ssrc_only))).Times(1);
+ EXPECT_TRUE(demuxer_.OnRtpPacket(*packet_ssrc_only));
+}
+
+// Tests that a packet which includes MID and RSID is dropped and not routed by
+// SSRC if the MID and RSID do not match an added sink.
+TEST_F(RtpDemuxerTest, PacketWithMidAndUnknownRsidIsNotRoutedBySsrc) {
+ constexpr uint32_t ssrc = 10;
+ const std::string mid = "v";
+ const std::string rsid = "1";
+ const std::string wrong_rsid = "2";
+
+ RtpDemuxerCriteria criteria(mid, rsid);
+ criteria.ssrcs().insert(ssrc);
+ MockRtpPacketSink sink;
+ AddSink(criteria, &sink);
+
+ auto packet = CreatePacketWithSsrcMidRsid(ssrc, mid, wrong_rsid);
+ EXPECT_CALL(sink, OnRtpPacket(_)).Times(0);
+ EXPECT_FALSE(demuxer_.OnRtpPacket(*packet));
+}
+
+// Tests that a packet which includes MID and RSID is dropped and not routed by
+// payload type if the MID and RSID do not match an added sink.
+TEST_F(RtpDemuxerTest, PacketWithMidAndUnknownRsidIsNotRoutedByPayloadType) {
+ constexpr uint32_t ssrc = 10;
+ const std::string mid = "v";
+ const std::string rsid = "1";
+ const std::string wrong_rsid = "2";
+ constexpr uint8_t payload_type = 30;
+
+ RtpDemuxerCriteria criteria(mid, rsid);
+ criteria.payload_types().insert(payload_type);
+ MockRtpPacketSink sink;
+ AddSink(criteria, &sink);
+
+ auto packet = CreatePacketWithSsrcMidRsid(ssrc, mid, wrong_rsid);
+ packet->SetPayloadType(payload_type);
+ EXPECT_CALL(sink, OnRtpPacket(_)).Times(0);
+ EXPECT_FALSE(demuxer_.OnRtpPacket(*packet));
+}
+
+TEST_F(RtpDemuxerTest, MidMustNotExceedMaximumLength) {
+ MockRtpPacketSink sink1;
+ std::string mid1(BaseRtpStringExtension::kMaxValueSizeBytes + 1, 'a');
+ // Adding the sink should pass even though the supplied mid is too long.
+ // The mid will be truncated though.
+ EXPECT_TRUE(AddSinkOnlyMid(mid1, &sink1));
+
+ // Adding a second sink with a mid that matches the truncated mid that was
+ // just added, should fail.
+ MockRtpPacketSink sink2;
+ std::string mid2(mid1.substr(0, BaseRtpStringExtension::kMaxValueSizeBytes));
+ EXPECT_FALSE(AddSinkOnlyMid(mid2, &sink2));
+ EXPECT_FALSE(RemoveSink(&sink2));
+
+ // Remove the original sink.
+ EXPECT_TRUE(RemoveSink(&sink1));
+}
+
+#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+
+TEST_F(RtpDemuxerDeathTest, CriteriaMustBeNonEmpty) {
+ MockRtpPacketSink sink;
+ RtpDemuxerCriteria criteria;
+ EXPECT_DEATH(AddSink(criteria, &sink), "");
+}
+
+TEST_F(RtpDemuxerDeathTest, RsidMustBeAlphaNumeric) {
+ MockRtpPacketSink sink;
+ EXPECT_DEATH(AddSinkOnlyRsid("a_3", &sink), "");
+}
+
+TEST_F(RtpDemuxerDeathTest, MidMustBeToken) {
+ MockRtpPacketSink sink;
+ EXPECT_DEATH(AddSinkOnlyMid("a(3)", &sink), "");
+}
+
+TEST_F(RtpDemuxerDeathTest, RsidMustNotExceedMaximumLength) {
+ MockRtpPacketSink sink;
+ std::string rsid(BaseRtpStringExtension::kMaxValueSizeBytes + 1, 'a');
+ EXPECT_DEATH(AddSinkOnlyRsid(rsid, &sink), "");
+}
+
+#endif
+
+} // namespace
+} // namespace webrtc