| /* |
| * 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 <memory> |
| #include <vector> |
| |
| #include "absl/memory/memory.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/events/rtc_event.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_header_parser.h" |
| #include "modules/rtp_rtcp/include/rtp_packet_pacer.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.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 "test/field_trial.h" |
| #include "test/gmock.h" |
| #include "test/gtest.h" |
| #include "test/mock_transport.h" |
| |
| namespace webrtc { |
| |
| namespace { |
| enum : int { // The first valid value is 1. |
| kAbsoluteSendTimeExtensionId = 1, |
| kAudioLevelExtensionId, |
| kGenericDescriptorId00, |
| kGenericDescriptorId01, |
| 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 = 0xaabbu; |
| 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::AtLeast; |
| using ::testing::DoAll; |
| using ::testing::ElementsAre; |
| using ::testing::ElementsAreArray; |
| using ::testing::Field; |
| using ::testing::Gt; |
| using ::testing::Invoke; |
| using ::testing::NiceMock; |
| using ::testing::Pointee; |
| using ::testing::Property; |
| using ::testing::Return; |
| using ::testing::SaveArg; |
| 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(kRtpExtensionTransmissionTimeOffset, |
| kTransmissionTimeOffsetExtensionId); |
| receivers_extensions_.Register(kRtpExtensionAbsoluteSendTime, |
| kAbsoluteSendTimeExtensionId); |
| receivers_extensions_.Register(kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId); |
| receivers_extensions_.Register(kRtpExtensionVideoRotation, |
| kVideoRotationExtensionId); |
| receivers_extensions_.Register(kRtpExtensionAudioLevel, |
| kAudioLevelExtensionId); |
| receivers_extensions_.Register(kRtpExtensionVideoTiming, |
| kVideoTimingExtensionId); |
| receivers_extensions_.Register(kRtpExtensionMid, kMidExtensionId); |
| receivers_extensions_.Register(kRtpExtensionGenericFrameDescriptor00, |
| kGenericDescriptorId00); |
| receivers_extensions_.Register(kRtpExtensionGenericFrameDescriptor01, |
| kGenericDescriptorId01); |
| receivers_extensions_.Register(kRtpExtensionRtpStreamId, kRidExtensionId); |
| receivers_extensions_.Register(kRtpExtensionRepairedRtpStreamId, |
| 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 { |
| TestConfig(bool with_overhead, bool pacer_references_packets) |
| : with_overhead(with_overhead), |
| pacer_references_packets(pacer_references_packets) {} |
| bool with_overhead = false; |
| bool pacer_references_packets = false; |
| }; |
| |
| std::string ToFieldTrialString(TestConfig config) { |
| std::string field_trials; |
| if (config.with_overhead) { |
| field_trials += "WebRTC-SendSideBwe-WithOverhead/Enabled/"; |
| } |
| if (config.pacer_references_packets) { |
| field_trials += "WebRTC-Pacer-LegacyPacketReferencing/Enabled/"; |
| } else { |
| field_trials += "WebRTC-Pacer-LegacyPacketReferencing/Disabled/"; |
| } |
| return field_trials; |
| } |
| |
| } // namespace |
| |
| class MockRtpPacketPacer : public RtpPacketPacer { |
| public: |
| MockRtpPacketPacer() {} |
| virtual ~MockRtpPacketPacer() {} |
| |
| MOCK_METHOD1(EnqueuePacket, void(std::unique_ptr<RtpPacketToSend>)); |
| |
| MOCK_METHOD6(InsertPacket, |
| void(Priority priority, |
| uint32_t ssrc, |
| uint16_t sequence_number, |
| int64_t capture_time_ms, |
| size_t bytes, |
| bool retransmission)); |
| }; |
| |
| class MockTransportSequenceNumberAllocator |
| : public TransportSequenceNumberAllocator { |
| public: |
| MOCK_METHOD0(AllocateSequenceNumber, uint16_t()); |
| }; |
| |
| 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&)); |
| MOCK_CONST_METHOD0(GetTransportFeedbackVector, std::vector<PacketFeedback>()); |
| }; |
| |
| class MockOverheadObserver : public OverheadObserver { |
| public: |
| MOCK_METHOD1(OnOverheadChanged, void(size_t overhead_bytes_per_packet)); |
| }; |
| |
| class RtpSenderTest : public ::testing::TestWithParam<TestConfig> { |
| protected: |
| RtpSenderTest() |
| : fake_clock_(kStartTime), |
| mock_rtc_event_log_(), |
| mock_paced_sender_(), |
| retransmission_rate_limiter_(&fake_clock_, 1000), |
| flexfec_sender_(0, |
| kFlexFecSsrc, |
| kSsrc, |
| "", |
| std::vector<RtpExtension>(), |
| std::vector<RtpExtensionSize>(), |
| nullptr, |
| &fake_clock_), |
| rtp_sender_(), |
| transport_(), |
| kMarkerBit(true), |
| field_trials_(ToFieldTrialString(GetParam())) {} |
| |
| void SetUp() override { SetUpRtpSender(true, false); } |
| |
| void SetUpRtpSender(bool pacer, bool populate_network2) { |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.media_send_ssrc = kSsrc; |
| config.rtx_send_ssrc = kRtxSsrc; |
| config.flexfec_sender = &flexfec_sender_; |
| config.transport_sequence_number_allocator = &seq_num_allocator_; |
| 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; |
| rtp_sender_.reset(new RTPSender(config)); |
| rtp_sender_->SetSequenceNumber(kSeqNum); |
| rtp_sender_->SetTimestampOffset(0); |
| } |
| |
| SimulatedClock fake_clock_; |
| NiceMock<MockRtcEventLog> mock_rtc_event_log_; |
| MockRtpPacketPacer mock_paced_sender_; |
| StrictMock<MockTransportSequenceNumberAllocator> seq_num_allocator_; |
| StrictMock<MockSendPacketObserver> send_packet_observer_; |
| StrictMock<MockTransportFeedbackObserver> feedback_observer_; |
| RateLimiter retransmission_rate_limiter_; |
| FlexfecSender flexfec_sender_; |
| std::unique_ptr<RTPSender> rtp_sender_; |
| LoopbackTransportTest transport_; |
| const bool kMarkerBit; |
| test::ScopedFieldTrials field_trials_; |
| |
| 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->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 should be stored in a send bucket. |
| EXPECT_TRUE(rtp_sender_->SendToNetwork( |
| absl::make_unique<RtpPacketToSend>(*packet), kAllowRetransmission, |
| RtpPacketSender::kNormalPriority)); |
| return packet; |
| } |
| |
| std::unique_ptr<RtpPacketToSend> SendGenericPacket() { |
| const int64_t kCaptureTimeMs = fake_clock_.TimeInMilliseconds(); |
| return SendPacket(kCaptureTimeMs, sizeof(kPayloadData)); |
| } |
| }; |
| |
| // 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); } |
| }; |
| |
| 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_FALSE(rtp_sender_->TimeToSendPadding(kPaddingSize, PacedPacketInfo())); |
| packet->SetMarker(false); |
| ASSERT_TRUE(rtp_sender_->AssignSequenceNumber(packet.get())); |
| // Packet without marker bit doesn't allow padding on video stream. |
| EXPECT_FALSE(rtp_sender_->TimeToSendPadding(kPaddingSize, PacedPacketInfo())); |
| |
| packet->SetMarker(true); |
| ASSERT_TRUE(rtp_sender_->AssignSequenceNumber(packet.get())); |
| // Packet with marker bit allows send padding. |
| EXPECT_TRUE(rtp_sender_->TimeToSendPadding(kPaddingSize, PacedPacketInfo())); |
| } |
| |
| 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.media_send_ssrc = kSsrc; |
| config.event_log = &mock_rtc_event_log_; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_ = absl::make_unique<RTPSender>(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, |
| rtp_sender_->TimeToSendPadding(kPaddingSize, PacedPacketInfo())); |
| |
| // 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, rtp_sender_->TimeToSendPadding(kMinPaddingSize - 5, |
| PacedPacketInfo())); |
| } |
| |
| 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())); |
| ASSERT_TRUE(rtp_sender_->TimeToSendPadding(kPaddingSize, PacedPacketInfo())); |
| |
| ASSERT_EQ(1u, transport_.sent_packets_.size()); |
| // Verify padding packet timestamp. |
| EXPECT_EQ(kTimestamp, transport_.last_sent_packet().Timestamp()); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, |
| TransportFeedbackObserverGetsCorrectByteCount) { |
| constexpr int kRtpOverheadBytesPerPacket = 12 + 8; |
| NiceMock<MockOverheadObserver> mock_overhead_observer; |
| |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.media_send_ssrc = kSsrc; |
| config.transport_sequence_number_allocator = &seq_num_allocator_; |
| config.transport_feedback_callback = &feedback_observer_; |
| config.event_log = &mock_rtc_event_log_; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| config.overhead_observer = &mock_overhead_observer; |
| rtp_sender_ = absl::make_unique<RTPSender>(config); |
| |
| EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( |
| kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId)); |
| EXPECT_CALL(seq_num_allocator_, AllocateSequenceNumber()) |
| .WillOnce(Return(kTransportSequenceNumber)); |
| |
| 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_CALL(mock_overhead_observer, |
| OnOverheadChanged(kRtpOverheadBytesPerPacket)) |
| .Times(1); |
| SendGenericPacket(); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, SendsPacketsWithTransportSequenceNumber) { |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.media_send_ssrc = kSsrc; |
| config.transport_sequence_number_allocator = &seq_num_allocator_; |
| 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_ = absl::make_unique<RTPSender>(config); |
| |
| EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( |
| kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId)); |
| |
| EXPECT_CALL(seq_num_allocator_, AllocateSequenceNumber()) |
| .WillOnce(Return(kTransportSequenceNumber)); |
| 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.media_send_ssrc = kSsrc; |
| config.transport_sequence_number_allocator = &seq_num_allocator_; |
| 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_ = absl::make_unique<RTPSender>(config); |
| |
| SendGenericPacket(); |
| |
| EXPECT_FALSE(transport_.last_options_.is_retransmit); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, |
| SetsIncludedInFeedbackWhenTransportSequenceNumberExtensionIsRegistered) { |
| SetUpRtpSender(false, false); |
| rtp_sender_->RegisterRtpHeaderExtension(kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId); |
| EXPECT_CALL(seq_num_allocator_, AllocateSequenceNumber()) |
| .WillOnce(Return(kTransportSequenceNumber)); |
| EXPECT_CALL(send_packet_observer_, OnSendPacket).Times(1); |
| SendGenericPacket(); |
| EXPECT_TRUE(transport_.last_options_.included_in_feedback); |
| } |
| |
| TEST_P( |
| RtpSenderTestWithoutPacer, |
| SetsIncludedInAllocationWhenTransportSequenceNumberExtensionIsRegistered) { |
| SetUpRtpSender(false, false); |
| rtp_sender_->RegisterRtpHeaderExtension(kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId); |
| EXPECT_CALL(seq_num_allocator_, AllocateSequenceNumber()) |
| .WillOnce(Return(kTransportSequenceNumber)); |
| EXPECT_CALL(send_packet_observer_, OnSendPacket).Times(1); |
| SendGenericPacket(); |
| EXPECT_TRUE(transport_.last_options_.included_in_allocation); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, |
| SetsIncludedInAllocationWhenForcedAsPartOfAllocation) { |
| SetUpRtpSender(false, false); |
| rtp_sender_->SetAsPartOfAllocation(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); |
| 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.media_send_ssrc = kSsrc; |
| config.send_side_delay_observer = &send_side_delay_observer_; |
| config.event_log = &mock_rtc_event_log_; |
| rtp_sender_ = absl::make_unique<RTPSender>(config); |
| |
| PlayoutDelayOracle playout_delay_oracle; |
| RTPSenderVideo rtp_sender_video(&fake_clock_, rtp_sender_.get(), nullptr, |
| &playout_delay_oracle, nullptr, false, false, |
| FieldTrialBasedConfig()); |
| |
| const uint8_t kPayloadType = 127; |
| const char payload_name[] = "GENERIC"; |
| |
| rtp_sender_video.RegisterPayloadType(kPayloadType, payload_name, |
| /*raw_payload=*/false); |
| |
| 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); |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| VideoFrameType::kVideoFrameKey, kPayloadType, |
| capture_time_ms * kCaptureTimeMsToRtpTimestamp, capture_time_ms, |
| kPayloadData, sizeof(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); |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| VideoFrameType::kVideoFrameKey, kPayloadType, |
| capture_time_ms * kCaptureTimeMsToRtpTimestamp, capture_time_ms, |
| kPayloadData, sizeof(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(); |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| VideoFrameType::kVideoFrameKey, kPayloadType, |
| capture_time_ms * kCaptureTimeMsToRtpTimestamp, capture_time_ms, |
| kPayloadData, sizeof(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); |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| VideoFrameType::kVideoFrameKey, kPayloadType, |
| capture_time_ms * kCaptureTimeMsToRtpTimestamp, capture_time_ms, |
| kPayloadData, sizeof(kPayloadData), nullptr, &video_header, |
| kDefaultExpectedRetransmissionTimeMs)); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, OnSendPacketUpdated) { |
| EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( |
| kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId)); |
| EXPECT_CALL(seq_num_allocator_, AllocateSequenceNumber()) |
| .WillOnce(Return(kTransportSequenceNumber)); |
| 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.media_send_ssrc = kSsrc; |
| config.transport_sequence_number_allocator = &seq_num_allocator_; |
| 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_ = absl::make_unique<RTPSender>(config); |
| |
| rtp_sender_->SetSequenceNumber(kSeqNum); |
| rtp_sender_->SetStorePacketsStatus(true, 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); |
| |
| if (GetParam().pacer_references_packets) { |
| EXPECT_CALL(mock_paced_sender_, InsertPacket(_, kSsrc, kSeqNum, _, _, _)); |
| SendGenericPacket(); |
| EXPECT_CALL(seq_num_allocator_, AllocateSequenceNumber()) |
| .WillOnce(Return(kTransportSequenceNumber)); |
| rtp_sender_->TimeToSendPacket(kSsrc, kSeqNum, |
| fake_clock_.TimeInMilliseconds(), false, |
| PacedPacketInfo()); |
| } else { |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePacket(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum))))); |
| auto packet = SendGenericPacket(); |
| packet->set_packet_type(RtpPacketToSend::Type::kVideo); |
| // Transport sequence number is set by PacketRouter, before TrySendPacket(). |
| packet->SetExtension<TransportSequenceNumber>(kTransportSequenceNumber); |
| rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()); |
| } |
| |
| const auto& packet = transport_.last_sent_packet(); |
| uint16_t transport_seq_no; |
| EXPECT_TRUE(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_->SetStorePacketsStatus(true, 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; |
| if (GetParam().pacer_references_packets) { |
| EXPECT_CALL( |
| mock_paced_sender_, |
| InsertPacket(RtpPacketSender::kNormalPriority, kSsrc, _, _, _, _)); |
| EXPECT_TRUE(rtp_sender_->SendToNetwork(std::move(packet), |
| kAllowRetransmission, |
| RtpPacketSender::kNormalPriority)); |
| fake_clock_.AdvanceTimeMilliseconds(kStoredTimeInMs); |
| rtp_sender_->TimeToSendPacket(kSsrc, kSeqNum, capture_time_ms, false, |
| PacedPacketInfo()); |
| } else { |
| packet->set_packet_type(RtpPacketToSend::Type::kVideo); |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePacket(Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)))); |
| EXPECT_TRUE(rtp_sender_->SendToNetwork( |
| absl::make_unique<RtpPacketToSend>(*packet), kAllowRetransmission)); |
| fake_clock_.AdvanceTimeMilliseconds(kStoredTimeInMs); |
| rtp_sender_->TrySendPacket(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); |
| rtp_sender_->SetStorePacketsStatus(true, 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; |
| |
| if (GetParam().pacer_references_packets) { |
| EXPECT_CALL( |
| mock_paced_sender_, |
| InsertPacket(RtpPacketSender::kNormalPriority, kSsrc, _, _, _, _)); |
| EXPECT_TRUE(rtp_sender_->SendToNetwork(std::move(packet), |
| kAllowRetransmission, |
| RtpPacketSender::kNormalPriority)); |
| fake_clock_.AdvanceTimeMilliseconds(kStoredTimeInMs); |
| rtp_sender_->TimeToSendPacket(kSsrc, kSeqNum, capture_time_ms, false, |
| PacedPacketInfo()); |
| } else { |
| packet->set_packet_type(RtpPacketToSend::Type::kVideo); |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePacket(Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)))); |
| EXPECT_TRUE(rtp_sender_->SendToNetwork( |
| absl::make_unique<RtpPacketToSend>(*packet), kAllowRetransmission, |
| RtpPacketSender::kNormalPriority)); |
| fake_clock_.AdvanceTimeMilliseconds(kStoredTimeInMs); |
| rtp_sender_->TrySendPacket(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); |
| 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); |
| EXPECT_TRUE(rtp_sender_->AssignSequenceNumber(packet.get())); |
| |
| const int kPropagateTimeMs = 10; |
| fake_clock_.AdvanceTimeMilliseconds(kPropagateTimeMs); |
| |
| EXPECT_TRUE(rtp_sender_->SendToNetwork(std::move(packet), |
| kAllowRetransmission, |
| RtpPacketSender::kNormalPriority)); |
| |
| 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_->SetStorePacketsStatus(true, 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; |
| if (GetParam().pacer_references_packets) { |
| EXPECT_CALL(mock_paced_sender_, |
| InsertPacket(RtpPacketSender::kNormalPriority, kSsrc, kSeqNum, |
| _, _, _)); |
| // Packet should be stored in a send bucket. |
| EXPECT_TRUE(rtp_sender_->SendToNetwork(std::move(packet), |
| kAllowRetransmission, |
| RtpPacketSender::kNormalPriority)); |
| EXPECT_EQ(0, transport_.packets_sent()); |
| fake_clock_.AdvanceTimeMilliseconds(kStoredTimeInMs); |
| rtp_sender_->TimeToSendPacket(kSsrc, kSeqNum, capture_time_ms, false, |
| PacedPacketInfo()); |
| } else { |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePacket(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum))))); |
| packet->set_packet_type(RtpPacketToSend::Type::kVideo); |
| EXPECT_TRUE(rtp_sender_->SendToNetwork( |
| absl::make_unique<RtpPacketToSend>(*packet), kAllowRetransmission)); |
| EXPECT_EQ(0, transport_.packets_sent()); |
| fake_clock_.AdvanceTimeMilliseconds(kStoredTimeInMs); |
| rtp_sender_->TrySendPacket(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_->SetStorePacketsStatus(true, 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. |
| if (GetParam().pacer_references_packets) { |
| EXPECT_CALL(mock_paced_sender_, |
| InsertPacket(RtpPacketSender::kNormalPriority, kSsrc, kSeqNum, |
| _, _, _)); |
| EXPECT_TRUE(rtp_sender_->SendToNetwork(std::move(packet), |
| kAllowRetransmission, |
| RtpPacketSender::kNormalPriority)); |
| // Immediately process send bucket and send packet. |
| rtp_sender_->TimeToSendPacket(kSsrc, kSeqNum, capture_time_ms, false, |
| PacedPacketInfo()); |
| } else { |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePacket(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum))))); |
| packet->set_packet_type(RtpPacketToSend::Type::kVideo); |
| packet->set_allow_retransmission(true); |
| EXPECT_TRUE(rtp_sender_->SendToNetwork( |
| absl::make_unique<RtpPacketToSend>(*packet), kAllowRetransmission)); |
| // Immediately process send bucket and send packet. |
| rtp_sender_->TrySendPacket(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))); |
| if (GetParam().pacer_references_packets) { |
| EXPECT_CALL(mock_paced_sender_, |
| InsertPacket(RtpPacketSender::kNormalPriority, kSsrc, kSeqNum, |
| _, _, _)); |
| EXPECT_EQ(static_cast<int>(packet_size), |
| rtp_sender_->ReSendPacket(kSeqNum)); |
| EXPECT_EQ(1, transport_.packets_sent()); |
| rtp_sender_->TimeToSendPacket(kSsrc, kSeqNum, capture_time_ms, true, |
| PacedPacketInfo()); |
| } else { |
| packet->set_packet_type(RtpPacketToSend::Type::kRetransmission); |
| packet->set_retransmitted_sequence_number(kSeqNum); |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePacket(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_sender_->TrySendPacket(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_->SetStorePacketsStatus(true, 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. |
| if (GetParam().pacer_references_packets) { |
| EXPECT_CALL(mock_paced_sender_, |
| InsertPacket(RtpPacketSender::kNormalPriority, kSsrc, kSeqNum, |
| _, _, _)); |
| EXPECT_TRUE(rtp_sender_->SendToNetwork(std::move(packet), |
| kAllowRetransmission, |
| RtpPacketSender::kNormalPriority)); |
| EXPECT_EQ(total_packets_sent, transport_.packets_sent()); |
| fake_clock_.AdvanceTimeMilliseconds(kStoredTimeInMs); |
| rtp_sender_->TimeToSendPacket(kSsrc, seq_num++, capture_time_ms, false, |
| PacedPacketInfo()); |
| } else { |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePacket(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum))))); |
| packet->set_packet_type(RtpPacketToSend::Type::kVideo); |
| packet->set_allow_retransmission(true); |
| EXPECT_TRUE(rtp_sender_->SendToNetwork( |
| absl::make_unique<RtpPacketToSend>(*packet), kAllowRetransmission)); |
| EXPECT_EQ(total_packets_sent, transport_.packets_sent()); |
| fake_clock_.AdvanceTimeMilliseconds(kStoredTimeInMs); |
| rtp_sender_->TrySendPacket(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, |
| rtp_sender_->TimeToSendPadding(kPaddingBytes, PacedPacketInfo())); |
| |
| // 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(); |
| |
| if (GetParam().pacer_references_packets) { |
| EXPECT_CALL(mock_paced_sender_, |
| InsertPacket(RtpPacketSender::kNormalPriority, kSsrc, seq_num, |
| _, _, _)); |
| // Packet should be stored in a send bucket. |
| EXPECT_TRUE(rtp_sender_->SendToNetwork(std::move(packet), |
| kAllowRetransmission, |
| RtpPacketSender::kNormalPriority)); |
| rtp_sender_->TimeToSendPacket(kSsrc, seq_num, capture_time_ms, false, |
| PacedPacketInfo()); |
| } else { |
| packet->set_packet_type(RtpPacketToSend::Type::kVideo); |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePacket(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, seq_num))))); |
| EXPECT_TRUE(rtp_sender_->SendToNetwork( |
| absl::make_unique<RtpPacketToSend>(*packet), kAllowRetransmission)); |
| rtp_sender_->TrySendPacket(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_->SetStorePacketsStatus(true, 10); |
| |
| EXPECT_CALL(send_packet_observer_, |
| OnSendPacket(kTransportSequenceNumber, _, _)) |
| .Times(1); |
| |
| if (GetParam().pacer_references_packets) { |
| const bool kIsRetransmit = false; |
| EXPECT_CALL(mock_paced_sender_, InsertPacket(_, kSsrc, kSeqNum, _, _, _)); |
| SendGenericPacket(); // Packet passed to pacer. |
| EXPECT_CALL(seq_num_allocator_, AllocateSequenceNumber()) |
| .WillOnce(::testing::Return(kTransportSequenceNumber)); |
| rtp_sender_->TimeToSendPacket(kSsrc, kSeqNum, |
| fake_clock_.TimeInMilliseconds(), |
| kIsRetransmit, PacedPacketInfo()); |
| } else { |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePacket(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum))))); |
| auto packet = SendGenericPacket(); |
| packet->set_packet_type(RtpPacketToSend::Type::kVideo); |
| packet->SetExtension<TransportSequenceNumber>(kTransportSequenceNumber); |
| rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()); |
| } |
| |
| EXPECT_EQ(1, transport_.packets_sent()); |
| } |
| |
| TEST_P(RtpSenderTest, OnSendPacketNotUpdatedForRetransmits) { |
| EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( |
| kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId)); |
| rtp_sender_->SetStorePacketsStatus(true, 10); |
| |
| EXPECT_CALL(send_packet_observer_, OnSendPacket(_, _, _)).Times(0); |
| |
| if (GetParam().pacer_references_packets) { |
| const bool kIsRetransmit = true; |
| EXPECT_CALL(mock_paced_sender_, InsertPacket(_, kSsrc, kSeqNum, _, _, _)); |
| SendGenericPacket(); // Packet passed to pacer. |
| EXPECT_CALL(seq_num_allocator_, AllocateSequenceNumber()) |
| .WillOnce(Return(kTransportSequenceNumber)); |
| rtp_sender_->TimeToSendPacket(kSsrc, kSeqNum, |
| fake_clock_.TimeInMilliseconds(), |
| kIsRetransmit, PacedPacketInfo()); |
| } else { |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePacket(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum))))); |
| auto packet = SendGenericPacket(); |
| packet->set_packet_type(RtpPacketToSend::Type::kRetransmission); |
| packet->SetExtension<TransportSequenceNumber>(kTransportSequenceNumber); |
| rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()); |
| } |
| |
| EXPECT_EQ(1, transport_.packets_sent()); |
| EXPECT_TRUE(transport_.last_options_.is_retransmit); |
| } |
| |
| TEST_P(RtpSenderTest, OnSendPacketNotUpdatedWithoutSeqNumAllocator) { |
| if (!GetParam().pacer_references_packets) { |
| // When PacedSender owns packets, there is no |
| // TransportSequenceNumberAllocator callback, so this test does not make any |
| // sense. |
| // TODO(bugs.webrtc.org/10633): Remove this test once old code is gone. |
| return; |
| } |
| |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.paced_sender = &mock_paced_sender_; |
| config.media_send_ssrc = kSsrc; |
| config.send_packet_observer = &send_packet_observer_; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_ = absl::make_unique<RTPSender>(config); |
| |
| rtp_sender_->SetSequenceNumber(kSeqNum); |
| EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( |
| kRtpExtensionTransportSequenceNumber, |
| kTransportSequenceNumberExtensionId)); |
| rtp_sender_->SetSequenceNumber(kSeqNum); |
| rtp_sender_->SetStorePacketsStatus(true, 10); |
| |
| EXPECT_CALL(send_packet_observer_, OnSendPacket(_, _, _)).Times(0); |
| |
| const bool kIsRetransmit = false; |
| EXPECT_CALL(mock_paced_sender_, InsertPacket(_, kSsrc, kSeqNum, _, _, _)); |
| SendGenericPacket(); // Packet passed to pacer. |
| rtp_sender_->TimeToSendPacket(kSsrc, kSeqNum, |
| fake_clock_.TimeInMilliseconds(), kIsRetransmit, |
| PacedPacketInfo()); |
| |
| EXPECT_EQ(1, transport_.packets_sent()); |
| } |
| |
| // TODO(bugs.webrtc.org/8975): Remove this test when non-useful padding is |
| // removed. |
| TEST_P(RtpSenderTest, SendRedundantPayloads) { |
| if (!GetParam().pacer_references_packets) { |
| // If PacedSender owns the RTP packets, GeneratePadding() family of methods |
| // will be called instead and this test makes no sense. |
| return; |
| } |
| |
| test::ScopedFieldTrials field_trials( |
| "WebRTC-PayloadPadding-UseMostUsefulPacket/Disabled/"); |
| MockTransport transport; |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport; |
| config.paced_sender = &mock_paced_sender_; |
| config.media_send_ssrc = kSsrc; |
| config.rtx_send_ssrc = kRtxSsrc; |
| config.event_log = &mock_rtc_event_log_; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_ = absl::make_unique<RTPSender>(config); |
| |
| rtp_sender_->SetSequenceNumber(kSeqNum); |
| rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload); |
| |
| uint16_t seq_num = kSeqNum; |
| rtp_sender_->SetStorePacketsStatus(true, 10); |
| int32_t rtp_header_len = kRtpHeaderSize; |
| 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. |
| |
| rtp_sender_->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); |
| |
| const size_t kNumPayloadSizes = 10; |
| const size_t kPayloadSizes[kNumPayloadSizes] = {500, 550, 600, 650, 700, |
| 750, 800, 850, 900, 950}; |
| // Expect all packets go through the pacer. |
| EXPECT_CALL(mock_rtc_event_log_, |
| LogProxy(SameRtcEventTypeAs(RtcEvent::Type::RtpPacketOutgoing))) |
| .Times(kNumPayloadSizes); |
| |
| // Send 10 packets of increasing size. |
| for (size_t i = 0; i < kNumPayloadSizes; ++i) { |
| int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); |
| |
| EXPECT_CALL(transport, SendRtp(_, _, _)).WillOnce(::testing::Return(true)); |
| |
| if (GetParam().pacer_references_packets) { |
| EXPECT_CALL(mock_paced_sender_, InsertPacket(_, kSsrc, seq_num, _, _, _)); |
| SendPacket(capture_time_ms, kPayloadSizes[i]); |
| rtp_sender_->TimeToSendPacket(kSsrc, seq_num, |
| fake_clock_.TimeInMilliseconds(), false, |
| PacedPacketInfo()); |
| } else { |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePacket(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, seq_num))))); |
| auto packet = SendPacket(capture_time_ms, kPayloadSizes[i]); |
| packet->set_packet_type(RtpPacketToSend::Type::kVideo); |
| rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()); |
| } |
| |
| ++seq_num; |
| fake_clock_.AdvanceTimeMilliseconds(33); |
| } |
| |
| EXPECT_CALL(mock_rtc_event_log_, |
| LogProxy(SameRtcEventTypeAs(RtcEvent::Type::RtpPacketOutgoing))) |
| .Times(AtLeast(4)); |
| |
| // The amount of padding to send it too small to send a payload packet. |
| EXPECT_CALL(transport, SendRtp(_, kMaxPaddingSize + rtp_header_len, _)) |
| .WillOnce(Return(true)); |
| EXPECT_EQ(kMaxPaddingSize, |
| rtp_sender_->TimeToSendPadding(49, PacedPacketInfo())); |
| |
| PacketOptions options; |
| EXPECT_CALL(transport, |
| SendRtp(_, kPayloadSizes[0] + rtp_header_len + kRtxHeaderSize, _)) |
| .WillOnce(DoAll(SaveArg<2>(&options), Return(true))); |
| EXPECT_EQ(kPayloadSizes[0], |
| rtp_sender_->TimeToSendPadding(500, PacedPacketInfo())); |
| EXPECT_TRUE(options.is_retransmit); |
| |
| EXPECT_CALL(transport, SendRtp(_, |
| kPayloadSizes[kNumPayloadSizes - 1] + |
| rtp_header_len + kRtxHeaderSize, |
| _)) |
| .WillOnce(Return(true)); |
| |
| options.is_retransmit = false; |
| EXPECT_CALL(transport, SendRtp(_, kMaxPaddingSize + rtp_header_len, _)) |
| .WillOnce(DoAll(SaveArg<2>(&options), Return(true))); |
| EXPECT_EQ(kPayloadSizes[kNumPayloadSizes - 1] + kMaxPaddingSize, |
| rtp_sender_->TimeToSendPadding(999, PacedPacketInfo())); |
| EXPECT_FALSE(options.is_retransmit); |
| } |
| |
| TEST_P(RtpSenderTest, SendRedundantPayloadsUsefulPadding) { |
| if (!GetParam().pacer_references_packets) { |
| // If PacedSender owns the RTP packets, GeneratePadding() family of methods |
| // will be called instead and this test makes no sense. |
| return; |
| } |
| |
| test::ScopedFieldTrials field_trials( |
| "WebRTC-PayloadPadding-UseMostUsefulPacket/Enabled/"); |
| MockTransport transport; |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport; |
| config.paced_sender = &mock_paced_sender_; |
| config.media_send_ssrc = kSsrc; |
| config.rtx_send_ssrc = kRtxSsrc; |
| config.event_log = &mock_rtc_event_log_; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_ = absl::make_unique<RTPSender>(config); |
| |
| rtp_sender_->SetSequenceNumber(kSeqNum); |
| rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload); |
| |
| uint16_t seq_num = kSeqNum; |
| rtp_sender_->SetStorePacketsStatus(true, 10); |
| int32_t rtp_header_len = kRtpHeaderSize; |
| 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. |
| |
| rtp_sender_->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); |
| |
| const size_t kNumPayloadSizes = 10; |
| const size_t kPayloadSizes[kNumPayloadSizes] = {500, 550, 600, 650, 700, |
| 750, 800, 850, 900, 950}; |
| // Expect all packets go through the pacer. |
| EXPECT_CALL(mock_rtc_event_log_, |
| LogProxy(SameRtcEventTypeAs(RtcEvent::Type::RtpPacketOutgoing))) |
| .Times(kNumPayloadSizes); |
| |
| // Send 10 packets of increasing size. |
| for (size_t i = 0; i < kNumPayloadSizes; ++i) { |
| int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); |
| |
| EXPECT_CALL(transport, SendRtp(_, _, _)).WillOnce(::testing::Return(true)); |
| |
| if (GetParam().pacer_references_packets) { |
| EXPECT_CALL(mock_paced_sender_, InsertPacket(_, kSsrc, seq_num, _, _, _)); |
| SendPacket(capture_time_ms, kPayloadSizes[i]); |
| rtp_sender_->TimeToSendPacket(kSsrc, seq_num, |
| fake_clock_.TimeInMilliseconds(), false, |
| PacedPacketInfo()); |
| } else { |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePacket(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, seq_num))))); |
| auto packet = SendPacket(capture_time_ms, kPayloadSizes[i]); |
| packet->set_packet_type(RtpPacketToSend::Type::kVideo); |
| rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()); |
| } |
| |
| ++seq_num; |
| fake_clock_.AdvanceTimeMilliseconds(33); |
| } |
| |
| EXPECT_CALL(mock_rtc_event_log_, |
| LogProxy(SameRtcEventTypeAs(RtcEvent::Type::RtpPacketOutgoing))) |
| .Times(AtLeast(4)); |
| |
| // The amount of padding to send it too small to send a payload packet. |
| EXPECT_CALL(transport, SendRtp(_, kMaxPaddingSize + rtp_header_len, _)) |
| .WillOnce(Return(true)); |
| EXPECT_EQ(kMaxPaddingSize, |
| rtp_sender_->TimeToSendPadding(49, PacedPacketInfo())); |
| |
| // Payload padding will prefer packets with lower transmit count first and |
| // lower age second. |
| EXPECT_CALL(transport, SendRtp(_, |
| kPayloadSizes[kNumPayloadSizes - 1] + |
| rtp_header_len + kRtxHeaderSize, |
| Field(&PacketOptions::is_retransmit, true))) |
| .WillOnce(Return(true)); |
| EXPECT_EQ(kPayloadSizes[kNumPayloadSizes - 1], |
| rtp_sender_->TimeToSendPadding(500, PacedPacketInfo())); |
| |
| EXPECT_CALL(transport, SendRtp(_, |
| kPayloadSizes[kNumPayloadSizes - 2] + |
| rtp_header_len + kRtxHeaderSize, |
| _)) |
| .WillOnce(Return(true)); |
| |
| EXPECT_CALL(transport, SendRtp(_, kMaxPaddingSize + rtp_header_len, |
| Field(&PacketOptions::is_retransmit, false))) |
| .WillOnce(Return(true)); |
| EXPECT_EQ(kPayloadSizes[kNumPayloadSizes - 2] + kMaxPaddingSize, |
| rtp_sender_->TimeToSendPadding( |
| kPayloadSizes[kNumPayloadSizes - 2] + 49, PacedPacketInfo())); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, SendGenericVideo) { |
| const char payload_name[] = "GENERIC"; |
| const uint8_t payload_type = 127; |
| PlayoutDelayOracle playout_delay_oracle; |
| RTPSenderVideo rtp_sender_video(&fake_clock_, rtp_sender_.get(), nullptr, |
| &playout_delay_oracle, nullptr, false, false, |
| FieldTrialBasedConfig()); |
| rtp_sender_video.RegisterPayloadType(payload_type, payload_name, |
| /*raw_payload=*/false); |
| uint8_t payload[] = {47, 11, 32, 93, 89}; |
| |
| // Send keyframe |
| RTPVideoHeader video_header; |
| ASSERT_TRUE(rtp_sender_video.SendVideo( |
| VideoFrameType::kVideoFrameKey, payload_type, 1234, 4321, payload, |
| sizeof(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; |
| |
| ASSERT_TRUE(rtp_sender_video.SendVideo( |
| VideoFrameType::kVideoFrameDelta, payload_type, 1234, 4321, payload, |
| sizeof(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 char payload_name[] = "VP8"; |
| const uint8_t payload_type = 111; |
| const uint8_t payload[] = {11, 22, 33, 44, 55}; |
| |
| PlayoutDelayOracle playout_delay_oracle; |
| RTPSenderVideo rtp_sender_video(&fake_clock_, rtp_sender_.get(), nullptr, |
| &playout_delay_oracle, nullptr, false, false, |
| FieldTrialBasedConfig()); |
| rtp_sender_video.RegisterPayloadType(payload_type, payload_name, |
| /*raw_payload=*/true); |
| |
| // Send a frame. |
| RTPVideoHeader video_header; |
| ASSERT_TRUE(rtp_sender_video.SendVideo( |
| VideoFrameType::kVideoFrameKey, payload_type, 1234, 4321, payload, |
| sizeof(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 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.media_send_ssrc = kSsrc; |
| config.flexfec_sender = &flexfec_sender_; |
| config.transport_sequence_number_allocator = &seq_num_allocator_; |
| config.event_log = &mock_rtc_event_log_; |
| config.send_packet_observer = &send_packet_observer_; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_ = absl::make_unique<RTPSender>(config); |
| |
| rtp_sender_->SetSequenceNumber(kSeqNum); |
| rtp_sender_->SetStorePacketsStatus(true, 10); |
| |
| PlayoutDelayOracle playout_delay_oracle; |
| RTPSenderVideo rtp_sender_video( |
| &fake_clock_, rtp_sender_.get(), &flexfec_sender, &playout_delay_oracle, |
| nullptr, false, false, FieldTrialBasedConfig()); |
| rtp_sender_video.RegisterPayloadType(kMediaPayloadType, "GENERIC", |
| /*raw_payload=*/false); |
| |
| // 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; |
| rtp_sender_video.SetFecParameters(params, params); |
| |
| uint16_t flexfec_seq_num; |
| RTPVideoHeader video_header; |
| |
| if (GetParam().pacer_references_packets) { |
| EXPECT_CALL(mock_paced_sender_, InsertPacket(RtpPacketSender::kLowPriority, |
| kSsrc, kSeqNum, _, _, false)); |
| EXPECT_CALL(mock_paced_sender_, InsertPacket(RtpPacketSender::kLowPriority, |
| kFlexFecSsrc, _, _, _, false)) |
| .WillOnce(::testing::SaveArg<2>(&flexfec_seq_num)); |
| |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| VideoFrameType::kVideoFrameKey, kMediaPayloadType, kTimestamp, |
| fake_clock_.TimeInMilliseconds(), kPayloadData, sizeof(kPayloadData), |
| nullptr, &video_header, kDefaultExpectedRetransmissionTimeMs)); |
| |
| EXPECT_EQ(RtpPacketSendResult::kSuccess, |
| rtp_sender_->TimeToSendPacket(kSsrc, kSeqNum, |
| fake_clock_.TimeInMilliseconds(), |
| false, PacedPacketInfo())); |
| EXPECT_EQ(RtpPacketSendResult::kSuccess, |
| rtp_sender_->TimeToSendPacket(kFlexFecSsrc, flexfec_seq_num, |
| fake_clock_.TimeInMilliseconds(), |
| false, PacedPacketInfo())); |
| } else { |
| std::unique_ptr<RtpPacketToSend> media_packet; |
| std::unique_ptr<RtpPacketToSend> fec_packet; |
| |
| EXPECT_CALL(mock_paced_sender_, EnqueuePacket) |
| .Times(2) |
| .WillRepeatedly([&](std::unique_ptr<RtpPacketToSend> packet) { |
| if (packet->packet_type() == RtpPacketToSend::Type::kVideo) { |
| EXPECT_EQ(packet->Ssrc(), kSsrc); |
| EXPECT_EQ(packet->SequenceNumber(), kSeqNum); |
| media_packet = std::move(packet); |
| } else { |
| EXPECT_EQ(packet->packet_type(), |
| RtpPacketToSend::Type::kForwardErrorCorrection); |
| EXPECT_EQ(packet->Ssrc(), kFlexFecSsrc); |
| fec_packet = std::move(packet); |
| } |
| }); |
| |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| VideoFrameType::kVideoFrameKey, kMediaPayloadType, kTimestamp, |
| fake_clock_.TimeInMilliseconds(), kPayloadData, sizeof(kPayloadData), |
| nullptr, &video_header, kDefaultExpectedRetransmissionTimeMs)); |
| ASSERT_TRUE(media_packet != nullptr); |
| ASSERT_TRUE(fec_packet != nullptr); |
| |
| flexfec_seq_num = fec_packet->SequenceNumber(); |
| rtp_sender_->TrySendPacket(media_packet.get(), PacedPacketInfo()); |
| rtp_sender_->TrySendPacket(fec_packet.get(), PacedPacketInfo()); |
| } |
| |
| ASSERT_EQ(2, transport_.packets_sent()); |
| const RtpPacketReceived& media_packet = transport_.sent_packets_[0]; |
| EXPECT_EQ(kMediaPayloadType, media_packet.PayloadType()); |
| EXPECT_EQ(kSeqNum, media_packet.SequenceNumber()); |
| EXPECT_EQ(kSsrc, media_packet.Ssrc()); |
| const RtpPacketReceived& flexfec_packet = transport_.sent_packets_[1]; |
| EXPECT_EQ(kFlexfecPayloadType, flexfec_packet.PayloadType()); |
| EXPECT_EQ(flexfec_seq_num, flexfec_packet.SequenceNumber()); |
| EXPECT_EQ(kFlexFecSsrc, flexfec_packet.Ssrc()); |
| } |
| |
| // TODO(ilnik): because of webrtc:7859. Once FEC moved below pacer, this test |
| // should be removed. |
| TEST_P(RtpSenderTest, NoFlexfecForTimingFrames) { |
| constexpr uint32_t kTimestamp = 1234; |
| const int64_t kCaptureTimeMs = fake_clock_.TimeInMilliseconds(); |
| constexpr int kMediaPayloadType = 127; |
| 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. |
| rtp_sender_.reset(new RTPSender( |
| false, &fake_clock_, &transport_, &mock_paced_sender_, |
| flexfec_sender.ssrc(), &seq_num_allocator_, nullptr, nullptr, nullptr, |
| &mock_rtc_event_log_, &send_packet_observer_, |
| &retransmission_rate_limiter_, nullptr, false, nullptr, false, false, |
| FieldTrialBasedConfig())); |
| rtp_sender_->SetSSRC(kSsrc); |
| rtp_sender_->SetSequenceNumber(kSeqNum); |
| rtp_sender_->SetStorePacketsStatus(true, 10); |
| |
| PlayoutDelayOracle playout_delay_oracle; |
| RTPSenderVideo rtp_sender_video( |
| &fake_clock_, rtp_sender_.get(), &flexfec_sender, &playout_delay_oracle, |
| nullptr, false, false, FieldTrialBasedConfig()); |
| rtp_sender_video.RegisterPayloadType(kMediaPayloadType, "GENERIC", |
| /*raw_payload=*/false); |
| |
| // Need extension to be registered for timing frames to be sent. |
| ASSERT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( |
| kRtpExtensionVideoTiming, kVideoTimingExtensionId)); |
| |
| // 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; |
| rtp_sender_video.SetFecParameters(params, params); |
| |
| RTPVideoHeader video_header; |
| video_header.video_timing.flags = VideoSendTiming::kTriggeredByTimer; |
| |
| EXPECT_CALL(mock_rtc_event_log_, |
| LogProxy(SameRtcEventTypeAs(RtcEvent::Type::RtpPacketOutgoing))) |
| .Times(1); |
| if (GetParam().pacer_references_packets) { |
| EXPECT_CALL(mock_paced_sender_, InsertPacket(RtpPacketSender::kLowPriority, |
| kSsrc, kSeqNum, _, _, false)); |
| EXPECT_CALL(mock_paced_sender_, InsertPacket(RtpPacketSender::kLowPriority, |
| kFlexFecSsrc, _, _, _, false)) |
| .Times(0); // Not called because packet should not be protected. |
| |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| VideoFrameType::kVideoFrameKey, kMediaPayloadType, kTimestamp, |
| kCaptureTimeMs, kPayloadData, sizeof(kPayloadData), nullptr, |
| &video_header, kDefaultExpectedRetransmissionTimeMs)); |
| |
| EXPECT_EQ(RtpPacketSendResult::kSuccess, |
| rtp_sender_->TimeToSendPacket(kSsrc, kSeqNum, |
| fake_clock_.TimeInMilliseconds(), |
| false, PacedPacketInfo())); |
| } else { |
| std::unique_ptr<RtpPacketToSend> rtp_packet; |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePacket(AllOf( |
| Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), |
| Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum))))) |
| .WillOnce([&rtp_packet](std::unique_ptr<RtpPacketToSend> packet) { |
| rtp_packet = std::move(packet); |
| }); |
| |
| EXPECT_CALL( |
| mock_paced_sender_, |
| EnqueuePacket(Pointee(Property(&RtpPacketToSend::Ssrc, kFlexFecSsrc)))) |
| .Times(0); // Not called because packet should not be protected. |
| |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| VideoFrameType::kVideoFrameKey, kMediaPayloadType, kTimestamp, |
| kCaptureTimeMs, kPayloadData, sizeof(kPayloadData), nullptr, |
| &video_header, kDefaultExpectedRetransmissionTimeMs)); |
| |
| EXPECT_TRUE( |
| rtp_sender_->TrySendPacket(rtp_packet.get(), PacedPacketInfo())); |
| } |
| |
| ASSERT_EQ(1, transport_.packets_sent()); |
| const RtpPacketReceived& media_packet = transport_.sent_packets_[0]; |
| EXPECT_EQ(kMediaPayloadType, media_packet.PayloadType()); |
| EXPECT_EQ(kSeqNum, media_packet.SequenceNumber()); |
| EXPECT_EQ(kSsrc, media_packet.Ssrc()); |
| |
| // Now try to send not a timing frame. |
| uint16_t flexfec_seq_num; |
| |
| EXPECT_CALL(mock_rtc_event_log_, |
| LogProxy(SameRtcEventTypeAs(RtcEvent::Type::RtpPacketOutgoing))) |
| .Times(2); |
| if (GetParam().pacer_references_packets) { |
| EXPECT_CALL(mock_paced_sender_, InsertPacket(RtpPacketSender::kLowPriority, |
| kFlexFecSsrc, _, _, _, false)) |
| .WillOnce(::testing::SaveArg<2>(&flexfec_seq_num)); |
| EXPECT_CALL(mock_paced_sender_, |
| InsertPacket(RtpPacketSender::kLowPriority, kSsrc, kSeqNum + 1, |
| _, _, false)); |
| video_header.video_timing.flags = VideoSendTiming::kInvalid; |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| VideoFrameType::kVideoFrameKey, kMediaPayloadType, kTimestamp + 1, |
| kCaptureTimeMs + 1, kPayloadData, sizeof(kPayloadData), nullptr, |
| &video_header, kDefaultExpectedRetransmissionTimeMs)); |
| |
| EXPECT_EQ(RtpPacketSendResult::kSuccess, |
| rtp_sender_->TimeToSendPacket(kSsrc, kSeqNum + 1, |
| fake_clock_.TimeInMilliseconds(), |
| false, PacedPacketInfo())); |
| EXPECT_EQ(RtpPacketSendResult::kSuccess, |
| rtp_sender_->TimeToSendPacket(kFlexFecSsrc, flexfec_seq_num, |
| fake_clock_.TimeInMilliseconds(), |
| false, PacedPacketInfo())); |
| } else { |
| std::unique_ptr<RtpPacketToSend> media_packet; |
| std::unique_ptr<RtpPacketToSend> fec_packet; |
| |
| EXPECT_CALL(mock_paced_sender_, EnqueuePacket) |
| .Times(2) |
| .WillRepeatedly([&](std::unique_ptr<RtpPacketToSend> packet) { |
| if (packet->packet_type() == RtpPacketToSend::Type::kVideo) { |
| EXPECT_EQ(packet->Ssrc(), kSsrc); |
| EXPECT_EQ(packet->SequenceNumber(), kSeqNum + 1); |
| media_packet = std::move(packet); |
| } else { |
| EXPECT_EQ(packet->packet_type(), |
| RtpPacketToSend::Type::kForwardErrorCorrection); |
| EXPECT_EQ(packet->Ssrc(), kFlexFecSsrc); |
| fec_packet = std::move(packet); |
| } |
| }); |
| |
| video_header.video_timing.flags = VideoSendTiming::kInvalid; |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| VideoFrameType::kVideoFrameKey, kMediaPayloadType, kTimestamp + 1, |
| kCaptureTimeMs + 1, kPayloadData, sizeof(kPayloadData), nullptr, |
| &video_header, kDefaultExpectedRetransmissionTimeMs)); |
| |
| ASSERT_TRUE(media_packet != nullptr); |
| ASSERT_TRUE(fec_packet != nullptr); |
| |
| flexfec_seq_num = fec_packet->SequenceNumber(); |
| rtp_sender_->TrySendPacket(media_packet.get(), PacedPacketInfo()); |
| rtp_sender_->TrySendPacket(fec_packet.get(), PacedPacketInfo()); |
| } |
| |
| ASSERT_EQ(3, transport_.packets_sent()); |
| const RtpPacketReceived& media_packet2 = transport_.sent_packets_[1]; |
| EXPECT_EQ(kMediaPayloadType, media_packet2.PayloadType()); |
| EXPECT_EQ(kSeqNum + 1, media_packet2.SequenceNumber()); |
| EXPECT_EQ(kSsrc, media_packet2.Ssrc()); |
| const RtpPacketReceived& flexfec_packet = transport_.sent_packets_[2]; |
| EXPECT_EQ(kFlexfecPayloadType, flexfec_packet.PayloadType()); |
| EXPECT_EQ(flexfec_seq_num, flexfec_packet.SequenceNumber()); |
| EXPECT_EQ(kFlexFecSsrc, flexfec_packet.Ssrc()); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, SendFlexfecPackets) { |
| constexpr uint32_t kTimestamp = 1234; |
| constexpr int kMediaPayloadType = 127; |
| 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.media_send_ssrc = kSsrc; |
| config.flexfec_sender = &flexfec_sender; |
| config.transport_sequence_number_allocator = &seq_num_allocator_; |
| config.event_log = &mock_rtc_event_log_; |
| config.send_packet_observer = &send_packet_observer_; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_ = absl::make_unique<RTPSender>(config); |
| |
| rtp_sender_->SetSequenceNumber(kSeqNum); |
| |
| PlayoutDelayOracle playout_delay_oracle; |
| RTPSenderVideo rtp_sender_video( |
| &fake_clock_, rtp_sender_.get(), &flexfec_sender, &playout_delay_oracle, |
| nullptr, false, false, FieldTrialBasedConfig()); |
| rtp_sender_video.RegisterPayloadType(kMediaPayloadType, "GENERIC", |
| /*raw_payload=*/false); |
| |
| // 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; |
| rtp_sender_video.SetFecParameters(params, params); |
| |
| EXPECT_CALL(mock_rtc_event_log_, |
| LogProxy(SameRtcEventTypeAs(RtcEvent::Type::RtpPacketOutgoing))) |
| .Times(2); |
| RTPVideoHeader video_header; |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| VideoFrameType::kVideoFrameKey, kMediaPayloadType, kTimestamp, |
| fake_clock_.TimeInMilliseconds(), kPayloadData, sizeof(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"; |
| |
| // Register MID header extension and set the MID for the RTPSender. |
| rtp_sender_->SetSendingMediaStatus(false); |
| rtp_sender_->RegisterRtpHeaderExtension(kRtpExtensionMid, kMidExtensionId); |
| rtp_sender_->SetMid(kMid); |
| rtp_sender_->SetSendingMediaStatus(true); |
| |
| // 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"; |
| |
| rtp_sender_->SetSendingMediaStatus(false); |
| rtp_sender_->RegisterRtpHeaderExtension(kRtpExtensionRtpStreamId, |
| kRidExtensionId); |
| rtp_sender_->SetRid(kRid); |
| rtp_sender_->SetSendingMediaStatus(true); |
| |
| 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"; |
| |
| rtp_sender_->SetSendingMediaStatus(false); |
| rtp_sender_->RegisterRtpHeaderExtension(kRtpExtensionRtpStreamId, |
| kRidExtensionId); |
| rtp_sender_->RegisterRtpHeaderExtension(kRtpExtensionRepairedRtpStreamId, |
| kRepairedRidExtensionId); |
| rtp_sender_->SetRid(kRid); |
| rtp_sender_->SetSendingMediaStatus(true); |
| |
| rtp_sender_->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); |
| rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload); |
| |
| rtp_sender_->SetStorePacketsStatus(true, 10); |
| |
| 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.GetExtension<RepairedRtpStreamId>(&rid)); |
| |
| 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(RtpSenderTest, FecOverheadRate) { |
| constexpr uint32_t kTimestamp = 1234; |
| constexpr int kMediaPayloadType = 127; |
| 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.media_send_ssrc = kSsrc; |
| config.flexfec_sender = &flexfec_sender; |
| config.transport_sequence_number_allocator = &seq_num_allocator_; |
| config.event_log = &mock_rtc_event_log_; |
| config.send_packet_observer = &send_packet_observer_; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_ = absl::make_unique<RTPSender>(config); |
| |
| rtp_sender_->SetSequenceNumber(kSeqNum); |
| |
| PlayoutDelayOracle playout_delay_oracle; |
| RTPSenderVideo rtp_sender_video( |
| &fake_clock_, rtp_sender_.get(), &flexfec_sender, &playout_delay_oracle, |
| nullptr, false, false, FieldTrialBasedConfig()); |
| rtp_sender_video.RegisterPayloadType(kMediaPayloadType, "GENERIC", |
| /*raw_payload=*/false); |
| // 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; |
| rtp_sender_video.SetFecParameters(params, params); |
| |
| constexpr size_t kNumMediaPackets = 10; |
| constexpr size_t kNumFecPackets = kNumMediaPackets; |
| constexpr int64_t kTimeBetweenPacketsMs = 10; |
| if (GetParam().pacer_references_packets) { |
| EXPECT_CALL(mock_paced_sender_, InsertPacket(_, _, _, _, _, false)) |
| .Times(kNumMediaPackets + kNumFecPackets); |
| } else { |
| EXPECT_CALL(mock_paced_sender_, EnqueuePacket) |
| .Times(kNumMediaPackets + kNumFecPackets); |
| } |
| for (size_t i = 0; i < kNumMediaPackets; ++i) { |
| RTPVideoHeader video_header; |
| |
| EXPECT_TRUE(rtp_sender_video.SendVideo( |
| VideoFrameType::kVideoFrameKey, kMediaPayloadType, kTimestamp, |
| fake_clock_.TimeInMilliseconds(), kPayloadData, sizeof(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), |
| rtp_sender_video.FecOverheadRate(), 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.media_send_ssrc = kSsrc; |
| config.send_bitrate_observer = &callback; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| rtp_sender_ = absl::make_unique<RTPSender>(config); |
| |
| PlayoutDelayOracle playout_delay_oracle; |
| RTPSenderVideo rtp_sender_video(&fake_clock_, rtp_sender_.get(), nullptr, |
| &playout_delay_oracle, nullptr, false, false, |
| FieldTrialBasedConfig()); |
| const char payload_name[] = "GENERIC"; |
| const uint8_t payload_type = 127; |
| rtp_sender_video.RegisterPayloadType(payload_type, payload_name, |
| /*raw_payload=*/false); |
| |
| // 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_->SetStorePacketsStatus(true, 1); |
| uint32_t ssrc = rtp_sender_->SSRC(); |
| |
| // Initial process call so we get a new time window. |
| rtp_sender_->ProcessBitrate(); |
| |
| // Send a few frames. |
| RTPVideoHeader video_header; |
| for (uint32_t i = 0; i < kNumPackets; ++i) { |
| ASSERT_TRUE(rtp_sender_video.SendVideo( |
| VideoFrameType::kVideoFrameKey, payload_type, 1234, 4321, payload, |
| sizeof(payload), nullptr, &video_header, |
| kDefaultExpectedRetransmissionTimeMs)); |
| fake_clock_.AdvanceTimeMilliseconds(kPacketInterval); |
| } |
| |
| rtp_sender_->ProcessBitrate(); |
| |
| // 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_); |
| |
| rtp_sender_.reset(); |
| } |
| |
| TEST_P(RtpSenderTestWithoutPacer, StreamDataCountersCallbacks) { |
| class TestCallback : public StreamDataCountersCallback { |
| public: |
| TestCallback() : StreamDataCountersCallback(), ssrc_(0), counters_() {} |
| ~TestCallback() 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); |
| } |
| } callback; |
| |
| const uint8_t kRedPayloadType = 96; |
| const uint8_t kUlpfecPayloadType = 97; |
| const char payload_name[] = "GENERIC"; |
| const uint8_t payload_type = 127; |
| PlayoutDelayOracle playout_delay_oracle; |
| RTPSenderVideo rtp_sender_video(&fake_clock_, rtp_sender_.get(), nullptr, |
| &playout_delay_oracle, nullptr, false, false, |
| FieldTrialBasedConfig()); |
| rtp_sender_video.RegisterPayloadType(payload_type, payload_name, |
| /*raw_payload=*/false); |
| uint8_t payload[] = {47, 11, 32, 93, 89}; |
| rtp_sender_->SetStorePacketsStatus(true, 1); |
| uint32_t ssrc = rtp_sender_->SSRC(); |
| |
| rtp_sender_->RegisterRtpStatisticsCallback(&callback); |
| |
| // Send a frame. |
| RTPVideoHeader video_header; |
| ASSERT_TRUE(rtp_sender_video.SendVideo( |
| VideoFrameType::kVideoFrameKey, payload_type, 1234, 4321, payload, |
| sizeof(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; |
| 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; |
| callback.Matches(ssrc, expected); |
| |
| // Send padding. |
| rtp_sender_->TimeToSendPadding(kMaxPaddingSize, PacedPacketInfo()); |
| expected.transmitted.payload_bytes = 12; |
| expected.transmitted.header_bytes = 36; |
| expected.transmitted.padding_bytes = kMaxPaddingSize; |
| expected.transmitted.packets = 3; |
| callback.Matches(ssrc, expected); |
| |
| // Send ULPFEC. |
| rtp_sender_video.SetUlpfecConfig(kRedPayloadType, kUlpfecPayloadType); |
| FecProtectionParams fec_params; |
| fec_params.fec_mask_type = kFecMaskRandom; |
| fec_params.fec_rate = 1; |
| fec_params.max_fec_frames = 1; |
| rtp_sender_video.SetFecParameters(fec_params, fec_params); |
| ASSERT_TRUE(rtp_sender_video.SendVideo( |
| VideoFrameType::kVideoFrameDelta, payload_type, 1234, 4321, payload, |
| sizeof(payload), nullptr, &video_header, |
| kDefaultExpectedRetransmissionTimeMs)); |
| expected.transmitted.payload_bytes = 40; |
| expected.transmitted.header_bytes = 60; |
| expected.transmitted.packets = 5; |
| expected.fec.packets = 1; |
| callback.Matches(ssrc, expected); |
| |
| rtp_sender_->RegisterRtpStatisticsCallback(nullptr); |
| } |
| |
| 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. |
| rtp_sender_->TimeToSendPadding(1, PacedPacketInfo()); |
| rtp_sender_->TimeToSendPadding(1, PacedPacketInfo()); |
| |
| StreamDataCounters rtp_stats; |
| StreamDataCounters rtx_stats; |
| rtp_sender_->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_->SetStorePacketsStatus(true, 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, OnOverheadChanged) { |
| MockOverheadObserver mock_overhead_observer; |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.media_send_ssrc = kSsrc; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| config.overhead_observer = &mock_overhead_observer; |
| rtp_sender_ = absl::make_unique<RTPSender>(config); |
| |
| // RTP overhead is 12B. |
| EXPECT_CALL(mock_overhead_observer, OnOverheadChanged(12)).Times(1); |
| SendGenericPacket(); |
| |
| rtp_sender_->RegisterRtpHeaderExtension(kRtpExtensionTransmissionTimeOffset, |
| kTransmissionTimeOffsetExtensionId); |
| |
| // TransmissionTimeOffset extension has a size of 8B. |
| // 12B + 8B = 20B |
| EXPECT_CALL(mock_overhead_observer, OnOverheadChanged(20)).Times(1); |
| SendGenericPacket(); |
| } |
| |
| TEST_P(RtpSenderTest, DoesNotUpdateOverheadOnEqualSize) { |
| MockOverheadObserver mock_overhead_observer; |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.media_send_ssrc = kSsrc; |
| config.retransmission_rate_limiter = &retransmission_rate_limiter_; |
| config.overhead_observer = &mock_overhead_observer; |
| rtp_sender_ = absl::make_unique<RTPSender>(config); |
| |
| EXPECT_CALL(mock_overhead_observer, OnOverheadChanged(_)).Times(1); |
| SendGenericPacket(); |
| SendGenericPacket(); |
| } |
| |
| TEST_P(RtpSenderTest, TrySendPacketMatchesVideo) { |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->set_packet_type(RtpPacketToSend::Type::kVideo); |
| |
| // Verify not sent with wrong SSRC. |
| packet->SetSsrc(kSsrc + 1); |
| EXPECT_FALSE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); |
| |
| // Verify sent with correct SSRC. |
| packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->SetSsrc(kSsrc); |
| packet->set_packet_type(RtpPacketToSend::Type::kVideo); |
| EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); |
| } |
| |
| TEST_P(RtpSenderTest, TrySendPacketMatchesAudio) { |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->set_packet_type(RtpPacketToSend::Type::kAudio); |
| |
| // Verify not sent with wrong SSRC. |
| packet->SetSsrc(kSsrc + 1); |
| EXPECT_FALSE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); |
| |
| // Verify sent with correct SSRC. |
| packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->SetSsrc(kSsrc); |
| packet->set_packet_type(RtpPacketToSend::Type::kAudio); |
| EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); |
| } |
| |
| TEST_P(RtpSenderTest, TrySendPacketMatchesRetransmissions) { |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->set_packet_type(RtpPacketToSend::Type::kRetransmission); |
| |
| // Verify not sent with wrong SSRC. |
| packet->SetSsrc(kSsrc + 1); |
| EXPECT_FALSE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); |
| |
| // Verify sent with correct SSRC (non-RTX). |
| packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->SetSsrc(kSsrc); |
| packet->set_packet_type(RtpPacketToSend::Type::kRetransmission); |
| EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); |
| |
| // RTX retransmission. |
| packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->SetSsrc(kRtxSsrc); |
| packet->set_packet_type(RtpPacketToSend::Type::kRetransmission); |
| EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); |
| } |
| |
| TEST_P(RtpSenderTest, TrySendPacketMatchesPadding) { |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->set_packet_type(RtpPacketToSend::Type::kPadding); |
| |
| // Verify not sent with wrong SSRC. |
| packet->SetSsrc(kSsrc + 1); |
| EXPECT_FALSE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); |
| |
| // Verify sent with correct SSRC (non-RTX). |
| packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->SetSsrc(kSsrc); |
| packet->set_packet_type(RtpPacketToSend::Type::kPadding); |
| EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); |
| |
| // RTX padding. |
| packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->SetSsrc(kRtxSsrc); |
| packet->set_packet_type(RtpPacketToSend::Type::kPadding); |
| EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); |
| } |
| |
| TEST_P(RtpSenderTest, TrySendPacketMatchesFlexfec) { |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->set_packet_type(RtpPacketToSend::Type::kForwardErrorCorrection); |
| |
| // Verify not sent with wrong SSRC. |
| packet->SetSsrc(kSsrc + 1); |
| EXPECT_FALSE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); |
| |
| // Verify sent with correct SSRC. |
| packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->SetSsrc(kFlexFecSsrc); |
| packet->set_packet_type(RtpPacketToSend::Type::kForwardErrorCorrection); |
| EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); |
| } |
| |
| TEST_P(RtpSenderTest, TrySendPacketMatchesUlpfec) { |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->set_packet_type(RtpPacketToSend::Type::kForwardErrorCorrection); |
| |
| // Verify not sent with wrong SSRC. |
| packet->SetSsrc(kSsrc + 1); |
| EXPECT_FALSE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); |
| |
| // Verify sent with correct SSRC. |
| packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->SetSsrc(kSsrc); |
| packet->set_packet_type(RtpPacketToSend::Type::kForwardErrorCorrection); |
| EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); |
| } |
| |
| TEST_P(RtpSenderTest, TrySendPacketHandlesRetransmissionHistory) { |
| rtp_sender_->SetStorePacketsStatus(true, 10); |
| |
| // Build a media packet and send it. |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| const uint16_t media_sequence_number = packet->SequenceNumber(); |
| packet->set_packet_type(RtpPacketToSend::Type::kVideo); |
| packet->set_allow_retransmission(true); |
| EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); |
| |
| // Simulate retransmission request. |
| fake_clock_.AdvanceTimeMilliseconds(30); |
| EXPECT_GT(rtp_sender_->ReSendPacket(media_sequence_number), 0); |
| |
| // Packet already pending, retransmission not allowed. |
| fake_clock_.AdvanceTimeMilliseconds(30); |
| EXPECT_EQ(rtp_sender_->ReSendPacket(media_sequence_number), 0); |
| |
| // Packet exiting pacer, mark as not longer pending. |
| packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| EXPECT_NE(packet->SequenceNumber(), media_sequence_number); |
| packet->set_packet_type(RtpPacketToSend::Type::kRetransmission); |
| packet->SetSsrc(kRtxSsrc); |
| packet->set_retransmitted_sequence_number(media_sequence_number); |
| packet->set_allow_retransmission(false); |
| EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); |
| |
| // Retransmissions allowed again. |
| fake_clock_.AdvanceTimeMilliseconds(30); |
| EXPECT_GT(rtp_sender_->ReSendPacket(media_sequence_number), 0); |
| |
| // Retransmission of RTX packet should not be allowed. |
| EXPECT_EQ(rtp_sender_->ReSendPacket(packet->SequenceNumber()), 0); |
| } |
| |
| TEST_P(RtpSenderTest, TrySendPacketUpdatesExtensions) { |
| ASSERT_EQ(rtp_sender_->RegisterRtpHeaderExtension( |
| kRtpExtensionTransmissionTimeOffset, |
| kTransmissionTimeOffsetExtensionId), |
| 0); |
| ASSERT_EQ(rtp_sender_->RegisterRtpHeaderExtension( |
| kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId), |
| 0); |
| ASSERT_EQ(rtp_sender_->RegisterRtpHeaderExtension(kRtpExtensionVideoTiming, |
| kVideoTimingExtensionId), |
| 0); |
| |
| std::unique_ptr<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(RtpPacketToSend::Type::kVideo); |
| EXPECT_TRUE(rtp_sender_->TrySendPacket(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, TrySendPacketSetsPacketOptions) { |
| 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(RtpPacketToSend::Type::kVideo); |
| EXPECT_CALL(send_packet_observer_, OnSendPacket); |
| EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); |
| |
| EXPECT_EQ(transport_.last_options_.packet_id, kPacketId); |
| EXPECT_TRUE(transport_.last_options_.included_in_allocation); |
| EXPECT_TRUE(transport_.last_options_.included_in_feedback); |
| EXPECT_FALSE(transport_.last_options_.is_retransmit); |
| |
| // Send another packet as retransmission, verify options are populated. |
| packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->SetExtension<TransportSequenceNumber>(kPacketId + 1); |
| packet->set_packet_type(RtpPacketToSend::Type::kRetransmission); |
| EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo())); |
| EXPECT_TRUE(transport_.last_options_.is_retransmit); |
| } |
| |
| TEST_P(RtpSenderTest, TrySendPacketUpdatesStats) { |
| const size_t kPayloadSize = 1000; |
| |
| StrictMock<MockSendSideDelayObserver> send_side_delay_observer; |
| |
| RtpRtcp::Configuration config; |
| config.clock = &fake_clock_; |
| config.outgoing_transport = &transport_; |
| config.media_send_ssrc = kSsrc; |
| config.rtx_send_ssrc = kRtxSsrc; |
| config.flexfec_sender = &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_ = absl::make_unique<RTPSender>(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(RtpPacketToSend::Type::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(RtpPacketToSend::Type::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(RtpPacketToSend::Type::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)); |
| EXPECT_TRUE( |
| rtp_sender_->TrySendPacket(video_packet.get(), PacedPacketInfo())); |
| |
| // Send packet observer not called for padding/retransmissions. |
| EXPECT_CALL(send_packet_observer_, OnSendPacket(2, _, _)).Times(0); |
| EXPECT_TRUE(rtp_sender_->TrySendPacket(rtx_packet.get(), PacedPacketInfo())); |
| |
| EXPECT_CALL(send_packet_observer_, |
| OnSendPacket(3, capture_time_ms, kFlexFecSsrc)); |
| EXPECT_TRUE(rtp_sender_->TrySendPacket(fec_packet.get(), PacedPacketInfo())); |
| |
| StreamDataCounters rtp_stats; |
| StreamDataCounters rtx_stats; |
| rtp_sender_->GetDataCounters(&rtp_stats, &rtx_stats); |
| EXPECT_EQ(rtp_stats.transmitted.packets, 2u); |
| EXPECT_EQ(rtp_stats.fec.packets, 1u); |
| EXPECT_EQ(rtx_stats.retransmitted.packets, 1u); |
| } |
| |
| TEST_P(RtpSenderTest, GeneratePaddingResendsOldPacketsWithRtx) { |
| rtp_sender_->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads); |
| rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload); |
| rtp_sender_->SetStorePacketsStatus(true, 1); |
| |
| const size_t kPayloadPacketSize = 1234; |
| std::unique_ptr<RtpPacketToSend> packet = |
| BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds()); |
| packet->set_allow_retransmission(true); |
| packet->SetPayloadSize(kPayloadPacketSize); |
| packet->set_packet_type(RtpPacketToSend::Type::kVideo); |
| |
| // Send a dummy video packet so it ends up in the packet history. |
| EXPECT_TRUE(rtp_sender_->TrySendPacket(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(kPayloadPacketSize + kRtxHeaderSize); |
| ASSERT_EQ(generated_packets.size(), 1u); |
| auto& padding_packet = generated_packets.front(); |
| EXPECT_EQ(padding_packet->packet_type(), RtpPacketToSend::Type::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 = kPayloadPacketSize + kRtxHeaderSize - 1; |
| const size_t kExpectedNumPaddingPackets = |
| (kPaddingBytesRequested + kMaxPaddingSize - 1) / kMaxPaddingSize; |
| |
| size_t padding_bytes_generated = 0; |
| generated_packets = rtp_sender_->GeneratePadding(kPaddingBytesRequested); |
| EXPECT_EQ(generated_packets.size(), kExpectedNumPaddingPackets); |
| for (auto& packet : generated_packets) { |
| EXPECT_EQ(packet->packet_type(), RtpPacketToSend::Type::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, |
| kExpectedNumPaddingPackets * kMaxPaddingSize); |
| } |
| |
| TEST_P(RtpSenderTest, GeneratePaddingCreatesPurePaddingWithoutRtx) { |
| rtp_sender_->SetStorePacketsStatus(true, 1); |
| |
| 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(RtpPacketToSend::Type::kVideo); |
| EXPECT_TRUE(rtp_sender_->TrySendPacket(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); |
| EXPECT_EQ(padding_packets.size(), kExpectedNumPaddingPackets); |
| for (auto& packet : padding_packets) { |
| EXPECT_EQ(packet->packet_type(), RtpPacketToSend::Type::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_EQ(padding_bytes_generated, |
| kExpectedNumPaddingPackets * kMaxPaddingSize); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(WithAndWithoutOverhead, |
| RtpSenderTest, |
| ::testing::Values(TestConfig{false, false}, |
| TestConfig{false, true}, |
| TestConfig{true, false}, |
| TestConfig{true, true})); |
| |
| INSTANTIATE_TEST_SUITE_P(WithAndWithoutOverhead, |
| RtpSenderTestWithoutPacer, |
| ::testing::Values(TestConfig{false, false}, |
| TestConfig{true, false})); |
| |
| } // namespace webrtc |