diff --git a/modules/rtp_rtcp/include/rtp_rtcp.h b/modules/rtp_rtcp/include/rtp_rtcp.h index 203871b319..6734e6c04d 100644 --- a/modules/rtp_rtcp/include/rtp_rtcp.h +++ b/modules/rtp_rtcp/include/rtp_rtcp.h @@ -268,6 +268,12 @@ class RtpRtcp : public Module, public RtcpFeedbackSenderInterface { bool retransmission, const PacedPacketInfo& pacing_info) = 0; + // Try to send the provided packet. Returns true iff packet matches any of + // the SSRCs for this module (media/rtx/fec etc) and was forwarded to the + // transport. + virtual bool TrySendPacket(RtpPacketToSend* packet, + const PacedPacketInfo& pacing_info) = 0; + virtual size_t TimeToSendPadding(size_t bytes, const PacedPacketInfo& pacing_info) = 0; diff --git a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h index 0514507dbc..2791d0e070 100644 --- a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h +++ b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h @@ -11,6 +11,7 @@ #ifndef MODULES_RTP_RTCP_MOCKS_MOCK_RTP_RTCP_H_ #define MODULES_RTP_RTCP_MOCKS_MOCK_RTP_RTCP_H_ +#include #include #include #include @@ -22,6 +23,7 @@ #include "modules/rtp_rtcp/include/rtp_rtcp.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" +#include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "rtc_base/checks.h" #include "test/gmock.h" @@ -88,6 +90,9 @@ class MockRtpRtcp : public RtpRtcp { int64_t capture_time_ms, bool retransmission, const PacedPacketInfo& pacing_info)); + MOCK_METHOD2(TrySendPacket, + bool(RtpPacketToSend* packet, + const PacedPacketInfo& pacing_info)); MOCK_METHOD2(TimeToSendPadding, size_t(size_t bytes, const PacedPacketInfo& pacing_info)); MOCK_METHOD2(RegisterRtcpObservers, diff --git a/modules/rtp_rtcp/source/rtp_packet_to_send.h b/modules/rtp_rtcp/source/rtp_packet_to_send.h index 89612841e7..1cbe7dedcc 100644 --- a/modules/rtp_rtcp/source/rtp_packet_to_send.h +++ b/modules/rtp_rtcp/source/rtp_packet_to_send.h @@ -50,6 +50,21 @@ class RtpPacketToSend : public RtpPacket { void set_packet_type(Type type) { packet_type_ = type; } absl::optional packet_type() const { return packet_type_; } + // If this is a retransmission, indicates the sequence number of the original + // media packet that this packet represents. If RTX is used this will likely + // be different from SequenceNumber(). + void set_retransmitted_sequence_number(uint16_t sequence_number) { + retransmitted_sequence_number_ = sequence_number; + } + absl::optional retransmitted_sequence_number() { + return retransmitted_sequence_number_; + } + + void set_allow_retransmission(bool allow_retransmission) { + allow_retransmission_ = allow_retransmission; + } + bool allow_retransmission() { return allow_retransmission_; } + // Additional data bound to the RTP packet for use in application code, // outside of WebRTC. rtc::ArrayView application_data() const { @@ -87,6 +102,8 @@ class RtpPacketToSend : public RtpPacket { private: int64_t capture_time_ms_ = 0; absl::optional packet_type_; + bool allow_retransmission_ = false; + absl::optional retransmitted_sequence_number_; std::vector application_data_; }; diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc index 09e8c523c4..900599b63c 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc +++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc @@ -416,6 +416,11 @@ RtpPacketSendResult ModuleRtpRtcpImpl::TimeToSendPacket( retransmission, pacing_info); } +bool ModuleRtpRtcpImpl::TrySendPacket(RtpPacketToSend* packet, + const PacedPacketInfo& pacing_info) { + return rtp_sender_->TrySendPacket(packet, pacing_info); +} + size_t ModuleRtpRtcpImpl::TimeToSendPadding( size_t bytes, const PacedPacketInfo& pacing_info) { diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/modules/rtp_rtcp/source/rtp_rtcp_impl.h index 817ec7ae14..38e1ede8d9 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl.h +++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.h @@ -29,6 +29,7 @@ #include "modules/rtp_rtcp/source/rtcp_packet/tmmb_item.h" #include "modules/rtp_rtcp/source/rtcp_receiver.h" #include "modules/rtp_rtcp/source/rtcp_sender.h" +#include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "modules/rtp_rtcp/source/rtp_sender.h" #include "rtc_base/critical_section.h" #include "rtc_base/gtest_prod_util.h" @@ -139,6 +140,9 @@ class ModuleRtpRtcpImpl : public RtpRtcp, public RTCPReceiver::ModuleRtpRtcp { bool retransmission, const PacedPacketInfo& pacing_info) override; + bool TrySendPacket(RtpPacketToSend* packet, + const PacedPacketInfo& pacing_info) override; + // Returns the number of padding bytes actually sent, which can be more or // less than |bytes|. size_t TimeToSendPadding(size_t bytes, diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc index b7838592df..2f23ddd8c0 100644 --- a/modules/rtp_rtcp/source/rtp_sender.cc +++ b/modules/rtp_rtcp/source/rtp_sender.cc @@ -571,6 +571,119 @@ RtpPacketSendResult RTPSender::TimeToSendPacket( : RtpPacketSendResult::kTransportUnavailable; } +// Called from pacer when we can send the packet. +bool RTPSender::TrySendPacket(RtpPacketToSend* packet, + const PacedPacketInfo& pacing_info) { + RTC_DCHECK(packet); + + const uint32_t packet_ssrc = packet->Ssrc(); + const auto packet_type = packet->packet_type(); + RTC_DCHECK(packet_type.has_value()); + + PacketOptions options; + bool is_media = false; + bool is_rtx = false; + { + rtc::CritScope lock(&send_critsect_); + if (!sending_media_) { + return false; + } + + switch (*packet_type) { + case RtpPacketToSend::Type::kAudio: + case RtpPacketToSend::Type::kVideo: + if (packet_ssrc != ssrc_) { + return false; + } + is_media = true; + break; + case RtpPacketToSend::Type::kRetransmission: + case RtpPacketToSend::Type::kPadding: + // Both padding and retransmission must be on either the media or the + // RTX stream. + if (packet_ssrc == ssrc_rtx_) { + is_rtx = true; + } else if (packet_ssrc != ssrc_) { + return false; + } + break; + case RtpPacketToSend::Type::kForwardErrorCorrection: + // FlexFEC is on separate SSRC, ULPFEC uses media SSRC. + if (packet_ssrc != ssrc_ && packet_ssrc != flexfec_ssrc_) { + return false; + } + break; + } + + options.included_in_allocation = force_part_of_allocation_; + } + + // Bug webrtc:7859. While FEC is invoked from rtp_sender_video, and not after + // the pacer, these modifications of the header below are happening after the + // FEC protection packets are calculated. This will corrupt recovered packets + // at the same place. It's not an issue for extensions, which are present in + // all the packets (their content just may be incorrect on recovered packets). + // In case of VideoTimingExtension, since it's present not in every packet, + // data after rtp header may be corrupted if these packets are protected by + // the FEC. + int64_t now_ms = clock_->TimeInMilliseconds(); + int64_t diff_ms = now_ms - packet->capture_time_ms(); + packet->SetExtension(kTimestampTicksPerMs * diff_ms); + packet->SetExtension(AbsoluteSendTime::MsTo24Bits(now_ms)); + + if (packet->HasExtension()) { + if (populate_network2_timestamp_) { + packet->set_network2_time_ms(now_ms); + } else { + packet->set_pacer_exit_time_ms(now_ms); + } + } + + // Downstream code actually uses this flag to distinguish between media and + // everything else. + options.is_retransmit = !is_media; + if (auto packet_id = packet->GetExtension()) { + options.packet_id = *packet_id; + options.included_in_feedback = true; + options.included_in_allocation = true; + AddPacketToTransportFeedback(*packet_id, *packet, pacing_info); + } + + options.application_data.assign(packet->application_data().begin(), + packet->application_data().end()); + + if (packet->packet_type() != RtpPacketToSend::Type::kPadding && + packet->packet_type() != RtpPacketToSend::Type::kRetransmission) { + UpdateDelayStatistics(packet->capture_time_ms(), now_ms, packet_ssrc); + UpdateOnSendPacket(options.packet_id, packet->capture_time_ms(), + packet_ssrc); + } + + const bool send_success = SendPacketToNetwork(*packet, options, pacing_info); + + // Put packet in retransmission history or update pending status even if + // actual sending fails. + if (is_media && packet->allow_retransmission()) { + packet_history_.PutRtpPacket(absl::make_unique(*packet), + StorageType::kAllowRetransmission, now_ms); + } else if (packet->retransmitted_sequence_number()) { + packet_history_.MarkPacketAsSent(*packet->retransmitted_sequence_number()); + } + + if (send_success) { + UpdateRtpStats(*packet, is_rtx, + packet_type == RtpPacketToSend::Type::kRetransmission); + + rtc::CritScope lock(&send_critsect_); + media_has_been_sent_ = true; + } + + // Return true even if transport failed (will be handled by retransmissions + // instead in that case), so that PacketRouter does not have to iterate over + // all other RTP modules and fail to send there too. + return true; +} + bool RTPSender::PrepareAndSendPacket(std::unique_ptr packet, bool send_over_rtx, bool is_retransmit, @@ -632,7 +745,7 @@ bool RTPSender::PrepareAndSendPacket(std::unique_ptr packet, packet_to_send->application_data().end()); if (!is_retransmit && !send_over_rtx) { - UpdateDelayStatistics(packet->capture_time_ms(), now_ms); + UpdateDelayStatistics(packet->capture_time_ms(), now_ms, packet->Ssrc()); UpdateOnSendPacket(options.packet_id, packet->capture_time_ms(), packet->Ssrc()); } @@ -744,7 +857,7 @@ bool RTPSender::SendToNetwork(std::unique_ptr packet, options.application_data.assign(packet->application_data().begin(), packet->application_data().end()); - UpdateDelayStatistics(packet->capture_time_ms(), now_ms); + UpdateDelayStatistics(packet->capture_time_ms(), now_ms, packet->Ssrc()); UpdateOnSendPacket(options.packet_id, packet->capture_time_ms(), packet->Ssrc()); @@ -777,20 +890,15 @@ void RTPSender::RecomputeMaxSendDelay() { } } -void RTPSender::UpdateDelayStatistics(int64_t capture_time_ms, int64_t now_ms) { +void RTPSender::UpdateDelayStatistics(int64_t capture_time_ms, + int64_t now_ms, + uint32_t ssrc) { if (!send_side_delay_observer_ || capture_time_ms <= 0) return; - uint32_t ssrc; int avg_delay_ms = 0; int max_delay_ms = 0; uint64_t total_packet_send_delay_ms = 0; - { - rtc::CritScope lock(&send_critsect_); - if (!ssrc_) - return; - ssrc = *ssrc_; - } { rtc::CritScope cs(&statistics_crit_); // Compute the max and average of the recent capture-to-send delays. diff --git a/modules/rtp_rtcp/source/rtp_sender.h b/modules/rtp_rtcp/source/rtp_sender.h index 3933ba3cf7..94ef809962 100644 --- a/modules/rtp_rtcp/source/rtp_sender.h +++ b/modules/rtp_rtcp/source/rtp_sender.h @@ -110,6 +110,8 @@ class RTPSender { int64_t capture_time_ms, bool retransmission, const PacedPacketInfo& pacing_info); + bool TrySendPacket(RtpPacketToSend* packet, + const PacedPacketInfo& pacing_info); size_t TimeToSendPadding(size_t bytes, const PacedPacketInfo& pacing_info); // NACK. @@ -204,7 +206,9 @@ class RTPSender { const PacedPacketInfo& pacing_info); void RecomputeMaxSendDelay() RTC_EXCLUSIVE_LOCKS_REQUIRED(statistics_crit_); - void UpdateDelayStatistics(int64_t capture_time_ms, int64_t now_ms); + void UpdateDelayStatistics(int64_t capture_time_ms, + int64_t now_ms, + uint32_t ssrc); void UpdateOnSendPacket(int packet_id, int64_t capture_time_ms, uint32_t ssrc); diff --git a/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_unittest.cc index 944ec07937..c492b81f88 100644 --- a/modules/rtp_rtcp/source/rtp_sender_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_sender_unittest.cc @@ -60,6 +60,8 @@ const int kRtxPayload = 98; const uint32_t kTimestamp = 10; const uint16_t kSeqNum = 33; const uint32_t kSsrc = 725242; +const uint32_t kRtxSsrc = 12345; +const uint32_t kFlexFecSsrc = 45678; const uint16_t kTransportSequenceNumber = 0xaabbu; const uint64_t kStartTime = 123456789; const size_t kMaxPaddingSize = 224u; @@ -74,7 +76,9 @@ using ::testing::ElementsAre; using ::testing::ElementsAreArray; using ::testing::Field; using ::testing::Invoke; +using ::testing::NiceMock; using ::testing::SizeIs; +using ::testing::StrictMock; uint64_t ConvertMsToAbsSendTime(int64_t time_ms) { return (((time_ms << 18) + 500) / 1000) & 0x00ffffff; @@ -192,22 +196,22 @@ class RtpSenderTest : public ::testing::TestWithParam { void SetUpRtpSender(bool pacer, bool populate_network2) { rtp_sender_.reset(new RTPSender( false, &fake_clock_, &transport_, pacer ? &mock_paced_sender_ : nullptr, - absl::nullopt, &seq_num_allocator_, nullptr, nullptr, nullptr, + kFlexFecSsrc, &seq_num_allocator_, nullptr, nullptr, nullptr, &mock_rtc_event_log_, &send_packet_observer_, &retransmission_rate_limiter_, nullptr, populate_network2, nullptr, false, false, FieldTrialBasedConfig())); rtp_sender_->SetSequenceNumber(kSeqNum); rtp_sender_->SetTimestampOffset(0); rtp_sender_->SetSSRC(kSsrc); + rtp_sender_->SetRtxSsrc(kRtxSsrc); } SimulatedClock fake_clock_; - ::testing::NiceMock mock_rtc_event_log_; + NiceMock mock_rtc_event_log_; MockRtpPacketSender mock_paced_sender_; - ::testing::StrictMock - seq_num_allocator_; - ::testing::StrictMock send_packet_observer_; - ::testing::StrictMock feedback_observer_; + StrictMock seq_num_allocator_; + StrictMock send_packet_observer_; + StrictMock feedback_observer_; RateLimiter retransmission_rate_limiter_; std::unique_ptr rtp_sender_; LoopbackTransportTest transport_; @@ -378,7 +382,7 @@ TEST_P(RtpSenderTestWithoutPacer, AssignSequenceNumberSetPaddingTimestamps) { TEST_P(RtpSenderTestWithoutPacer, TransportFeedbackObserverGetsCorrectByteCount) { constexpr int kRtpOverheadBytesPerPacket = 12 + 8; - ::testing::NiceMock mock_overhead_observer; + NiceMock mock_overhead_observer; rtp_sender_.reset( new RTPSender(false, &fake_clock_, &transport_, nullptr, absl::nullopt, &seq_num_allocator_, &feedback_observer_, nullptr, nullptr, @@ -506,7 +510,7 @@ TEST_P(RtpSenderTestWithoutPacer, DoesnSetIncludedInAllocationByDefault) { } TEST_P(RtpSenderTestWithoutPacer, OnSendSideDelayUpdated) { - ::testing::StrictMock send_side_delay_observer_; + StrictMock send_side_delay_observer_; rtp_sender_.reset( new RTPSender(false, &fake_clock_, &transport_, nullptr, absl::nullopt, nullptr, nullptr, nullptr, &send_side_delay_observer_, @@ -1444,7 +1448,7 @@ TEST_P(RtpSenderTestWithoutPacer, RidIncludedOnRtxSentPackets) { rtp_sender_->SetSendingMediaStatus(true); rtp_sender_->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); - rtp_sender_->SetRtxSsrc(1234); + rtp_sender_->SetRtxSsrc(kRtxSsrc); rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload); rtp_sender_->SetStorePacketsStatus(true, 10); @@ -1723,7 +1727,7 @@ TEST_P(RtpSenderTestWithoutPacer, BytesReportedCorrectly) { // XXX const char* kPayloadName = "GENERIC"; const uint8_t kPayloadType = 127; rtp_sender_->SetSSRC(1234); - rtp_sender_->SetRtxSsrc(4321); + rtp_sender_->SetRtxSsrc(kRtxSsrc); rtp_sender_->SetRtxPayloadType(kPayloadType - 1, kPayloadType); rtp_sender_->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); @@ -1827,6 +1831,277 @@ TEST_P(RtpSenderTest, DoesNotUpdateOverheadOnEqualSize) { SendGenericPacket(); } +TEST_P(RtpSenderTest, TrySendPacketMatchesVideo) { + std::unique_ptr packet = + BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + packet->set_packet_type(RtpPacketToSend::Type::kVideo); + + // Verify not sent with wrong SSRC. + packet->SetSsrc(kSsrc + 1); + EXPECT_FALSE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); + + // Verify sent with correct SSRC. + packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + packet->SetSsrc(kSsrc); + packet->set_packet_type(RtpPacketToSend::Type::kVideo); + EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); +} + +TEST_P(RtpSenderTest, TrySendPacketMatchesAudio) { + std::unique_ptr packet = + BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + packet->set_packet_type(RtpPacketToSend::Type::kAudio); + + // Verify not sent with wrong SSRC. + packet->SetSsrc(kSsrc + 1); + EXPECT_FALSE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); + + // Verify sent with correct SSRC. + packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + packet->SetSsrc(kSsrc); + packet->set_packet_type(RtpPacketToSend::Type::kAudio); + EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); +} + +TEST_P(RtpSenderTest, TrySendPacketMatchesRetransmissions) { + std::unique_ptr packet = + BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + packet->set_packet_type(RtpPacketToSend::Type::kRetransmission); + + // Verify not sent with wrong SSRC. + packet->SetSsrc(kSsrc + 1); + EXPECT_FALSE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); + + // Verify sent with correct SSRC (non-RTX). + packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + packet->SetSsrc(kSsrc); + packet->set_packet_type(RtpPacketToSend::Type::kRetransmission); + EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); + + // RTX retransmission. + packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + packet->SetSsrc(kRtxSsrc); + packet->set_packet_type(RtpPacketToSend::Type::kRetransmission); + EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); +} + +TEST_P(RtpSenderTest, TrySendPacketMatchesPadding) { + std::unique_ptr packet = + BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + packet->set_packet_type(RtpPacketToSend::Type::kPadding); + + // Verify not sent with wrong SSRC. + packet->SetSsrc(kSsrc + 1); + EXPECT_FALSE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); + + // Verify sent with correct SSRC (non-RTX). + packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + packet->SetSsrc(kSsrc); + packet->set_packet_type(RtpPacketToSend::Type::kPadding); + EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); + + // RTX padding. + packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + packet->SetSsrc(kRtxSsrc); + packet->set_packet_type(RtpPacketToSend::Type::kPadding); + EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); +} + +TEST_P(RtpSenderTest, TrySendPacketMatchesFlexfec) { + std::unique_ptr packet = + BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + packet->set_packet_type(RtpPacketToSend::Type::kForwardErrorCorrection); + + // Verify not sent with wrong SSRC. + packet->SetSsrc(kSsrc + 1); + EXPECT_FALSE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); + + // Verify sent with correct SSRC. + packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + packet->SetSsrc(kFlexFecSsrc); + packet->set_packet_type(RtpPacketToSend::Type::kForwardErrorCorrection); + EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); +} + +TEST_P(RtpSenderTest, TrySendPacketMatchesUlpfec) { + std::unique_ptr packet = + BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + packet->set_packet_type(RtpPacketToSend::Type::kForwardErrorCorrection); + + // Verify not sent with wrong SSRC. + packet->SetSsrc(kSsrc + 1); + EXPECT_FALSE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); + + // Verify sent with correct SSRC. + packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + packet->SetSsrc(kSsrc); + packet->set_packet_type(RtpPacketToSend::Type::kForwardErrorCorrection); + EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); +} + +TEST_P(RtpSenderTest, TrySendPacketHandlesRetransmissionHistory) { + rtp_sender_->SetStorePacketsStatus(true, 10); + + // Build a media packet and send it. + std::unique_ptr packet = + BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + const uint16_t media_sequence_number = packet->SequenceNumber(); + packet->set_packet_type(RtpPacketToSend::Type::kVideo); + packet->set_allow_retransmission(true); + EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); + + // Simulate retransmission request. + fake_clock_.AdvanceTimeMilliseconds(30); + EXPECT_GT(rtp_sender_->ReSendPacket(media_sequence_number), 0); + + // Packet already pending, retransmission not allowed. + fake_clock_.AdvanceTimeMilliseconds(30); + EXPECT_EQ(rtp_sender_->ReSendPacket(media_sequence_number), 0); + + // Packet exiting pacer, mark as not longer pending. + packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + EXPECT_NE(packet->SequenceNumber(), media_sequence_number); + packet->set_packet_type(RtpPacketToSend::Type::kRetransmission); + packet->SetSsrc(kRtxSsrc); + packet->set_retransmitted_sequence_number(media_sequence_number); + packet->set_allow_retransmission(false); + EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); + + // Retransmissions allowed again. + fake_clock_.AdvanceTimeMilliseconds(30); + EXPECT_GT(rtp_sender_->ReSendPacket(media_sequence_number), 0); + + // Retransmission of RTX packet should not be allowed. + EXPECT_EQ(rtp_sender_->ReSendPacket(packet->SequenceNumber()), 0); +} + +TEST_P(RtpSenderTest, TrySendPacketUpdatesExtensions) { + ASSERT_EQ(rtp_sender_->RegisterRtpHeaderExtension( + kRtpExtensionTransmissionTimeOffset, + kTransmissionTimeOffsetExtensionId), + 0); + ASSERT_EQ(rtp_sender_->RegisterRtpHeaderExtension( + kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId), + 0); + ASSERT_EQ(rtp_sender_->RegisterRtpHeaderExtension(kRtpExtensionVideoTiming, + kVideoTimingExtensionId), + 0); + + std::unique_ptr packet = + BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + packet->set_packetization_finish_time_ms(fake_clock_.TimeInMilliseconds()); + + const int32_t kDiffMs = 10; + fake_clock_.AdvanceTimeMilliseconds(kDiffMs); + + packet->set_packet_type(RtpPacketToSend::Type::kVideo); + EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); + + const RtpPacketReceived& received_packet = transport_.last_sent_packet(); + + EXPECT_EQ(received_packet.GetExtension(), kDiffMs * 90); + + EXPECT_EQ(received_packet.GetExtension(), + AbsoluteSendTime::MsTo24Bits(fake_clock_.TimeInMilliseconds())); + + VideoSendTiming timing; + EXPECT_TRUE(received_packet.GetExtension(&timing)); + EXPECT_EQ(timing.pacer_exit_delta_ms, kDiffMs); +} + +TEST_P(RtpSenderTest, TrySendPacketSetsPacketOptions) { + const uint16_t kPacketId = 42; + ASSERT_EQ(rtp_sender_->RegisterRtpHeaderExtension( + kRtpExtensionTransportSequenceNumber, + kTransportSequenceNumberExtensionId), + 0); + std::unique_ptr packet = + BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + packet->SetExtension(kPacketId); + + packet->set_packet_type(RtpPacketToSend::Type::kVideo); + EXPECT_CALL(send_packet_observer_, OnSendPacket); + EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); + + EXPECT_EQ(transport_.last_options_.packet_id, kPacketId); + EXPECT_TRUE(transport_.last_options_.included_in_allocation); + EXPECT_TRUE(transport_.last_options_.included_in_feedback); + EXPECT_FALSE(transport_.last_options_.is_retransmit); + + // Send another packet as retransmission, verify options are populated. + packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + packet->SetExtension(kPacketId + 1); + packet->set_packet_type(RtpPacketToSend::Type::kRetransmission); + EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); + EXPECT_TRUE(transport_.last_options_.is_retransmit); +} + +TEST_P(RtpSenderTest, TrySendPacketUpdatesStats) { + const size_t kPayloadSize = 1000; + + StrictMock send_side_delay_observer; + rtp_sender_.reset(new RTPSender( + false, &fake_clock_, &transport_, nullptr, kFlexFecSsrc, nullptr, nullptr, + nullptr, &send_side_delay_observer, &mock_rtc_event_log_, + &send_packet_observer_, nullptr, nullptr, false, nullptr, false, false, + FieldTrialBasedConfig())); + rtp_sender_->SetSSRC(kSsrc); + rtp_sender_->SetRtxSsrc(kRtxSsrc); + ASSERT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( + kRtpExtensionTransportSequenceNumber, + kTransportSequenceNumberExtensionId)); + + const int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); + + std::unique_ptr video_packet = + BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + video_packet->set_packet_type(RtpPacketToSend::Type::kVideo); + video_packet->SetPayloadSize(kPayloadSize); + video_packet->SetExtension(1); + + std::unique_ptr rtx_packet = + BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + rtx_packet->SetSsrc(kRtxSsrc); + rtx_packet->set_packet_type(RtpPacketToSend::Type::kRetransmission); + rtx_packet->SetPayloadSize(kPayloadSize); + rtx_packet->SetExtension(2); + + std::unique_ptr fec_packet = + BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); + fec_packet->SetSsrc(kFlexFecSsrc); + fec_packet->set_packet_type(RtpPacketToSend::Type::kForwardErrorCorrection); + fec_packet->SetPayloadSize(kPayloadSize); + fec_packet->SetExtension(3); + + const int64_t kDiffMs = 25; + fake_clock_.AdvanceTimeMilliseconds(kDiffMs); + + EXPECT_CALL(send_side_delay_observer, + SendSideDelayUpdated(kDiffMs, kDiffMs, kDiffMs, kSsrc)); + EXPECT_CALL( + send_side_delay_observer, + SendSideDelayUpdated(kDiffMs, kDiffMs, 2 * kDiffMs, kFlexFecSsrc)); + + EXPECT_CALL(send_packet_observer_, OnSendPacket(1, capture_time_ms, kSsrc)); + EXPECT_TRUE( + rtp_sender_->TrySendPacket(video_packet.get(), PacedPacketInfo())); + + // Send packet observer not called for padding/retransmissions. + EXPECT_CALL(send_packet_observer_, OnSendPacket(2, _, _)).Times(0); + EXPECT_TRUE(rtp_sender_->TrySendPacket(rtx_packet.get(), PacedPacketInfo())); + + EXPECT_CALL(send_packet_observer_, + OnSendPacket(3, capture_time_ms, kFlexFecSsrc)); + EXPECT_TRUE(rtp_sender_->TrySendPacket(fec_packet.get(), PacedPacketInfo())); + + StreamDataCounters rtp_stats; + StreamDataCounters rtx_stats; + rtp_sender_->GetDataCounters(&rtp_stats, &rtx_stats); + EXPECT_EQ(rtp_stats.transmitted.packets, 2u); + EXPECT_EQ(rtp_stats.fec.packets, 1u); + EXPECT_EQ(rtx_stats.retransmitted.packets, 1u); +} + INSTANTIATE_TEST_SUITE_P(WithAndWithoutOverhead, RtpSenderTest, ::testing::Bool());