webrtc_m130/modules/pacing/packet_router_unittest.cc
Per Kjellander 776866774f Propagate desicion if RTP packet should be ECT(1) marked to socket
With this CL, the decision if an RTP packet should be sent as ect(1) is made in RtpControllerSend depending on if RFC 8888 has been negotiated and if CCFB is received with ECN enabled.
Since webrtc does not yet adapt to ECN feedback, packets are sent as ECT(1) until the first feedback is received.

Change-Id: Iddf63849328afbe54a7c8f921f2e8db134aeff6a
Bug: webrtc:42225697
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/367388
Commit-Queue: Per Kjellander <perkj@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43609}
2024-12-19 01:59:49 -08:00

873 lines
32 KiB
C++

/*
* 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) {
rtc::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) {
rtc::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) {
rtc::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) {
rtc::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) {
rtc::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) {
rtc::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) {
rtc::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) {
rtc::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) {
rtc::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