| /* |
| * Copyright (c) 2012 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/rtp_rtcp/source/rtp_sender.h" |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/strings/string_view.h" |
| #include "api/environment/environment.h" |
| #include "api/environment/environment_factory.h" |
| #include "api/rtc_event_log/rtc_event.h" |
| #include "api/rtp_packet_sender.h" |
| #include "api/units/frequency.h" |
| #include "api/units/time_delta.h" |
| #include "api/units/timestamp.h" |
| #include "api/video/video_codec_constants.h" |
| #include "api/video/video_timing.h" |
| #include "modules/rtp_rtcp/include/rtp_cvo.h" |
| #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" |
| #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" |
| #include "modules/rtp_rtcp/source/packet_sequencer.h" |
| #include "modules/rtp_rtcp/source/rtp_format_video_generic.h" |
| #include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h" |
| #include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h" |
| #include "modules/rtp_rtcp/source/rtp_header_extensions.h" |
| #include "modules/rtp_rtcp/source/rtp_packet_received.h" |
| #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" |
| #include "modules/rtp_rtcp/source/rtp_sender_video.h" |
| #include "modules/rtp_rtcp/source/video_fec_generator.h" |
| #include "rtc_base/arraysize.h" |
| #include "rtc_base/logging.h" |
| #include "rtc_base/rate_limiter.h" |
| #include "rtc_base/strings/string_builder.h" |
| #include "test/gmock.h" |
| #include "test/gtest.h" |
| #include "test/mock_transport.h" |
| #include "test/time_controller/simulated_time_controller.h" |
| |
| namespace webrtc { |
| |
| namespace { |
| enum : int { // The first valid value is 1. |
| kAbsoluteSendTimeExtensionId = 1, |
| kAudioLevelExtensionId, |
| kGenericDescriptorId, |
| kMidExtensionId, |
| kRepairedRidExtensionId, |
| kRidExtensionId, |
| kTransmissionTimeOffsetExtensionId, |
| kTransportSequenceNumberExtensionId, |
| kVideoRotationExtensionId, |
| kVideoTimingExtensionId, |
| }; |
| |
| const int kPayload = 100; |
| 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 uint64_t kStartTime = 123456789; |
| const uint8_t kPayloadData[] = {47, 11, 32, 93, 89}; |
| constexpr TimeDelta kDefaultExpectedRetransmissionTime = TimeDelta::Millis(125); |
| constexpr Frequency kRtpClockRate = Frequency::Hertz(90'000); |
| constexpr absl::string_view kMid = "mid"; |
| constexpr absl::string_view kRid = "f"; |
| constexpr bool kMarkerBit = true; |
| |
| using ::testing::_; |
| using ::testing::AllOf; |
| using ::testing::AtLeast; |
| using ::testing::Contains; |
| using ::testing::Each; |
| using ::testing::ElementsAre; |
| using ::testing::ElementsAreArray; |
| using ::testing::Eq; |
| using ::testing::Field; |
| using ::testing::Gt; |
| using ::testing::IsEmpty; |
| using ::testing::NiceMock; |
| using ::testing::Not; |
| using ::testing::Pointee; |
| using ::testing::Property; |
| using ::testing::Return; |
| using ::testing::SizeIs; |
| |
| class MockRtpPacketPacer : public RtpPacketSender { |
| public: |
| MockRtpPacketPacer() {} |
| virtual ~MockRtpPacketPacer() {} |
| |
| MOCK_METHOD(void, |
| EnqueuePackets, |
| (std::vector<std::unique_ptr<RtpPacketToSend>>), |
| (override)); |
| MOCK_METHOD(void, RemovePacketsForSsrc, (uint32_t), (override)); |
| }; |
| |
| uint32_t ToRtpTimestamp(Timestamp time) { |
| return static_cast<int64_t>((time - Timestamp::Zero()) * kRtpClockRate) & |
| 0xFFFF'FFFF; |
| } |
| |
| } // namespace |
| |
| class RtpSenderTest : public ::testing::Test { |
| protected: |
| RtpSenderTest() |
| : time_controller_(Timestamp::Millis(kStartTime)), |
| env_(CreateEnvironment(time_controller_.GetClock())), |
| retransmission_rate_limiter_(&env_.clock(), 1000), |
| flexfec_sender_(env_, |
| 0, |
| kFlexFecSsrc, |
| kSsrc, |
| "", |
| std::vector<RtpExtension>(), |
| std::vector<RtpExtensionSize>(), |
| nullptr) {} |
| |
| void SetUp() override { SetUpRtpSender(true, false, nullptr); } |
| |
| void SetUpRtpSender(bool populate_network2, |
| bool always_send_mid_and_rid, |
| VideoFecGenerator* fec_generator) { |
| RtpRtcpInterface::Configuration config = GetDefaultConfig(); |
| config.fec_generator = fec_generator; |
| config.populate_network2_timestamp = populate_network2; |
| config.always_send_mid_and_rid = always_send_mid_and_rid; |
| CreateSender(config); |
| } |
| |
| RtpRtcpInterface::Configuration GetDefaultConfig() { |
| RtpRtcpInterface::Configuration config; |
| config.local_media_ssrc = kSsrc; |
| config.rtx_send_ssrc = kRtxSsrc; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| config.paced_sender = &mock_paced_sender_; |
| // Configure rid unconditionally, it has effect only if |
| // corresponding header extension is enabled. |
| config.rid = std::string(kRid); |
| return config; |
| } |
| |
| void CreateSender(const RtpRtcpInterface::Configuration& config) { |
| packet_history_ = std::make_unique<RtpPacketHistory>( |
| env_, RtpPacketHistory::PaddingMode::kRecentLargePacket); |
| sequencer_.emplace(kSsrc, kRtxSsrc, |
| /*require_marker_before_media_padding=*/!config.audio, |
| &env_.clock()); |
| rtp_sender_ = std::make_unique<RTPSender>( |
| env_, config, packet_history_.get(), config.paced_sender); |
| sequencer_->set_media_sequence_number(kSeqNum); |
| rtp_sender_->SetTimestampOffset(0); |
| } |
| |
| GlobalSimulatedTimeController time_controller_; |
| const Environment env_; |
| MockRtpPacketPacer mock_paced_sender_; |
| RateLimiter retransmission_rate_limiter_; |
| FlexfecSender flexfec_sender_; |
| |
| std::optional<PacketSequencer> sequencer_; |
| std::unique_ptr<RtpPacketHistory> packet_history_; |
| std::unique_ptr<RTPSender> rtp_sender_; |
| |
| std::unique_ptr<RtpPacketToSend> BuildRtpPacket(int payload_type, |
| bool marker_bit, |
| uint32_t rtp_timestamp, |
| Timestamp capture_time) { |
| auto packet = rtp_sender_->AllocatePacket(); |
| packet->SetPayloadType(payload_type); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| packet->SetMarker(marker_bit); |
| packet->SetTimestamp(rtp_timestamp); |
| packet->set_capture_time(capture_time); |
| return packet; |
| } |
| |
| std::unique_ptr<RtpPacketToSend> SendPacket(Timestamp capture_time, |
| int payload_length) { |
| uint32_t rtp_timestamp = ToRtpTimestamp(capture_time); |
| auto packet = |
| BuildRtpPacket(kPayload, kMarkerBit, rtp_timestamp, capture_time); |
| packet->AllocatePayload(payload_length); |
| packet->set_allow_retransmission(true); |
| |
| // Packet should be stored in a send bucket. |
| std::vector<std::unique_ptr<RtpPacketToSend>> packets(1); |
| packets[0] = std::make_unique<RtpPacketToSend>(*packet); |
| rtp_sender_->EnqueuePackets(std::move(packets)); |
| return packet; |
| } |
| |
| std::unique_ptr<RtpPacketToSend> SendGenericPacket() { |
| // Use maximum allowed size to catch corner cases when packet is dropped |
| // because of lack of capacity for the media packet, or for an rtx packet |
| // containing the media packet. |
| return SendPacket(/*capture_time=*/env_.clock().CurrentTime(), |
| /*payload_length=*/rtp_sender_->MaxRtpPacketSize() - |
| rtp_sender_->ExpectedPerPacketOverhead()); |
| } |
| |
| std::vector<std::unique_ptr<RtpPacketToSend>> GeneratePadding( |
| size_t target_size_bytes) { |
| return rtp_sender_->GeneratePadding( |
| target_size_bytes, /*media_has_been_sent=*/true, |
| sequencer_->CanSendPaddingOnMediaSsrc()); |
| } |
| |
| std::vector<std::unique_ptr<RtpPacketToSend>> Sequence( |
| std::vector<std::unique_ptr<RtpPacketToSend>> packets) { |
| for (auto& packet : packets) { |
| sequencer_->Sequence(*packet); |
| } |
| return packets; |
| } |
| |
| size_t GenerateAndSendPadding(size_t target_size_bytes) { |
| size_t generated_bytes = 0; |
| std::vector<std::unique_ptr<RtpPacketToSend>> packets; |
| for (auto& packet : GeneratePadding(target_size_bytes)) { |
| generated_bytes += packet->payload_size() + packet->padding_size(); |
| packets.push_back(std::move(packet)); |
| } |
| rtp_sender_->EnqueuePackets(std::move(packets)); |
| return generated_bytes; |
| } |
| |
| // The following are helpers for configuring the RTPSender. They must be |
| // called before sending any packets. |
| |
| // Enable the retransmission stream with sizable packet storage. |
| void EnableRtx() { |
| // RTX needs to be able to read the source packets from the packet store. |
| // Pick a number of packets to store big enough for any unit test. |
| constexpr uint16_t kNumberOfPacketsToStore = 100; |
| packet_history_->SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, kNumberOfPacketsToStore); |
| rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload); |
| rtp_sender_->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); |
| } |
| |
| // Enable sending of the MID header extension for both the primary SSRC and |
| // the RTX SSRC. |
| void EnableMidSending(absl::string_view mid) { |
| rtp_sender_->RegisterRtpHeaderExtension(RtpMid::Uri(), kMidExtensionId); |
| rtp_sender_->SetMid(mid); |
| } |
| |
| // Enable sending of the RSID header extension for the primary SSRC and the |
| // RRSID header extension for the RTX SSRC. |
| void EnableRidSending() { |
| rtp_sender_->RegisterRtpHeaderExtension(RtpStreamId::Uri(), |
| kRidExtensionId); |
| rtp_sender_->RegisterRtpHeaderExtension(RepairedRtpStreamId::Uri(), |
| kRepairedRidExtensionId); |
| } |
| }; |
| |
| TEST_F(RtpSenderTest, AllocatePacketSetCsrcs) { |
| // Configure rtp_sender with csrc. |
| uint32_t csrcs[] = {0x23456789}; |
| |
| auto packet = rtp_sender_->AllocatePacket(csrcs); |
| |
| ASSERT_TRUE(packet); |
| EXPECT_EQ(rtp_sender_->SSRC(), packet->Ssrc()); |
| EXPECT_THAT(packet->Csrcs(), ElementsAreArray(csrcs)); |
| } |
| |
| TEST_F(RtpSenderTest, AllocatePacketReserveExtensions) { |
| // Configure rtp_sender with extensions. |
| ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( |
| TransmissionOffset::Uri(), kTransmissionTimeOffsetExtensionId)); |
| ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( |
| AbsoluteSendTime::Uri(), kAbsoluteSendTimeExtensionId)); |
| ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( |
| AudioLevelExtension::Uri(), kAudioLevelExtensionId)); |
| ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( |
| TransportSequenceNumber::Uri(), kTransportSequenceNumberExtensionId)); |
| ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( |
| VideoOrientation::Uri(), kVideoRotationExtensionId)); |
| |
| auto packet = rtp_sender_->AllocatePacket(); |
| |
| ASSERT_TRUE(packet); |
| // Preallocate BWE extensions RtpSender set itself. |
| EXPECT_TRUE(packet->HasExtension<TransmissionOffset>()); |
| EXPECT_TRUE(packet->HasExtension<AbsoluteSendTime>()); |
| EXPECT_TRUE(packet->HasExtension<TransportSequenceNumber>()); |
| // Do not allocate media specific extensions. |
| EXPECT_FALSE(packet->HasExtension<AudioLevelExtension>()); |
| EXPECT_FALSE(packet->HasExtension<VideoOrientation>()); |
| } |
| |
| TEST_F(RtpSenderTest, PaddingAlwaysAllowedOnAudio) { |
| RtpRtcpInterface::Configuration config = GetDefaultConfig(); |
| config.audio = true; |
| CreateSender(config); |
| |
| std::unique_ptr<RtpPacketToSend> audio_packet = rtp_sender_->AllocatePacket(); |
| // Padding on audio stream allowed regardless of marker in the last packet. |
| audio_packet->SetMarker(false); |
| audio_packet->SetPayloadType(kPayload); |
| sequencer_->Sequence(*audio_packet); |
| |
| const size_t kPaddingSize = 59; |
| |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(ElementsAre(AllOf( |
| Pointee(Property(&RtpPacketToSend::packet_type, |
| RtpPacketMediaType::kPadding)), |
| Pointee(Property(&RtpPacketToSend::padding_size, kPaddingSize)))))); |
| EXPECT_EQ(kPaddingSize, GenerateAndSendPadding(kPaddingSize)); |
| |
| // Requested padding size is too small, will send a larger one. |
| const size_t kMinPaddingSize = 50; |
| EXPECT_CALL(mock_paced_sender_, |
| EnqueuePackets(ElementsAre( |
| AllOf(Pointee(Property(&RtpPacketToSend::packet_type, |
| RtpPacketMediaType::kPadding)), |
| Pointee(Property(&RtpPacketToSend::padding_size, |
| kMinPaddingSize)))))); |
| EXPECT_EQ(kMinPaddingSize, GenerateAndSendPadding(kMinPaddingSize - 5)); |
| } |
| |
| TEST_F(RtpSenderTest, SendToNetworkForwardsPacketsToPacer) { |
| std::vector<std::unique_ptr<RtpPacketToSend>> packets(1); |
| packets[0] = |
| BuildRtpPacket(kPayload, kMarkerBit, kTimestamp, Timestamp::Zero()); |
| Timestamp now = env_.clock().CurrentTime(); |
| |
| EXPECT_CALL(mock_paced_sender_, |
| EnqueuePackets(ElementsAre(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::capture_time, now)))))); |
| |
| rtp_sender_->EnqueuePackets(std::move(packets)); |
| } |
| |
| TEST_F(RtpSenderTest, ReSendPacketForwardsPacketsToPacer) { |
| packet_history_->SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 10); |
| Timestamp now = env_.clock().CurrentTime(); |
| auto packet = BuildRtpPacket(kPayload, kMarkerBit, kTimestamp, now); |
| packet->SetSequenceNumber(kSeqNum); |
| packet->set_allow_retransmission(true); |
| packet_history_->PutRtpPacket(std::move(packet), now); |
| |
| EXPECT_CALL(mock_paced_sender_, |
| EnqueuePackets(ElementsAre(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum)), |
| Pointee(Property(&RtpPacketToSend::capture_time, now)), |
| Pointee(Property(&RtpPacketToSend::packet_type, |
| RtpPacketMediaType::kRetransmission)))))); |
| EXPECT_TRUE(rtp_sender_->ReSendPacket(kSeqNum)); |
| } |
| |
| // This test sends 1 regular video packet, then 4 padding packets, and then |
| // 1 more regular packet. |
| TEST_F(RtpSenderTest, SendPadding) { |
| constexpr int kNumPaddingPackets = 4; |
| EXPECT_CALL(mock_paced_sender_, EnqueuePackets); |
| std::unique_ptr<RtpPacketToSend> media_packet = |
| SendPacket(/*capture_time=*/env_.clock().CurrentTime(), |
| /*payload_size=*/100); |
| sequencer_->Sequence(*media_packet); |
| |
| // Wait 50 ms before generating each padding packet. |
| for (int i = 0; i < kNumPaddingPackets; ++i) { |
| time_controller_.AdvanceTime(TimeDelta::Millis(50)); |
| const size_t kPaddingTargetBytes = 100; // Request 100 bytes of padding. |
| |
| // Padding should be sent on the media ssrc, with a continous sequence |
| // number range. Size will be forced to full pack size and the timestamp |
| // shall be that of the last media packet. |
| EXPECT_CALL(mock_paced_sender_, |
| EnqueuePackets(ElementsAre(Pointee(AllOf( |
| Property(&RtpPacketToSend::Ssrc, kSsrc), |
| Property(&RtpPacketToSend::padding_size, kMaxPaddingLength), |
| Property(&RtpPacketToSend::SequenceNumber, |
| media_packet->SequenceNumber() + i + 1), |
| Property(&RtpPacketToSend::Timestamp, |
| media_packet->Timestamp())))))); |
| std::vector<std::unique_ptr<RtpPacketToSend>> padding_packets = |
| Sequence(GeneratePadding(kPaddingTargetBytes)); |
| ASSERT_THAT(padding_packets, SizeIs(1)); |
| rtp_sender_->EnqueuePackets(std::move(padding_packets)); |
| } |
| |
| // Send a regular video packet again. |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(ElementsAre(Pointee(Property( |
| &RtpPacketToSend::Timestamp, Gt(media_packet->Timestamp())))))); |
| |
| std::unique_ptr<RtpPacketToSend> next_media_packet = |
| SendPacket(/*capture_time=*/env_.clock().CurrentTime(), |
| /*payload_size=*/100); |
| } |
| |
| TEST_F(RtpSenderTest, NoPaddingAsFirstPacketWithoutBweExtensions) { |
| EXPECT_THAT(rtp_sender_->GeneratePadding( |
| /*target_size_bytes=*/100, |
| /*media_has_been_sent=*/false, |
| /*can_send_padding_on_media_ssrc=*/false), |
| IsEmpty()); |
| |
| // Don't send padding before media even with RTX. |
| EnableRtx(); |
| EXPECT_THAT(rtp_sender_->GeneratePadding( |
| /*target_size_bytes=*/100, |
| /*media_has_been_sent=*/false, |
| /*can_send_padding_on_media_ssrc=*/false), |
| IsEmpty()); |
| } |
| |
| TEST_F(RtpSenderTest, RequiresRtxSsrcToEnableRtx) { |
| RtpRtcpInterface::Configuration config = GetDefaultConfig(); |
| config.rtx_send_ssrc = std::nullopt; |
| RTPSender rtp_sender(env_, config, packet_history_.get(), |
| config.paced_sender); |
| rtp_sender.SetRtxPayloadType(kRtxPayload, kPayload); |
| |
| rtp_sender.SetRtxStatus(kRtxRetransmitted); |
| |
| EXPECT_EQ(rtp_sender.RtxStatus(), kRtxOff); |
| } |
| |
| TEST_F(RtpSenderTest, RequiresRtxPayloadTypesToEnableRtx) { |
| RtpRtcpInterface::Configuration config = GetDefaultConfig(); |
| config.rtx_send_ssrc = kRtxSsrc; |
| RTPSender rtp_sender(env_, config, packet_history_.get(), |
| config.paced_sender); |
| |
| rtp_sender.SetRtxStatus(kRtxRetransmitted); |
| |
| EXPECT_EQ(rtp_sender.RtxStatus(), kRtxOff); |
| } |
| |
| TEST_F(RtpSenderTest, CanEnableRtxWhenRtxSsrcAndPayloadTypeAreConfigured) { |
| RtpRtcpInterface::Configuration config = GetDefaultConfig(); |
| config.rtx_send_ssrc = kRtxSsrc; |
| RTPSender rtp_sender(env_, config, packet_history_.get(), |
| config.paced_sender); |
| rtp_sender.SetRtxPayloadType(kRtxPayload, kPayload); |
| |
| ASSERT_EQ(rtp_sender.RtxStatus(), kRtxOff); |
| rtp_sender.SetRtxStatus(kRtxRetransmitted); |
| |
| EXPECT_EQ(rtp_sender.RtxStatus(), kRtxRetransmitted); |
| } |
| |
| TEST_F(RtpSenderTest, AllowPaddingAsFirstPacketOnRtxWithTransportCc) { |
| ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( |
| TransportSequenceNumber::Uri(), kTransportSequenceNumberExtensionId)); |
| |
| // Padding can't be sent as first packet on media SSRC since we don't know |
| // what payload type to assign. |
| EXPECT_THAT(rtp_sender_->GeneratePadding( |
| /*target_size_bytes=*/100, |
| /*media_has_been_sent=*/false, |
| /*can_send_padding_on_media_ssrc=*/false), |
| IsEmpty()); |
| |
| // With transportcc padding can be sent as first packet on the RTX SSRC. |
| EnableRtx(); |
| EXPECT_THAT(rtp_sender_->GeneratePadding( |
| /*target_size_bytes=*/100, |
| /*media_has_been_sent=*/false, |
| /*can_send_padding_on_media_ssrc=*/false), |
| Not(IsEmpty())); |
| } |
| |
| TEST_F(RtpSenderTest, AllowPaddingAsFirstPacketOnRtxWithAbsSendTime) { |
| ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( |
| AbsoluteSendTime::Uri(), kAbsoluteSendTimeExtensionId)); |
| |
| // Padding can't be sent as first packet on media SSRC since we don't know |
| // what payload type to assign. |
| EXPECT_THAT(rtp_sender_->GeneratePadding( |
| /*target_size_bytes=*/100, |
| /*media_has_been_sent=*/false, |
| /*can_send_padding_on_media_ssrc=*/false), |
| IsEmpty()); |
| |
| // With abs send time, padding can be sent as first packet on the RTX SSRC. |
| EnableRtx(); |
| EXPECT_THAT(rtp_sender_->GeneratePadding( |
| /*target_size_bytes=*/100, |
| /*media_has_been_sent=*/false, |
| /*can_send_padding_on_media_ssrc=*/false), |
| Not(IsEmpty())); |
| } |
| |
| TEST_F(RtpSenderTest, UpdatesTimestampsOnPlainRtxPadding) { |
| EnableRtx(); |
| // Timestamps as set based on capture time in RtpSenderTest. |
| const Timestamp start_time = env_.clock().CurrentTime(); |
| const uint32_t start_timestamp = ToRtpTimestamp(start_time); |
| |
| // Start by sending one media packet. |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(ElementsAre(AllOf( |
| Pointee(Property(&RtpPacketToSend::padding_size, 0u)), |
| Pointee(Property(&RtpPacketToSend::Timestamp, start_timestamp)), |
| Pointee(Property(&RtpPacketToSend::capture_time, start_time)))))); |
| std::unique_ptr<RtpPacketToSend> media_packet = |
| SendPacket(start_time, /*payload_size=*/600); |
| sequencer_->Sequence(*media_packet); |
| |
| // Advance time before sending padding. |
| const TimeDelta kTimeDiff = TimeDelta::Millis(17); |
| time_controller_.AdvanceTime(kTimeDiff); |
| |
| // Timestamps on padding should be offset from the sent media. |
| EXPECT_THAT( |
| Sequence(GeneratePadding(/*target_size_bytes=*/100)), |
| Each(Pointee(AllOf( |
| Property(&RtpPacketToSend::padding_size, kMaxPaddingLength), |
| Property(&RtpPacketToSend::Timestamp, |
| start_timestamp + kRtpClockRate * kTimeDiff), |
| Property(&RtpPacketToSend::capture_time, start_time + kTimeDiff))))); |
| } |
| |
| TEST_F(RtpSenderTest, KeepsTimestampsOnPayloadPadding) { |
| ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( |
| TransportSequenceNumber::Uri(), kTransportSequenceNumberExtensionId)); |
| EnableRtx(); |
| // Timestamps as set based on capture time in RtpSenderTest. |
| const Timestamp start_time = env_.clock().CurrentTime(); |
| const uint32_t start_timestamp = ToRtpTimestamp(start_time); |
| const size_t kPayloadSize = 200; |
| const size_t kRtxHeaderSize = 2; |
| |
| // Start by sending one media packet and putting in the packet history. |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(ElementsAre(AllOf( |
| Pointee(Property(&RtpPacketToSend::padding_size, 0u)), |
| Pointee(Property(&RtpPacketToSend::Timestamp, start_timestamp)), |
| Pointee(Property(&RtpPacketToSend::capture_time, start_time)))))); |
| std::unique_ptr<RtpPacketToSend> media_packet = |
| SendPacket(start_time, kPayloadSize); |
| packet_history_->PutRtpPacket(std::move(media_packet), start_time); |
| |
| // Advance time before sending padding. |
| const TimeDelta kTimeDiff = TimeDelta::Millis(17); |
| time_controller_.AdvanceTime(kTimeDiff); |
| |
| // Timestamps on payload padding should be set to original. |
| EXPECT_THAT( |
| GeneratePadding(/*target_size_bytes=*/100), |
| Each(AllOf( |
| Pointee(Property(&RtpPacketToSend::padding_size, 0u)), |
| Pointee(Property(&RtpPacketToSend::payload_size, |
| kPayloadSize + kRtxHeaderSize)), |
| Pointee(Property(&RtpPacketToSend::Timestamp, start_timestamp)), |
| Pointee(Property(&RtpPacketToSend::capture_time, start_time))))); |
| } |
| |
| // Test that the MID header extension is included on sent packets when |
| // configured. |
| TEST_F(RtpSenderTest, MidIncludedOnSentPackets) { |
| EnableMidSending(kMid); |
| |
| // Send a couple packets, expect both packets to have the MID set. |
| EXPECT_CALL(mock_paced_sender_, |
| EnqueuePackets(ElementsAre(Pointee( |
| Property(&RtpPacketToSend::GetExtension<RtpMid>, kMid))))) |
| .Times(2); |
| SendGenericPacket(); |
| SendGenericPacket(); |
| } |
| |
| TEST_F(RtpSenderTest, RidIncludedOnSentPackets) { |
| EnableRidSending(); |
| |
| EXPECT_CALL(mock_paced_sender_, |
| EnqueuePackets(ElementsAre(Pointee(Property( |
| &RtpPacketToSend::GetExtension<RtpStreamId>, kRid))))); |
| SendGenericPacket(); |
| } |
| |
| TEST_F(RtpSenderTest, RidIncludedOnRtxSentPackets) { |
| EnableRtx(); |
| EnableRidSending(); |
| |
| EXPECT_CALL(mock_paced_sender_, |
| EnqueuePackets(ElementsAre(Pointee(AllOf( |
| Property(&RtpPacketToSend::GetExtension<RtpStreamId>, kRid), |
| Property(&RtpPacketToSend::HasExtension<RepairedRtpStreamId>, |
| false)))))) |
| .WillOnce([&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) { |
| sequencer_->Sequence(*packets[0]); |
| packet_history_->PutRtpPacket(std::move(packets[0]), |
| env_.clock().CurrentTime()); |
| }); |
| SendGenericPacket(); |
| |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(ElementsAre(Pointee(AllOf( |
| Property(&RtpPacketToSend::GetExtension<RepairedRtpStreamId>, kRid), |
| Property(&RtpPacketToSend::HasExtension<RtpStreamId>, false)))))); |
| rtp_sender_->ReSendPacket(kSeqNum); |
| } |
| |
| TEST_F(RtpSenderTest, MidAndRidNotIncludedOnSentPacketsAfterAck) { |
| EnableMidSending(kMid); |
| EnableRidSending(); |
| |
| // This first packet should include both MID and RID. |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(ElementsAre(Pointee(AllOf( |
| Property(&RtpPacketToSend::GetExtension<RtpMid>, kMid), |
| Property(&RtpPacketToSend::GetExtension<RtpStreamId>, kRid)))))); |
| auto first_built_packet = SendGenericPacket(); |
| rtp_sender_->OnReceivedAckOnSsrc(first_built_packet->SequenceNumber()); |
| |
| // The second packet should include neither since an ack was received. |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(ElementsAre(Pointee(AllOf( |
| Property(&RtpPacketToSend::HasExtension<RtpMid>, false), |
| Property(&RtpPacketToSend::HasExtension<RtpStreamId>, false)))))); |
| SendGenericPacket(); |
| } |
| |
| TEST_F(RtpSenderTest, MidAndRidAlwaysIncludedOnSentPacketsWhenConfigured) { |
| SetUpRtpSender(false, /*always_send_mid_and_rid=*/true, nullptr); |
| EnableMidSending(kMid); |
| EnableRidSending(); |
| |
| // Send two media packets: one before and one after the ack. |
| // Due to the configuration, both sent packets should contain MID and RID. |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(ElementsAre(Pointee( |
| AllOf(Property(&RtpPacketToSend::GetExtension<RtpMid>, kMid), |
| Property(&RtpPacketToSend::GetExtension<RtpStreamId>, kRid)))))) |
| .Times(2); |
| auto first_built_packet = SendGenericPacket(); |
| rtp_sender_->OnReceivedAckOnSsrc(first_built_packet->SequenceNumber()); |
| SendGenericPacket(); |
| } |
| |
| // Test that the first RTX packet includes both MID and RRID even if the packet |
| // being retransmitted did not have MID or RID. The MID and RID are needed on |
| // the first packets for a given SSRC, and RTX packets are sent on a separate |
| // SSRC. |
| TEST_F(RtpSenderTest, MidAndRidIncludedOnFirstRtxPacket) { |
| EnableRtx(); |
| EnableMidSending(kMid); |
| EnableRidSending(); |
| |
| // This first packet will include both MID and RID. |
| EXPECT_CALL(mock_paced_sender_, EnqueuePackets); |
| auto first_built_packet = SendGenericPacket(); |
| rtp_sender_->OnReceivedAckOnSsrc(first_built_packet->SequenceNumber()); |
| |
| // The second packet will include neither since an ack was received, put |
| // it in the packet history for retransmission. |
| EXPECT_CALL(mock_paced_sender_, EnqueuePackets(SizeIs(1))) |
| .WillOnce([&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) { |
| packet_history_->PutRtpPacket(std::move(packets[0]), |
| env_.clock().CurrentTime()); |
| }); |
| auto second_built_packet = SendGenericPacket(); |
| |
| // The first RTX packet should include MID and RRID. |
| EXPECT_CALL(mock_paced_sender_, |
| EnqueuePackets(ElementsAre(Pointee(AllOf( |
| Property(&RtpPacketToSend::GetExtension<RtpMid>, kMid), |
| Property(&RtpPacketToSend::GetExtension<RepairedRtpStreamId>, |
| kRid)))))); |
| rtp_sender_->ReSendPacket(second_built_packet->SequenceNumber()); |
| } |
| |
| // Test that the RTX packets sent after receving an ACK on the RTX SSRC does |
| // not include either MID or RRID even if the packet being retransmitted did |
| // had a MID or RID. |
| TEST_F(RtpSenderTest, MidAndRidNotIncludedOnRtxPacketsAfterAck) { |
| EnableRtx(); |
| EnableMidSending(kMid); |
| EnableRidSending(); |
| |
| // This first packet will include both MID and RID. |
| auto first_built_packet = SendGenericPacket(); |
| sequencer_->Sequence(*first_built_packet); |
| packet_history_->PutRtpPacket( |
| std::make_unique<RtpPacketToSend>(*first_built_packet), |
| /*send_time=*/env_.clock().CurrentTime()); |
| rtp_sender_->OnReceivedAckOnSsrc(first_built_packet->SequenceNumber()); |
| |
| // The second packet will include neither since an ack was received. |
| auto second_built_packet = SendGenericPacket(); |
| sequencer_->Sequence(*second_built_packet); |
| packet_history_->PutRtpPacket( |
| std::make_unique<RtpPacketToSend>(*second_built_packet), |
| /*send_time=*/env_.clock().CurrentTime()); |
| |
| // The first RTX packet will include MID and RRID. |
| EXPECT_CALL(mock_paced_sender_, EnqueuePackets(SizeIs(1))) |
| .WillOnce([&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) { |
| rtp_sender_->OnReceivedAckOnRtxSsrc(packets[0]->SequenceNumber()); |
| packet_history_->MarkPacketAsSent( |
| *packets[0]->retransmitted_sequence_number()); |
| }); |
| rtp_sender_->ReSendPacket(second_built_packet->SequenceNumber()); |
| |
| // The second and third RTX packets should not include MID nor RRID. |
| EXPECT_CALL(mock_paced_sender_, |
| EnqueuePackets(ElementsAre(Pointee(AllOf( |
| Property(&RtpPacketToSend::HasExtension<RtpMid>, false), |
| Property(&RtpPacketToSend::HasExtension<RepairedRtpStreamId>, |
| false)))))) |
| .Times(2); |
| rtp_sender_->ReSendPacket(first_built_packet->SequenceNumber()); |
| rtp_sender_->ReSendPacket(second_built_packet->SequenceNumber()); |
| } |
| |
| TEST_F(RtpSenderTest, MidAndRidAlwaysIncludedOnRtxPacketsWhenConfigured) { |
| SetUpRtpSender(false, /*always_send_mid_and_rid=*/true, nullptr); |
| EnableRtx(); |
| EnableMidSending(kMid); |
| EnableRidSending(); |
| |
| // Send two media packets: one before and one after the ack. |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(ElementsAre(Pointee( |
| AllOf(Property(&RtpPacketToSend::GetExtension<RtpMid>, kMid), |
| Property(&RtpPacketToSend::GetExtension<RtpStreamId>, kRid)))))) |
| .Times(2) |
| .WillRepeatedly( |
| [&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) { |
| packet_history_->PutRtpPacket(std::move(packets[0]), |
| env_.clock().CurrentTime()); |
| }); |
| auto media_packet1 = SendGenericPacket(); |
| rtp_sender_->OnReceivedAckOnSsrc(media_packet1->SequenceNumber()); |
| auto media_packet2 = SendGenericPacket(); |
| |
| // Send three RTX packets with different combinations of orders w.r.t. the |
| // media and RTX acks. |
| // Due to the configuration, all sent packets should contain MID |
| // and either RID (media) or RRID (RTX). |
| EXPECT_CALL(mock_paced_sender_, |
| EnqueuePackets(ElementsAre(Pointee(AllOf( |
| Property(&RtpPacketToSend::GetExtension<RtpMid>, kMid), |
| Property(&RtpPacketToSend::GetExtension<RepairedRtpStreamId>, |
| kRid)))))) |
| .Times(3) |
| .WillRepeatedly( |
| [&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) { |
| rtp_sender_->OnReceivedAckOnRtxSsrc(packets[0]->SequenceNumber()); |
| packet_history_->MarkPacketAsSent( |
| *packets[0]->retransmitted_sequence_number()); |
| }); |
| rtp_sender_->ReSendPacket(media_packet2->SequenceNumber()); |
| rtp_sender_->ReSendPacket(media_packet1->SequenceNumber()); |
| rtp_sender_->ReSendPacket(media_packet2->SequenceNumber()); |
| } |
| |
| // Test that if the RtpState indicates an ACK has been received on that SSRC |
| // then neither the MID nor RID header extensions will be sent. |
| TEST_F(RtpSenderTest, MidAndRidNotIncludedOnSentPacketsAfterRtpStateRestored) { |
| EnableMidSending(kMid); |
| EnableRidSending(); |
| |
| RtpState state = rtp_sender_->GetRtpState(); |
| EXPECT_FALSE(state.ssrc_has_acked); |
| state.ssrc_has_acked = true; |
| rtp_sender_->SetRtpState(state); |
| |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(ElementsAre(Pointee(AllOf( |
| Property(&RtpPacketToSend::HasExtension<RtpMid>, false), |
| Property(&RtpPacketToSend::HasExtension<RtpStreamId>, false)))))); |
| SendGenericPacket(); |
| } |
| |
| // Test that if the RTX RtpState indicates an ACK has been received on that |
| // RTX SSRC then neither the MID nor RRID header extensions will be sent on |
| // RTX packets. |
| TEST_F(RtpSenderTest, MidAndRridNotIncludedOnRtxPacketsAfterRtpStateRestored) { |
| EnableRtx(); |
| EnableMidSending(kMid); |
| EnableRidSending(); |
| |
| RtpState rtx_state = rtp_sender_->GetRtxRtpState(); |
| EXPECT_FALSE(rtx_state.ssrc_has_acked); |
| rtx_state.ssrc_has_acked = true; |
| rtp_sender_->SetRtxRtpState(rtx_state); |
| |
| EXPECT_CALL(mock_paced_sender_, EnqueuePackets(SizeIs(1))) |
| .WillOnce([&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) { |
| packet_history_->PutRtpPacket(std::move(packets[0]), |
| env_.clock().CurrentTime()); |
| }); |
| auto built_packet = SendGenericPacket(); |
| |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(ElementsAre(Pointee(AllOf( |
| Property(&RtpPacketToSend::HasExtension<RtpMid>, false), |
| Property(&RtpPacketToSend::HasExtension<RtpStreamId>, false)))))); |
| ASSERT_LT(0, rtp_sender_->ReSendPacket(built_packet->SequenceNumber())); |
| } |
| |
| TEST_F(RtpSenderTest, RespectsNackBitrateLimit) { |
| const int32_t kPacketSize = 1400; |
| const int32_t kNumPackets = 30; |
| retransmission_rate_limiter_.SetMaxRate(kPacketSize * kNumPackets * 8); |
| EnableRtx(); |
| |
| std::vector<uint16_t> sequence_numbers; |
| for (int32_t i = 0; i < kNumPackets; ++i) { |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, /*marker_bit=*/true, /*rtp_timestamp=*/0, |
| /*capture_time=*/env_.clock().CurrentTime()); |
| packet->set_allow_retransmission(true); |
| sequencer_->Sequence(*packet); |
| sequence_numbers.push_back(packet->SequenceNumber()); |
| packet_history_->PutRtpPacket(std::move(packet), |
| /*send_time=*/env_.clock().CurrentTime()); |
| time_controller_.AdvanceTime(TimeDelta::Millis(1)); |
| } |
| |
| time_controller_.AdvanceTime(TimeDelta::Millis(1000 - kNumPackets)); |
| |
| // Resending should work - brings the bandwidth up to the limit. |
| // NACK bitrate is capped to the same bitrate as the encoder, since the max |
| // protection overhead is 50% (see MediaOptimization::SetTargetRates). |
| EXPECT_CALL(mock_paced_sender_, EnqueuePackets(ElementsAre(Pointee(Property( |
| &RtpPacketToSend::packet_type, |
| RtpPacketMediaType::kRetransmission))))) |
| .Times(kNumPackets) |
| .WillRepeatedly( |
| [&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) { |
| for (const auto& packet : packets) { |
| packet_history_->MarkPacketAsSent( |
| *packet->retransmitted_sequence_number()); |
| } |
| }); |
| rtp_sender_->OnReceivedNack(sequence_numbers, 0); |
| |
| // Must be at least 5ms in between retransmission attempts. |
| time_controller_.AdvanceTime(TimeDelta::Millis(5)); |
| |
| // Resending should not work, bandwidth exceeded. |
| EXPECT_CALL(mock_paced_sender_, EnqueuePackets).Times(0); |
| rtp_sender_->OnReceivedNack(sequence_numbers, 0); |
| } |
| |
| TEST_F(RtpSenderTest, UpdatingCsrcsUpdatedOverhead) { |
| RtpRtcpInterface::Configuration config = GetDefaultConfig(); |
| config.rtx_send_ssrc = {}; |
| CreateSender(config); |
| |
| // Base RTP overhead is 12B. |
| EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u); |
| |
| // Using packet with two csrcs adds 2*4 bytes to the header. |
| uint32_t csrcs[] = {1, 2}; |
| rtp_sender_->AllocatePacket(csrcs); |
| EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 20u); |
| } |
| |
| TEST_F(RtpSenderTest, OnOverheadChanged) { |
| RtpRtcpInterface::Configuration config = GetDefaultConfig(); |
| config.rtx_send_ssrc = {}; |
| CreateSender(config); |
| |
| // Base RTP overhead is 12B. |
| EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u); |
| |
| rtp_sender_->RegisterRtpHeaderExtension(TransmissionOffset::Uri(), |
| kTransmissionTimeOffsetExtensionId); |
| |
| // TransmissionTimeOffset extension has a size of 3B, but with the addition |
| // of header index and rounding to 4 byte boundary we end up with 20B total. |
| EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 20u); |
| } |
| |
| TEST_F(RtpSenderTest, CountMidOnlyUntilAcked) { |
| RtpRtcpInterface::Configuration config = GetDefaultConfig(); |
| config.rtx_send_ssrc = {}; |
| CreateSender(config); |
| |
| // Base RTP overhead is 12B. |
| EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u); |
| |
| rtp_sender_->RegisterRtpHeaderExtension(RtpMid::Uri(), kMidExtensionId); |
| |
| // Counted only if set. |
| EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u); |
| rtp_sender_->SetMid("foo"); |
| EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 36u); |
| rtp_sender_->RegisterRtpHeaderExtension(RtpStreamId::Uri(), kRidExtensionId); |
| EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 52u); |
| |
| // Ack received, mid/rid no longer sent. |
| rtp_sender_->OnReceivedAckOnSsrc(0); |
| EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u); |
| } |
| |
| TEST_F(RtpSenderTest, CountMidRidRridUntilAcked) { |
| RtpRtcpInterface::Configuration config = GetDefaultConfig(); |
| CreateSender(config); |
| |
| // Base RTP overhead is 12B and we use RTX which has an additional 2 bytes |
| // overhead. |
| EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 14u); |
| |
| rtp_sender_->RegisterRtpHeaderExtension(RtpMid::Uri(), kMidExtensionId); |
| |
| // Counted only if set. |
| EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 14u); |
| rtp_sender_->SetMid("foo"); |
| EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 38u); |
| |
| rtp_sender_->RegisterRtpHeaderExtension(RtpStreamId::Uri(), kRidExtensionId); |
| EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 54u); |
| |
| // mid/rrid may be shared with mid/rid when both are active. |
| rtp_sender_->RegisterRtpHeaderExtension(RepairedRtpStreamId::Uri(), |
| kRepairedRidExtensionId); |
| EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 54u); |
| |
| // Ack received, mid/rid no longer sent but we still need space for |
| // mid/rrid which can no longer be shared with mid/rid. |
| rtp_sender_->OnReceivedAckOnSsrc(0); |
| EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 54u); |
| |
| // Ack received for RTX, no need to send RRID anymore. |
| rtp_sender_->OnReceivedAckOnRtxSsrc(0); |
| EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 14u); |
| } |
| |
| TEST_F(RtpSenderTest, DontCountVolatileExtensionsIntoOverhead) { |
| RtpRtcpInterface::Configuration config = GetDefaultConfig(); |
| config.rtx_send_ssrc = {}; |
| CreateSender(config); |
| |
| // Base RTP overhead is 12B. |
| EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u); |
| |
| rtp_sender_->RegisterRtpHeaderExtension(InbandComfortNoiseExtension::Uri(), |
| 1); |
| rtp_sender_->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::Uri(), |
| 2); |
| rtp_sender_->RegisterRtpHeaderExtension(VideoOrientation::Uri(), 3); |
| rtp_sender_->RegisterRtpHeaderExtension(PlayoutDelayLimits::Uri(), 4); |
| rtp_sender_->RegisterRtpHeaderExtension(VideoContentTypeExtension::Uri(), 5); |
| rtp_sender_->RegisterRtpHeaderExtension(VideoTimingExtension::Uri(), 6); |
| rtp_sender_->RegisterRtpHeaderExtension(RepairedRtpStreamId::Uri(), 7); |
| rtp_sender_->RegisterRtpHeaderExtension(ColorSpaceExtension::Uri(), 8); |
| |
| // Still only 12B counted since can't count on above being sent. |
| EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u); |
| } |
| |
| TEST_F(RtpSenderTest, SendPacketHandlesRetransmissionHistory) { |
| packet_history_->SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 10); |
| |
| // Ignore calls to EnqueuePackets() for this test. |
| EXPECT_CALL(mock_paced_sender_, EnqueuePackets).WillRepeatedly(Return()); |
| |
| // Build a media packet and put in the packet history. |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, env_.clock().CurrentTime()); |
| const uint16_t media_sequence_number = packet->SequenceNumber(); |
| packet->set_allow_retransmission(true); |
| packet_history_->PutRtpPacket(std::move(packet), env_.clock().CurrentTime()); |
| |
| // Simulate successful retransmission request. |
| time_controller_.AdvanceTime(TimeDelta::Millis(30)); |
| EXPECT_THAT(rtp_sender_->ReSendPacket(media_sequence_number), Gt(0)); |
| |
| // Packet already pending, retransmission not allowed. |
| time_controller_.AdvanceTime(TimeDelta::Millis(30)); |
| EXPECT_THAT(rtp_sender_->ReSendPacket(media_sequence_number), Eq(0)); |
| |
| // Simulate packet exiting pacer, mark as not longer pending. |
| packet_history_->MarkPacketAsSent(media_sequence_number); |
| |
| // Retransmissions allowed again. |
| time_controller_.AdvanceTime(TimeDelta::Millis(30)); |
| EXPECT_THAT(rtp_sender_->ReSendPacket(media_sequence_number), Gt(0)); |
| } |
| |
| TEST_F(RtpSenderTest, MarksRetransmittedPackets) { |
| packet_history_->SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 10); |
| |
| // Build a media packet and put in the packet history. |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, env_.clock().CurrentTime()); |
| const uint32_t kMediaSsrc = 567; |
| const uint16_t kMediaSequenceNumber = 123; |
| packet->SetSsrc(kMediaSsrc); |
| packet->SetSequenceNumber(kMediaSequenceNumber); |
| packet->set_allow_retransmission(true); |
| packet_history_->PutRtpPacket(std::move(packet), env_.clock().CurrentTime()); |
| |
| // Expect a retransmission packet marked with which packet it is a |
| // retransmit of. |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(ElementsAre(AllOf( |
| Pointee(Property(&RtpPacketToSend::packet_type, |
| RtpPacketMediaType::kRetransmission)), |
| Pointee(Property(&RtpPacketToSend::original_ssrc, kMediaSsrc)), |
| Pointee(Property(&RtpPacketToSend::retransmitted_sequence_number, |
| Eq(kMediaSequenceNumber))))))); |
| EXPECT_THAT(rtp_sender_->ReSendPacket(kMediaSequenceNumber), Gt(0)); |
| } |
| |
| TEST_F(RtpSenderTest, GeneratedPaddingHasBweExtensions) { |
| // Min requested size in order to use RTX payload. |
| const size_t kMinPaddingSize = 50; |
| EnableRtx(); |
| |
| ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( |
| TransmissionOffset::Uri(), kTransmissionTimeOffsetExtensionId)); |
| ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( |
| AbsoluteSendTime::Uri(), kAbsoluteSendTimeExtensionId)); |
| ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( |
| TransportSequenceNumber::Uri(), kTransportSequenceNumberExtensionId)); |
| |
| // Put a packet in the history, in order to facilitate payload padding. |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, env_.clock().CurrentTime()); |
| packet->set_allow_retransmission(true); |
| packet->SetPayloadSize(kMinPaddingSize); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| packet_history_->PutRtpPacket(std::move(packet), env_.clock().CurrentTime()); |
| |
| // Generate a plain padding packet, check that extensions are registered. |
| std::vector<std::unique_ptr<RtpPacketToSend>> generated_packets = |
| GeneratePadding(/*target_size_bytes=*/1); |
| ASSERT_THAT(generated_packets, SizeIs(1)); |
| auto& plain_padding = generated_packets.front(); |
| EXPECT_GT(plain_padding->padding_size(), 0u); |
| EXPECT_TRUE(plain_padding->HasExtension<TransportSequenceNumber>()); |
| EXPECT_TRUE(plain_padding->HasExtension<AbsoluteSendTime>()); |
| EXPECT_TRUE(plain_padding->HasExtension<TransmissionOffset>()); |
| EXPECT_GT(plain_padding->padding_size(), 0u); |
| |
| // Generate a payload padding packets, check that extensions are registered. |
| generated_packets = GeneratePadding(kMinPaddingSize); |
| ASSERT_EQ(generated_packets.size(), 1u); |
| auto& payload_padding = generated_packets.front(); |
| EXPECT_EQ(payload_padding->padding_size(), 0u); |
| EXPECT_TRUE(payload_padding->HasExtension<TransportSequenceNumber>()); |
| EXPECT_TRUE(payload_padding->HasExtension<AbsoluteSendTime>()); |
| EXPECT_TRUE(payload_padding->HasExtension<TransmissionOffset>()); |
| EXPECT_GT(payload_padding->payload_size(), 0u); |
| } |
| |
| TEST_F(RtpSenderTest, GeneratedPaddingHasMidRidExtensions) { |
| EnableMidSending("mid"); |
| EnableRidSending(); |
| |
| // Send a dummy video packet so it ends up in the packet history. Since we |
| // are not using RTX, it should never be used as padding. |
| packet_history_->SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 1); |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, env_.clock().CurrentTime()); |
| packet->set_allow_retransmission(true); |
| packet->SetPayloadSize(1234); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| sequencer_->Sequence(*packet); |
| packet_history_->PutRtpPacket(std::move(packet), env_.clock().CurrentTime()); |
| |
| std::vector<std::unique_ptr<RtpPacketToSend>> padding_packets = |
| GeneratePadding(/*target_size_bytes=*/1); |
| ASSERT_THAT(padding_packets, SizeIs(1)); |
| |
| EXPECT_TRUE(padding_packets[0]->HasExtension<RtpMid>()); |
| EXPECT_TRUE(padding_packets[0]->HasExtension<RtpStreamId>()); |
| } |
| |
| TEST_F(RtpSenderTest, GeneratedPaddingOnRtxHasMidRidExtensions) { |
| EnableRtx(); |
| EnableMidSending("mid"); |
| EnableRidSending(); |
| |
| std::vector<std::unique_ptr<RtpPacketToSend>> padding_packets = |
| GeneratePadding(/*target_size_bytes=*/1); |
| ASSERT_THAT(padding_packets, SizeIs(1)); |
| |
| EXPECT_TRUE(padding_packets[0]->HasExtension<RtpMid>()); |
| EXPECT_TRUE(padding_packets[0]->HasExtension<RepairedRtpStreamId>()); |
| } |
| |
| TEST_F(RtpSenderTest, GeneratePaddingResendsOldPacketsWithRtx) { |
| // Min requested size in order to use RTX payload. |
| const size_t kMinPaddingSize = 50; |
| |
| rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload); |
| rtp_sender_->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); |
| packet_history_->SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 1); |
| |
| ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( |
| TransportSequenceNumber::Uri(), kTransportSequenceNumberExtensionId)); |
| |
| const size_t kPayloadPacketSize = kMinPaddingSize; |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, env_.clock().CurrentTime()); |
| packet->set_allow_retransmission(true); |
| packet->SetPayloadSize(kPayloadPacketSize); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| packet_history_->PutRtpPacket(std::move(packet), env_.clock().CurrentTime()); |
| |
| // Generated padding has large enough budget that the video packet should be |
| // retransmitted as padding. |
| std::vector<std::unique_ptr<RtpPacketToSend>> generated_packets = |
| GeneratePadding(kMinPaddingSize); |
| ASSERT_EQ(generated_packets.size(), 1u); |
| auto& padding_packet = generated_packets.front(); |
| EXPECT_EQ(padding_packet->packet_type(), RtpPacketMediaType::kPadding); |
| EXPECT_EQ(padding_packet->Ssrc(), kRtxSsrc); |
| EXPECT_EQ(padding_packet->payload_size(), |
| kPayloadPacketSize + kRtxHeaderSize); |
| |
| // Not enough budged for payload padding, use plain padding instead. |
| const size_t kPaddingBytesRequested = kMinPaddingSize - 1; |
| |
| size_t padding_bytes_generated = 0; |
| generated_packets = GeneratePadding(kPaddingBytesRequested); |
| EXPECT_EQ(generated_packets.size(), 1u); |
| for (auto& packet : generated_packets) { |
| EXPECT_EQ(packet->packet_type(), RtpPacketMediaType::kPadding); |
| EXPECT_EQ(packet->Ssrc(), kRtxSsrc); |
| EXPECT_EQ(packet->payload_size(), 0u); |
| EXPECT_GT(packet->padding_size(), 0u); |
| padding_bytes_generated += packet->padding_size(); |
| } |
| |
| EXPECT_EQ(padding_bytes_generated, kMaxPaddingLength); |
| } |
| |
| TEST_F(RtpSenderTest, LimitsPayloadPaddingSize) { |
| // RTX payload padding is limited to 3x target size. |
| const double kFactor = 3.0; |
| SetUpRtpSender(false, false, nullptr); |
| rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload); |
| rtp_sender_->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); |
| packet_history_->SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 1); |
| |
| ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( |
| TransportSequenceNumber::Uri(), kTransportSequenceNumberExtensionId)); |
| |
| // Send a dummy video packet so it ends up in the packet history. |
| const size_t kPayloadPacketSize = 1234u; |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, env_.clock().CurrentTime()); |
| packet->set_allow_retransmission(true); |
| packet->SetPayloadSize(kPayloadPacketSize); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| packet_history_->PutRtpPacket(std::move(packet), env_.clock().CurrentTime()); |
| |
| // Smallest target size that will result in the sent packet being returned as |
| // padding. |
| const size_t kMinTargerSizeForPayload = |
| (kPayloadPacketSize + kRtxHeaderSize) / kFactor; |
| |
| // Generated padding has large enough budget that the video packet should be |
| // retransmitted as padding. |
| EXPECT_THAT( |
| GeneratePadding(kMinTargerSizeForPayload), |
| AllOf(Not(IsEmpty()), |
| Each(Pointee(Property(&RtpPacketToSend::padding_size, Eq(0u)))))); |
| |
| // If payload padding is > 2x requested size, plain padding is returned |
| // instead. |
| EXPECT_THAT( |
| GeneratePadding(kMinTargerSizeForPayload - 1), |
| AllOf(Not(IsEmpty()), |
| Each(Pointee(Property(&RtpPacketToSend::padding_size, Gt(0u)))))); |
| } |
| |
| TEST_F(RtpSenderTest, GeneratePaddingCreatesPurePaddingWithoutRtx) { |
| packet_history_->SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 1); |
| ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( |
| TransmissionOffset::Uri(), kTransmissionTimeOffsetExtensionId)); |
| ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( |
| AbsoluteSendTime::Uri(), kAbsoluteSendTimeExtensionId)); |
| ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension( |
| TransportSequenceNumber::Uri(), kTransportSequenceNumberExtensionId)); |
| |
| const size_t kPayloadPacketSize = 1234; |
| // Send a dummy video packet so it ends up in the packet history. Since we |
| // are not using RTX, it should never be used as padding. |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, env_.clock().CurrentTime()); |
| packet->set_allow_retransmission(true); |
| packet->SetPayloadSize(kPayloadPacketSize); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| sequencer_->Sequence(*packet); |
| packet_history_->PutRtpPacket(std::move(packet), env_.clock().CurrentTime()); |
| |
| // Payload padding not available without RTX, only generate plain padding on |
| // the media SSRC. |
| // Number of padding packets is the requested padding size divided by max |
| // padding packet size, rounded up. Pure padding packets are always of the |
| // maximum size. |
| const size_t kPaddingBytesRequested = kPayloadPacketSize + kRtxHeaderSize; |
| const size_t kExpectedNumPaddingPackets = |
| (kPaddingBytesRequested + kMaxPaddingLength - 1) / kMaxPaddingLength; |
| size_t padding_bytes_generated = 0; |
| std::vector<std::unique_ptr<RtpPacketToSend>> padding_packets = |
| GeneratePadding(kPaddingBytesRequested); |
| EXPECT_EQ(padding_packets.size(), kExpectedNumPaddingPackets); |
| for (auto& packet : padding_packets) { |
| EXPECT_EQ(packet->packet_type(), RtpPacketMediaType::kPadding); |
| EXPECT_EQ(packet->Ssrc(), kSsrc); |
| EXPECT_EQ(packet->payload_size(), 0u); |
| EXPECT_GT(packet->padding_size(), 0u); |
| padding_bytes_generated += packet->padding_size(); |
| EXPECT_TRUE(packet->HasExtension<TransportSequenceNumber>()); |
| EXPECT_TRUE(packet->HasExtension<AbsoluteSendTime>()); |
| EXPECT_TRUE(packet->HasExtension<TransmissionOffset>()); |
| } |
| |
| EXPECT_EQ(padding_bytes_generated, |
| kExpectedNumPaddingPackets * kMaxPaddingLength); |
| } |
| |
| TEST_F(RtpSenderTest, SupportsPadding) { |
| bool kSendingMediaStats[] = {true, false}; |
| bool kEnableRedundantPayloads[] = {true, false}; |
| absl::string_view kBweExtensionUris[] = { |
| TransportSequenceNumber::Uri(), TransportSequenceNumberV2::Uri(), |
| AbsoluteSendTime::Uri(), TransmissionOffset::Uri()}; |
| const int kExtensionsId = 7; |
| |
| for (bool sending_media : kSendingMediaStats) { |
| rtp_sender_->SetSendingMediaStatus(sending_media); |
| for (bool redundant_payloads : kEnableRedundantPayloads) { |
| int rtx_mode = kRtxRetransmitted; |
| if (redundant_payloads) { |
| rtx_mode |= kRtxRedundantPayloads; |
| } |
| rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload); |
| rtp_sender_->SetRtxStatus(rtx_mode); |
| |
| for (auto extension_uri : kBweExtensionUris) { |
| EXPECT_FALSE(rtp_sender_->SupportsPadding()); |
| rtp_sender_->RegisterRtpHeaderExtension(extension_uri, kExtensionsId); |
| if (!sending_media) { |
| EXPECT_FALSE(rtp_sender_->SupportsPadding()); |
| } else { |
| EXPECT_TRUE(rtp_sender_->SupportsPadding()); |
| if (redundant_payloads) { |
| EXPECT_TRUE(rtp_sender_->SupportsRtxPayloadPadding()); |
| } else { |
| EXPECT_FALSE(rtp_sender_->SupportsRtxPayloadPadding()); |
| } |
| } |
| rtp_sender_->DeregisterRtpHeaderExtension(extension_uri); |
| EXPECT_FALSE(rtp_sender_->SupportsPadding()); |
| } |
| } |
| } |
| } |
| |
| TEST_F(RtpSenderTest, SetsCaptureTimeOnRtxRetransmissions) { |
| EnableRtx(); |
| |
| // Put a packet in the packet history, with current time as capture time. |
| const Timestamp start_time = env_.clock().CurrentTime(); |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, kMarkerBit, /*rtp_timestamp=*/0, |
| /*capture_time=*/start_time); |
| packet->set_allow_retransmission(true); |
| sequencer_->Sequence(*packet); |
| packet_history_->PutRtpPacket(std::move(packet), start_time); |
| |
| // Advance time, request an RTX retransmission. Capture timestamp should be |
| // preserved. |
| time_controller_.AdvanceTime(TimeDelta::Millis(10)); |
| |
| EXPECT_CALL(mock_paced_sender_, |
| EnqueuePackets(ElementsAre(Pointee( |
| Property(&RtpPacketToSend::capture_time, start_time))))); |
| EXPECT_GT(rtp_sender_->ReSendPacket(kSeqNum), 0); |
| } |
| |
| TEST_F(RtpSenderTest, IgnoresNackAfterDisablingMedia) { |
| const TimeDelta kRtt = TimeDelta::Millis(10); |
| |
| EnableRtx(); |
| packet_history_->SetRtt(kRtt); |
| |
| // Put a packet in the history. |
| const Timestamp start_time = env_.clock().CurrentTime(); |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, kMarkerBit, 0, /*capture_time=*/start_time); |
| packet->set_allow_retransmission(true); |
| sequencer_->Sequence(*packet); |
| packet_history_->PutRtpPacket(std::move(packet), start_time); |
| |
| // Disable media sending and try to retransmit the packet, it should fail. |
| rtp_sender_->SetSendingMediaStatus(false); |
| time_controller_.AdvanceTime(kRtt); |
| EXPECT_LT(rtp_sender_->ReSendPacket(kSeqNum), 0); |
| } |
| |
| TEST_F(RtpSenderTest, DoesntFecProtectRetransmissions) { |
| // Set up retranmission without RTX, so that a plain copy of the old packet is |
| // re-sent instead. |
| const TimeDelta kRtt = TimeDelta::Millis(10); |
| rtp_sender_->SetSendingMediaStatus(true); |
| rtp_sender_->SetRtxStatus(kRtxOff); |
| packet_history_->SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 10); |
| packet_history_->SetRtt(kRtt); |
| |
| // Put a fec protected packet in the history. |
| const Timestamp start_time = env_.clock().CurrentTime(); |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, kMarkerBit, 0, start_time); |
| packet->set_allow_retransmission(true); |
| packet->set_fec_protect_packet(true); |
| sequencer_->Sequence(*packet); |
| packet_history_->PutRtpPacket(std::move(packet), start_time); |
| |
| // Re-send packet, the retransmitted packet should not have the FEC protection |
| // flag set. |
| EXPECT_CALL(mock_paced_sender_, |
| EnqueuePackets(ElementsAre(Pointee( |
| Property(&RtpPacketToSend::fec_protect_packet, false))))); |
| |
| time_controller_.AdvanceTime(kRtt); |
| EXPECT_GT(rtp_sender_->ReSendPacket(kSeqNum), 0); |
| } |
| |
| TEST_F(RtpSenderTest, MarksPacketsWithKeyframeStatus) { |
| RTPSenderVideo::Config video_config; |
| video_config.clock = &env_.clock(); |
| video_config.rtp_sender = rtp_sender_.get(); |
| video_config.field_trials = &env_.field_trials(); |
| RTPSenderVideo rtp_sender_video(video_config); |
| |
| const uint8_t kPayloadType = 127; |
| const std::optional<VideoCodecType> kCodecType = |
| VideoCodecType::kVideoCodecGeneric; |
| |
| const uint32_t kCaptureTimeMsToRtpTimestamp = 90; // 90 kHz clock |
| |
| { |
| EXPECT_CALL(mock_paced_sender_, |
| EnqueuePackets(Each( |
| Pointee(Property(&RtpPacketToSend::is_key_frame, true))))) |
| .Times(AtLeast(1)); |
| RTPVideoHeader video_header; |
| video_header.frame_type = VideoFrameType::kVideoFrameKey; |
| Timestamp capture_time = env_.clock().CurrentTime(); |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| kPayloadType, kCodecType, |
| capture_time.ms() * kCaptureTimeMsToRtpTimestamp, capture_time, |
| kPayloadData, sizeof(kPayloadData), video_header, |
| kDefaultExpectedRetransmissionTime, {})); |
| |
| time_controller_.AdvanceTime(TimeDelta::Millis(33)); |
| } |
| |
| { |
| EXPECT_CALL(mock_paced_sender_, |
| EnqueuePackets(Each( |
| Pointee(Property(&RtpPacketToSend::is_key_frame, false))))) |
| .Times(AtLeast(1)); |
| RTPVideoHeader video_header; |
| video_header.frame_type = VideoFrameType::kVideoFrameDelta; |
| Timestamp capture_time = env_.clock().CurrentTime(); |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| kPayloadType, kCodecType, |
| capture_time.ms() * kCaptureTimeMsToRtpTimestamp, capture_time, |
| kPayloadData, sizeof(kPayloadData), video_header, |
| kDefaultExpectedRetransmissionTime, {})); |
| |
| time_controller_.AdvanceTime(TimeDelta::Millis(33)); |
| } |
| } |
| |
| } // namespace webrtc |