diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/libwebrtc/call/rtp_demuxer_unittest.cc | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
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.cc | 1287 |
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 |