| /* | 
 |  *  Copyright (c) 2015 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 "modules/pacing/packet_router.h" | 
 |  | 
 | #include <cstddef> | 
 | #include <cstdint> | 
 | #include <memory> | 
 | #include <optional> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "api/rtp_headers.h" | 
 | #include "api/transport/network_types.h" | 
 | #include "api/units/data_size.h" | 
 | #include "api/units/time_delta.h" | 
 | #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" | 
 | #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" | 
 | #include "modules/rtp_rtcp/mocks/mock_rtp_rtcp.h" | 
 | #include "modules/rtp_rtcp/source/rtcp_packet.h" | 
 | #include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" | 
 | #include "modules/rtp_rtcp/source/rtp_header_extensions.h" | 
 | #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" | 
 | #include "rtc_base/checks.h" | 
 | #include "rtc_base/fake_clock.h" | 
 | #include "test/gmock.h" | 
 | #include "test/gtest.h" | 
 |  | 
 | namespace webrtc { | 
 |  | 
 | // TODO(eladalon): Restructure and/or replace the existing monolithic tests | 
 | // (only some of the test are monolithic) according to the new | 
 | // guidelines - small tests for one thing at a time. | 
 | // (I'm not removing any tests during CL, so as to demonstrate no regressions.) | 
 |  | 
 | namespace { | 
 |  | 
 | using ::testing::_; | 
 | using ::testing::ElementsAreArray; | 
 | using ::testing::InSequence; | 
 | using ::testing::MockFunction; | 
 | using ::testing::NiceMock; | 
 | using ::testing::Pointee; | 
 | using ::testing::Property; | 
 | using ::testing::Return; | 
 |  | 
 | constexpr int kProbeMinProbes = 5; | 
 | constexpr int kProbeMinBytes = 1000; | 
 |  | 
 | }  // namespace | 
 |  | 
 | class PacketRouterTest : public ::testing::Test { | 
 |  public: | 
 |   PacketRouterTest() { | 
 |     extension_manager.Register<TransportSequenceNumber>(/*id=*/1); | 
 |   } | 
 |  | 
 |  protected: | 
 |   std::unique_ptr<RtpPacketToSend> BuildRtpPacket(uint32_t ssrc) { | 
 |     std::unique_ptr<RtpPacketToSend> packet = | 
 |         std::make_unique<RtpPacketToSend>(&extension_manager); | 
 |     packet->SetSsrc(ssrc); | 
 |     return packet; | 
 |   } | 
 |  | 
 |   PacketRouter packet_router_; | 
 |   RtpHeaderExtensionMap extension_manager; | 
 | }; | 
 |  | 
 | TEST_F(PacketRouterTest, Sanity_NoModuleRegistered_GeneratePadding) { | 
 |   constexpr DataSize bytes = DataSize::Bytes(300); | 
 |   const PacedPacketInfo paced_info(1, kProbeMinProbes, kProbeMinBytes); | 
 |  | 
 |   EXPECT_TRUE(packet_router_.GeneratePadding(bytes).empty()); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, Sanity_NoModuleRegistered_SendRemb) { | 
 |   const std::vector<uint32_t> ssrcs = {1, 2, 3}; | 
 |   constexpr uint32_t bitrate_bps = 10000; | 
 |   // Expect not to crash | 
 |   packet_router_.SendRemb(bitrate_bps, ssrcs); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, Sanity_NoModuleRegistered_SendTransportFeedback) { | 
 |   std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback; | 
 |   feedback.push_back(std::make_unique<rtcp::TransportFeedback>()); | 
 |   // Expect not to crash | 
 |   packet_router_.SendCombinedRtcpPacket(std::move(feedback)); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, GeneratePaddingPrioritizesRtx) { | 
 |   // Two RTP modules. The first (prioritized due to rtx) isn't sending media so | 
 |   // should not be called. | 
 |   const uint16_t kSsrc1 = 1234; | 
 |   const uint16_t kSsrc2 = 4567; | 
 |  | 
 |   NiceMock<MockRtpRtcpInterface> rtp_1; | 
 |   ON_CALL(rtp_1, RtxSendStatus()).WillByDefault(Return(kRtxRedundantPayloads)); | 
 |   ON_CALL(rtp_1, SSRC()).WillByDefault(Return(kSsrc1)); | 
 |   ON_CALL(rtp_1, SupportsPadding).WillByDefault(Return(false)); | 
 |  | 
 |   NiceMock<MockRtpRtcpInterface> rtp_2; | 
 |   ON_CALL(rtp_2, RtxSendStatus()).WillByDefault(Return(kRtxOff)); | 
 |   ON_CALL(rtp_2, SSRC()).WillByDefault(Return(kSsrc2)); | 
 |   ON_CALL(rtp_2, SupportsPadding).WillByDefault(Return(true)); | 
 |  | 
 |   packet_router_.AddSendRtpModule(&rtp_1, false); | 
 |   packet_router_.AddSendRtpModule(&rtp_2, false); | 
 |  | 
 |   const size_t kPaddingSize = 123; | 
 |   const size_t kExpectedPaddingPackets = 1; | 
 |   EXPECT_CALL(rtp_1, GeneratePadding(_)).Times(0); | 
 |   EXPECT_CALL(rtp_2, GeneratePadding(kPaddingSize)) | 
 |       .WillOnce([&](size_t /* padding_size */) { | 
 |         return std::vector<std::unique_ptr<RtpPacketToSend>>( | 
 |             kExpectedPaddingPackets); | 
 |       }); | 
 |   auto generated_padding = | 
 |       packet_router_.GeneratePadding(DataSize::Bytes(kPaddingSize)); | 
 |   EXPECT_EQ(generated_padding.size(), kExpectedPaddingPackets); | 
 |  | 
 |   packet_router_.RemoveSendRtpModule(&rtp_1); | 
 |   packet_router_.RemoveSendRtpModule(&rtp_2); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, SupportsRtxPayloadPaddingFalseIfNoRtxSendModule) { | 
 |   EXPECT_FALSE(packet_router_.SupportsRtxPayloadPadding()); | 
 |  | 
 |   NiceMock<MockRtpRtcpInterface> none_rtx_module; | 
 |   ON_CALL(none_rtx_module, SupportsRtxPayloadPadding()) | 
 |       .WillByDefault(Return(false)); | 
 |  | 
 |   packet_router_.AddSendRtpModule(&none_rtx_module, false); | 
 |   EXPECT_FALSE(packet_router_.SupportsRtxPayloadPadding()); | 
 |  | 
 |   packet_router_.RemoveSendRtpModule(&none_rtx_module); | 
 |   EXPECT_FALSE(packet_router_.SupportsRtxPayloadPadding()); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, SupportsRtxPayloadPaddingTrueIfRtxSendModule) { | 
 |   NiceMock<MockRtpRtcpInterface> rtx_module; | 
 |   ON_CALL(rtx_module, SupportsRtxPayloadPadding()).WillByDefault(Return(true)); | 
 |  | 
 |   packet_router_.AddSendRtpModule(&rtx_module, false); | 
 |   EXPECT_TRUE(packet_router_.SupportsRtxPayloadPadding()); | 
 |  | 
 |   packet_router_.RemoveSendRtpModule(&rtx_module); | 
 |   EXPECT_FALSE(packet_router_.SupportsRtxPayloadPadding()); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, GeneratePaddingPrioritizesVideo) { | 
 |   // Two RTP modules. Neither support RTX, both support padding, | 
 |   // but the first one is for audio and second for video. | 
 |   const uint16_t kSsrc1 = 1234; | 
 |   const uint16_t kSsrc2 = 4567; | 
 |   const size_t kPaddingSize = 123; | 
 |   const size_t kExpectedPaddingPackets = 1; | 
 |  | 
 |   auto generate_padding = [&](size_t /* padding_size */) { | 
 |     return std::vector<std::unique_ptr<RtpPacketToSend>>( | 
 |         kExpectedPaddingPackets); | 
 |   }; | 
 |  | 
 |   NiceMock<MockRtpRtcpInterface> audio_module; | 
 |   ON_CALL(audio_module, RtxSendStatus()).WillByDefault(Return(kRtxOff)); | 
 |   ON_CALL(audio_module, SSRC()).WillByDefault(Return(kSsrc1)); | 
 |   ON_CALL(audio_module, SupportsPadding).WillByDefault(Return(true)); | 
 |   ON_CALL(audio_module, IsAudioConfigured).WillByDefault(Return(true)); | 
 |  | 
 |   NiceMock<MockRtpRtcpInterface> video_module; | 
 |   ON_CALL(video_module, RtxSendStatus()).WillByDefault(Return(kRtxOff)); | 
 |   ON_CALL(video_module, SSRC()).WillByDefault(Return(kSsrc2)); | 
 |   ON_CALL(video_module, SupportsPadding).WillByDefault(Return(true)); | 
 |   ON_CALL(video_module, IsAudioConfigured).WillByDefault(Return(false)); | 
 |  | 
 |   // First add only the audio module. Since this is the only choice we have, | 
 |   // padding should be sent on the audio ssrc. | 
 |   packet_router_.AddSendRtpModule(&audio_module, false); | 
 |   EXPECT_CALL(audio_module, GeneratePadding(kPaddingSize)) | 
 |       .WillOnce(generate_padding); | 
 |   packet_router_.GeneratePadding(DataSize::Bytes(kPaddingSize)); | 
 |  | 
 |   // Add the video module, this should now be prioritized since we cannot | 
 |   // guarantee that audio packets will be included in the BWE. | 
 |   packet_router_.AddSendRtpModule(&video_module, false); | 
 |   EXPECT_CALL(audio_module, GeneratePadding).Times(0); | 
 |   EXPECT_CALL(video_module, GeneratePadding(kPaddingSize)) | 
 |       .WillOnce(generate_padding); | 
 |   packet_router_.GeneratePadding(DataSize::Bytes(kPaddingSize)); | 
 |  | 
 |   // Remove and the add audio module again. Module order shouldn't matter; | 
 |   // video should still be prioritized. | 
 |   packet_router_.RemoveSendRtpModule(&audio_module); | 
 |   packet_router_.AddSendRtpModule(&audio_module, false); | 
 |   EXPECT_CALL(audio_module, GeneratePadding).Times(0); | 
 |   EXPECT_CALL(video_module, GeneratePadding(kPaddingSize)) | 
 |       .WillOnce(generate_padding); | 
 |   packet_router_.GeneratePadding(DataSize::Bytes(kPaddingSize)); | 
 |  | 
 |   // Remove and the video module, we should fall back to padding on the | 
 |   // audio module again. | 
 |   packet_router_.RemoveSendRtpModule(&video_module); | 
 |   EXPECT_CALL(audio_module, GeneratePadding(kPaddingSize)) | 
 |       .WillOnce(generate_padding); | 
 |   packet_router_.GeneratePadding(DataSize::Bytes(kPaddingSize)); | 
 |  | 
 |   packet_router_.RemoveSendRtpModule(&audio_module); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, PadsOnLastActiveMediaStream) { | 
 |   const uint16_t kSsrc1 = 1234; | 
 |   const uint16_t kSsrc2 = 4567; | 
 |   const uint16_t kSsrc3 = 8901; | 
 |  | 
 |   // First two rtp modules send media and have rtx. | 
 |   NiceMock<MockRtpRtcpInterface> rtp_1; | 
 |   EXPECT_CALL(rtp_1, SSRC()).WillRepeatedly(Return(kSsrc1)); | 
 |   EXPECT_CALL(rtp_1, SupportsPadding).WillRepeatedly(Return(true)); | 
 |   EXPECT_CALL(rtp_1, SupportsRtxPayloadPadding).WillRepeatedly(Return(true)); | 
 |   EXPECT_CALL(rtp_1, CanSendPacket) | 
 |       .WillRepeatedly([&](const RtpPacketToSend& packet) { | 
 |         if (packet.Ssrc() == kSsrc1) { | 
 |           return true; | 
 |         } | 
 |         return false; | 
 |       }); | 
 |  | 
 |   NiceMock<MockRtpRtcpInterface> rtp_2; | 
 |   EXPECT_CALL(rtp_2, SSRC()).WillRepeatedly(Return(kSsrc2)); | 
 |   EXPECT_CALL(rtp_2, SupportsPadding).WillRepeatedly(Return(true)); | 
 |   EXPECT_CALL(rtp_2, SupportsRtxPayloadPadding).WillRepeatedly(Return(true)); | 
 |   EXPECT_CALL(rtp_2, CanSendPacket) | 
 |       .WillRepeatedly([&](const RtpPacketToSend& packet) { | 
 |         if (packet.Ssrc() == kSsrc2) { | 
 |           return true; | 
 |         } | 
 |         return false; | 
 |       }); | 
 |  | 
 |   // Third module is sending media, but does not support rtx. | 
 |   NiceMock<MockRtpRtcpInterface> rtp_3; | 
 |   EXPECT_CALL(rtp_3, SSRC()).WillRepeatedly(Return(kSsrc3)); | 
 |   EXPECT_CALL(rtp_3, SupportsPadding).WillRepeatedly(Return(true)); | 
 |   EXPECT_CALL(rtp_3, SupportsRtxPayloadPadding).WillRepeatedly(Return(false)); | 
 |   EXPECT_CALL(rtp_3, CanSendPacket) | 
 |       .WillRepeatedly([&](const RtpPacketToSend& packet) { | 
 |         if (packet.Ssrc() == kSsrc3) { | 
 |           return true; | 
 |         } | 
 |         return false; | 
 |       }); | 
 |  | 
 |   packet_router_.AddSendRtpModule(&rtp_1, false); | 
 |   packet_router_.AddSendRtpModule(&rtp_2, false); | 
 |   packet_router_.AddSendRtpModule(&rtp_3, false); | 
 |  | 
 |   const size_t kPaddingBytes = 100; | 
 |  | 
 |   // Initially, padding will be sent on last added rtp module that sends media | 
 |   // and supports rtx. | 
 |   EXPECT_CALL(rtp_2, GeneratePadding(kPaddingBytes)) | 
 |       .Times(1) | 
 |       .WillOnce([&](size_t /* target_size_bytes */) { | 
 |         std::vector<std::unique_ptr<RtpPacketToSend>> packets; | 
 |         packets.push_back(BuildRtpPacket(kSsrc2)); | 
 |         return packets; | 
 |       }); | 
 |   packet_router_.GeneratePadding(DataSize::Bytes(kPaddingBytes)); | 
 |  | 
 |   // Send media on first module. Padding should be sent on that module. | 
 |   packet_router_.SendPacket(BuildRtpPacket(kSsrc1), PacedPacketInfo()); | 
 |  | 
 |   EXPECT_CALL(rtp_1, GeneratePadding(kPaddingBytes)) | 
 |       .Times(1) | 
 |       .WillOnce([&](size_t /* target_size_bytes */) { | 
 |         std::vector<std::unique_ptr<RtpPacketToSend>> packets; | 
 |         packets.push_back(BuildRtpPacket(kSsrc1)); | 
 |         return packets; | 
 |       }); | 
 |   packet_router_.GeneratePadding(DataSize::Bytes(kPaddingBytes)); | 
 |  | 
 |   // Send media on second module. Padding should be sent there. | 
 |   packet_router_.SendPacket(BuildRtpPacket(kSsrc2), PacedPacketInfo()); | 
 |  | 
 |   // If the last active module is removed, and no module sends media before | 
 |   // the next padding request, and arbitrary module will be selected. | 
 |   packet_router_.OnBatchComplete(); | 
 |   packet_router_.RemoveSendRtpModule(&rtp_2); | 
 |  | 
 |   // Send on and then remove all remaining modules. | 
 |   RtpRtcpInterface* last_send_module; | 
 |   EXPECT_CALL(rtp_1, GeneratePadding(kPaddingBytes)) | 
 |       .Times(1) | 
 |       .WillOnce([&](size_t /* target_size_bytes */) { | 
 |         last_send_module = &rtp_1; | 
 |         std::vector<std::unique_ptr<RtpPacketToSend>> packets; | 
 |         packets.push_back(BuildRtpPacket(kSsrc1)); | 
 |         return packets; | 
 |       }); | 
 |   EXPECT_CALL(rtp_3, GeneratePadding(kPaddingBytes)) | 
 |       .Times(1) | 
 |       .WillOnce([&](size_t /* target_size_bytes */) { | 
 |         last_send_module = &rtp_3; | 
 |         std::vector<std::unique_ptr<RtpPacketToSend>> packets; | 
 |         packets.push_back(BuildRtpPacket(kSsrc3)); | 
 |         return packets; | 
 |       }); | 
 |  | 
 |   for (int i = 0; i < 2; ++i) { | 
 |     last_send_module = nullptr; | 
 |     packet_router_.GeneratePadding(DataSize::Bytes(kPaddingBytes)); | 
 |     EXPECT_NE(last_send_module, nullptr); | 
 |     packet_router_.RemoveSendRtpModule(last_send_module); | 
 |   } | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, AllocatesRtpSequenceNumbersIfPacketCanBeSent) { | 
 |   const uint16_t kSsrc1 = 1234; | 
 |   PacketRouter packet_router; | 
 |   NiceMock<MockRtpRtcpInterface> rtp; | 
 |   ON_CALL(rtp, SSRC()).WillByDefault(Return(kSsrc1)); | 
 |  | 
 |   InSequence s; | 
 |   EXPECT_CALL(rtp, CanSendPacket).WillRepeatedly(Return(true)); | 
 |   EXPECT_CALL(rtp, AssignSequenceNumber); | 
 |   packet_router.AddSendRtpModule(&rtp, false); | 
 |   packet_router.SendPacket(BuildRtpPacket(kSsrc1), PacedPacketInfo()); | 
 |  | 
 |   packet_router.OnBatchComplete(); | 
 |   packet_router.RemoveSendRtpModule(&rtp); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, DoNotAllocatesRtpSequenceNumbersIfPacketCanNotBeSent) { | 
 |   const uint16_t kSsrc1 = 1234; | 
 |   PacketRouter packet_router; | 
 |   NiceMock<MockRtpRtcpInterface> rtp; | 
 |   ON_CALL(rtp, SSRC()).WillByDefault(Return(kSsrc1)); | 
 |  | 
 |   EXPECT_CALL(rtp, CanSendPacket).WillRepeatedly(Return(false)); | 
 |   EXPECT_CALL(rtp, AssignSequenceNumber).Times(0); | 
 |   packet_router.AddSendRtpModule(&rtp, false); | 
 |   packet_router.SendPacket(BuildRtpPacket(kSsrc1), PacedPacketInfo()); | 
 |  | 
 |   packet_router.OnBatchComplete(); | 
 |   packet_router.RemoveSendRtpModule(&rtp); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, AllocatesTransportSequenceNumbers) { | 
 |   const uint16_t kSsrc1 = 1234; | 
 |  | 
 |   PacketRouter packet_router; | 
 |   testing::MockFunction<void(const RtpPacketToSend& packet, | 
 |                              const PacedPacketInfo& pacing_info)> | 
 |       notify_bwe_callback; | 
 |   NiceMock<MockRtpRtcpInterface> rtp_1; | 
 |   packet_router.RegisterNotifyBweCallback(notify_bwe_callback.AsStdFunction()); | 
 |  | 
 |   EXPECT_CALL(rtp_1, SSRC()).WillRepeatedly(Return(kSsrc1)); | 
 |   EXPECT_CALL(rtp_1, CanSendPacket).WillRepeatedly(Return(true)); | 
 |  | 
 |   packet_router.AddSendRtpModule(&rtp_1, false); | 
 |  | 
 |   auto packet = BuildRtpPacket(kSsrc1); | 
 |   EXPECT_TRUE(packet->ReserveExtension<TransportSequenceNumber>()); | 
 |   EXPECT_CALL(notify_bwe_callback, Call) | 
 |       .WillOnce([](const RtpPacketToSend& packet, | 
 |                    const PacedPacketInfo& /* pacing_info */) { | 
 |         EXPECT_EQ(packet.transport_sequence_number(), 1); | 
 |       }); | 
 |   packet_router.SendPacket(std::move(packet), PacedPacketInfo()); | 
 |  | 
 |   packet_router.OnBatchComplete(); | 
 |   packet_router.RemoveSendRtpModule(&rtp_1); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, | 
 |        DoesNotAllocateTransportSequenceNumberWithoutExtension) { | 
 |   const uint16_t kSsrc1 = 1234; | 
 |  | 
 |   PacketRouter packet_router; | 
 |   testing::MockFunction<void(const RtpPacketToSend& packet, | 
 |                              const PacedPacketInfo& pacing_info)> | 
 |       notify_bwe_callback; | 
 |   NiceMock<MockRtpRtcpInterface> rtp_1; | 
 |   packet_router.RegisterNotifyBweCallback(notify_bwe_callback.AsStdFunction()); | 
 |  | 
 |   EXPECT_CALL(rtp_1, SSRC()).WillRepeatedly(Return(kSsrc1)); | 
 |   EXPECT_CALL(rtp_1, CanSendPacket).WillRepeatedly(Return(true)); | 
 |  | 
 |   packet_router.AddSendRtpModule(&rtp_1, false); | 
 |  | 
 |   auto packet = BuildRtpPacket(kSsrc1); | 
 |   EXPECT_CALL(notify_bwe_callback, Call) | 
 |       .WillOnce([](const RtpPacketToSend& packet, | 
 |                    const PacedPacketInfo& /* pacing_info */) { | 
 |         EXPECT_EQ(packet.transport_sequence_number(), std::nullopt); | 
 |       }); | 
 |   packet_router.SendPacket(std::move(packet), PacedPacketInfo()); | 
 |  | 
 |   packet_router.OnBatchComplete(); | 
 |   packet_router.RemoveSendRtpModule(&rtp_1); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, | 
 |        AllocateTransportSequenceNumberWithoutExtensionIfRfc8888Enabled) { | 
 |   const uint16_t kSsrc1 = 1234; | 
 |  | 
 |   PacketRouter packet_router; | 
 |   testing::MockFunction<void(const RtpPacketToSend& packet, | 
 |                              const PacedPacketInfo& pacing_info)> | 
 |       notify_bwe_callback; | 
 |   NiceMock<MockRtpRtcpInterface> rtp_1; | 
 |   packet_router.RegisterNotifyBweCallback(notify_bwe_callback.AsStdFunction()); | 
 |  | 
 |   EXPECT_CALL(rtp_1, SSRC()).WillRepeatedly(Return(kSsrc1)); | 
 |   EXPECT_CALL(rtp_1, CanSendPacket).WillRepeatedly(Return(true)); | 
 |  | 
 |   packet_router.AddSendRtpModule(&rtp_1, false); | 
 |   packet_router.ConfigureForRfc8888Feedback(/*send_rtp_packets_as_ect1=*/false); | 
 |  | 
 |   auto packet = BuildRtpPacket(kSsrc1); | 
 |   EXPECT_CALL(notify_bwe_callback, Call) | 
 |       .WillOnce([](const RtpPacketToSend& packet, | 
 |                    const PacedPacketInfo& /* pacing_info */) { | 
 |         EXPECT_EQ(packet.transport_sequence_number(), 1); | 
 |       }); | 
 |   packet_router.SendPacket(std::move(packet), PacedPacketInfo()); | 
 |  | 
 |   packet_router.OnBatchComplete(); | 
 |   packet_router.RemoveSendRtpModule(&rtp_1); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, SendPacketsAsEct1IfConfigured) { | 
 |   const uint16_t kSsrc1 = 1234; | 
 |   PacketRouter packet_router; | 
 |   NiceMock<MockRtpRtcpInterface> rtp_1; | 
 |   ON_CALL(rtp_1, SSRC()).WillByDefault(Return(kSsrc1)); | 
 |   ON_CALL(rtp_1, CanSendPacket).WillByDefault(Return(kSsrc1)); | 
 |  | 
 |   packet_router.AddSendRtpModule(&rtp_1, false); | 
 |   packet_router.ConfigureForRfc8888Feedback(/*send_rtp_packets_as_ect1=*/true); | 
 |  | 
 |   testing::Sequence s; | 
 |   EXPECT_CALL( | 
 |       rtp_1, | 
 |       SendPacket(Pointee(Property(&RtpPacketToSend::send_as_ect1, true)), _)) | 
 |       .InSequence(s); | 
 |   EXPECT_CALL( | 
 |       rtp_1, | 
 |       SendPacket(Pointee(Property(&RtpPacketToSend::send_as_ect1, false)), _)) | 
 |       .InSequence(s); | 
 |  | 
 |   packet_router.SendPacket(BuildRtpPacket(kSsrc1), PacedPacketInfo()); | 
 |   packet_router.ConfigureForRfc8888Feedback(/*send_rtp_packets_as_ect1=*/false); | 
 |   packet_router.SendPacket(BuildRtpPacket(kSsrc1), PacedPacketInfo()); | 
 |  | 
 |   packet_router.OnBatchComplete(); | 
 |   packet_router.RemoveSendRtpModule(&rtp_1); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, SendTransportFeedback) { | 
 |   NiceMock<MockRtpRtcpInterface> rtp_1; | 
 |   NiceMock<MockRtpRtcpInterface> rtp_2; | 
 |  | 
 |   ON_CALL(rtp_1, RTCP()).WillByDefault(Return(RtcpMode::kCompound)); | 
 |   ON_CALL(rtp_2, RTCP()).WillByDefault(Return(RtcpMode::kCompound)); | 
 |  | 
 |   packet_router_.AddSendRtpModule(&rtp_1, false); | 
 |   packet_router_.AddReceiveRtpModule(&rtp_2, false); | 
 |  | 
 |   std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback; | 
 |   feedback.push_back(std::make_unique<rtcp::TransportFeedback>()); | 
 |   EXPECT_CALL(rtp_1, SendCombinedRtcpPacket); | 
 |   packet_router_.SendCombinedRtcpPacket(std::move(feedback)); | 
 |   packet_router_.RemoveSendRtpModule(&rtp_1); | 
 |   EXPECT_CALL(rtp_2, SendCombinedRtcpPacket); | 
 |   std::vector<std::unique_ptr<rtcp::RtcpPacket>> new_feedback; | 
 |   new_feedback.push_back(std::make_unique<rtcp::TransportFeedback>()); | 
 |   packet_router_.SendCombinedRtcpPacket(std::move(new_feedback)); | 
 |   packet_router_.RemoveReceiveRtpModule(&rtp_2); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, SendPacketWithoutTransportSequenceNumbers) { | 
 |   const uint16_t kSsrc1 = 1234; | 
 |   NiceMock<MockRtpRtcpInterface> rtp_1; | 
 |   ON_CALL(rtp_1, SendingMedia).WillByDefault(Return(true)); | 
 |   ON_CALL(rtp_1, SSRC).WillByDefault(Return(kSsrc1)); | 
 |   ON_CALL(rtp_1, CanSendPacket).WillByDefault(Return(true)); | 
 |   packet_router_.AddSendRtpModule(&rtp_1, false); | 
 |  | 
 |   // Send a packet without TransportSequenceNumber extension registered, | 
 |   // packets sent should not have the extension set. | 
 |   RtpHeaderExtensionMap extension_manager; | 
 |   auto packet = std::make_unique<RtpPacketToSend>(&extension_manager); | 
 |   packet->SetSsrc(kSsrc1); | 
 |   EXPECT_CALL( | 
 |       rtp_1, | 
 |       SendPacket( | 
 |           AllOf(Pointee(Property( | 
 |                     &RtpPacketToSend::HasExtension<TransportSequenceNumber>, | 
 |                     false)), | 
 |                 Pointee(Property(&RtpPacketToSend::transport_sequence_number, | 
 |                                  std::nullopt))), | 
 |           _)); | 
 |   packet_router_.SendPacket(std::move(packet), PacedPacketInfo()); | 
 |   packet_router_.OnBatchComplete(); | 
 |   packet_router_.RemoveSendRtpModule(&rtp_1); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, DoesNotIncrementTransportSequenceNumberOnSendFailure) { | 
 |   NiceMock<MockRtpRtcpInterface> rtp; | 
 |   constexpr uint32_t kSsrc = 1234; | 
 |   ON_CALL(rtp, SSRC).WillByDefault(Return(kSsrc)); | 
 |   packet_router_.AddSendRtpModule(&rtp, false); | 
 |  | 
 |   // Transport sequence numbers start at 1, for historical reasons. | 
 |   const uint16_t kStartTransportSequenceNumber = 1; | 
 |  | 
 |   // Build and send a packet - it should be assigned the start sequence number. | 
 |   // Return failure status code to make sure sequence number is not incremented. | 
 |   auto packet = BuildRtpPacket(kSsrc); | 
 |   EXPECT_TRUE(packet->ReserveExtension<TransportSequenceNumber>()); | 
 |   EXPECT_CALL(rtp, CanSendPacket) | 
 |       .WillOnce([&](const RtpPacketToSend& /* packet */) { return false; }); | 
 |   packet_router_.SendPacket(std::move(packet), PacedPacketInfo()); | 
 |  | 
 |   // Send another packet, verify transport sequence number is still at the | 
 |   // start state. | 
 |   packet = BuildRtpPacket(kSsrc); | 
 |   EXPECT_TRUE(packet->ReserveExtension<TransportSequenceNumber>()); | 
 |  | 
 |   EXPECT_CALL(rtp, CanSendPacket).WillOnce(Return(true)); | 
 |   EXPECT_CALL(rtp, SendPacket) | 
 |       .WillOnce([&](std::unique_ptr<RtpPacketToSend> packet, | 
 |                     const PacedPacketInfo& /* pacing_info */) { | 
 |         EXPECT_EQ(packet->transport_sequence_number(), | 
 |                   kStartTransportSequenceNumber); | 
 |       }); | 
 |   packet_router_.SendPacket(std::move(packet), PacedPacketInfo()); | 
 |  | 
 |   packet_router_.OnBatchComplete(); | 
 |   packet_router_.RemoveSendRtpModule(&rtp); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, ForwardsAbortedRetransmissions) { | 
 |   NiceMock<MockRtpRtcpInterface> rtp_1; | 
 |   NiceMock<MockRtpRtcpInterface> rtp_2; | 
 |  | 
 |   const uint32_t kSsrc1 = 1234; | 
 |   const uint32_t kSsrc2 = 2345; | 
 |   const uint32_t kInvalidSsrc = 3456; | 
 |  | 
 |   ON_CALL(rtp_1, SSRC).WillByDefault(Return(kSsrc1)); | 
 |   ON_CALL(rtp_2, SSRC).WillByDefault(Return(kSsrc2)); | 
 |  | 
 |   packet_router_.AddSendRtpModule(&rtp_1, false); | 
 |   packet_router_.AddSendRtpModule(&rtp_2, false); | 
 |  | 
 |   // Sets of retransmission sequence numbers we wish to abort, per ssrc. | 
 |   const uint16_t kAbortedRetransmissionsOnSsrc1[] = {17, 42}; | 
 |   const uint16_t kAbortedRetransmissionsOnSsrc2[] = {1337, 4711}; | 
 |   const uint16_t kAbortedRetransmissionsOnSsrc3[] = {123}; | 
 |  | 
 |   EXPECT_CALL(rtp_1, OnAbortedRetransmissions( | 
 |                          ElementsAreArray(kAbortedRetransmissionsOnSsrc1))); | 
 |   EXPECT_CALL(rtp_2, OnAbortedRetransmissions( | 
 |                          ElementsAreArray(kAbortedRetransmissionsOnSsrc2))); | 
 |  | 
 |   packet_router_.OnAbortedRetransmissions(kSsrc1, | 
 |                                           kAbortedRetransmissionsOnSsrc1); | 
 |   packet_router_.OnAbortedRetransmissions(kSsrc2, | 
 |                                           kAbortedRetransmissionsOnSsrc2); | 
 |  | 
 |   // Should be noop and not cause any issues. | 
 |   packet_router_.OnAbortedRetransmissions(kInvalidSsrc, | 
 |                                           kAbortedRetransmissionsOnSsrc3); | 
 |  | 
 |   packet_router_.RemoveSendRtpModule(&rtp_1); | 
 |   packet_router_.RemoveSendRtpModule(&rtp_2); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, ReportsRtxSsrc) { | 
 |   NiceMock<MockRtpRtcpInterface> rtp_1; | 
 |   NiceMock<MockRtpRtcpInterface> rtp_2; | 
 |  | 
 |   const uint32_t kSsrc1 = 1234; | 
 |   const uint32_t kRtxSsrc1 = 1235; | 
 |   const uint32_t kSsrc2 = 2345; | 
 |   const uint32_t kInvalidSsrc = 3456; | 
 |  | 
 |   ON_CALL(rtp_1, SSRC).WillByDefault(Return(kSsrc1)); | 
 |   ON_CALL(rtp_1, RtxSsrc).WillByDefault(Return(kRtxSsrc1)); | 
 |   ON_CALL(rtp_2, SSRC).WillByDefault(Return(kSsrc2)); | 
 |  | 
 |   packet_router_.AddSendRtpModule(&rtp_1, false); | 
 |   packet_router_.AddSendRtpModule(&rtp_2, false); | 
 |  | 
 |   EXPECT_EQ(packet_router_.GetRtxSsrcForMedia(kSsrc1), kRtxSsrc1); | 
 |   EXPECT_EQ(packet_router_.GetRtxSsrcForMedia(kRtxSsrc1), std::nullopt); | 
 |   EXPECT_EQ(packet_router_.GetRtxSsrcForMedia(kSsrc2), std::nullopt); | 
 |   EXPECT_EQ(packet_router_.GetRtxSsrcForMedia(kInvalidSsrc), std::nullopt); | 
 |  | 
 |   packet_router_.RemoveSendRtpModule(&rtp_1); | 
 |   packet_router_.RemoveSendRtpModule(&rtp_2); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, RoutesBatchCompleteToActiveModules) { | 
 |   NiceMock<MockRtpRtcpInterface> rtp_1; | 
 |   NiceMock<MockRtpRtcpInterface> rtp_2; | 
 |   constexpr uint32_t kSsrc1 = 4711; | 
 |   constexpr uint32_t kSsrc2 = 1234; | 
 |   ON_CALL(rtp_1, SSRC).WillByDefault(Return(kSsrc1)); | 
 |   ON_CALL(rtp_2, SSRC).WillByDefault(Return(kSsrc2)); | 
 |   packet_router_.AddSendRtpModule(&rtp_1, false); | 
 |   packet_router_.AddSendRtpModule(&rtp_2, false); | 
 |   EXPECT_CALL(rtp_1, CanSendPacket).WillOnce(Return(true)); | 
 |   packet_router_.SendPacket(BuildRtpPacket(kSsrc1), PacedPacketInfo()); | 
 |   EXPECT_CALL(rtp_1, OnBatchComplete); | 
 |   EXPECT_CALL(rtp_2, OnBatchComplete).Times(0); | 
 |   packet_router_.OnBatchComplete(); | 
 |   packet_router_.RemoveSendRtpModule(&rtp_1); | 
 |   packet_router_.RemoveSendRtpModule(&rtp_2); | 
 | } | 
 |  | 
 | #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) | 
 | using PacketRouterDeathTest = PacketRouterTest; | 
 | TEST_F(PacketRouterDeathTest, DoubleRegistrationOfSendModuleDisallowed) { | 
 |   NiceMock<MockRtpRtcpInterface> module; | 
 |  | 
 |   constexpr bool remb_candidate = false;  // Value irrelevant. | 
 |   packet_router_.AddSendRtpModule(&module, remb_candidate); | 
 |   EXPECT_DEATH(packet_router_.AddSendRtpModule(&module, remb_candidate), ""); | 
 |  | 
 |   // Test tear-down | 
 |   packet_router_.RemoveSendRtpModule(&module); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterDeathTest, DoubleRegistrationOfReceiveModuleDisallowed) { | 
 |   NiceMock<MockRtpRtcpInterface> module; | 
 |  | 
 |   constexpr bool remb_candidate = false;  // Value irrelevant. | 
 |   packet_router_.AddReceiveRtpModule(&module, remb_candidate); | 
 |   EXPECT_DEATH(packet_router_.AddReceiveRtpModule(&module, remb_candidate), ""); | 
 |  | 
 |   // Test tear-down | 
 |   packet_router_.RemoveReceiveRtpModule(&module); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterDeathTest, RemovalOfNeverAddedReceiveModuleDisallowed) { | 
 |   NiceMock<MockRtpRtcpInterface> module; | 
 |  | 
 |   EXPECT_DEATH(packet_router_.RemoveReceiveRtpModule(&module), ""); | 
 | } | 
 | #endif  // RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) | 
 |  | 
 | TEST_F(PacketRouterTest, RemovalOfNeverAddedSendModuleIgnored) { | 
 |   NiceMock<MockRtpRtcpInterface> module; | 
 |   packet_router_.RemoveSendRtpModule(&module); | 
 | } | 
 |  | 
 | TEST_F(PacketRouterTest, DuplicateRemovalOfSendModuleIgnored) { | 
 |   NiceMock<MockRtpRtcpInterface> module; | 
 |   packet_router_.AddSendRtpModule(&module, false); | 
 |   packet_router_.RemoveSendRtpModule(&module); | 
 |   packet_router_.RemoveSendRtpModule(&module); | 
 | } | 
 |  | 
 | TEST(PacketRouterRembTest, ChangeSendRtpModuleChangeRembSender) { | 
 |   ScopedFakeClock clock; | 
 |   NiceMock<MockRtpRtcpInterface> rtp_send; | 
 |   NiceMock<MockRtpRtcpInterface> rtp_recv; | 
 |   PacketRouter packet_router; | 
 |   packet_router.AddSendRtpModule(&rtp_send, true); | 
 |   packet_router.AddReceiveRtpModule(&rtp_recv, true); | 
 |  | 
 |   uint32_t bitrate_estimate = 456; | 
 |   std::vector<uint32_t> ssrcs = {1234, 5678}; | 
 |  | 
 |   EXPECT_CALL(rtp_send, SetRemb(bitrate_estimate, ssrcs)); | 
 |   packet_router.SendRemb(bitrate_estimate, ssrcs); | 
 |  | 
 |   // Remove the sending module -> should get remb on the second module. | 
 |   packet_router.RemoveSendRtpModule(&rtp_send); | 
 |  | 
 |   EXPECT_CALL(rtp_recv, SetRemb(bitrate_estimate, ssrcs)); | 
 |   packet_router.SendRemb(bitrate_estimate, ssrcs); | 
 |  | 
 |   packet_router.RemoveReceiveRtpModule(&rtp_recv); | 
 | } | 
 |  | 
 | // Only register receiving modules and make sure we fallback to trigger a REMB | 
 | // packet on this one. | 
 | TEST(PacketRouterRembTest, NoSendingRtpModule) { | 
 |   ScopedFakeClock clock; | 
 |   NiceMock<MockRtpRtcpInterface> rtp; | 
 |   PacketRouter packet_router; | 
 |  | 
 |   packet_router.AddReceiveRtpModule(&rtp, true); | 
 |  | 
 |   uint32_t bitrate_estimate = 456; | 
 |   const std::vector<uint32_t> ssrcs = {1234}; | 
 |  | 
 |   EXPECT_CALL(rtp, SetRemb(bitrate_estimate, ssrcs)); | 
 |   packet_router.SendRemb(bitrate_estimate, ssrcs); | 
 |  | 
 |   // Lower the estimate to trigger a new packet REMB packet. | 
 |   EXPECT_CALL(rtp, SetRemb(bitrate_estimate, ssrcs)); | 
 |   packet_router.SendRemb(bitrate_estimate, ssrcs); | 
 |  | 
 |   EXPECT_CALL(rtp, UnsetRemb()); | 
 |   packet_router.RemoveReceiveRtpModule(&rtp); | 
 | } | 
 |  | 
 | TEST(PacketRouterRembTest, NonCandidateSendRtpModuleNotUsedForRemb) { | 
 |   ScopedFakeClock clock; | 
 |   PacketRouter packet_router; | 
 |   NiceMock<MockRtpRtcpInterface> module; | 
 |  | 
 |   constexpr bool remb_candidate = false; | 
 |  | 
 |   packet_router.AddSendRtpModule(&module, remb_candidate); | 
 |  | 
 |   constexpr uint32_t bitrate_estimate = 456; | 
 |   const std::vector<uint32_t> ssrcs = {1234}; | 
 |   EXPECT_CALL(module, SetRemb(_, _)).Times(0); | 
 |   packet_router.SendRemb(bitrate_estimate, ssrcs); | 
 |  | 
 |   // Test tear-down | 
 |   packet_router.RemoveSendRtpModule(&module); | 
 | } | 
 |  | 
 | TEST(PacketRouterRembTest, CandidateSendRtpModuleUsedForRemb) { | 
 |   ScopedFakeClock clock; | 
 |   PacketRouter packet_router; | 
 |   NiceMock<MockRtpRtcpInterface> module; | 
 |  | 
 |   constexpr bool remb_candidate = true; | 
 |  | 
 |   packet_router.AddSendRtpModule(&module, remb_candidate); | 
 |  | 
 |   constexpr uint32_t bitrate_estimate = 456; | 
 |   const std::vector<uint32_t> ssrcs = {1234}; | 
 |   EXPECT_CALL(module, SetRemb(bitrate_estimate, ssrcs)); | 
 |   packet_router.SendRemb(bitrate_estimate, ssrcs); | 
 |  | 
 |   // Test tear-down | 
 |   packet_router.RemoveSendRtpModule(&module); | 
 | } | 
 |  | 
 | TEST(PacketRouterRembTest, NonCandidateReceiveRtpModuleNotUsedForRemb) { | 
 |   ScopedFakeClock clock; | 
 |   PacketRouter packet_router; | 
 |   NiceMock<MockRtpRtcpInterface> module; | 
 |  | 
 |   constexpr bool remb_candidate = false; | 
 |  | 
 |   packet_router.AddReceiveRtpModule(&module, remb_candidate); | 
 |  | 
 |   constexpr uint32_t bitrate_estimate = 456; | 
 |   const std::vector<uint32_t> ssrcs = {1234}; | 
 |   EXPECT_CALL(module, SetRemb(_, _)).Times(0); | 
 |   packet_router.SendRemb(bitrate_estimate, ssrcs); | 
 |  | 
 |   // Test tear-down | 
 |   packet_router.RemoveReceiveRtpModule(&module); | 
 | } | 
 |  | 
 | TEST(PacketRouterRembTest, CandidateReceiveRtpModuleUsedForRemb) { | 
 |   ScopedFakeClock clock; | 
 |   PacketRouter packet_router; | 
 |   NiceMock<MockRtpRtcpInterface> module; | 
 |  | 
 |   constexpr bool remb_candidate = true; | 
 |  | 
 |   packet_router.AddReceiveRtpModule(&module, remb_candidate); | 
 |  | 
 |   constexpr uint32_t bitrate_estimate = 456; | 
 |   const std::vector<uint32_t> ssrcs = {1234}; | 
 |   EXPECT_CALL(module, SetRemb(bitrate_estimate, ssrcs)); | 
 |   packet_router.SendRemb(bitrate_estimate, ssrcs); | 
 |  | 
 |   // Test tear-down | 
 |   packet_router.RemoveReceiveRtpModule(&module); | 
 | } | 
 |  | 
 | TEST(PacketRouterRembTest, | 
 |      SendCandidatePreferredOverReceiveCandidate_SendModuleAddedFirst) { | 
 |   ScopedFakeClock clock; | 
 |   PacketRouter packet_router; | 
 |   NiceMock<MockRtpRtcpInterface> send_module; | 
 |   NiceMock<MockRtpRtcpInterface> receive_module; | 
 |  | 
 |   constexpr bool remb_candidate = true; | 
 |  | 
 |   // Send module added - activated. | 
 |   packet_router.AddSendRtpModule(&send_module, remb_candidate); | 
 |  | 
 |   // Receive module added - the send module remains the active one. | 
 |   packet_router.AddReceiveRtpModule(&receive_module, remb_candidate); | 
 |  | 
 |   constexpr uint32_t bitrate_estimate = 456; | 
 |   const std::vector<uint32_t> ssrcs = {1234}; | 
 |   EXPECT_CALL(send_module, SetRemb(bitrate_estimate, ssrcs)); | 
 |   EXPECT_CALL(receive_module, SetRemb(_, _)).Times(0); | 
 |  | 
 |   packet_router.SendRemb(bitrate_estimate, ssrcs); | 
 |  | 
 |   // Test tear-down | 
 |   packet_router.RemoveReceiveRtpModule(&receive_module); | 
 |   packet_router.RemoveSendRtpModule(&send_module); | 
 | } | 
 |  | 
 | TEST(PacketRouterRembTest, | 
 |      SendCandidatePreferredOverReceiveCandidate_ReceiveModuleAddedFirst) { | 
 |   ScopedFakeClock clock; | 
 |   PacketRouter packet_router; | 
 |   NiceMock<MockRtpRtcpInterface> send_module; | 
 |   NiceMock<MockRtpRtcpInterface> receive_module; | 
 |  | 
 |   constexpr bool remb_candidate = true; | 
 |  | 
 |   // Receive module added - activated. | 
 |   packet_router.AddReceiveRtpModule(&receive_module, remb_candidate); | 
 |  | 
 |   // Send module added - replaces receive module as active. | 
 |   packet_router.AddSendRtpModule(&send_module, remb_candidate); | 
 |  | 
 |   constexpr uint32_t bitrate_estimate = 456; | 
 |   const std::vector<uint32_t> ssrcs = {1234}; | 
 |   EXPECT_CALL(send_module, SetRemb(bitrate_estimate, ssrcs)); | 
 |   EXPECT_CALL(receive_module, SetRemb(_, _)).Times(0); | 
 |  | 
 |   clock.AdvanceTime(TimeDelta::Millis(1000)); | 
 |   packet_router.SendRemb(bitrate_estimate, ssrcs); | 
 |  | 
 |   // Test tear-down | 
 |   packet_router.RemoveReceiveRtpModule(&receive_module); | 
 |   packet_router.RemoveSendRtpModule(&send_module); | 
 | } | 
 |  | 
 | TEST(PacketRouterRembTest, ReceiveModuleTakesOverWhenLastSendModuleRemoved) { | 
 |   ScopedFakeClock clock; | 
 |   PacketRouter packet_router; | 
 |   NiceMock<MockRtpRtcpInterface> send_module; | 
 |   NiceMock<MockRtpRtcpInterface> receive_module; | 
 |  | 
 |   constexpr bool remb_candidate = true; | 
 |  | 
 |   // Send module active, receive module inactive. | 
 |   packet_router.AddSendRtpModule(&send_module, remb_candidate); | 
 |   packet_router.AddReceiveRtpModule(&receive_module, remb_candidate); | 
 |  | 
 |   // Send module removed - receive module becomes active. | 
 |   packet_router.RemoveSendRtpModule(&send_module); | 
 |   constexpr uint32_t bitrate_estimate = 456; | 
 |   const std::vector<uint32_t> ssrcs = {1234}; | 
 |   EXPECT_CALL(send_module, SetRemb(_, _)).Times(0); | 
 |   EXPECT_CALL(receive_module, SetRemb(bitrate_estimate, ssrcs)); | 
 |   packet_router.SendRemb(bitrate_estimate, ssrcs); | 
 |  | 
 |   // Test tear-down | 
 |   packet_router.RemoveReceiveRtpModule(&receive_module); | 
 | } | 
 |  | 
 | }  // namespace webrtc |