| /* |
| * 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 <vector> |
| |
| #include "api/rtc_event_log/rtc_event.h" |
| #include "api/transport/field_trial_based_config.h" |
| #include "api/video/video_codec_constants.h" |
| #include "api/video/video_timing.h" |
| #include "logging/rtc_event_log/mock/mock_rtc_event_log.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_packet_sender.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_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_egress.h" |
| #include "modules/rtp_rtcp/source/rtp_sender_video.h" |
| #include "modules/rtp_rtcp/source/rtp_utility.h" |
| #include "rtc_base/arraysize.h" |
| #include "rtc_base/rate_limiter.h" |
| #include "rtc_base/strings/string_builder.h" |
| #include "test/field_trial.h" |
| #include "test/gmock.h" |
| #include "test/gtest.h" |
| #include "test/mock_transport.h" |
| #include "test/rtp_header_parser.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 uint16_t kTransportSequenceNumber = 1; |
| const uint64_t kStartTime = 123456789; |
| const size_t kMaxPaddingSize = 224u; |
| const uint8_t kPayloadData[] = {47, 11, 32, 93, 89}; |
| const int64_t kDefaultExpectedRetransmissionTimeMs = 125; |
| const char kNoRid[] = ""; |
| const char kNoMid[] = ""; |
| |
| using ::testing::_; |
| using ::testing::AllOf; |
| using ::testing::Contains; |
| using ::testing::Each; |
| 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; |
| using ::testing::StrictMock; |
| |
| uint64_t ConvertMsToAbsSendTime(int64_t time_ms) { |
| return (((time_ms << 18) + 500) / 1000) & 0x00ffffff; |
| } |
| |
| class LoopbackTransportTest : public webrtc::Transport { |
| public: |
| LoopbackTransportTest() : total_bytes_sent_(0) { |
| receivers_extensions_.Register<TransmissionOffset>( |
| kTransmissionTimeOffsetExtensionId); |
| receivers_extensions_.Register<AbsoluteSendTime>( |
| kAbsoluteSendTimeExtensionId); |
| receivers_extensions_.Register<TransportSequenceNumber>( |
| kTransportSequenceNumberExtensionId); |
| receivers_extensions_.Register<VideoOrientation>(kVideoRotationExtensionId); |
| receivers_extensions_.Register<AudioLevel>(kAudioLevelExtensionId); |
| receivers_extensions_.Register<VideoTimingExtension>( |
| kVideoTimingExtensionId); |
| receivers_extensions_.Register<RtpMid>(kMidExtensionId); |
| receivers_extensions_.Register<RtpGenericFrameDescriptorExtension00>( |
| kGenericDescriptorId); |
| receivers_extensions_.Register<RtpStreamId>(kRidExtensionId); |
| receivers_extensions_.Register<RepairedRtpStreamId>( |
| kRepairedRidExtensionId); |
| } |
| |
| bool SendRtp(const uint8_t* data, |
| size_t len, |
| const PacketOptions& options) override { |
| last_options_ = options; |
| total_bytes_sent_ += len; |
| sent_packets_.push_back(RtpPacketReceived(&receivers_extensions_)); |
| EXPECT_TRUE(sent_packets_.back().Parse(data, len)); |
| return true; |
| } |
| bool SendRtcp(const uint8_t* data, size_t len) override { return false; } |
| const RtpPacketReceived& last_sent_packet() { return sent_packets_.back(); } |
| int packets_sent() { return sent_packets_.size(); } |
| |
| size_t total_bytes_sent_; |
| PacketOptions last_options_; |
| std::vector<RtpPacketReceived> sent_packets_; |
| |
| private: |
| RtpHeaderExtensionMap receivers_extensions_; |
| }; |
| |
| MATCHER_P(SameRtcEventTypeAs, value, "") { |
| return value == arg->GetType(); |
| } |
| |
| struct TestConfig { |
| explicit TestConfig(bool with_overhead) : with_overhead(with_overhead) {} |
| bool with_overhead = false; |
| }; |
| |
| class MockRtpPacketPacer : public RtpPacketSender { |
| public: |
| MockRtpPacketPacer() {} |
| virtual ~MockRtpPacketPacer() {} |
| |
| MOCK_METHOD1(EnqueuePackets, |
| void(std::vector<std::unique_ptr<RtpPacketToSend>>)); |
| |
| MOCK_METHOD2(CreateProbeCluster, void(int bitrate_bps, int cluster_id)); |
| |
| MOCK_METHOD0(Pause, void()); |
| MOCK_METHOD0(Resume, void()); |
| MOCK_METHOD1(SetCongestionWindow, |
| void(absl::optional<int64_t> congestion_window_bytes)); |
| MOCK_METHOD1(UpdateOutstandingData, void(int64_t outstanding_bytes)); |
| MOCK_METHOD1(SetAccountForAudioPackets, void(bool account_for_audio)); |
| }; |
| |
| class MockSendSideDelayObserver : public SendSideDelayObserver { |
| public: |
| MOCK_METHOD4(SendSideDelayUpdated, void(int, int, uint64_t, uint32_t)); |
| }; |
| |
| class MockSendPacketObserver : public SendPacketObserver { |
| public: |
| MOCK_METHOD3(OnSendPacket, void(uint16_t, int64_t, uint32_t)); |
| }; |
| |
| class MockTransportFeedbackObserver : public TransportFeedbackObserver { |
| public: |
| MOCK_METHOD1(OnAddPacket, void(const RtpPacketSendInfo&)); |
| MOCK_METHOD1(OnTransportFeedback, void(const rtcp::TransportFeedback&)); |
| }; |
| |
| class StreamDataTestCallback : public StreamDataCountersCallback { |
| public: |
| StreamDataTestCallback() |
| : StreamDataCountersCallback(), ssrc_(0), counters_() {} |
| ~StreamDataTestCallback() override = default; |
| |
| void DataCountersUpdated(const StreamDataCounters& counters, |
| uint32_t ssrc) override { |
| ssrc_ = ssrc; |
| counters_ = counters; |
| } |
| |
| uint32_t ssrc_; |
| StreamDataCounters counters_; |
| |
| void MatchPacketCounter(const RtpPacketCounter& expected, |
| const RtpPacketCounter& actual) { |
| EXPECT_EQ(expected.payload_bytes, actual.payload_bytes); |
| EXPECT_EQ(expected.header_bytes, actual.header_bytes); |
| EXPECT_EQ(expected.padding_bytes, actual.padding_bytes); |
| EXPECT_EQ(expected.packets, actual.packets); |
| } |
| |
| void Matches(uint32_t ssrc, const StreamDataCounters& counters) { |
| EXPECT_EQ(ssrc, ssrc_); |
| MatchPacketCounter(counters.transmitted, counters_.transmitted); |
| MatchPacketCounter(counters.retransmitted, counters_.retransmitted); |
| EXPECT_EQ(counters.fec.packets, counters_.fec.packets); |
| } |
| }; |
| |
| // Mimics ModuleRtpRtcp::RtpSenderContext. |
| // TODO(sprang): Split up unit tests and test these components individually |
| // wherever possible. |
| struct RtpSenderContext { |
| explicit RtpSenderContext(const RtpRtcp::Configuration& config) |
| : packet_history_(config.clock, config.enable_rtx_padding_prioritization), |
| packet_sender_(config, &packet_history_), |
| non_paced_sender_(&packet_sender_), |
| packet_generator_( |
| config, |
| &packet_history_, |
| config.paced_sender ? config.paced_sender : &non_paced_sender_) {} |
| RtpPacketHistory packet_history_; |
| RtpSenderEgress packet_sender_; |
| RtpSenderEgress::NonPacedPacketSender non_paced_sender_; |
| RTPSender packet_generator_; |
| }; |
| |
| class FieldTrialConfig : public WebRtcKeyValueConfig { |
| public: |
| FieldTrialConfig() : overhead_enabled_(false), max_padding_factor_(1200) {} |
| ~FieldTrialConfig() override {} |
| |
| void SetOverHeadEnabled(bool enabled) { overhead_enabled_ = enabled; } |
| void SetMaxPaddingFactor(double factor) { max_padding_factor_ = factor; } |
| |
| std::string Lookup(absl::string_view key) const override { |
| if (key == "WebRTC-LimitPaddingSize") { |
| char string_buf[32]; |
| rtc::SimpleStringBuilder ssb(string_buf); |
| ssb << "factor:" << max_padding_factor_; |
| return ssb.str(); |
| } else if (key == "WebRTC-SendSideBwe-WithOverhead") { |
| return overhead_enabled_ ? "Enabled" : "Disabled"; |
| } |
| return ""; |
| } |
| |
| private: |
| bool overhead_enabled_; |
| double max_padding_factor_; |
| }; |
| |
| } // namespace |
| |
| class RtpSenderTest : public ::testing::TestWithParam<TestConfig> { |
| protected: |
| RtpSenderTest() |
| : fake_clock_(kStartTime), |
| retransmission_rate_limiter_(&fake_clock_, 1000), |
| flexfec_sender_(0, |
| kFlexFecSsrc, |
| kSsrc, |
| "", |
| std::vector<RtpExtension>(), |
| std::vector<RtpExtensionSize>(), |
| nullptr, |
| &fake_clock_), |
| kMarkerBit(true) { |
| field_trials_.SetOverHeadEnabled(GetParam().with_overhead); |
| } |
| |
| void SetUp() override { SetUpRtpSender(true, false, false); } |
| |
| RTPSender* rtp_sender() { |
| RTC_DCHECK(rtp_sender_context_); |
| return &rtp_sender_context_->packet_generator_; |
| } |
| |
| RtpSenderEgress* rtp_egress() { |
| RTC_DCHECK(rtp_sender_context_); |
| return &rtp_sender_context_->packet_sender_; |
| } |
| |
| void SetUpRtpSender(bool pacer, |
| bool populate_network2, |
| bool always_send_mid_and_rid) { |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.local_media_ssrc = kSsrc; |
| config.rtx_send_ssrc = kRtxSsrc; |
| config.fec_generator = &flexfec_sender_; |
| config.event_log = &mock_rtc_event_log_; |
| config.send_packet_observer = &send_packet_observer_; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| config.paced_sender = pacer ? &mock_paced_sender_ : nullptr; |
| config.populate_network2_timestamp = populate_network2; |
| config.rtp_stats_callback = &rtp_stats_callback_; |
| config.always_send_mid_and_rid = always_send_mid_and_rid; |
| config.field_trials = &field_trials_; |
| |
| rtp_sender_context_ = std::make_unique<RtpSenderContext>(config); |
| rtp_sender()->SetSequenceNumber(kSeqNum); |
| rtp_sender()->SetTimestampOffset(0); |
| } |
| |
| SimulatedClock fake_clock_; |
| NiceMock<MockRtcEventLog> mock_rtc_event_log_; |
| MockRtpPacketPacer mock_paced_sender_; |
| StrictMock<MockSendPacketObserver> send_packet_observer_; |
| StrictMock<MockTransportFeedbackObserver> feedback_observer_; |
| RateLimiter retransmission_rate_limiter_; |
| FlexfecSender flexfec_sender_; |
| |
| std::unique_ptr<RtpSenderContext> rtp_sender_context_; |
| |
| LoopbackTransportTest transport_; |
| const bool kMarkerBit; |
| FieldTrialConfig field_trials_; |
| StreamDataTestCallback rtp_stats_callback_; |
| |
| std::unique_ptr<RtpPacketToSend> BuildRtpPacket(int payload_type, |
| bool marker_bit, |
| uint32_t timestamp, |
| int64_t capture_time_ms) { |
| auto packet = rtp_sender()->AllocatePacket(); |
| packet->SetPayloadType(payload_type); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| packet->SetMarker(marker_bit); |
| packet->SetTimestamp(timestamp); |
| packet->set_capture_time_ms(capture_time_ms); |
| EXPECT_TRUE(rtp_sender()->AssignSequenceNumber(packet.get())); |
| return packet; |
| } |
| |
| std::unique_ptr<RtpPacketToSend> SendPacket(int64_t capture_time_ms, |
| int payload_length) { |
| uint32_t timestamp = capture_time_ms * 90; |
| auto packet = |
| BuildRtpPacket(kPayload, kMarkerBit, timestamp, capture_time_ms); |
| packet->AllocatePayload(payload_length); |
| packet->set_allow_retransmission(true); |
| |
| // Packet should be stored in a send bucket. |
| EXPECT_TRUE(rtp_sender()->SendToNetwork( |
| std::make_unique<RtpPacketToSend>(*packet))); |
| return packet; |
| } |
| |
| std::unique_ptr<RtpPacketToSend> SendGenericPacket() { |
| const int64_t kCaptureTimeMs = fake_clock_.TimeInMilliseconds(); |
| return SendPacket(kCaptureTimeMs, sizeof(kPayloadData)); |
| } |
| |
| size_t GenerateAndSendPadding(size_t target_size_bytes) { |
| size_t generated_bytes = 0; |
| for (auto& packet : |
| rtp_sender()->GeneratePadding(target_size_bytes, true)) { |
| generated_bytes += packet->payload_size() + packet->padding_size(); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| } |
| 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; |
| rtp_sender_context_->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(const std::string& mid) { |
| rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionMid, 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(const std::string& rid) { |
| rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionRtpStreamId, |
| kRidExtensionId); |
| rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionRepairedRtpStreamId, |
| kRepairedRidExtensionId); |
| rtp_sender()->SetRid(rid); |
| } |
| }; |
| |
| // TODO(pbos): Move tests over from WithoutPacer to RtpSenderTest as this is our |
| // default code path. |
| class RtpSenderTestWithoutPacer : public RtpSenderTest { |
| public: |
| void SetUp() override { SetUpRtpSender(false, false, false); } |
| }; |
| |
| TEST_P(RtpSenderTestWithoutPacer, AllocatePacketSetCsrc) { |
| // Configure rtp_sender with csrc. |
| std::vector<uint32_t> csrcs; |
| csrcs.push_back(0x23456789); |
| rtp_sender()->SetCsrcs(csrcs); |
| |
| auto packet = rtp_sender()->AllocatePacket(); |
| |
| ASSERT_TRUE(packet); |
| EXPECT_EQ(rtp_sender()->SSRC(), packet->Ssrc()); |
| EXPECT_EQ(csrcs, packet->Csrcs()); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, AllocatePacketReserveExtensions) { |
| // Configure rtp_sender with extensions. |
| ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransmissionTimeOffset, |
| kTransmissionTimeOffsetExtensionId)); |
| ASSERT_EQ(0, |
| rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId)); |
| ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionAudioLevel, kAudioLevelExtensionId)); |
| ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId)); |
| ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionVideoRotation, 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<AudioLevel>()); |
| EXPECT_FALSE(packet->HasExtension<VideoOrientation>()); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, AssignSequenceNumberAdvanceSequenceNumber) { |
| auto packet = rtp_sender()->AllocatePacket(); |
| ASSERT_TRUE(packet); |
| const uint16_t sequence_number = rtp_sender()->SequenceNumber(); |
| |
| EXPECT_TRUE(rtp_sender()->AssignSequenceNumber(packet.get())); |
| |
| EXPECT_EQ(sequence_number, packet->SequenceNumber()); |
| EXPECT_EQ(sequence_number + 1, rtp_sender()->SequenceNumber()); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, AssignSequenceNumberFailsOnNotSending) { |
| auto packet = rtp_sender()->AllocatePacket(); |
| ASSERT_TRUE(packet); |
| |
| rtp_sender()->SetSendingMediaStatus(false); |
| EXPECT_FALSE(rtp_sender()->AssignSequenceNumber(packet.get())); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, AssignSequenceNumberMayAllowPaddingOnVideo) { |
| constexpr size_t kPaddingSize = 100; |
| auto packet = rtp_sender()->AllocatePacket(); |
| ASSERT_TRUE(packet); |
| |
| ASSERT_TRUE(rtp_sender()->GeneratePadding(kPaddingSize, true).empty()); |
| packet->SetMarker(false); |
| ASSERT_TRUE(rtp_sender()->AssignSequenceNumber(packet.get())); |
| // Packet without marker bit doesn't allow padding on video stream. |
| ASSERT_TRUE(rtp_sender()->GeneratePadding(kPaddingSize, true).empty()); |
| |
| packet->SetMarker(true); |
| ASSERT_TRUE(rtp_sender()->AssignSequenceNumber(packet.get())); |
| // Packet with marker bit allows send padding. |
| ASSERT_FALSE(rtp_sender()->GeneratePadding(kPaddingSize, true).empty()); |
| } |
| |
| TEST_P(RtpSenderTest, AssignSequenceNumberAllowsPaddingOnAudio) { |
| MockTransport transport; |
| RtpRtcp::Configuration config; |
| config.audio = true; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport; |
| config.paced_sender = &mock_paced_sender_; |
| config.local_media_ssrc = kSsrc; |
| config.event_log = &mock_rtc_event_log_; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_context_ = std::make_unique<RtpSenderContext>(config); |
| |
| rtp_sender()->SetTimestampOffset(0); |
| |
| 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); |
| rtp_sender()->AssignSequenceNumber(audio_packet.get()); |
| |
| const size_t kPaddingSize = 59; |
| EXPECT_CALL(transport, SendRtp(_, kPaddingSize + kRtpHeaderSize, _)) |
| .WillOnce(Return(true)); |
| EXPECT_EQ(kPaddingSize, GenerateAndSendPadding(kPaddingSize)); |
| |
| // Requested padding size is too small, will send a larger one. |
| const size_t kMinPaddingSize = 50; |
| EXPECT_CALL(transport, SendRtp(_, kMinPaddingSize + kRtpHeaderSize, _)) |
| .WillOnce(Return(true)); |
| EXPECT_EQ(kMinPaddingSize, GenerateAndSendPadding(kMinPaddingSize - 5)); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, AssignSequenceNumberSetPaddingTimestamps) { |
| constexpr size_t kPaddingSize = 100; |
| auto packet = rtp_sender()->AllocatePacket(); |
| ASSERT_TRUE(packet); |
| packet->SetMarker(true); |
| packet->SetTimestamp(kTimestamp); |
| |
| ASSERT_TRUE(rtp_sender()->AssignSequenceNumber(packet.get())); |
| auto padding_packets = rtp_sender()->GeneratePadding(kPaddingSize, true); |
| |
| ASSERT_EQ(1u, padding_packets.size()); |
| // Verify padding packet timestamp. |
| EXPECT_EQ(kTimestamp, padding_packets[0]->Timestamp()); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, |
| TransportFeedbackObserverGetsCorrectByteCount) { |
| constexpr size_t kRtpOverheadBytesPerPacket = 12 + 8; |
| |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.local_media_ssrc = kSsrc; |
| config.transport_feedback_callback = &feedback_observer_; |
| config.event_log = &mock_rtc_event_log_; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| config.field_trials = &field_trials_; |
| rtp_sender_context_ = std::make_unique<RtpSenderContext>(config); |
| |
| EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId)); |
| |
| const size_t expected_bytes = |
| GetParam().with_overhead |
| ? sizeof(kPayloadData) + kRtpOverheadBytesPerPacket |
| : sizeof(kPayloadData); |
| |
| EXPECT_CALL(feedback_observer_, |
| OnAddPacket(AllOf( |
| Field(&RtpPacketSendInfo::ssrc, rtp_sender()->SSRC()), |
| Field(&RtpPacketSendInfo::transport_sequence_number, |
| kTransportSequenceNumber), |
| Field(&RtpPacketSendInfo::rtp_sequence_number, |
| rtp_sender()->SequenceNumber()), |
| Field(&RtpPacketSendInfo::length, expected_bytes), |
| Field(&RtpPacketSendInfo::pacing_info, PacedPacketInfo())))) |
| .Times(1); |
| EXPECT_EQ(rtp_sender()->ExpectedPerPacketOverhead(), |
| kRtpOverheadBytesPerPacket); |
| SendGenericPacket(); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, SendsPacketsWithTransportSequenceNumber) { |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.local_media_ssrc = kSsrc; |
| config.transport_feedback_callback = &feedback_observer_; |
| config.event_log = &mock_rtc_event_log_; |
| config.send_packet_observer = &send_packet_observer_; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_context_ = std::make_unique<RtpSenderContext>(config); |
| |
| EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId)); |
| |
| EXPECT_CALL(send_packet_observer_, |
| OnSendPacket(kTransportSequenceNumber, _, _)) |
| .Times(1); |
| |
| EXPECT_CALL(feedback_observer_, |
| OnAddPacket(AllOf( |
| Field(&RtpPacketSendInfo::ssrc, rtp_sender()->SSRC()), |
| Field(&RtpPacketSendInfo::transport_sequence_number, |
| kTransportSequenceNumber), |
| Field(&RtpPacketSendInfo::rtp_sequence_number, |
| rtp_sender()->SequenceNumber()), |
| Field(&RtpPacketSendInfo::pacing_info, PacedPacketInfo())))) |
| .Times(1); |
| |
| SendGenericPacket(); |
| |
| const auto& packet = transport_.last_sent_packet(); |
| uint16_t transport_seq_no; |
| ASSERT_TRUE(packet.GetExtension<TransportSequenceNumber>(&transport_seq_no)); |
| EXPECT_EQ(kTransportSequenceNumber, transport_seq_no); |
| EXPECT_EQ(transport_.last_options_.packet_id, transport_seq_no); |
| EXPECT_TRUE(transport_.last_options_.included_in_allocation); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, PacketOptionsNoRetransmission) { |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.local_media_ssrc = kSsrc; |
| config.transport_feedback_callback = &feedback_observer_; |
| config.event_log = &mock_rtc_event_log_; |
| config.send_packet_observer = &send_packet_observer_; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_context_ = std::make_unique<RtpSenderContext>(config); |
| |
| SendGenericPacket(); |
| |
| EXPECT_FALSE(transport_.last_options_.is_retransmit); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, |
| SetsIncludedInFeedbackWhenTransportSequenceNumberExtensionIsRegistered) { |
| SetUpRtpSender(false, false, false); |
| rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId); |
| EXPECT_CALL(send_packet_observer_, OnSendPacket).Times(1); |
| SendGenericPacket(); |
| EXPECT_TRUE(transport_.last_options_.included_in_feedback); |
| } |
| |
| TEST_P( |
| RtpSenderTestWithoutPacer, |
| SetsIncludedInAllocationWhenTransportSequenceNumberExtensionIsRegistered) { |
| SetUpRtpSender(false, false, false); |
| rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId); |
| EXPECT_CALL(send_packet_observer_, OnSendPacket).Times(1); |
| SendGenericPacket(); |
| EXPECT_TRUE(transport_.last_options_.included_in_allocation); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, |
| SetsIncludedInAllocationWhenForcedAsPartOfAllocation) { |
| SetUpRtpSender(false, false, false); |
| rtp_egress()->ForceIncludeSendPacketsInAllocation(true); |
| SendGenericPacket(); |
| EXPECT_FALSE(transport_.last_options_.included_in_feedback); |
| EXPECT_TRUE(transport_.last_options_.included_in_allocation); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, DoesnSetIncludedInAllocationByDefault) { |
| SetUpRtpSender(false, false, false); |
| SendGenericPacket(); |
| EXPECT_FALSE(transport_.last_options_.included_in_feedback); |
| EXPECT_FALSE(transport_.last_options_.included_in_allocation); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, OnSendSideDelayUpdated) { |
| StrictMock<MockSendSideDelayObserver> send_side_delay_observer_; |
| |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.local_media_ssrc = kSsrc; |
| config.send_side_delay_observer = &send_side_delay_observer_; |
| config.event_log = &mock_rtc_event_log_; |
| rtp_sender_context_ = std::make_unique<RtpSenderContext>(config); |
| |
| FieldTrialBasedConfig field_trials; |
| RTPSenderVideo::Config video_config; |
| video_config.clock = &fake_clock_; |
| video_config.rtp_sender = rtp_sender(); |
| video_config.field_trials = &field_trials; |
| RTPSenderVideo rtp_sender_video(video_config); |
| |
| const uint8_t kPayloadType = 127; |
| const absl::optional<VideoCodecType> kCodecType = |
| VideoCodecType::kVideoCodecGeneric; |
| |
| const uint32_t kCaptureTimeMsToRtpTimestamp = 90; // 90 kHz clock |
| RTPVideoHeader video_header; |
| |
| // Send packet with 10 ms send-side delay. The average, max and total should |
| // be 10 ms. |
| EXPECT_CALL(send_side_delay_observer_, |
| SendSideDelayUpdated(10, 10, 10, kSsrc)) |
| .Times(1); |
| int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); |
| fake_clock_.AdvanceTimeMilliseconds(10); |
| video_header.frame_type = VideoFrameType::kVideoFrameKey; |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| kPayloadType, kCodecType, capture_time_ms * kCaptureTimeMsToRtpTimestamp, |
| capture_time_ms, kPayloadData, nullptr, video_header, |
| kDefaultExpectedRetransmissionTimeMs)); |
| |
| // Send another packet with 20 ms delay. The average, max and total should be |
| // 15, 20 and 30 ms respectively. |
| EXPECT_CALL(send_side_delay_observer_, |
| SendSideDelayUpdated(15, 20, 30, kSsrc)) |
| .Times(1); |
| fake_clock_.AdvanceTimeMilliseconds(10); |
| video_header.frame_type = VideoFrameType::kVideoFrameKey; |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| kPayloadType, kCodecType, capture_time_ms * kCaptureTimeMsToRtpTimestamp, |
| capture_time_ms, kPayloadData, nullptr, video_header, |
| kDefaultExpectedRetransmissionTimeMs)); |
| |
| // Send another packet at the same time, which replaces the last packet. |
| // Since this packet has 0 ms delay, the average is now 5 ms and max is 10 ms. |
| // The total counter stays the same though. |
| // TODO(terelius): Is is not clear that this is the right behavior. |
| EXPECT_CALL(send_side_delay_observer_, SendSideDelayUpdated(5, 10, 30, kSsrc)) |
| .Times(1); |
| capture_time_ms = fake_clock_.TimeInMilliseconds(); |
| video_header.frame_type = VideoFrameType::kVideoFrameKey; |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| kPayloadType, kCodecType, capture_time_ms * kCaptureTimeMsToRtpTimestamp, |
| capture_time_ms, kPayloadData, nullptr, video_header, |
| kDefaultExpectedRetransmissionTimeMs)); |
| |
| // Send a packet 1 second later. The earlier packets should have timed |
| // out, so both max and average should be the delay of this packet. The total |
| // keeps increasing. |
| fake_clock_.AdvanceTimeMilliseconds(1000); |
| capture_time_ms = fake_clock_.TimeInMilliseconds(); |
| fake_clock_.AdvanceTimeMilliseconds(1); |
| EXPECT_CALL(send_side_delay_observer_, SendSideDelayUpdated(1, 1, 31, kSsrc)) |
| .Times(1); |
| video_header.frame_type = VideoFrameType::kVideoFrameKey; |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| kPayloadType, kCodecType, capture_time_ms * kCaptureTimeMsToRtpTimestamp, |
| capture_time_ms, kPayloadData, nullptr, video_header, |
| kDefaultExpectedRetransmissionTimeMs)); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, OnSendPacketUpdated) { |
| EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId)); |
| EXPECT_CALL(send_packet_observer_, |
| OnSendPacket(kTransportSequenceNumber, _, _)) |
| .Times(1); |
| |
| SendGenericPacket(); |
| } |
| |
| TEST_P(RtpSenderTest, SendsPacketsWithTransportSequenceNumber) { |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.paced_sender = &mock_paced_sender_; |
| config.local_media_ssrc = kSsrc; |
| config.transport_feedback_callback = &feedback_observer_; |
| config.event_log = &mock_rtc_event_log_; |
| config.send_packet_observer = &send_packet_observer_; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_context_ = std::make_unique<RtpSenderContext>(config); |
| |
| rtp_sender()->SetSequenceNumber(kSeqNum); |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 10); |
| EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId)); |
| |
| EXPECT_CALL(send_packet_observer_, |
| OnSendPacket(kTransportSequenceNumber, _, _)) |
| .Times(1); |
| EXPECT_CALL(feedback_observer_, |
| OnAddPacket(AllOf( |
| Field(&RtpPacketSendInfo::ssrc, rtp_sender()->SSRC()), |
| Field(&RtpPacketSendInfo::transport_sequence_number, |
| kTransportSequenceNumber), |
| Field(&RtpPacketSendInfo::rtp_sequence_number, |
| rtp_sender()->SequenceNumber()), |
| Field(&RtpPacketSendInfo::pacing_info, PacedPacketInfo())))) |
| .Times(1); |
| |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(Contains(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum)))))); |
| auto packet = SendGenericPacket(); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| // Transport sequence number is set by PacketRouter, before SendPacket(). |
| packet->SetExtension<TransportSequenceNumber>(kTransportSequenceNumber); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| |
| uint16_t transport_seq_no; |
| EXPECT_TRUE( |
| transport_.last_sent_packet().GetExtension<TransportSequenceNumber>( |
| &transport_seq_no)); |
| EXPECT_EQ(kTransportSequenceNumber, transport_seq_no); |
| EXPECT_EQ(transport_.last_options_.packet_id, transport_seq_no); |
| } |
| |
| TEST_P(RtpSenderTest, WritesPacerExitToTimingExtension) { |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 10); |
| EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionVideoTiming, kVideoTimingExtensionId)); |
| int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); |
| auto packet = rtp_sender()->AllocatePacket(); |
| packet->SetPayloadType(kPayload); |
| packet->SetMarker(true); |
| packet->SetTimestamp(kTimestamp); |
| packet->set_capture_time_ms(capture_time_ms); |
| const VideoSendTiming kVideoTiming = {0u, 0u, 0u, 0u, 0u, 0u, true}; |
| packet->SetExtension<VideoTimingExtension>(kVideoTiming); |
| EXPECT_TRUE(rtp_sender()->AssignSequenceNumber(packet.get())); |
| size_t packet_size = packet->size(); |
| |
| const int kStoredTimeInMs = 100; |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| packet->set_allow_retransmission(true); |
| EXPECT_CALL(mock_paced_sender_, EnqueuePackets(Contains(Pointee(Property( |
| &RtpPacketToSend::Ssrc, kSsrc))))); |
| EXPECT_TRUE( |
| rtp_sender()->SendToNetwork(std::make_unique<RtpPacketToSend>(*packet))); |
| fake_clock_.AdvanceTimeMilliseconds(kStoredTimeInMs); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| EXPECT_EQ(1, transport_.packets_sent()); |
| EXPECT_EQ(packet_size, transport_.last_sent_packet().size()); |
| |
| VideoSendTiming video_timing; |
| EXPECT_TRUE(transport_.last_sent_packet().GetExtension<VideoTimingExtension>( |
| &video_timing)); |
| EXPECT_EQ(kStoredTimeInMs, video_timing.pacer_exit_delta_ms); |
| } |
| |
| TEST_P(RtpSenderTest, WritesNetwork2ToTimingExtensionWithPacer) { |
| SetUpRtpSender(/*pacer=*/true, /*populate_network2=*/true, false); |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 10); |
| EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionVideoTiming, kVideoTimingExtensionId)); |
| int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); |
| auto packet = rtp_sender()->AllocatePacket(); |
| packet->SetPayloadType(kPayload); |
| packet->SetMarker(true); |
| packet->SetTimestamp(kTimestamp); |
| packet->set_capture_time_ms(capture_time_ms); |
| const uint16_t kPacerExitMs = 1234u; |
| const VideoSendTiming kVideoTiming = {0u, 0u, 0u, kPacerExitMs, 0u, 0u, true}; |
| packet->SetExtension<VideoTimingExtension>(kVideoTiming); |
| EXPECT_TRUE(rtp_sender()->AssignSequenceNumber(packet.get())); |
| size_t packet_size = packet->size(); |
| |
| const int kStoredTimeInMs = 100; |
| |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| packet->set_allow_retransmission(true); |
| EXPECT_CALL(mock_paced_sender_, EnqueuePackets(Contains(Pointee(Property( |
| &RtpPacketToSend::Ssrc, kSsrc))))); |
| EXPECT_TRUE( |
| rtp_sender()->SendToNetwork(std::make_unique<RtpPacketToSend>(*packet))); |
| fake_clock_.AdvanceTimeMilliseconds(kStoredTimeInMs); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| |
| EXPECT_EQ(1, transport_.packets_sent()); |
| EXPECT_EQ(packet_size, transport_.last_sent_packet().size()); |
| |
| VideoSendTiming video_timing; |
| EXPECT_TRUE(transport_.last_sent_packet().GetExtension<VideoTimingExtension>( |
| &video_timing)); |
| EXPECT_EQ(kStoredTimeInMs, video_timing.network2_timestamp_delta_ms); |
| EXPECT_EQ(kPacerExitMs, video_timing.pacer_exit_delta_ms); |
| } |
| |
| TEST_P(RtpSenderTest, WritesNetwork2ToTimingExtensionWithoutPacer) { |
| SetUpRtpSender(/*pacer=*/false, /*populate_network2=*/true, false); |
| EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionVideoTiming, kVideoTimingExtensionId)); |
| auto packet = rtp_sender()->AllocatePacket(); |
| packet->SetMarker(true); |
| packet->set_capture_time_ms(fake_clock_.TimeInMilliseconds()); |
| const VideoSendTiming kVideoTiming = {0u, 0u, 0u, 0u, 0u, 0u, true}; |
| packet->SetExtension<VideoTimingExtension>(kVideoTiming); |
| packet->set_allow_retransmission(true); |
| EXPECT_TRUE(rtp_sender()->AssignSequenceNumber(packet.get())); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| |
| const int kPropagateTimeMs = 10; |
| fake_clock_.AdvanceTimeMilliseconds(kPropagateTimeMs); |
| |
| EXPECT_TRUE(rtp_sender()->SendToNetwork(std::move(packet))); |
| |
| EXPECT_EQ(1, transport_.packets_sent()); |
| absl::optional<VideoSendTiming> video_timing = |
| transport_.last_sent_packet().GetExtension<VideoTimingExtension>(); |
| ASSERT_TRUE(video_timing); |
| EXPECT_EQ(kPropagateTimeMs, video_timing->network2_timestamp_delta_ms); |
| } |
| |
| TEST_P(RtpSenderTest, TrafficSmoothingWithExtensions) { |
| EXPECT_CALL(mock_rtc_event_log_, |
| LogProxy(SameRtcEventTypeAs(RtcEvent::Type::RtpPacketOutgoing))); |
| |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 10); |
| EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransmissionTimeOffset, |
| kTransmissionTimeOffsetExtensionId)); |
| EXPECT_EQ(0, |
| rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId)); |
| int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); |
| auto packet = |
| BuildRtpPacket(kPayload, kMarkerBit, kTimestamp, capture_time_ms); |
| size_t packet_size = packet->size(); |
| |
| const int kStoredTimeInMs = 100; |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(Contains(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum)))))); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| packet->set_allow_retransmission(true); |
| EXPECT_TRUE( |
| rtp_sender()->SendToNetwork(std::make_unique<RtpPacketToSend>(*packet))); |
| EXPECT_EQ(0, transport_.packets_sent()); |
| fake_clock_.AdvanceTimeMilliseconds(kStoredTimeInMs); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| |
| // Process send bucket. Packet should now be sent. |
| EXPECT_EQ(1, transport_.packets_sent()); |
| EXPECT_EQ(packet_size, transport_.last_sent_packet().size()); |
| |
| webrtc::RTPHeader rtp_header; |
| transport_.last_sent_packet().GetHeader(&rtp_header); |
| |
| // Verify transmission time offset. |
| EXPECT_EQ(kStoredTimeInMs * 90, rtp_header.extension.transmissionTimeOffset); |
| uint64_t expected_send_time = |
| ConvertMsToAbsSendTime(fake_clock_.TimeInMilliseconds()); |
| EXPECT_EQ(expected_send_time, rtp_header.extension.absoluteSendTime); |
| } |
| |
| TEST_P(RtpSenderTest, TrafficSmoothingRetransmits) { |
| EXPECT_CALL(mock_rtc_event_log_, |
| LogProxy(SameRtcEventTypeAs(RtcEvent::Type::RtpPacketOutgoing))); |
| |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 10); |
| EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransmissionTimeOffset, |
| kTransmissionTimeOffsetExtensionId)); |
| EXPECT_EQ(0, |
| rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId)); |
| int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); |
| auto packet = |
| BuildRtpPacket(kPayload, kMarkerBit, kTimestamp, capture_time_ms); |
| size_t packet_size = packet->size(); |
| |
| // Packet should be stored in a send bucket. |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(Contains(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum)))))); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| packet->set_allow_retransmission(true); |
| EXPECT_TRUE( |
| rtp_sender()->SendToNetwork(std::make_unique<RtpPacketToSend>(*packet))); |
| // Immediately process send bucket and send packet. |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| |
| EXPECT_EQ(1, transport_.packets_sent()); |
| |
| // Retransmit packet. |
| const int kStoredTimeInMs = 100; |
| fake_clock_.AdvanceTimeMilliseconds(kStoredTimeInMs); |
| |
| EXPECT_CALL(mock_rtc_event_log_, |
| LogProxy(SameRtcEventTypeAs(RtcEvent::Type::RtpPacketOutgoing))); |
| packet->set_packet_type(RtpPacketMediaType::kRetransmission); |
| packet->set_retransmitted_sequence_number(kSeqNum); |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(Contains(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum)))))); |
| EXPECT_EQ(static_cast<int>(packet_size), rtp_sender()->ReSendPacket(kSeqNum)); |
| EXPECT_EQ(1, transport_.packets_sent()); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| |
| // Process send bucket. Packet should now be sent. |
| EXPECT_EQ(2, transport_.packets_sent()); |
| EXPECT_EQ(packet_size, transport_.last_sent_packet().size()); |
| |
| webrtc::RTPHeader rtp_header; |
| transport_.last_sent_packet().GetHeader(&rtp_header); |
| |
| // Verify transmission time offset. |
| EXPECT_EQ(kStoredTimeInMs * 90, rtp_header.extension.transmissionTimeOffset); |
| uint64_t expected_send_time = |
| ConvertMsToAbsSendTime(fake_clock_.TimeInMilliseconds()); |
| EXPECT_EQ(expected_send_time, rtp_header.extension.absoluteSendTime); |
| } |
| |
| // This test sends 1 regular video packet, then 4 padding packets, and then |
| // 1 more regular packet. |
| TEST_P(RtpSenderTest, SendPadding) { |
| // Make all (non-padding) packets go to send queue. |
| EXPECT_CALL(mock_rtc_event_log_, |
| LogProxy(SameRtcEventTypeAs(RtcEvent::Type::RtpPacketOutgoing))) |
| .Times(1 + 4 + 1); |
| |
| uint16_t seq_num = kSeqNum; |
| uint32_t timestamp = kTimestamp; |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 10); |
| size_t rtp_header_len = kRtpHeaderSize; |
| EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransmissionTimeOffset, |
| kTransmissionTimeOffsetExtensionId)); |
| rtp_header_len += 4; // 4 bytes extension. |
| EXPECT_EQ(0, |
| rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId)); |
| rtp_header_len += 4; // 4 bytes extension. |
| rtp_header_len += 4; // 4 extra bytes common to all extension headers. |
| |
| webrtc::RTPHeader rtp_header; |
| |
| int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); |
| auto packet = |
| BuildRtpPacket(kPayload, kMarkerBit, timestamp, capture_time_ms); |
| const uint32_t media_packet_timestamp = timestamp; |
| size_t packet_size = packet->size(); |
| int total_packets_sent = 0; |
| const int kStoredTimeInMs = 100; |
| |
| // Packet should be stored in a send bucket. |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(Contains(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum)))))); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| packet->set_allow_retransmission(true); |
| EXPECT_TRUE( |
| rtp_sender()->SendToNetwork(std::make_unique<RtpPacketToSend>(*packet))); |
| EXPECT_EQ(total_packets_sent, transport_.packets_sent()); |
| fake_clock_.AdvanceTimeMilliseconds(kStoredTimeInMs); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| ++seq_num; |
| |
| // Packet should now be sent. This test doesn't verify the regular video |
| // packet, since it is tested in another test. |
| EXPECT_EQ(++total_packets_sent, transport_.packets_sent()); |
| timestamp += 90 * kStoredTimeInMs; |
| |
| // Send padding 4 times, waiting 50 ms between each. |
| for (int i = 0; i < 4; ++i) { |
| const int kPaddingPeriodMs = 50; |
| const size_t kPaddingBytes = 100; |
| const size_t kMaxPaddingLength = 224; // Value taken from rtp_sender.cc. |
| // Padding will be forced to full packets. |
| EXPECT_EQ(kMaxPaddingLength, GenerateAndSendPadding(kPaddingBytes)); |
| |
| // Process send bucket. Padding should now be sent. |
| EXPECT_EQ(++total_packets_sent, transport_.packets_sent()); |
| EXPECT_EQ(kMaxPaddingLength + rtp_header_len, |
| transport_.last_sent_packet().size()); |
| |
| transport_.last_sent_packet().GetHeader(&rtp_header); |
| EXPECT_EQ(kMaxPaddingLength, rtp_header.paddingLength); |
| |
| // Verify sequence number and timestamp. The timestamp should be the same |
| // as the last media packet. |
| EXPECT_EQ(seq_num++, rtp_header.sequenceNumber); |
| EXPECT_EQ(media_packet_timestamp, rtp_header.timestamp); |
| // Verify transmission time offset. |
| int offset = timestamp - media_packet_timestamp; |
| EXPECT_EQ(offset, rtp_header.extension.transmissionTimeOffset); |
| uint64_t expected_send_time = |
| ConvertMsToAbsSendTime(fake_clock_.TimeInMilliseconds()); |
| EXPECT_EQ(expected_send_time, rtp_header.extension.absoluteSendTime); |
| fake_clock_.AdvanceTimeMilliseconds(kPaddingPeriodMs); |
| timestamp += 90 * kPaddingPeriodMs; |
| } |
| |
| // Send a regular video packet again. |
| capture_time_ms = fake_clock_.TimeInMilliseconds(); |
| packet = BuildRtpPacket(kPayload, kMarkerBit, timestamp, capture_time_ms); |
| packet_size = packet->size(); |
| |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| packet->set_allow_retransmission(true); |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(Contains(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, seq_num)))))); |
| EXPECT_TRUE( |
| rtp_sender()->SendToNetwork(std::make_unique<RtpPacketToSend>(*packet))); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| |
| // Process send bucket. |
| EXPECT_EQ(++total_packets_sent, transport_.packets_sent()); |
| EXPECT_EQ(packet_size, transport_.last_sent_packet().size()); |
| transport_.last_sent_packet().GetHeader(&rtp_header); |
| |
| // Verify sequence number and timestamp. |
| EXPECT_EQ(seq_num, rtp_header.sequenceNumber); |
| EXPECT_EQ(timestamp, rtp_header.timestamp); |
| // Verify transmission time offset. This packet is sent without delay. |
| EXPECT_EQ(0, rtp_header.extension.transmissionTimeOffset); |
| uint64_t expected_send_time = |
| ConvertMsToAbsSendTime(fake_clock_.TimeInMilliseconds()); |
| EXPECT_EQ(expected_send_time, rtp_header.extension.absoluteSendTime); |
| } |
| |
| TEST_P(RtpSenderTest, OnSendPacketUpdated) { |
| EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId)); |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 10); |
| |
| EXPECT_CALL(send_packet_observer_, |
| OnSendPacket(kTransportSequenceNumber, _, _)) |
| .Times(1); |
| |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(Contains(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum)))))); |
| auto packet = SendGenericPacket(); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| packet->SetExtension<TransportSequenceNumber>(kTransportSequenceNumber); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| |
| EXPECT_EQ(1, transport_.packets_sent()); |
| } |
| |
| TEST_P(RtpSenderTest, OnSendPacketNotUpdatedForRetransmits) { |
| EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId)); |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 10); |
| |
| EXPECT_CALL(send_packet_observer_, OnSendPacket(_, _, _)).Times(0); |
| |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePackets(Contains(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum)))))); |
| auto packet = SendGenericPacket(); |
| packet->set_packet_type(RtpPacketMediaType::kRetransmission); |
| packet->SetExtension<TransportSequenceNumber>(kTransportSequenceNumber); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| |
| EXPECT_EQ(1, transport_.packets_sent()); |
| EXPECT_TRUE(transport_.last_options_.is_retransmit); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, SendGenericVideo) { |
| const uint8_t kPayloadType = 127; |
| const VideoCodecType kCodecType = VideoCodecType::kVideoCodecGeneric; |
| FieldTrialBasedConfig field_trials; |
| RTPSenderVideo::Config video_config; |
| video_config.clock = &fake_clock_; |
| video_config.rtp_sender = rtp_sender(); |
| video_config.field_trials = &field_trials; |
| RTPSenderVideo rtp_sender_video(video_config); |
| uint8_t payload[] = {47, 11, 32, 93, 89}; |
| |
| // Send keyframe |
| RTPVideoHeader video_header; |
| video_header.frame_type = VideoFrameType::kVideoFrameKey; |
| ASSERT_TRUE(rtp_sender_video.SendVideo(kPayloadType, kCodecType, 1234, 4321, |
| payload, nullptr, video_header, |
| kDefaultExpectedRetransmissionTimeMs)); |
| |
| auto sent_payload = transport_.last_sent_packet().payload(); |
| uint8_t generic_header = sent_payload[0]; |
| EXPECT_TRUE(generic_header & RtpFormatVideoGeneric::kKeyFrameBit); |
| EXPECT_TRUE(generic_header & RtpFormatVideoGeneric::kFirstPacketBit); |
| EXPECT_THAT(sent_payload.subview(1), ElementsAreArray(payload)); |
| |
| // Send delta frame |
| payload[0] = 13; |
| payload[1] = 42; |
| payload[4] = 13; |
| |
| video_header.frame_type = VideoFrameType::kVideoFrameDelta; |
| ASSERT_TRUE(rtp_sender_video.SendVideo(kPayloadType, kCodecType, 1234, 4321, |
| payload, nullptr, video_header, |
| kDefaultExpectedRetransmissionTimeMs)); |
| |
| sent_payload = transport_.last_sent_packet().payload(); |
| generic_header = sent_payload[0]; |
| EXPECT_FALSE(generic_header & RtpFormatVideoGeneric::kKeyFrameBit); |
| EXPECT_TRUE(generic_header & RtpFormatVideoGeneric::kFirstPacketBit); |
| EXPECT_THAT(sent_payload.subview(1), ElementsAreArray(payload)); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, SendRawVideo) { |
| const uint8_t kPayloadType = 111; |
| const uint8_t payload[] = {11, 22, 33, 44, 55}; |
| |
| FieldTrialBasedConfig field_trials; |
| RTPSenderVideo::Config video_config; |
| video_config.clock = &fake_clock_; |
| video_config.rtp_sender = rtp_sender(); |
| video_config.field_trials = &field_trials; |
| RTPSenderVideo rtp_sender_video(video_config); |
| |
| // Send a frame. |
| RTPVideoHeader video_header; |
| video_header.frame_type = VideoFrameType::kVideoFrameKey; |
| ASSERT_TRUE(rtp_sender_video.SendVideo(kPayloadType, absl::nullopt, 1234, |
| 4321, payload, nullptr, video_header, |
| kDefaultExpectedRetransmissionTimeMs)); |
| |
| auto sent_payload = transport_.last_sent_packet().payload(); |
| EXPECT_THAT(sent_payload, ElementsAreArray(payload)); |
| } |
| |
| TEST_P(RtpSenderTest, SendFlexfecPackets) { |
| constexpr uint32_t kTimestamp = 1234; |
| constexpr int kMediaPayloadType = 127; |
| constexpr VideoCodecType kCodecType = VideoCodecType::kVideoCodecGeneric; |
| constexpr int kFlexfecPayloadType = 118; |
| const std::vector<RtpExtension> kNoRtpExtensions; |
| const std::vector<RtpExtensionSize> kNoRtpExtensionSizes; |
| FlexfecSender flexfec_sender(kFlexfecPayloadType, kFlexFecSsrc, kSsrc, kNoMid, |
| kNoRtpExtensions, kNoRtpExtensionSizes, |
| nullptr /* rtp_state */, &fake_clock_); |
| |
| // Reset |rtp_sender_| to use FlexFEC. |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.paced_sender = &mock_paced_sender_; |
| config.local_media_ssrc = kSsrc; |
| config.fec_generator = &flexfec_sender_; |
| config.event_log = &mock_rtc_event_log_; |
| config.send_packet_observer = &send_packet_observer_; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_context_ = std::make_unique<RtpSenderContext>(config); |
| |
| rtp_sender()->SetSequenceNumber(kSeqNum); |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 10); |
| |
| FieldTrialBasedConfig field_trials; |
| RTPSenderVideo::Config video_config; |
| video_config.clock = &fake_clock_; |
| video_config.rtp_sender = rtp_sender(); |
| video_config.fec_generator = &flexfec_sender; |
| video_config.fec_type = flexfec_sender.GetFecType(); |
| video_config.fec_overhead_bytes = flexfec_sender.MaxPacketOverhead(); |
| video_config.field_trials = &field_trials; |
| RTPSenderVideo rtp_sender_video(video_config); |
| |
| // Parameters selected to generate a single FEC packet per media packet. |
| FecProtectionParams params; |
| params.fec_rate = 15; |
| params.max_fec_frames = 1; |
| params.fec_mask_type = kFecMaskRandom; |
| flexfec_sender.SetProtectionParameters(params, params); |
| |
| uint16_t flexfec_seq_num; |
| RTPVideoHeader video_header; |
| |
| std::unique_ptr<RtpPacketToSend> media_packet; |
| std::unique_ptr<RtpPacketToSend> fec_packet; |
| |
| EXPECT_CALL(mock_paced_sender_, EnqueuePackets) |
| .WillOnce([&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) { |
| for (auto& packet : packets) { |
| if (packet->packet_type() == RtpPacketMediaType::kVideo) { |
| EXPECT_EQ(packet->Ssrc(), kSsrc); |
| EXPECT_EQ(packet->SequenceNumber(), kSeqNum); |
| media_packet = std::move(packet); |
| } else { |
| EXPECT_EQ(packet->packet_type(), |
| RtpPacketMediaType::kForwardErrorCorrection); |
| EXPECT_EQ(packet->Ssrc(), kFlexFecSsrc); |
| fec_packet = std::move(packet); |
| } |
| } |
| }); |
| |
| video_header.frame_type = VideoFrameType::kVideoFrameKey; |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| kMediaPayloadType, kCodecType, kTimestamp, |
| fake_clock_.TimeInMilliseconds(), kPayloadData, nullptr, video_header, |
| kDefaultExpectedRetransmissionTimeMs)); |
| ASSERT_TRUE(media_packet != nullptr); |
| ASSERT_TRUE(fec_packet != nullptr); |
| |
| flexfec_seq_num = fec_packet->SequenceNumber(); |
| rtp_egress()->SendPacket(media_packet.get(), PacedPacketInfo()); |
| rtp_egress()->SendPacket(fec_packet.get(), PacedPacketInfo()); |
| |
| ASSERT_EQ(2, transport_.packets_sent()); |
| const RtpPacketReceived& sent_media_packet = transport_.sent_packets_[0]; |
| EXPECT_EQ(kMediaPayloadType, sent_media_packet.PayloadType()); |
| EXPECT_EQ(kSeqNum, sent_media_packet.SequenceNumber()); |
| EXPECT_EQ(kSsrc, sent_media_packet.Ssrc()); |
| const RtpPacketReceived& sent_flexfec_packet = transport_.sent_packets_[1]; |
| EXPECT_EQ(kFlexfecPayloadType, sent_flexfec_packet.PayloadType()); |
| EXPECT_EQ(flexfec_seq_num, sent_flexfec_packet.SequenceNumber()); |
| EXPECT_EQ(kFlexFecSsrc, sent_flexfec_packet.Ssrc()); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, SendFlexfecPackets) { |
| constexpr uint32_t kTimestamp = 1234; |
| constexpr int kMediaPayloadType = 127; |
| constexpr VideoCodecType kCodecType = VideoCodecType::kVideoCodecGeneric; |
| constexpr int kFlexfecPayloadType = 118; |
| const std::vector<RtpExtension> kNoRtpExtensions; |
| const std::vector<RtpExtensionSize> kNoRtpExtensionSizes; |
| FlexfecSender flexfec_sender(kFlexfecPayloadType, kFlexFecSsrc, kSsrc, kNoMid, |
| kNoRtpExtensions, kNoRtpExtensionSizes, |
| nullptr /* rtp_state */, &fake_clock_); |
| |
| // Reset |rtp_sender_| to use FlexFEC. |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.local_media_ssrc = kSsrc; |
| config.fec_generator = &flexfec_sender; |
| config.event_log = &mock_rtc_event_log_; |
| config.send_packet_observer = &send_packet_observer_; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_context_ = std::make_unique<RtpSenderContext>(config); |
| |
| rtp_sender()->SetSequenceNumber(kSeqNum); |
| |
| FieldTrialBasedConfig field_trials; |
| RTPSenderVideo::Config video_config; |
| video_config.clock = &fake_clock_; |
| video_config.rtp_sender = rtp_sender(); |
| video_config.fec_generator = &flexfec_sender; |
| video_config.fec_type = flexfec_sender.GetFecType(); |
| video_config.fec_overhead_bytes = flexfec_sender_.MaxPacketOverhead(); |
| video_config.field_trials = &field_trials; |
| RTPSenderVideo rtp_sender_video(video_config); |
| |
| // Parameters selected to generate a single FEC packet per media packet. |
| FecProtectionParams params; |
| params.fec_rate = 15; |
| params.max_fec_frames = 1; |
| params.fec_mask_type = kFecMaskRandom; |
| flexfec_sender.SetProtectionParameters(params, params); |
| |
| EXPECT_CALL(mock_rtc_event_log_, |
| LogProxy(SameRtcEventTypeAs(RtcEvent::Type::RtpPacketOutgoing))) |
| .Times(2); |
| RTPVideoHeader video_header; |
| video_header.frame_type = VideoFrameType::kVideoFrameKey; |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| kMediaPayloadType, kCodecType, kTimestamp, |
| fake_clock_.TimeInMilliseconds(), kPayloadData, nullptr, video_header, |
| kDefaultExpectedRetransmissionTimeMs)); |
| |
| ASSERT_EQ(2, transport_.packets_sent()); |
| const RtpPacketReceived& media_packet = transport_.sent_packets_[0]; |
| EXPECT_EQ(kMediaPayloadType, media_packet.PayloadType()); |
| EXPECT_EQ(kSsrc, media_packet.Ssrc()); |
| const RtpPacketReceived& flexfec_packet = transport_.sent_packets_[1]; |
| EXPECT_EQ(kFlexfecPayloadType, flexfec_packet.PayloadType()); |
| EXPECT_EQ(kFlexFecSsrc, flexfec_packet.Ssrc()); |
| } |
| |
| // Test that the MID header extension is included on sent packets when |
| // configured. |
| TEST_P(RtpSenderTestWithoutPacer, MidIncludedOnSentPackets) { |
| const char kMid[] = "mid"; |
| |
| EnableMidSending(kMid); |
| |
| // Send a couple packets. |
| SendGenericPacket(); |
| SendGenericPacket(); |
| |
| // Expect both packets to have the MID set. |
| ASSERT_EQ(2u, transport_.sent_packets_.size()); |
| for (const RtpPacketReceived& packet : transport_.sent_packets_) { |
| std::string mid; |
| ASSERT_TRUE(packet.GetExtension<RtpMid>(&mid)); |
| EXPECT_EQ(kMid, mid); |
| } |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, RidIncludedOnSentPackets) { |
| const char kRid[] = "f"; |
| |
| EnableRidSending(kRid); |
| |
| SendGenericPacket(); |
| |
| ASSERT_EQ(1u, transport_.sent_packets_.size()); |
| const RtpPacketReceived& packet = transport_.sent_packets_[0]; |
| std::string rid; |
| ASSERT_TRUE(packet.GetExtension<RtpStreamId>(&rid)); |
| EXPECT_EQ(kRid, rid); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, RidIncludedOnRtxSentPackets) { |
| const char kRid[] = "f"; |
| |
| EnableRtx(); |
| EnableRidSending(kRid); |
| |
| SendGenericPacket(); |
| ASSERT_EQ(1u, transport_.sent_packets_.size()); |
| const RtpPacketReceived& packet = transport_.sent_packets_[0]; |
| std::string rid; |
| ASSERT_TRUE(packet.GetExtension<RtpStreamId>(&rid)); |
| EXPECT_EQ(kRid, rid); |
| rid = kNoRid; |
| EXPECT_FALSE(packet.HasExtension<RepairedRtpStreamId>()); |
| |
| uint16_t packet_id = packet.SequenceNumber(); |
| rtp_sender()->ReSendPacket(packet_id); |
| ASSERT_EQ(2u, transport_.sent_packets_.size()); |
| const RtpPacketReceived& rtx_packet = transport_.sent_packets_[1]; |
| ASSERT_TRUE(rtx_packet.GetExtension<RepairedRtpStreamId>(&rid)); |
| EXPECT_EQ(kRid, rid); |
| EXPECT_FALSE(rtx_packet.HasExtension<RtpStreamId>()); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, MidAndRidNotIncludedOnSentPacketsAfterAck) { |
| const char kMid[] = "mid"; |
| const char kRid[] = "f"; |
| |
| EnableMidSending(kMid); |
| EnableRidSending(kRid); |
| |
| // This first packet should include both MID and RID. |
| auto first_built_packet = SendGenericPacket(); |
| |
| rtp_sender()->OnReceivedAckOnSsrc(first_built_packet->SequenceNumber()); |
| |
| // The second packet should include neither since an ack was received. |
| SendGenericPacket(); |
| |
| ASSERT_EQ(2u, transport_.sent_packets_.size()); |
| |
| const RtpPacketReceived& first_packet = transport_.sent_packets_[0]; |
| std::string mid, rid; |
| ASSERT_TRUE(first_packet.GetExtension<RtpMid>(&mid)); |
| EXPECT_EQ(kMid, mid); |
| ASSERT_TRUE(first_packet.GetExtension<RtpStreamId>(&rid)); |
| EXPECT_EQ(kRid, rid); |
| |
| const RtpPacketReceived& second_packet = transport_.sent_packets_[1]; |
| EXPECT_FALSE(second_packet.HasExtension<RtpMid>()); |
| EXPECT_FALSE(second_packet.HasExtension<RtpStreamId>()); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, |
| MidAndRidAlwaysIncludedOnSentPacketsWhenConfigured) { |
| SetUpRtpSender(false, false, /*always_send_mid_and_rid=*/true); |
| const char kMid[] = "mid"; |
| const char kRid[] = "f"; |
| EnableMidSending(kMid); |
| EnableRidSending(kRid); |
| |
| // Send two media packets: one before and one after the ack. |
| auto first_packet = SendGenericPacket(); |
| rtp_sender()->OnReceivedAckOnSsrc(first_packet->SequenceNumber()); |
| SendGenericPacket(); |
| |
| // Due to the configuration, both sent packets should contain MID and RID. |
| ASSERT_EQ(2u, transport_.sent_packets_.size()); |
| for (const RtpPacketReceived& packet : transport_.sent_packets_) { |
| EXPECT_EQ(packet.GetExtension<RtpMid>(), kMid); |
| EXPECT_EQ(packet.GetExtension<RtpStreamId>(), kRid); |
| } |
| } |
| |
| // 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_P(RtpSenderTestWithoutPacer, MidAndRidIncludedOnFirstRtxPacket) { |
| const char kMid[] = "mid"; |
| const char kRid[] = "f"; |
| |
| EnableRtx(); |
| EnableMidSending(kMid); |
| EnableRidSending(kRid); |
| |
| // This first packet will include both MID and RID. |
| auto first_built_packet = SendGenericPacket(); |
| rtp_sender()->OnReceivedAckOnSsrc(first_built_packet->SequenceNumber()); |
| |
| // The second packet will include neither since an ack was received. |
| auto second_built_packet = SendGenericPacket(); |
| |
| // The first RTX packet should include MID and RRID. |
| ASSERT_LT(0, |
| rtp_sender()->ReSendPacket(second_built_packet->SequenceNumber())); |
| |
| ASSERT_EQ(3u, transport_.sent_packets_.size()); |
| |
| const RtpPacketReceived& rtx_packet = transport_.sent_packets_[2]; |
| std::string mid, rrid; |
| ASSERT_TRUE(rtx_packet.GetExtension<RtpMid>(&mid)); |
| EXPECT_EQ(kMid, mid); |
| ASSERT_TRUE(rtx_packet.GetExtension<RepairedRtpStreamId>(&rrid)); |
| EXPECT_EQ(kRid, rrid); |
| } |
| |
| // 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_P(RtpSenderTestWithoutPacer, MidAndRidNotIncludedOnRtxPacketsAfterAck) { |
| const char kMid[] = "mid"; |
| const char kRid[] = "f"; |
| |
| EnableRtx(); |
| EnableMidSending(kMid); |
| EnableRidSending(kRid); |
| |
| // This first packet will include both MID and RID. |
| auto first_built_packet = SendGenericPacket(); |
| rtp_sender()->OnReceivedAckOnSsrc(first_built_packet->SequenceNumber()); |
| |
| // The second packet will include neither since an ack was received. |
| auto second_built_packet = SendGenericPacket(); |
| |
| // The first RTX packet will include MID and RRID. |
| ASSERT_LT(0, |
| rtp_sender()->ReSendPacket(second_built_packet->SequenceNumber())); |
| |
| ASSERT_EQ(3u, transport_.sent_packets_.size()); |
| const RtpPacketReceived& first_rtx_packet = transport_.sent_packets_[2]; |
| |
| rtp_sender()->OnReceivedAckOnRtxSsrc(first_rtx_packet.SequenceNumber()); |
| |
| // The second and third RTX packets should not include MID nor RRID. |
| ASSERT_LT(0, |
| rtp_sender()->ReSendPacket(first_built_packet->SequenceNumber())); |
| ASSERT_LT(0, |
| rtp_sender()->ReSendPacket(second_built_packet->SequenceNumber())); |
| |
| ASSERT_EQ(5u, transport_.sent_packets_.size()); |
| |
| const RtpPacketReceived& second_rtx_packet = transport_.sent_packets_[3]; |
| EXPECT_FALSE(second_rtx_packet.HasExtension<RtpMid>()); |
| EXPECT_FALSE(second_rtx_packet.HasExtension<RepairedRtpStreamId>()); |
| |
| const RtpPacketReceived& third_rtx_packet = transport_.sent_packets_[4]; |
| EXPECT_FALSE(third_rtx_packet.HasExtension<RtpMid>()); |
| EXPECT_FALSE(third_rtx_packet.HasExtension<RepairedRtpStreamId>()); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, |
| MidAndRidAlwaysIncludedOnRtxPacketsWhenConfigured) { |
| SetUpRtpSender(false, false, /*always_send_mid_and_rid=*/true); |
| const char kMid[] = "mid"; |
| const char kRid[] = "f"; |
| EnableRtx(); |
| EnableMidSending(kMid); |
| EnableRidSending(kRid); |
| |
| // Send two media packets: one before and one after the ack. |
| 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. |
| ASSERT_LT(0, rtp_sender()->ReSendPacket(media_packet2->SequenceNumber())); |
| ASSERT_EQ(3u, transport_.sent_packets_.size()); |
| rtp_sender()->OnReceivedAckOnRtxSsrc( |
| transport_.sent_packets_[2].SequenceNumber()); |
| ASSERT_LT(0, rtp_sender()->ReSendPacket(media_packet1->SequenceNumber())); |
| ASSERT_LT(0, rtp_sender()->ReSendPacket(media_packet2->SequenceNumber())); |
| |
| // Due to the configuration, all sent packets should contain MID |
| // and either RID (media) or RRID (RTX). |
| ASSERT_EQ(5u, transport_.sent_packets_.size()); |
| for (const auto& packet : transport_.sent_packets_) { |
| EXPECT_EQ(packet.GetExtension<RtpMid>(), kMid); |
| } |
| for (size_t i = 0; i < 2; ++i) { |
| const RtpPacketReceived& packet = transport_.sent_packets_[i]; |
| EXPECT_EQ(packet.GetExtension<RtpStreamId>(), kRid); |
| } |
| for (size_t i = 2; i < transport_.sent_packets_.size(); ++i) { |
| const RtpPacketReceived& packet = transport_.sent_packets_[i]; |
| EXPECT_EQ(packet.GetExtension<RepairedRtpStreamId>(), kRid); |
| } |
| } |
| |
| // 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_P(RtpSenderTestWithoutPacer, |
| MidAndRidNotIncludedOnSentPacketsAfterRtpStateRestored) { |
| const char kMid[] = "mid"; |
| const char kRid[] = "f"; |
| |
| EnableMidSending(kMid); |
| EnableRidSending(kRid); |
| |
| RtpState state = rtp_sender()->GetRtpState(); |
| EXPECT_FALSE(state.ssrc_has_acked); |
| state.ssrc_has_acked = true; |
| rtp_sender()->SetRtpState(state); |
| |
| SendGenericPacket(); |
| |
| ASSERT_EQ(1u, transport_.sent_packets_.size()); |
| const RtpPacketReceived& packet = transport_.sent_packets_[0]; |
| EXPECT_FALSE(packet.HasExtension<RtpMid>()); |
| EXPECT_FALSE(packet.HasExtension<RtpStreamId>()); |
| } |
| |
| // 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_P(RtpSenderTestWithoutPacer, |
| MidAndRridNotIncludedOnRtxPacketsAfterRtpStateRestored) { |
| const char kMid[] = "mid"; |
| const char kRid[] = "f"; |
| |
| EnableRtx(); |
| EnableMidSending(kMid); |
| EnableRidSending(kRid); |
| |
| RtpState rtx_state = rtp_sender()->GetRtxRtpState(); |
| EXPECT_FALSE(rtx_state.ssrc_has_acked); |
| rtx_state.ssrc_has_acked = true; |
| rtp_sender()->SetRtxRtpState(rtx_state); |
| |
| auto built_packet = SendGenericPacket(); |
| ASSERT_LT(0, rtp_sender()->ReSendPacket(built_packet->SequenceNumber())); |
| |
| ASSERT_EQ(2u, transport_.sent_packets_.size()); |
| const RtpPacketReceived& rtx_packet = transport_.sent_packets_[1]; |
| EXPECT_FALSE(rtx_packet.HasExtension<RtpMid>()); |
| EXPECT_FALSE(rtx_packet.HasExtension<RepairedRtpStreamId>()); |
| } |
| |
| TEST_P(RtpSenderTest, FecOverheadRate) { |
| constexpr uint32_t kTimestamp = 1234; |
| constexpr int kMediaPayloadType = 127; |
| constexpr VideoCodecType kCodecType = VideoCodecType::kVideoCodecGeneric; |
| constexpr int kFlexfecPayloadType = 118; |
| const std::vector<RtpExtension> kNoRtpExtensions; |
| const std::vector<RtpExtensionSize> kNoRtpExtensionSizes; |
| FlexfecSender flexfec_sender(kFlexfecPayloadType, kFlexFecSsrc, kSsrc, kNoMid, |
| kNoRtpExtensions, kNoRtpExtensionSizes, |
| nullptr /* rtp_state */, &fake_clock_); |
| |
| // Reset |rtp_sender_| to use FlexFEC. |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.paced_sender = &mock_paced_sender_; |
| config.local_media_ssrc = kSsrc; |
| config.fec_generator = &flexfec_sender; |
| config.event_log = &mock_rtc_event_log_; |
| config.send_packet_observer = &send_packet_observer_; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_context_ = std::make_unique<RtpSenderContext>(config); |
| |
| rtp_sender()->SetSequenceNumber(kSeqNum); |
| |
| FieldTrialBasedConfig field_trials; |
| RTPSenderVideo::Config video_config; |
| video_config.clock = &fake_clock_; |
| video_config.rtp_sender = rtp_sender(); |
| video_config.fec_generator = &flexfec_sender; |
| video_config.fec_type = flexfec_sender.GetFecType(); |
| video_config.fec_overhead_bytes = flexfec_sender.MaxPacketOverhead(); |
| video_config.field_trials = &field_trials; |
| RTPSenderVideo rtp_sender_video(video_config); |
| // Parameters selected to generate a single FEC packet per media packet. |
| FecProtectionParams params; |
| params.fec_rate = 15; |
| params.max_fec_frames = 1; |
| params.fec_mask_type = kFecMaskRandom; |
| flexfec_sender.SetProtectionParameters(params, params); |
| |
| constexpr size_t kNumMediaPackets = 10; |
| constexpr size_t kNumFecPackets = kNumMediaPackets; |
| constexpr int64_t kTimeBetweenPacketsMs = 10; |
| EXPECT_CALL(mock_paced_sender_, EnqueuePackets).Times(kNumMediaPackets); |
| for (size_t i = 0; i < kNumMediaPackets; ++i) { |
| RTPVideoHeader video_header; |
| |
| video_header.frame_type = VideoFrameType::kVideoFrameKey; |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| kMediaPayloadType, kCodecType, kTimestamp, |
| fake_clock_.TimeInMilliseconds(), kPayloadData, nullptr, video_header, |
| kDefaultExpectedRetransmissionTimeMs)); |
| |
| fake_clock_.AdvanceTimeMilliseconds(kTimeBetweenPacketsMs); |
| } |
| constexpr size_t kRtpHeaderLength = 12; |
| constexpr size_t kFlexfecHeaderLength = 20; |
| constexpr size_t kGenericCodecHeaderLength = 1; |
| constexpr size_t kPayloadLength = sizeof(kPayloadData); |
| constexpr size_t kPacketLength = kRtpHeaderLength + kFlexfecHeaderLength + |
| kGenericCodecHeaderLength + kPayloadLength; |
| EXPECT_NEAR(kNumFecPackets * kPacketLength * 8 / |
| (kNumFecPackets * kTimeBetweenPacketsMs / 1000.0f), |
| flexfec_sender.CurrentFecRate().bps<double>(), 500); |
| } |
| |
| TEST_P(RtpSenderTest, BitrateCallbacks) { |
| class TestCallback : public BitrateStatisticsObserver { |
| public: |
| TestCallback() |
| : BitrateStatisticsObserver(), |
| num_calls_(0), |
| ssrc_(0), |
| total_bitrate_(0), |
| retransmit_bitrate_(0) {} |
| ~TestCallback() override = default; |
| |
| void Notify(uint32_t total_bitrate, |
| uint32_t retransmit_bitrate, |
| uint32_t ssrc) override { |
| ++num_calls_; |
| ssrc_ = ssrc; |
| total_bitrate_ = total_bitrate; |
| retransmit_bitrate_ = retransmit_bitrate; |
| } |
| |
| uint32_t num_calls_; |
| uint32_t ssrc_; |
| uint32_t total_bitrate_; |
| uint32_t retransmit_bitrate_; |
| } callback; |
| |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.local_media_ssrc = kSsrc; |
| config.send_bitrate_observer = &callback; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_context_ = std::make_unique<RtpSenderContext>(config); |
| |
| FieldTrialBasedConfig field_trials; |
| RTPSenderVideo::Config video_config; |
| video_config.clock = &fake_clock_; |
| video_config.rtp_sender = rtp_sender(); |
| video_config.field_trials = &field_trials; |
| RTPSenderVideo rtp_sender_video(video_config); |
| const VideoCodecType kCodecType = VideoCodecType::kVideoCodecGeneric; |
| const uint8_t kPayloadType = 127; |
| |
| // Simulate kNumPackets sent with kPacketInterval ms intervals, with the |
| // number of packets selected so that we fill (but don't overflow) the one |
| // second averaging window. |
| const uint32_t kWindowSizeMs = 1000; |
| const uint32_t kPacketInterval = 20; |
| const uint32_t kNumPackets = |
| (kWindowSizeMs - kPacketInterval) / kPacketInterval; |
| // Overhead = 12 bytes RTP header + 1 byte generic header. |
| const uint32_t kPacketOverhead = 13; |
| |
| uint8_t payload[] = {47, 11, 32, 93, 89}; |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 1); |
| uint32_t ssrc = rtp_sender()->SSRC(); |
| |
| // Initial process call so we get a new time window. |
| rtp_egress()->ProcessBitrateAndNotifyObservers(); |
| |
| // Send a few frames. |
| RTPVideoHeader video_header; |
| for (uint32_t i = 0; i < kNumPackets; ++i) { |
| video_header.frame_type = VideoFrameType::kVideoFrameKey; |
| ASSERT_TRUE(rtp_sender_video.SendVideo( |
| kPayloadType, kCodecType, 1234, 4321, payload, nullptr, video_header, |
| kDefaultExpectedRetransmissionTimeMs)); |
| fake_clock_.AdvanceTimeMilliseconds(kPacketInterval); |
| } |
| |
| rtp_egress()->ProcessBitrateAndNotifyObservers(); |
| |
| // We get one call for every stats updated, thus two calls since both the |
| // stream stats and the retransmit stats are updated once. |
| EXPECT_EQ(2u, callback.num_calls_); |
| EXPECT_EQ(ssrc, callback.ssrc_); |
| const uint32_t kTotalPacketSize = kPacketOverhead + sizeof(payload); |
| // Bitrate measured over delta between last and first timestamp, plus one. |
| const uint32_t kExpectedWindowMs = kNumPackets * kPacketInterval + 1; |
| const uint32_t kExpectedBitsAccumulated = kTotalPacketSize * kNumPackets * 8; |
| const uint32_t kExpectedRateBps = |
| (kExpectedBitsAccumulated * 1000 + (kExpectedWindowMs / 2)) / |
| kExpectedWindowMs; |
| EXPECT_EQ(kExpectedRateBps, callback.total_bitrate_); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, StreamDataCountersCallbacks) { |
| const uint8_t kPayloadType = 127; |
| const VideoCodecType kCodecType = VideoCodecType::kVideoCodecGeneric; |
| FieldTrialBasedConfig field_trials; |
| RTPSenderVideo::Config video_config; |
| video_config.clock = &fake_clock_; |
| video_config.rtp_sender = rtp_sender(); |
| video_config.field_trials = &field_trials; |
| RTPSenderVideo rtp_sender_video(video_config); |
| uint8_t payload[] = {47, 11, 32, 93, 89}; |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 1); |
| uint32_t ssrc = rtp_sender()->SSRC(); |
| |
| // Send a frame. |
| RTPVideoHeader video_header; |
| video_header.frame_type = VideoFrameType::kVideoFrameKey; |
| ASSERT_TRUE(rtp_sender_video.SendVideo(kPayloadType, kCodecType, 1234, 4321, |
| payload, nullptr, video_header, |
| kDefaultExpectedRetransmissionTimeMs)); |
| StreamDataCounters expected; |
| expected.transmitted.payload_bytes = 6; |
| expected.transmitted.header_bytes = 12; |
| expected.transmitted.padding_bytes = 0; |
| expected.transmitted.packets = 1; |
| expected.retransmitted.payload_bytes = 0; |
| expected.retransmitted.header_bytes = 0; |
| expected.retransmitted.padding_bytes = 0; |
| expected.retransmitted.packets = 0; |
| expected.fec.packets = 0; |
| rtp_stats_callback_.Matches(ssrc, expected); |
| |
| // Retransmit a frame. |
| uint16_t seqno = rtp_sender()->SequenceNumber() - 1; |
| rtp_sender()->ReSendPacket(seqno); |
| expected.transmitted.payload_bytes = 12; |
| expected.transmitted.header_bytes = 24; |
| expected.transmitted.packets = 2; |
| expected.retransmitted.payload_bytes = 6; |
| expected.retransmitted.header_bytes = 12; |
| expected.retransmitted.padding_bytes = 0; |
| expected.retransmitted.packets = 1; |
| rtp_stats_callback_.Matches(ssrc, expected); |
| |
| // Send padding. |
| GenerateAndSendPadding(kMaxPaddingSize); |
| expected.transmitted.payload_bytes = 12; |
| expected.transmitted.header_bytes = 36; |
| expected.transmitted.padding_bytes = kMaxPaddingSize; |
| expected.transmitted.packets = 3; |
| rtp_stats_callback_.Matches(ssrc, expected); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, StreamDataCountersCallbacksUlpfec) { |
| const uint8_t kRedPayloadType = 96; |
| const uint8_t kUlpfecPayloadType = 97; |
| const uint8_t kPayloadType = 127; |
| const VideoCodecType kCodecType = VideoCodecType::kVideoCodecGeneric; |
| FieldTrialBasedConfig field_trials; |
| UlpfecGenerator ulpfec_generator(kRedPayloadType, kUlpfecPayloadType, |
| &fake_clock_); |
| RTPSenderVideo::Config video_config; |
| video_config.clock = &fake_clock_; |
| video_config.rtp_sender = rtp_sender(); |
| video_config.field_trials = &field_trials; |
| video_config.red_payload_type = kRedPayloadType; |
| video_config.fec_generator = &ulpfec_generator; |
| video_config.fec_type = ulpfec_generator.GetFecType(); |
| video_config.fec_overhead_bytes = ulpfec_generator.MaxPacketOverhead(); |
| RTPSenderVideo rtp_sender_video(video_config); |
| uint8_t payload[] = {47, 11, 32, 93, 89}; |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 1); |
| uint32_t ssrc = rtp_sender()->SSRC(); |
| |
| RTPVideoHeader video_header; |
| StreamDataCounters expected; |
| |
| // Send ULPFEC. |
| FecProtectionParams fec_params; |
| fec_params.fec_mask_type = kFecMaskRandom; |
| fec_params.fec_rate = 1; |
| fec_params.max_fec_frames = 1; |
| ulpfec_generator.SetProtectionParameters(fec_params, fec_params); |
| video_header.frame_type = VideoFrameType::kVideoFrameDelta; |
| ASSERT_TRUE(rtp_sender_video.SendVideo(kPayloadType, kCodecType, 1234, 4321, |
| payload, nullptr, video_header, |
| kDefaultExpectedRetransmissionTimeMs)); |
| expected.transmitted.payload_bytes = 28; |
| expected.transmitted.header_bytes = 24; |
| expected.transmitted.packets = 2; |
| expected.fec.packets = 1; |
| rtp_stats_callback_.Matches(ssrc, expected); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, BytesReportedCorrectly) { |
| // XXX const char* kPayloadName = "GENERIC"; |
| const uint8_t kPayloadType = 127; |
| rtp_sender()->SetRtxPayloadType(kPayloadType - 1, kPayloadType); |
| rtp_sender()->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); |
| |
| SendGenericPacket(); |
| // Will send 2 full-size padding packets. |
| GenerateAndSendPadding(1); |
| GenerateAndSendPadding(1); |
| |
| StreamDataCounters rtp_stats; |
| StreamDataCounters rtx_stats; |
| rtp_egress()->GetDataCounters(&rtp_stats, &rtx_stats); |
| |
| // Payload |
| EXPECT_GT(rtp_stats.first_packet_time_ms, -1); |
| EXPECT_EQ(rtp_stats.transmitted.payload_bytes, sizeof(kPayloadData)); |
| EXPECT_EQ(rtp_stats.transmitted.header_bytes, 12u); |
| EXPECT_EQ(rtp_stats.transmitted.padding_bytes, 0u); |
| EXPECT_EQ(rtx_stats.transmitted.payload_bytes, 0u); |
| EXPECT_EQ(rtx_stats.transmitted.header_bytes, 24u); |
| EXPECT_EQ(rtx_stats.transmitted.padding_bytes, 2 * kMaxPaddingSize); |
| |
| EXPECT_EQ(rtp_stats.transmitted.TotalBytes(), |
| rtp_stats.transmitted.payload_bytes + |
| rtp_stats.transmitted.header_bytes + |
| rtp_stats.transmitted.padding_bytes); |
| EXPECT_EQ(rtx_stats.transmitted.TotalBytes(), |
| rtx_stats.transmitted.payload_bytes + |
| rtx_stats.transmitted.header_bytes + |
| rtx_stats.transmitted.padding_bytes); |
| |
| EXPECT_EQ( |
| transport_.total_bytes_sent_, |
| rtp_stats.transmitted.TotalBytes() + rtx_stats.transmitted.TotalBytes()); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, RespectsNackBitrateLimit) { |
| const int32_t kPacketSize = 1400; |
| const int32_t kNumPackets = 30; |
| |
| retransmission_rate_limiter_.SetMaxRate(kPacketSize * kNumPackets * 8); |
| |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, kNumPackets); |
| const uint16_t kStartSequenceNumber = rtp_sender()->SequenceNumber(); |
| std::vector<uint16_t> sequence_numbers; |
| for (int32_t i = 0; i < kNumPackets; ++i) { |
| sequence_numbers.push_back(kStartSequenceNumber + i); |
| fake_clock_.AdvanceTimeMilliseconds(1); |
| SendPacket(fake_clock_.TimeInMilliseconds(), kPacketSize); |
| } |
| EXPECT_EQ(kNumPackets, transport_.packets_sent()); |
| |
| fake_clock_.AdvanceTimeMilliseconds(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). |
| rtp_sender()->OnReceivedNack(sequence_numbers, 0); |
| EXPECT_EQ(kNumPackets * 2, transport_.packets_sent()); |
| |
| // Must be at least 5ms in between retransmission attempts. |
| fake_clock_.AdvanceTimeMilliseconds(5); |
| |
| // Resending should not work, bandwidth exceeded. |
| rtp_sender()->OnReceivedNack(sequence_numbers, 0); |
| EXPECT_EQ(kNumPackets * 2, transport_.packets_sent()); |
| } |
| |
| TEST_P(RtpSenderTest, UpdatingCsrcsUpdatedOverhead) { |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.local_media_ssrc = kSsrc; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_context_ = std::make_unique<RtpSenderContext>(config); |
| |
| // Base RTP overhead is 12B. |
| EXPECT_EQ(rtp_sender()->ExpectedPerPacketOverhead(), 12u); |
| |
| // Adding two csrcs adds 2*4 bytes to the header. |
| rtp_sender()->SetCsrcs({1, 2}); |
| EXPECT_EQ(rtp_sender()->ExpectedPerPacketOverhead(), 20u); |
| } |
| |
| TEST_P(RtpSenderTest, OnOverheadChanged) { |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.local_media_ssrc = kSsrc; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_context_ = std::make_unique<RtpSenderContext>(config); |
| |
| // Base RTP overhead is 12B. |
| EXPECT_EQ(rtp_sender()->ExpectedPerPacketOverhead(), 12u); |
| |
| rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionTransmissionTimeOffset, |
| 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_P(RtpSenderTest, CountMidOnlyUntilAcked) { |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.local_media_ssrc = kSsrc; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_context_ = std::make_unique<RtpSenderContext>(config); |
| |
| // Base RTP overhead is 12B. |
| EXPECT_EQ(rtp_sender()->ExpectedPerPacketOverhead(), 12u); |
| |
| rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionMid, kMidExtensionId); |
| rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionRtpStreamId, |
| kRidExtensionId); |
| |
| // Counted only if set. |
| EXPECT_EQ(rtp_sender()->ExpectedPerPacketOverhead(), 12u); |
| rtp_sender()->SetMid("foo"); |
| EXPECT_EQ(rtp_sender()->ExpectedPerPacketOverhead(), 36u); |
| rtp_sender()->SetRid("bar"); |
| EXPECT_EQ(rtp_sender()->ExpectedPerPacketOverhead(), 52u); |
| |
| // Ack received, mid/rid no longer sent. |
| rtp_sender()->OnReceivedAckOnSsrc(0); |
| EXPECT_EQ(rtp_sender()->ExpectedPerPacketOverhead(), 12u); |
| } |
| |
| TEST_P(RtpSenderTest, DontCountVolatileExtensionsIntoOverhead) { |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.local_media_ssrc = kSsrc; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_context_ = std::make_unique<RtpSenderContext>(config); |
| |
| // Base RTP overhead is 12B. |
| EXPECT_EQ(rtp_sender()->ExpectedPerPacketOverhead(), 12u); |
| |
| rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionInbandComfortNoise, 1); |
| rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionAbsoluteCaptureTime, 2); |
| rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionVideoRotation, 3); |
| rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionPlayoutDelay, 4); |
| rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionVideoContentType, 5); |
| rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionVideoTiming, 6); |
| rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionRepairedRtpStreamId, 7); |
| rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionColorSpace, 8); |
| |
| // Still only 12B counted since can't count on above being sent. |
| EXPECT_EQ(rtp_sender()->ExpectedPerPacketOverhead(), 12u); |
| } |
| |
| TEST_P(RtpSenderTest, SendPacketMatchesVideo) { |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| |
| // Verify sent with correct SSRC. |
| packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->SetSsrc(kSsrc); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| EXPECT_EQ(transport_.packets_sent(), 1); |
| } |
| |
| TEST_P(RtpSenderTest, SendPacketMatchesAudio) { |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->set_packet_type(RtpPacketMediaType::kAudio); |
| |
| // Verify sent with correct SSRC. |
| packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->SetSsrc(kSsrc); |
| packet->set_packet_type(RtpPacketMediaType::kAudio); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| EXPECT_EQ(transport_.packets_sent(), 1); |
| } |
| |
| TEST_P(RtpSenderTest, SendPacketMatchesRetransmissions) { |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->set_packet_type(RtpPacketMediaType::kRetransmission); |
| |
| // Verify sent with correct SSRC (non-RTX). |
| packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->SetSsrc(kSsrc); |
| packet->set_packet_type(RtpPacketMediaType::kRetransmission); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| EXPECT_EQ(transport_.packets_sent(), 1); |
| |
| // RTX retransmission. |
| packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->SetSsrc(kRtxSsrc); |
| packet->set_packet_type(RtpPacketMediaType::kRetransmission); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| EXPECT_EQ(transport_.packets_sent(), 2); |
| } |
| |
| TEST_P(RtpSenderTest, SendPacketMatchesPadding) { |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->set_packet_type(RtpPacketMediaType::kPadding); |
| |
| // Verify sent with correct SSRC (non-RTX). |
| packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->SetSsrc(kSsrc); |
| packet->set_packet_type(RtpPacketMediaType::kPadding); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| EXPECT_EQ(transport_.packets_sent(), 1); |
| |
| // RTX padding. |
| packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->SetSsrc(kRtxSsrc); |
| packet->set_packet_type(RtpPacketMediaType::kPadding); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| EXPECT_EQ(transport_.packets_sent(), 2); |
| } |
| |
| TEST_P(RtpSenderTest, SendPacketMatchesFlexfec) { |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->set_packet_type(RtpPacketMediaType::kForwardErrorCorrection); |
| |
| // Verify sent with correct SSRC. |
| packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->SetSsrc(kFlexFecSsrc); |
| packet->set_packet_type(RtpPacketMediaType::kForwardErrorCorrection); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| EXPECT_EQ(transport_.packets_sent(), 1); |
| } |
| |
| TEST_P(RtpSenderTest, SendPacketMatchesUlpfec) { |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->set_packet_type(RtpPacketMediaType::kForwardErrorCorrection); |
| |
| // Verify sent with correct SSRC. |
| packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->SetSsrc(kSsrc); |
| packet->set_packet_type(RtpPacketMediaType::kForwardErrorCorrection); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| EXPECT_EQ(transport_.packets_sent(), 1); |
| } |
| |
| TEST_P(RtpSenderTest, SendPacketHandlesRetransmissionHistory) { |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 10); |
| |
| // Build a media packet and send it. |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| const uint16_t media_sequence_number = packet->SequenceNumber(); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| packet->set_allow_retransmission(true); |
| rtp_egress()->SendPacket(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(RtpPacketMediaType::kRetransmission); |
| packet->SetSsrc(kRtxSsrc); |
| packet->set_retransmitted_sequence_number(media_sequence_number); |
| packet->set_allow_retransmission(false); |
| rtp_egress()->SendPacket(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, SendPacketUpdatesExtensions) { |
| 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<RtpPacketToSend> 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(RtpPacketMediaType::kVideo); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| |
| const RtpPacketReceived& received_packet = transport_.last_sent_packet(); |
| |
| EXPECT_EQ(received_packet.GetExtension<TransmissionOffset>(), kDiffMs * 90); |
| |
| EXPECT_EQ(received_packet.GetExtension<AbsoluteSendTime>(), |
| AbsoluteSendTime::MsTo24Bits(fake_clock_.TimeInMilliseconds())); |
| |
| VideoSendTiming timing; |
| EXPECT_TRUE(received_packet.GetExtension<VideoTimingExtension>(&timing)); |
| EXPECT_EQ(timing.pacer_exit_delta_ms, kDiffMs); |
| } |
| |
| TEST_P(RtpSenderTest, SendPacketSetsPacketOptions) { |
| const uint16_t kPacketId = 42; |
| ASSERT_EQ(rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId), |
| 0); |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->SetExtension<TransportSequenceNumber>(kPacketId); |
| |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| EXPECT_CALL(send_packet_observer_, OnSendPacket); |
| rtp_egress()->SendPacket(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<TransportSequenceNumber>(kPacketId + 1); |
| packet->set_packet_type(RtpPacketMediaType::kRetransmission); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| EXPECT_TRUE(transport_.last_options_.is_retransmit); |
| } |
| |
| TEST_P(RtpSenderTest, SendPacketUpdatesStats) { |
| const size_t kPayloadSize = 1000; |
| |
| StrictMock<MockSendSideDelayObserver> send_side_delay_observer; |
| |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.local_media_ssrc = kSsrc; |
| config.rtx_send_ssrc = kRtxSsrc; |
| config.fec_generator = &flexfec_sender_; |
| config.send_side_delay_observer = &send_side_delay_observer; |
| config.event_log = &mock_rtc_event_log_; |
| config.send_packet_observer = &send_packet_observer_; |
| rtp_sender_context_ = std::make_unique<RtpSenderContext>(config); |
| ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId)); |
| |
| const int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); |
| |
| std::unique_ptr<RtpPacketToSend> video_packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| video_packet->set_packet_type(RtpPacketMediaType::kVideo); |
| video_packet->SetPayloadSize(kPayloadSize); |
| video_packet->SetExtension<TransportSequenceNumber>(1); |
| |
| std::unique_ptr<RtpPacketToSend> rtx_packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| rtx_packet->SetSsrc(kRtxSsrc); |
| rtx_packet->set_packet_type(RtpPacketMediaType::kRetransmission); |
| rtx_packet->SetPayloadSize(kPayloadSize); |
| rtx_packet->SetExtension<TransportSequenceNumber>(2); |
| |
| std::unique_ptr<RtpPacketToSend> fec_packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| fec_packet->SetSsrc(kFlexFecSsrc); |
| fec_packet->set_packet_type(RtpPacketMediaType::kForwardErrorCorrection); |
| fec_packet->SetPayloadSize(kPayloadSize); |
| fec_packet->SetExtension<TransportSequenceNumber>(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)); |
| |
| rtp_egress()->SendPacket(video_packet.get(), PacedPacketInfo()); |
| |
| // Send packet observer not called for padding/retransmissions. |
| EXPECT_CALL(send_packet_observer_, OnSendPacket(2, _, _)).Times(0); |
| rtp_egress()->SendPacket(rtx_packet.get(), PacedPacketInfo()); |
| |
| EXPECT_CALL(send_packet_observer_, |
| OnSendPacket(3, capture_time_ms, kFlexFecSsrc)); |
| rtp_egress()->SendPacket(fec_packet.get(), PacedPacketInfo()); |
| |
| StreamDataCounters rtp_stats; |
| StreamDataCounters rtx_stats; |
| rtp_egress()->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); |
| } |
| |
| TEST_P(RtpSenderTest, GeneratedPaddingHasBweExtensions) { |
| // Min requested size in order to use RTX payload. |
| const size_t kMinPaddingSize = 50; |
| |
| rtp_sender()->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); |
| rtp_sender()->SetRtxPayloadType(kRtxPayload, kPayload); |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 1); |
| |
| ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransmissionTimeOffset, |
| kTransmissionTimeOffsetExtensionId)); |
| ASSERT_EQ(0, |
| rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId)); |
| ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId)); |
| |
| // Send a payload packet first, to enable padding and populate the packet |
| // history. |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->set_allow_retransmission(true); |
| packet->SetPayloadSize(kMinPaddingSize); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| EXPECT_CALL(send_packet_observer_, OnSendPacket).Times(1); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| |
| // Generate a plain padding packet, check that extensions are registered. |
| std::vector<std::unique_ptr<RtpPacketToSend>> generated_packets = |
| rtp_sender()->GeneratePadding(/*target_size_bytes=*/1, true); |
| 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>()); |
| |
| // Verify all header extensions have been written. |
| rtp_egress()->SendPacket(plain_padding.get(), PacedPacketInfo()); |
| const auto& sent_plain_padding = transport_.last_sent_packet(); |
| EXPECT_TRUE(sent_plain_padding.HasExtension<TransportSequenceNumber>()); |
| EXPECT_TRUE(sent_plain_padding.HasExtension<AbsoluteSendTime>()); |
| EXPECT_TRUE(sent_plain_padding.HasExtension<TransmissionOffset>()); |
| webrtc::RTPHeader rtp_header; |
| sent_plain_padding.GetHeader(&rtp_header); |
| EXPECT_TRUE(rtp_header.extension.hasAbsoluteSendTime); |
| EXPECT_TRUE(rtp_header.extension.hasTransmissionTimeOffset); |
| EXPECT_TRUE(rtp_header.extension.hasTransportSequenceNumber); |
| |
| // Generate a payload padding packets, check that extensions are registered. |
| generated_packets = rtp_sender()->GeneratePadding(kMinPaddingSize, true); |
| 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>()); |
| |
| // Verify all header extensions have been written. |
| rtp_egress()->SendPacket(payload_padding.get(), PacedPacketInfo()); |
| const auto& sent_payload_padding = transport_.last_sent_packet(); |
| EXPECT_TRUE(sent_payload_padding.HasExtension<TransportSequenceNumber>()); |
| EXPECT_TRUE(sent_payload_padding.HasExtension<AbsoluteSendTime>()); |
| EXPECT_TRUE(sent_payload_padding.HasExtension<TransmissionOffset>()); |
| sent_payload_padding.GetHeader(&rtp_header); |
| EXPECT_TRUE(rtp_header.extension.hasAbsoluteSendTime); |
| EXPECT_TRUE(rtp_header.extension.hasTransmissionTimeOffset); |
| EXPECT_TRUE(rtp_header.extension.hasTransportSequenceNumber); |
| } |
| |
| TEST_P(RtpSenderTest, GeneratePaddingResendsOldPacketsWithRtx) { |
| // Min requested size in order to use RTX payload. |
| const size_t kMinPaddingSize = 50; |
| |
| rtp_sender()->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); |
| rtp_sender()->SetRtxPayloadType(kRtxPayload, kPayload); |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 1); |
| |
| ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId)); |
| |
| const size_t kPayloadPacketSize = kMinPaddingSize; |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->set_allow_retransmission(true); |
| packet->SetPayloadSize(kPayloadPacketSize); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| |
| // Send a dummy video packet so it ends up in the packet history. |
| EXPECT_CALL(send_packet_observer_, OnSendPacket).Times(1); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| |
| // Generated padding has large enough budget that the video packet should be |
| // retransmitted as padding. |
| std::vector<std::unique_ptr<RtpPacketToSend>> generated_packets = |
| rtp_sender()->GeneratePadding(kMinPaddingSize, true); |
| 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 = |
| rtp_sender()->GeneratePadding(kPaddingBytesRequested, true); |
| 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, kMaxPaddingSize); |
| } |
| |
| TEST_P(RtpSenderTest, LimitsPayloadPaddingSize) { |
| // Limit RTX payload padding to 2x target size. |
| const double kFactor = 2.0; |
| field_trials_.SetMaxPaddingFactor(kFactor); |
| SetUpRtpSender(true, false, false); |
| rtp_sender()->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); |
| rtp_sender()->SetRtxPayloadType(kRtxPayload, kPayload); |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 1); |
| |
| ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransportSequenceNumber, |
| 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, fake_clock_.TimeInMilliseconds()); |
| packet->set_allow_retransmission(true); |
| packet->SetPayloadSize(kPayloadPacketSize); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| EXPECT_CALL(send_packet_observer_, OnSendPacket).Times(1); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| |
| // 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( |
| rtp_sender()->GeneratePadding(kMinTargerSizeForPayload, true), |
| 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( |
| rtp_sender()->GeneratePadding(kMinTargerSizeForPayload - 1, true), |
| AllOf(Not(IsEmpty()), |
| Each(Pointee(Property(&RtpPacketToSend::padding_size, Gt(0u)))))); |
| } |
| |
| TEST_P(RtpSenderTest, GeneratePaddingCreatesPurePaddingWithoutRtx) { |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 1); |
| ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransmissionTimeOffset, |
| kTransmissionTimeOffsetExtensionId)); |
| ASSERT_EQ(0, |
| rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId)); |
| ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension( |
| kRtpExtensionTransportSequenceNumber, |
| 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, fake_clock_.TimeInMilliseconds()); |
| packet->set_allow_retransmission(true); |
| packet->SetPayloadSize(kPayloadPacketSize); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| EXPECT_CALL(send_packet_observer_, OnSendPacket).Times(1); |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| |
| // 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 + kMaxPaddingSize - 1) / kMaxPaddingSize; |
| size_t padding_bytes_generated = 0; |
| std::vector<std::unique_ptr<RtpPacketToSend>> padding_packets = |
| rtp_sender()->GeneratePadding(kPaddingBytesRequested, true); |
| 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>()); |
| |
| // Verify all header extensions are received. |
| rtp_egress()->SendPacket(packet.get(), PacedPacketInfo()); |
| webrtc::RTPHeader rtp_header; |
| transport_.last_sent_packet().GetHeader(&rtp_header); |
| EXPECT_TRUE(rtp_header.extension.hasAbsoluteSendTime); |
| EXPECT_TRUE(rtp_header.extension.hasTransmissionTimeOffset); |
| EXPECT_TRUE(rtp_header.extension.hasTransportSequenceNumber); |
| } |
| |
| EXPECT_EQ(padding_bytes_generated, |
| kExpectedNumPaddingPackets * kMaxPaddingSize); |
| } |
| |
| TEST_P(RtpSenderTest, SupportsPadding) { |
| bool kSendingMediaStats[] = {true, false}; |
| bool kEnableRedundantPayloads[] = {true, false}; |
| RTPExtensionType kBweExtensionTypes[] = { |
| kRtpExtensionTransportSequenceNumber, |
| kRtpExtensionTransportSequenceNumber02, kRtpExtensionAbsoluteSendTime, |
| kRtpExtensionTransmissionTimeOffset}; |
| 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()->SetRtxStatus(rtx_mode); |
| |
| for (auto extension_type : kBweExtensionTypes) { |
| EXPECT_FALSE(rtp_sender()->SupportsPadding()); |
| rtp_sender()->RegisterRtpHeaderExtension(extension_type, 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_type); |
| EXPECT_FALSE(rtp_sender()->SupportsPadding()); |
| } |
| } |
| } |
| } |
| |
| TEST_P(RtpSenderTest, SetsCaptureTimeAndPopulatesTransmissionOffset) { |
| rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionTransmissionTimeOffset, |
| kTransmissionTimeOffsetExtensionId); |
| |
| rtp_sender()->SetSendingMediaStatus(true); |
| rtp_sender()->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); |
| rtp_sender()->SetRtxPayloadType(kRtxPayload, kPayload); |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 10); |
| |
| const int64_t kMissingCaptureTimeMs = 0; |
| const uint32_t kTimestampTicksPerMs = 90; |
| const int64_t kOffsetMs = 10; |
| |
| auto packet = |
| BuildRtpPacket(kPayload, kMarkerBit, fake_clock_.TimeInMilliseconds(), |
| kMissingCaptureTimeMs); |
| packet->set_packet_type(RtpPacketMediaType::kVideo); |
| packet->ReserveExtension<TransmissionOffset>(); |
| packet->AllocatePayload(sizeof(kPayloadData)); |
| |
| std::unique_ptr<RtpPacketToSend> packet_to_pace; |
| EXPECT_CALL(mock_paced_sender_, EnqueuePackets) |
| .WillOnce([&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) { |
| EXPECT_EQ(packets.size(), 1u); |
| EXPECT_GT(packets[0]->capture_time_ms(), 0); |
| packet_to_pace = std::move(packets[0]); |
| }); |
| |
| packet->set_allow_retransmission(true); |
| EXPECT_TRUE(rtp_sender()->SendToNetwork(std::move(packet))); |
| |
| fake_clock_.AdvanceTimeMilliseconds(kOffsetMs); |
| |
| rtp_egress()->SendPacket(packet_to_pace.get(), PacedPacketInfo()); |
| |
| EXPECT_EQ(1, transport_.packets_sent()); |
| absl::optional<int32_t> transmission_time_extension = |
| transport_.sent_packets_.back().GetExtension<TransmissionOffset>(); |
| ASSERT_TRUE(transmission_time_extension.has_value()); |
| EXPECT_EQ(*transmission_time_extension, kOffsetMs * kTimestampTicksPerMs); |
| |
| // Retransmit packet. The RTX packet should get the same capture time as the |
| // original packet, so offset is delta from original packet to now. |
| fake_clock_.AdvanceTimeMilliseconds(kOffsetMs); |
| |
| std::unique_ptr<RtpPacketToSend> rtx_packet_to_pace; |
| EXPECT_CALL(mock_paced_sender_, EnqueuePackets) |
| .WillOnce([&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) { |
| EXPECT_GT(packets[0]->capture_time_ms(), 0); |
| rtx_packet_to_pace = std::move(packets[0]); |
| }); |
| |
| EXPECT_GT(rtp_sender()->ReSendPacket(kSeqNum), 0); |
| rtp_egress()->SendPacket(rtx_packet_to_pace.get(), PacedPacketInfo()); |
| |
| EXPECT_EQ(2, transport_.packets_sent()); |
| transmission_time_extension = |
| transport_.sent_packets_.back().GetExtension<TransmissionOffset>(); |
| ASSERT_TRUE(transmission_time_extension.has_value()); |
| EXPECT_EQ(*transmission_time_extension, |
| 2 * kOffsetMs * kTimestampTicksPerMs); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, ClearHistoryOnSequenceNumberCange) { |
| const int64_t kRtt = 10; |
| |
| rtp_sender()->SetSendingMediaStatus(true); |
| rtp_sender()->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); |
| rtp_sender()->SetRtxPayloadType(kRtxPayload, kPayload); |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 10); |
| rtp_sender_context_->packet_history_.SetRtt(kRtt); |
| |
| // Send a packet and record its sequence numbers. |
| SendGenericPacket(); |
| ASSERT_EQ(1u, transport_.sent_packets_.size()); |
| const uint16_t packet_seqence_number = |
| transport_.sent_packets_.back().SequenceNumber(); |
| |
| // Advance time and make sure it can be retransmitted, even if we try to set |
| // the ssrc the what it already is. |
| rtp_sender()->SetSequenceNumber(rtp_sender()->SequenceNumber()); |
| fake_clock_.AdvanceTimeMilliseconds(kRtt); |
| EXPECT_GT(rtp_sender()->ReSendPacket(packet_seqence_number), 0); |
| |
| // Change the sequence number, then move the time and try to retransmit again. |
| // The old packet should now be gone. |
| rtp_sender()->SetSequenceNumber(rtp_sender()->SequenceNumber() - 1); |
| fake_clock_.AdvanceTimeMilliseconds(kRtt); |
| EXPECT_EQ(rtp_sender()->ReSendPacket(packet_seqence_number), 0); |
| } |
| |
| TEST_P(RtpSenderTest, IgnoresNackAfterDisablingMedia) { |
| const int64_t kRtt = 10; |
| |
| rtp_sender()->SetSendingMediaStatus(true); |
| rtp_sender()->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); |
| rtp_sender()->SetRtxPayloadType(kRtxPayload, kPayload); |
| rtp_sender_context_->packet_history_.SetStorePacketsStatus( |
| RtpPacketHistory::StorageMode::kStoreAndCull, 10); |
| rtp_sender_context_->packet_history_.SetRtt(kRtt); |
| |
| // Send a packet so it is in the packet history. |
| std::unique_ptr<RtpPacketToSend> packet_to_pace; |
| EXPECT_CALL(mock_paced_sender_, EnqueuePackets) |
| .WillOnce([&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) { |
| packet_to_pace = std::move(packets[0]); |
| }); |
| |
| SendGenericPacket(); |
| rtp_egress()->SendPacket(packet_to_pace.get(), PacedPacketInfo()); |
| |
| ASSERT_EQ(1u, transport_.sent_packets_.size()); |
| |
| // Disable media sending and try to retransmit the packet, it should fail. |
| rtp_sender()->SetSendingMediaStatus(false); |
| fake_clock_.AdvanceTimeMilliseconds(kRtt); |
| EXPECT_LT(rtp_sender()->ReSendPacket(kSeqNum), 0); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(WithAndWithoutOverhead, |
| RtpSenderTest, |
| ::testing::Values(TestConfig{false}, |
| TestConfig{true})); |
| |
| INSTANTIATE_TEST_SUITE_P(WithAndWithoutOverhead, |
| RtpSenderTestWithoutPacer, |
| ::testing::Values(TestConfig{false}, |
| TestConfig{true})); |
| |
| } // namespace webrtc |