webrtc_m130/modules/rtp_rtcp/source/rtp_sender_unittest.cc
Erik Språng f6468d2569 Wire up new PacedSender code path.
This CL makes the new code path for paced sending functionally complete.
By default, the field trial WebRTC-Pacer-ReferencePackets is Enabled,
meaning that there is no behavior change unless the field trial is
forced to Disabled. This is done in tests, and can be done on the
command line for manual testing.

Bug: webrtc:10633
Change-Id: I0d66c94ef83b5847dee437a785018f09ba3f828d
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/144050
Commit-Queue: Erik Språng <sprang@webrtc.org>
Reviewed-by: Åsa Persson <asapersson@webrtc.org>
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28497}
2019-07-05 15:38:59 +00:00

2646 lines
109 KiB
C++

/*
* 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