| /* |
| * 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 |