blob: 02462b40d93fd3c6c591159cfe10550e4ceb5668 [file] [log] [blame]
/*
* 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