diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn index b738624d6e..b70ad72796 100644 --- a/modules/rtp_rtcp/BUILD.gn +++ b/modules/rtp_rtcp/BUILD.gn @@ -206,6 +206,7 @@ rtc_static_library("rtp_rtcp") { "..:module_fec_api", "../..:webrtc_common", "../../api:array_view", + "../../api:function_view", "../../api:libjingle_peerconnection_api", "../../api:rtp_headers", "../../api:scoped_refptr", diff --git a/modules/rtp_rtcp/source/rtp_packet_history.cc b/modules/rtp_rtcp/source/rtp_packet_history.cc index 0930b826be..53361811f8 100644 --- a/modules/rtp_rtcp/source/rtp_packet_history.cc +++ b/modules/rtp_rtcp/source/rtp_packet_history.cc @@ -205,6 +205,71 @@ std::unique_ptr RtpPacketHistory::GetPacketAndSetSendTime( return absl::make_unique(*packet.packet_); } +std::unique_ptr RtpPacketHistory::GetPacketAndMarkAsPending( + uint16_t sequence_number) { + return GetPacketAndMarkAsPending( + sequence_number, [](const RtpPacketToSend& packet) { + return absl::make_unique(packet); + }); +} + +std::unique_ptr RtpPacketHistory::GetPacketAndMarkAsPending( + uint16_t sequence_number, + rtc::FunctionView(const RtpPacketToSend&)> + encapsulate) { + rtc::CritScope cs(&lock_); + if (mode_ == StorageMode::kDisabled) { + return nullptr; + } + + int64_t now_ms = clock_->TimeInMilliseconds(); + StoredPacketIterator rtp_it = packet_history_.find(sequence_number); + if (rtp_it == packet_history_.end()) { + return nullptr; + } + + StoredPacket& packet = rtp_it->second; + RTC_DCHECK(packet.storage_type() != StorageType::kDontRetransmit); + + if (packet.pending_transmission_) { + // Packet already in pacer queue, ignore this request. + return nullptr; + } + + if (!VerifyRtt(rtp_it->second, now_ms)) { + // Packet already resent within too short a time window, ignore. + return nullptr; + } + + packet.pending_transmission_ = true; + + // Copy and/or encapsulate packet. + return encapsulate(*packet.packet_); +} + +void RtpPacketHistory::MarkPacketAsSent(uint16_t sequence_number) { + rtc::CritScope cs(&lock_); + if (mode_ == StorageMode::kDisabled) { + return; + } + + int64_t now_ms = clock_->TimeInMilliseconds(); + StoredPacketIterator rtp_it = packet_history_.find(sequence_number); + if (rtp_it == packet_history_.end()) { + return; + } + + StoredPacket& packet = rtp_it->second; + RTC_CHECK(packet.storage_type() != StorageType::kDontRetransmit); + RTC_DCHECK(packet.send_time_ms_); + + // Update send-time, mark as no longer in pacer queue, and increment + // transmission count. + packet.send_time_ms_ = now_ms; + packet.pending_transmission_ = false; + packet.IncrementTimesRetransmitted(&padding_priority_); +} + absl::optional RtpPacketHistory::GetPacketState( uint16_t sequence_number) const { rtc::CritScope cs(&lock_); diff --git a/modules/rtp_rtcp/source/rtp_packet_history.h b/modules/rtp_rtcp/source/rtp_packet_history.h index 0246e8c056..44ebf4325f 100644 --- a/modules/rtp_rtcp/source/rtp_packet_history.h +++ b/modules/rtp_rtcp/source/rtp_packet_history.h @@ -16,6 +16,7 @@ #include #include +#include "api/function_view.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "rtc_base/constructor_magic.h" #include "rtc_base/critical_section.h" @@ -82,6 +83,25 @@ class RtpPacketHistory { std::unique_ptr GetPacketAndSetSendTime( uint16_t sequence_number); + // Gets stored RTP packet corresponding to the input |sequence number|. + // Returns nullptr if packet is not found or was (re)sent too recently. + // If a packet copy is returned, it will be marked as pending transmission but + // does not update send time, that must be done by MarkPacketAsSent(). + std::unique_ptr GetPacketAndMarkAsPending( + uint16_t sequence_number); + + // In addition to getting packet and marking as sent, this method takes an + // encapsulator function that takes a reference to the packet and outputs a + // copy that may be wrapped in a container, eg RTX. + std::unique_ptr GetPacketAndMarkAsPending( + uint16_t sequence_number, + rtc::FunctionView( + const RtpPacketToSend&)> encapsulate); + + // Updates the send time for the given packet and increments the transmission + // counter. Marks the packet as no longer being in the pacer queue. + void MarkPacketAsSent(uint16_t sequence_number); + // Similar to GetPacketAndSetSendTime(), but only returns a snapshot of the // current state for packet, and never updates internal state. absl::optional GetPacketState(uint16_t sequence_number) const; diff --git a/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc b/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc index 5074ac6f3c..6a577fa185 100644 --- a/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc @@ -13,6 +13,7 @@ #include #include +#include "absl/memory/memory.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "system_wrappers/include/clock.h" @@ -678,6 +679,61 @@ TEST_F(RtpPacketHistoryTest, SetsPendingTransmissionState) { EXPECT_FALSE(packet_state->pending_transmission); } +TEST_F(RtpPacketHistoryTest, GetPacketAndSetSent) { + const int64_t kRttMs = RtpPacketHistory::kMinPacketDurationMs * 2; + hist_.SetRtt(kRttMs); + + // Set size to remove old packets as soon as possible. + hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); + + // Add a sent packet to the history. + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), kAllowRetransmission, + fake_clock_.TimeInMicroseconds()); + + // Retransmission request, first retransmission is allowed immediately. + EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); + + // Packet not yet sent, new retransmission not allowed. + fake_clock_.AdvanceTimeMilliseconds(kRttMs); + EXPECT_FALSE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); + + // Mark as sent, but too early for retransmission. + hist_.MarkPacketAsSent(kStartSeqNum); + EXPECT_FALSE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); + + // Enough time has passed, retransmission is allowed again. + fake_clock_.AdvanceTimeMilliseconds(kRttMs); + EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); +} + +TEST_F(RtpPacketHistoryTest, GetPacketWithEncapsulation) { + const uint32_t kSsrc = 92384762; + const int64_t kRttMs = RtpPacketHistory::kMinPacketDurationMs * 2; + hist_.SetRtt(kRttMs); + + // Set size to remove old packets as soon as possible. + hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); + + // Add a sent packet to the history, with a set SSRC. + std::unique_ptr packet = CreateRtpPacket(kStartSeqNum); + packet->SetSsrc(kSsrc); + hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, + fake_clock_.TimeInMicroseconds()); + + // Retransmission request, simulate an RTX-like encapsulation, were the packet + // is sent on a different SSRC. + std::unique_ptr retransmit_packet = + hist_.GetPacketAndMarkAsPending( + kStartSeqNum, [](const RtpPacketToSend& packet) { + auto encapsulated_packet = + absl::make_unique(packet); + encapsulated_packet->SetSsrc(packet.Ssrc() + 1); + return encapsulated_packet; + }); + ASSERT_TRUE(retransmit_packet); + EXPECT_EQ(retransmit_packet->Ssrc(), kSsrc + 1); +} + TEST_F(RtpPacketHistoryTest, DontRemovePendingTransmissions) { const int64_t kRttMs = RtpPacketHistory::kMinPacketDurationMs * 2; const int64_t kPacketTimeoutMs =