diff --git a/modules/pacing/pacing_controller.h b/modules/pacing/pacing_controller.h index 9899fcd3dd..988fe50536 100644 --- a/modules/pacing/pacing_controller.h +++ b/modules/pacing/pacing_controller.h @@ -52,6 +52,15 @@ class PacingController { virtual std::vector> FetchFec() = 0; virtual std::vector> GeneratePadding( DataSize size) = 0; + + // TODO(bugs.webrtc.org/11340): Make pure virtual once downstream projects + // have been updated. + virtual void OnAbortedRetransmissions( + uint32_t ssrc, + rtc::ArrayView sequence_numbers) {} + virtual absl::optional GetRtxSsrcForMedia(uint32_t ssrc) const { + return absl::nullopt; + } }; // Expected max pacer delay. If ExpectedQueueTime() is higher than diff --git a/modules/pacing/pacing_controller_unittest.cc b/modules/pacing/pacing_controller_unittest.cc index db6a22b814..37b8605e2e 100644 --- a/modules/pacing/pacing_controller_unittest.cc +++ b/modules/pacing/pacing_controller_unittest.cc @@ -128,6 +128,14 @@ class MockPacingControllerCallback : public PacingController::PacketSender { (), (override)); MOCK_METHOD(size_t, SendPadding, (size_t target_size)); + MOCK_METHOD(void, + OnAbortedRetransmissions, + (uint32_t, rtc::ArrayView), + (override)); + MOCK_METHOD(absl::optional, + GetRtxSsrcForMedia, + (uint32_t), + (const, override)); }; // Mock callback implementing the raw api. @@ -147,6 +155,14 @@ class MockPacketSender : public PacingController::PacketSender { GeneratePadding, (DataSize target_size), (override)); + MOCK_METHOD(void, + OnAbortedRetransmissions, + (uint32_t, rtc::ArrayView), + (override)); + MOCK_METHOD(absl::optional, + GetRtxSsrcForMedia, + (uint32_t), + (const, override)); }; class PacingControllerPadding : public PacingController::PacketSender { @@ -178,6 +194,12 @@ class PacingControllerPadding : public PacingController::PacketSender { return packets; } + void OnAbortedRetransmissions(uint32_t, + rtc::ArrayView) override {} + absl::optional GetRtxSsrcForMedia(uint32_t) const override { + return absl::nullopt; + } + size_t padding_sent() { return padding_sent_; } size_t total_bytes_sent() { return total_bytes_sent_; } @@ -220,6 +242,12 @@ class PacingControllerProbing : public PacingController::PacketSender { return packets; } + void OnAbortedRetransmissions(uint32_t, + rtc::ArrayView) override {} + absl::optional GetRtxSsrcForMedia(uint32_t) const override { + return absl::nullopt; + } + int packets_sent() const { return packets_sent_; } int padding_sent() const { return padding_sent_; } int total_packets_sent() const { return packets_sent_ + padding_sent_; } diff --git a/modules/pacing/packet_router.cc b/modules/pacing/packet_router.cc index a09f191bbd..b28d9776dc 100644 --- a/modules/pacing/packet_router.cc +++ b/modules/pacing/packet_router.cc @@ -86,10 +86,10 @@ void PacketRouter::AddSendRtpModuleToMap(RtpRtcpInterface* rtp_module, } void PacketRouter::RemoveSendRtpModuleFromMap(uint32_t ssrc) { - auto kv = send_modules_map_.find(ssrc); - RTC_DCHECK(kv != send_modules_map_.end()); - send_modules_list_.remove(kv->second); - send_modules_map_.erase(kv); + auto it = send_modules_map_.find(ssrc); + RTC_DCHECK(it != send_modules_map_.end()); + send_modules_list_.remove(it->second); + send_modules_map_.erase(it); } void PacketRouter::RemoveSendRtpModule(RtpRtcpInterface* rtp_module) { @@ -151,8 +151,8 @@ void PacketRouter::SendPacket(std::unique_ptr packet, } uint32_t ssrc = packet->Ssrc(); - auto kv = send_modules_map_.find(ssrc); - if (kv == send_modules_map_.end()) { + auto it = send_modules_map_.find(ssrc); + if (it == send_modules_map_.end()) { RTC_LOG(LS_WARNING) << "Failed to send packet, matching RTP module not found " "or transport error. SSRC = " @@ -160,7 +160,7 @@ void PacketRouter::SendPacket(std::unique_ptr packet, return; } - RtpRtcpInterface* rtp_module = kv->second; + RtpRtcpInterface* rtp_module = it->second; if (!rtp_module->TrySendPacket(packet.get(), cluster_info)) { RTC_LOG(LS_WARNING) << "Failed to send packet, rejected by RTP module."; return; @@ -235,6 +235,27 @@ std::vector> PacketRouter::GeneratePadding( return padding_packets; } +void PacketRouter::OnAbortedRetransmissions( + uint32_t ssrc, + rtc::ArrayView sequence_numbers) { + MutexLock lock(&modules_mutex_); + auto it = send_modules_map_.find(ssrc); + if (it != send_modules_map_.end()) { + it->second->OnAbortedRetransmissions(sequence_numbers); + } +} + +absl::optional PacketRouter::GetRtxSsrcForMedia(uint32_t ssrc) const { + MutexLock lock(&modules_mutex_); + auto it = send_modules_map_.find(ssrc); + if (it != send_modules_map_.end() && it->second->SSRC() == ssrc) { + // A module is registered with the given SSRC, and that SSRC is the main + // media SSRC for that RTP module. + return it->second->RtxSsrc(); + } + return absl::nullopt; +} + uint16_t PacketRouter::CurrentTransportSequenceNumber() const { MutexLock lock(&modules_mutex_); return transport_seq_ & 0xFFFF; diff --git a/modules/pacing/packet_router.h b/modules/pacing/packet_router.h index 11d8979052..68b82c6bd4 100644 --- a/modules/pacing/packet_router.h +++ b/modules/pacing/packet_router.h @@ -58,6 +58,10 @@ class PacketRouter : public PacingController::PacketSender { std::vector> FetchFec() override; std::vector> GeneratePadding( DataSize size) override; + void OnAbortedRetransmissions( + uint32_t ssrc, + rtc::ArrayView sequence_numbers) override; + absl::optional GetRtxSsrcForMedia(uint32_t ssrc) const override; uint16_t CurrentTransportSequenceNumber() const; diff --git a/modules/pacing/packet_router_unittest.cc b/modules/pacing/packet_router_unittest.cc index fc26922850..65b2ad24d6 100644 --- a/modules/pacing/packet_router_unittest.cc +++ b/modules/pacing/packet_router_unittest.cc @@ -36,6 +36,7 @@ namespace { using ::testing::_; using ::testing::AnyNumber; using ::testing::AtLeast; +using ::testing::ElementsAreArray; using ::testing::Field; using ::testing::Gt; using ::testing::Le; @@ -436,6 +437,68 @@ TEST_F(PacketRouterTest, DoesNotIncrementTransportSequenceNumberOnSendFailure) { packet_router_.RemoveSendRtpModule(&rtp); } +TEST_F(PacketRouterTest, ForwardsAbortedRetransmissions) { + NiceMock rtp_1; + NiceMock 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 rtp_1; + NiceMock 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), absl::nullopt); + EXPECT_EQ(packet_router_.GetRtxSsrcForMedia(kSsrc2), absl::nullopt); + EXPECT_EQ(packet_router_.GetRtxSsrcForMedia(kInvalidSsrc), absl::nullopt); + + 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) {