mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-14 14:20:45 +01:00

Bug: None Change-Id: I3c532eee7f2d9e5295874dd538730625c8d423ff Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/227086 Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Minyue Li <minyue@google.com> Reviewed-by: Minyue Li <minyue@webrtc.org> Reviewed-by: Erik Språng <sprang@webrtc.org> Commit-Queue: Danil Chapovalov <danilchap@webrtc.org> Cr-Commit-Position: refs/heads/master@{#34676}
1446 lines
58 KiB
C++
1446 lines
58 KiB
C++
/*
|
|
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include "modules/rtp_rtcp/source/rtp_sender.h"
|
|
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
#include "api/rtc_event_log/rtc_event.h"
|
|
#include "api/transport/field_trial_based_config.h"
|
|
#include "api/video/video_codec_constants.h"
|
|
#include "api/video/video_timing.h"
|
|
#include "logging/rtc_event_log/mock/mock_rtc_event_log.h"
|
|
#include "modules/rtp_rtcp/include/rtp_cvo.h"
|
|
#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
|
|
#include "modules/rtp_rtcp/include/rtp_packet_sender.h"
|
|
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
|
#include "modules/rtp_rtcp/source/packet_sequencer.h"
|
|
#include "modules/rtp_rtcp/source/rtp_format_video_generic.h"
|
|
#include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h"
|
|
#include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h"
|
|
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
|
|
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
|
|
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
|
|
#include "modules/rtp_rtcp/source/rtp_sender_video.h"
|
|
#include "modules/rtp_rtcp/source/video_fec_generator.h"
|
|
#include "rtc_base/arraysize.h"
|
|
#include "rtc_base/logging.h"
|
|
#include "rtc_base/rate_limiter.h"
|
|
#include "rtc_base/strings/string_builder.h"
|
|
#include "rtc_base/task_utils/to_queued_task.h"
|
|
#include "test/field_trial.h"
|
|
#include "test/gmock.h"
|
|
#include "test/gtest.h"
|
|
#include "test/mock_transport.h"
|
|
#include "test/time_controller/simulated_time_controller.h"
|
|
|
|
namespace webrtc {
|
|
|
|
namespace {
|
|
enum : int { // The first valid value is 1.
|
|
kAbsoluteSendTimeExtensionId = 1,
|
|
kAudioLevelExtensionId,
|
|
kGenericDescriptorId,
|
|
kMidExtensionId,
|
|
kRepairedRidExtensionId,
|
|
kRidExtensionId,
|
|
kTransmissionTimeOffsetExtensionId,
|
|
kTransportSequenceNumberExtensionId,
|
|
kVideoRotationExtensionId,
|
|
kVideoTimingExtensionId,
|
|
};
|
|
|
|
const int kPayload = 100;
|
|
const int kRtxPayload = 98;
|
|
const uint32_t kTimestamp = 10;
|
|
const uint16_t kSeqNum = 33;
|
|
const uint32_t kSsrc = 725242;
|
|
const uint32_t kRtxSsrc = 12345;
|
|
const uint32_t kFlexFecSsrc = 45678;
|
|
const uint64_t kStartTime = 123456789;
|
|
const size_t kMaxPaddingSize = 224u;
|
|
const uint8_t kPayloadData[] = {47, 11, 32, 93, 89};
|
|
const int64_t kDefaultExpectedRetransmissionTimeMs = 125;
|
|
const size_t kMaxPaddingLength = 224; // Value taken from rtp_sender.cc.
|
|
const uint32_t kTimestampTicksPerMs = 90; // 90kHz clock.
|
|
|
|
using ::testing::_;
|
|
using ::testing::AllOf;
|
|
using ::testing::AtLeast;
|
|
using ::testing::Contains;
|
|
using ::testing::Each;
|
|
using ::testing::ElementsAre;
|
|
using ::testing::Eq;
|
|
using ::testing::Field;
|
|
using ::testing::Gt;
|
|
using ::testing::IsEmpty;
|
|
using ::testing::NiceMock;
|
|
using ::testing::Not;
|
|
using ::testing::Pointee;
|
|
using ::testing::Property;
|
|
using ::testing::Return;
|
|
using ::testing::SizeIs;
|
|
|
|
class MockRtpPacketPacer : public RtpPacketSender {
|
|
public:
|
|
MockRtpPacketPacer() {}
|
|
virtual ~MockRtpPacketPacer() {}
|
|
|
|
MOCK_METHOD(void,
|
|
EnqueuePackets,
|
|
(std::vector<std::unique_ptr<RtpPacketToSend>>),
|
|
(override));
|
|
};
|
|
|
|
class FieldTrialConfig : public WebRtcKeyValueConfig {
|
|
public:
|
|
FieldTrialConfig() : max_padding_factor_(1200) {}
|
|
~FieldTrialConfig() override {}
|
|
|
|
void SetMaxPaddingFactor(double factor) { max_padding_factor_ = factor; }
|
|
|
|
std::string Lookup(absl::string_view key) const override {
|
|
if (key == "WebRTC-LimitPaddingSize") {
|
|
char string_buf[32];
|
|
rtc::SimpleStringBuilder ssb(string_buf);
|
|
ssb << "factor:" << max_padding_factor_;
|
|
return ssb.str();
|
|
}
|
|
return "";
|
|
}
|
|
|
|
private:
|
|
double max_padding_factor_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
class RtpSenderTest : public ::testing::Test {
|
|
protected:
|
|
RtpSenderTest()
|
|
: time_controller_(Timestamp::Millis(kStartTime)),
|
|
clock_(time_controller_.GetClock()),
|
|
retransmission_rate_limiter_(clock_, 1000),
|
|
flexfec_sender_(0,
|
|
kFlexFecSsrc,
|
|
kSsrc,
|
|
"",
|
|
std::vector<RtpExtension>(),
|
|
std::vector<RtpExtensionSize>(),
|
|
nullptr,
|
|
clock_),
|
|
deferred_sequencing_(false),
|
|
kMarkerBit(true) {}
|
|
|
|
void SetUp() override { SetUpRtpSender(true, false, nullptr); }
|
|
|
|
void SetUpRtpSender(bool populate_network2,
|
|
bool always_send_mid_and_rid,
|
|
VideoFecGenerator* fec_generator) {
|
|
RtpRtcpInterface::Configuration config = GetDefaultConfig();
|
|
config.fec_generator = fec_generator;
|
|
config.populate_network2_timestamp = populate_network2;
|
|
config.always_send_mid_and_rid = always_send_mid_and_rid;
|
|
CreateSender(config);
|
|
}
|
|
|
|
RtpRtcpInterface::Configuration GetDefaultConfig() {
|
|
RtpRtcpInterface::Configuration config;
|
|
config.clock = clock_;
|
|
config.local_media_ssrc = kSsrc;
|
|
config.rtx_send_ssrc = kRtxSsrc;
|
|
config.event_log = &mock_rtc_event_log_;
|
|
config.retransmission_rate_limiter = &retransmission_rate_limiter_;
|
|
config.paced_sender = &mock_paced_sender_;
|
|
config.field_trials = &field_trials_;
|
|
return config;
|
|
}
|
|
|
|
void CreateSender(const RtpRtcpInterface::Configuration& config) {
|
|
packet_history_ = std::make_unique<RtpPacketHistory>(
|
|
config.clock, config.enable_rtx_padding_prioritization);
|
|
sequencer_.emplace(kSsrc, kRtxSsrc,
|
|
/*require_marker_before_media_padding=*/!config.audio,
|
|
clock_);
|
|
rtp_sender_ =
|
|
std::make_unique<RTPSender>(config, packet_history_.get(),
|
|
config.paced_sender, &sequencer_.value());
|
|
rtp_sender_->SetSequenceNumber(kSeqNum);
|
|
rtp_sender_->SetTimestampOffset(0);
|
|
deferred_sequencing_ = false;
|
|
}
|
|
|
|
void CreateSenderWithDeferredSequencing(
|
|
const RtpRtcpInterface::Configuration& config) {
|
|
packet_history_ = std::make_unique<RtpPacketHistory>(
|
|
config.clock, config.enable_rtx_padding_prioritization);
|
|
sequencer_.emplace(kSsrc, kRtxSsrc,
|
|
/*require_marker_before_media_padding=*/!config.audio,
|
|
clock_);
|
|
rtp_sender_ = std::make_unique<RTPSender>(config, packet_history_.get(),
|
|
config.paced_sender, nullptr);
|
|
sequencer_->set_media_sequence_number(kSeqNum);
|
|
rtp_sender_->SetTimestampOffset(0);
|
|
deferred_sequencing_ = true;
|
|
}
|
|
|
|
GlobalSimulatedTimeController time_controller_;
|
|
Clock* const clock_;
|
|
NiceMock<MockRtcEventLog> mock_rtc_event_log_;
|
|
MockRtpPacketPacer mock_paced_sender_;
|
|
RateLimiter retransmission_rate_limiter_;
|
|
FlexfecSender flexfec_sender_;
|
|
|
|
bool deferred_sequencing_;
|
|
absl::optional<PacketSequencer> sequencer_;
|
|
std::unique_ptr<RtpPacketHistory> packet_history_;
|
|
std::unique_ptr<RTPSender> rtp_sender_;
|
|
|
|
const bool kMarkerBit;
|
|
FieldTrialConfig 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->set_packet_type(RtpPacketMediaType::kVideo);
|
|
packet->SetMarker(marker_bit);
|
|
packet->SetTimestamp(timestamp);
|
|
packet->set_capture_time_ms(capture_time_ms);
|
|
if (!deferred_sequencing_) {
|
|
EXPECT_TRUE(rtp_sender_->AssignSequenceNumber(packet.get()));
|
|
}
|
|
return packet;
|
|
}
|
|
|
|
std::unique_ptr<RtpPacketToSend> SendPacket(int64_t capture_time_ms,
|
|
int payload_length) {
|
|
uint32_t timestamp = capture_time_ms * 90;
|
|
auto packet =
|
|
BuildRtpPacket(kPayload, kMarkerBit, timestamp, capture_time_ms);
|
|
packet->AllocatePayload(payload_length);
|
|
packet->set_allow_retransmission(true);
|
|
|
|
// Packet should be stored in a send bucket.
|
|
EXPECT_TRUE(
|
|
rtp_sender_->SendToNetwork(std::make_unique<RtpPacketToSend>(*packet)));
|
|
return packet;
|
|
}
|
|
|
|
std::unique_ptr<RtpPacketToSend> SendGenericPacket() {
|
|
const int64_t kCaptureTimeMs = clock_->TimeInMilliseconds();
|
|
// Use maximum allowed size to catch corner cases when packet is dropped
|
|
// because of lack of capacity for the media packet, or for an rtx packet
|
|
// containing the media packet.
|
|
return SendPacket(kCaptureTimeMs,
|
|
/*payload_length=*/rtp_sender_->MaxRtpPacketSize() -
|
|
rtp_sender_->ExpectedPerPacketOverhead());
|
|
}
|
|
|
|
std::vector<std::unique_ptr<RtpPacketToSend>> GeneratePadding(
|
|
size_t target_size_bytes) {
|
|
return rtp_sender_->GeneratePadding(
|
|
target_size_bytes, /*media_has_been_sent=*/true,
|
|
sequencer_->CanSendPaddingOnMediaSsrc());
|
|
}
|
|
|
|
size_t GenerateAndSendPadding(size_t target_size_bytes) {
|
|
size_t generated_bytes = 0;
|
|
for (auto& packet : GeneratePadding(target_size_bytes)) {
|
|
generated_bytes += packet->payload_size() + packet->padding_size();
|
|
rtp_sender_->SendToNetwork(std::move(packet));
|
|
}
|
|
return generated_bytes;
|
|
}
|
|
|
|
// The following are helpers for configuring the RTPSender. They must be
|
|
// called before sending any packets.
|
|
|
|
// Enable the retransmission stream with sizable packet storage.
|
|
void EnableRtx() {
|
|
// RTX needs to be able to read the source packets from the packet store.
|
|
// Pick a number of packets to store big enough for any unit test.
|
|
constexpr uint16_t kNumberOfPacketsToStore = 100;
|
|
packet_history_->SetStorePacketsStatus(
|
|
RtpPacketHistory::StorageMode::kStoreAndCull, kNumberOfPacketsToStore);
|
|
rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload);
|
|
rtp_sender_->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads);
|
|
}
|
|
|
|
// Enable sending of the MID header extension for both the primary SSRC and
|
|
// the RTX SSRC.
|
|
void EnableMidSending(const std::string& mid) {
|
|
rtp_sender_->RegisterRtpHeaderExtension(RtpMid::kUri, kMidExtensionId);
|
|
rtp_sender_->SetMid(mid);
|
|
}
|
|
|
|
// Enable sending of the RSID header extension for the primary SSRC and the
|
|
// RRSID header extension for the RTX SSRC.
|
|
void EnableRidSending(const std::string& rid) {
|
|
rtp_sender_->RegisterRtpHeaderExtension(RtpStreamId::kUri, kRidExtensionId);
|
|
rtp_sender_->RegisterRtpHeaderExtension(RepairedRtpStreamId::kUri,
|
|
kRepairedRidExtensionId);
|
|
rtp_sender_->SetRid(rid);
|
|
}
|
|
};
|
|
|
|
TEST_F(RtpSenderTest, 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_F(RtpSenderTest, AllocatePacketReserveExtensions) {
|
|
// Configure rtp_sender with extensions.
|
|
ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(
|
|
TransmissionOffset::kUri, kTransmissionTimeOffsetExtensionId));
|
|
ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(
|
|
AbsoluteSendTime::kUri, kAbsoluteSendTimeExtensionId));
|
|
ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(AudioLevel::kUri,
|
|
kAudioLevelExtensionId));
|
|
ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(
|
|
TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
|
|
ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(
|
|
VideoOrientation::kUri, 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_F(RtpSenderTest, PaddingAlwaysAllowedOnAudio) {
|
|
RtpRtcpInterface::Configuration config = GetDefaultConfig();
|
|
config.audio = true;
|
|
CreateSender(config);
|
|
|
|
std::unique_ptr<RtpPacketToSend> audio_packet = rtp_sender_->AllocatePacket();
|
|
// Padding on audio stream allowed regardless of marker in the last packet.
|
|
audio_packet->SetMarker(false);
|
|
audio_packet->SetPayloadType(kPayload);
|
|
rtp_sender_->AssignSequenceNumber(audio_packet.get());
|
|
|
|
const size_t kPaddingSize = 59;
|
|
|
|
EXPECT_CALL(
|
|
mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(AllOf(
|
|
Pointee(Property(&RtpPacketToSend::packet_type,
|
|
RtpPacketMediaType::kPadding)),
|
|
Pointee(Property(&RtpPacketToSend::padding_size, kPaddingSize))))));
|
|
EXPECT_EQ(kPaddingSize, GenerateAndSendPadding(kPaddingSize));
|
|
|
|
// Requested padding size is too small, will send a larger one.
|
|
const size_t kMinPaddingSize = 50;
|
|
EXPECT_CALL(mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(
|
|
AllOf(Pointee(Property(&RtpPacketToSend::packet_type,
|
|
RtpPacketMediaType::kPadding)),
|
|
Pointee(Property(&RtpPacketToSend::padding_size,
|
|
kMinPaddingSize))))));
|
|
EXPECT_EQ(kMinPaddingSize, GenerateAndSendPadding(kMinPaddingSize - 5));
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, SendToNetworkForwardsPacketsToPacer) {
|
|
auto packet = BuildRtpPacket(kPayload, kMarkerBit, kTimestamp, 0);
|
|
int64_t now_ms = clock_->TimeInMilliseconds();
|
|
|
|
EXPECT_CALL(
|
|
mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(AllOf(
|
|
Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)),
|
|
Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum)),
|
|
Pointee(Property(&RtpPacketToSend::capture_time_ms, now_ms))))));
|
|
EXPECT_TRUE(
|
|
rtp_sender_->SendToNetwork(std::make_unique<RtpPacketToSend>(*packet)));
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, ReSendPacketForwardsPacketsToPacer) {
|
|
packet_history_->SetStorePacketsStatus(
|
|
RtpPacketHistory::StorageMode::kStoreAndCull, 10);
|
|
int64_t now_ms = clock_->TimeInMilliseconds();
|
|
auto packet = BuildRtpPacket(kPayload, kMarkerBit, kTimestamp, now_ms);
|
|
uint16_t seq_no = packet->SequenceNumber();
|
|
packet->set_allow_retransmission(true);
|
|
packet_history_->PutRtpPacket(std::move(packet), now_ms);
|
|
|
|
EXPECT_CALL(mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(AllOf(
|
|
Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)),
|
|
Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum)),
|
|
Pointee(Property(&RtpPacketToSend::capture_time_ms, now_ms)),
|
|
Pointee(Property(&RtpPacketToSend::packet_type,
|
|
RtpPacketMediaType::kRetransmission))))));
|
|
EXPECT_TRUE(rtp_sender_->ReSendPacket(seq_no));
|
|
}
|
|
|
|
// This test sends 1 regular video packet, then 4 padding packets, and then
|
|
// 1 more regular packet.
|
|
TEST_F(RtpSenderTest, SendPadding) {
|
|
constexpr int kNumPaddingPackets = 4;
|
|
EXPECT_CALL(mock_paced_sender_, EnqueuePackets);
|
|
std::unique_ptr<RtpPacketToSend> media_packet =
|
|
SendPacket(/*capture_time_ms=*/clock_->TimeInMilliseconds(),
|
|
/*payload_size=*/100);
|
|
|
|
// Wait 50 ms before generating each padding packet.
|
|
for (int i = 0; i < kNumPaddingPackets; ++i) {
|
|
time_controller_.AdvanceTime(TimeDelta::Millis(50));
|
|
const size_t kPaddingTargetBytes = 100; // Request 100 bytes of padding.
|
|
|
|
// Padding should be sent on the media ssrc, with a continous sequence
|
|
// number range. Size will be forced to full pack size and the timestamp
|
|
// shall be that of the last media packet.
|
|
EXPECT_CALL(mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(AllOf(
|
|
Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)),
|
|
Pointee(Property(&RtpPacketToSend::SequenceNumber,
|
|
media_packet->SequenceNumber() + i + 1)),
|
|
Pointee(Property(&RtpPacketToSend::padding_size,
|
|
kMaxPaddingLength)),
|
|
Pointee(Property(&RtpPacketToSend::Timestamp,
|
|
media_packet->Timestamp()))))));
|
|
std::vector<std::unique_ptr<RtpPacketToSend>> padding_packets =
|
|
rtp_sender_->GeneratePadding(kPaddingTargetBytes,
|
|
/*media_has_been_sent=*/true,
|
|
/*can_send_padding_on_media_ssrc=*/true);
|
|
ASSERT_THAT(padding_packets, SizeIs(1));
|
|
rtp_sender_->SendToNetwork(std::move(padding_packets[0]));
|
|
}
|
|
|
|
// Send a regular video packet again.
|
|
EXPECT_CALL(mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(AllOf(
|
|
Pointee(Property(
|
|
&RtpPacketToSend::SequenceNumber,
|
|
media_packet->SequenceNumber() + kNumPaddingPackets + 1)),
|
|
Pointee(Property(&RtpPacketToSend::Timestamp,
|
|
Gt(media_packet->Timestamp())))))));
|
|
|
|
std::unique_ptr<RtpPacketToSend> next_media_packet =
|
|
SendPacket(/*capture_time_ms=*/clock_->TimeInMilliseconds(),
|
|
/*payload_size=*/100);
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, NoPaddingAsFirstPacketWithoutBweExtensions) {
|
|
EXPECT_THAT(rtp_sender_->GeneratePadding(
|
|
/*target_size_bytes=*/100,
|
|
/*media_has_been_sent=*/false,
|
|
/*can_send_padding_on_media_ssrc=*/false),
|
|
IsEmpty());
|
|
|
|
// Don't send padding before media even with RTX.
|
|
EnableRtx();
|
|
EXPECT_THAT(rtp_sender_->GeneratePadding(
|
|
/*target_size_bytes=*/100,
|
|
/*media_has_been_sent=*/false,
|
|
/*can_send_padding_on_media_ssrc=*/false),
|
|
IsEmpty());
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, AllowPaddingAsFirstPacketOnRtxWithTransportCc) {
|
|
ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(
|
|
TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
|
|
|
|
// Padding can't be sent as first packet on media SSRC since we don't know
|
|
// what payload type to assign.
|
|
EXPECT_THAT(rtp_sender_->GeneratePadding(
|
|
/*target_size_bytes=*/100,
|
|
/*media_has_been_sent=*/false,
|
|
/*can_send_padding_on_media_ssrc=*/false),
|
|
IsEmpty());
|
|
|
|
// With transportcc padding can be sent as first packet on the RTX SSRC.
|
|
EnableRtx();
|
|
EXPECT_THAT(rtp_sender_->GeneratePadding(
|
|
/*target_size_bytes=*/100,
|
|
/*media_has_been_sent=*/false,
|
|
/*can_send_padding_on_media_ssrc=*/false),
|
|
Not(IsEmpty()));
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, AllowPaddingAsFirstPacketOnRtxWithAbsSendTime) {
|
|
ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(
|
|
AbsoluteSendTime::kUri, kAbsoluteSendTimeExtensionId));
|
|
|
|
// Padding can't be sent as first packet on media SSRC since we don't know
|
|
// what payload type to assign.
|
|
EXPECT_THAT(rtp_sender_->GeneratePadding(
|
|
/*target_size_bytes=*/100,
|
|
/*media_has_been_sent=*/false,
|
|
/*can_send_padding_on_media_ssrc=*/false),
|
|
IsEmpty());
|
|
|
|
// With abs send time, padding can be sent as first packet on the RTX SSRC.
|
|
EnableRtx();
|
|
EXPECT_THAT(rtp_sender_->GeneratePadding(
|
|
/*target_size_bytes=*/100,
|
|
/*media_has_been_sent=*/false,
|
|
/*can_send_padding_on_media_ssrc=*/false),
|
|
Not(IsEmpty()));
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, UpdatesTimestampsOnPlainRtxPadding) {
|
|
EnableRtx();
|
|
// Timestamps as set based on capture time in RtpSenderTest.
|
|
const int64_t start_time = clock_->TimeInMilliseconds();
|
|
const uint32_t start_timestamp = start_time * kTimestampTicksPerMs;
|
|
|
|
// Start by sending one media packet.
|
|
EXPECT_CALL(
|
|
mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(AllOf(
|
|
Pointee(Property(&RtpPacketToSend::padding_size, 0u)),
|
|
Pointee(Property(&RtpPacketToSend::Timestamp, start_timestamp)),
|
|
Pointee(Property(&RtpPacketToSend::capture_time_ms, start_time))))));
|
|
std::unique_ptr<RtpPacketToSend> media_packet =
|
|
SendPacket(start_time, /*payload_size=*/600);
|
|
|
|
// Advance time before sending padding.
|
|
const TimeDelta kTimeDiff = TimeDelta::Millis(17);
|
|
time_controller_.AdvanceTime(kTimeDiff);
|
|
|
|
// Timestamps on padding should be offset from the sent media.
|
|
EXPECT_THAT(
|
|
GeneratePadding(/*target_size_bytes=*/100),
|
|
Each(AllOf(
|
|
Pointee(Property(&RtpPacketToSend::padding_size, kMaxPaddingLength)),
|
|
Pointee(Property(
|
|
&RtpPacketToSend::Timestamp,
|
|
start_timestamp + (kTimestampTicksPerMs * kTimeDiff.ms()))),
|
|
Pointee(Property(&RtpPacketToSend::capture_time_ms,
|
|
start_time + kTimeDiff.ms())))));
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, KeepsTimestampsOnPayloadPadding) {
|
|
ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(
|
|
TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
|
|
EnableRtx();
|
|
// Timestamps as set based on capture time in RtpSenderTest.
|
|
const int64_t start_time = clock_->TimeInMilliseconds();
|
|
const uint32_t start_timestamp = start_time * kTimestampTicksPerMs;
|
|
const size_t kPayloadSize = 600;
|
|
const size_t kRtxHeaderSize = 2;
|
|
|
|
// Start by sending one media packet and putting in the packet history.
|
|
EXPECT_CALL(
|
|
mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(AllOf(
|
|
Pointee(Property(&RtpPacketToSend::padding_size, 0u)),
|
|
Pointee(Property(&RtpPacketToSend::Timestamp, start_timestamp)),
|
|
Pointee(Property(&RtpPacketToSend::capture_time_ms, start_time))))));
|
|
std::unique_ptr<RtpPacketToSend> media_packet =
|
|
SendPacket(start_time, kPayloadSize);
|
|
packet_history_->PutRtpPacket(std::move(media_packet), start_time);
|
|
|
|
// Advance time before sending padding.
|
|
const TimeDelta kTimeDiff = TimeDelta::Millis(17);
|
|
time_controller_.AdvanceTime(kTimeDiff);
|
|
|
|
// Timestamps on payload padding should be set to original.
|
|
EXPECT_THAT(
|
|
GeneratePadding(/*target_size_bytes=*/100),
|
|
Each(AllOf(
|
|
Pointee(Property(&RtpPacketToSend::padding_size, 0u)),
|
|
Pointee(Property(&RtpPacketToSend::payload_size,
|
|
kPayloadSize + kRtxHeaderSize)),
|
|
Pointee(Property(&RtpPacketToSend::Timestamp, start_timestamp)),
|
|
Pointee(Property(&RtpPacketToSend::capture_time_ms, start_time)))));
|
|
}
|
|
|
|
// Test that the MID header extension is included on sent packets when
|
|
// configured.
|
|
TEST_F(RtpSenderTest, MidIncludedOnSentPackets) {
|
|
const char kMid[] = "mid";
|
|
EnableMidSending(kMid);
|
|
|
|
// Send a couple packets, expect both packets to have the MID set.
|
|
EXPECT_CALL(mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(Pointee(
|
|
Property(&RtpPacketToSend::GetExtension<RtpMid>, kMid)))))
|
|
.Times(2);
|
|
SendGenericPacket();
|
|
SendGenericPacket();
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, RidIncludedOnSentPackets) {
|
|
const char kRid[] = "f";
|
|
EnableRidSending(kRid);
|
|
|
|
EXPECT_CALL(mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(Pointee(Property(
|
|
&RtpPacketToSend::GetExtension<RtpStreamId>, kRid)))));
|
|
SendGenericPacket();
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, RidIncludedOnRtxSentPackets) {
|
|
const char kRid[] = "f";
|
|
EnableRtx();
|
|
EnableRidSending(kRid);
|
|
|
|
EXPECT_CALL(mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(Pointee(AllOf(
|
|
Property(&RtpPacketToSend::GetExtension<RtpStreamId>, kRid),
|
|
Property(&RtpPacketToSend::HasExtension<RepairedRtpStreamId>,
|
|
false))))))
|
|
.WillOnce([&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) {
|
|
packet_history_->PutRtpPacket(std::move(packets[0]),
|
|
clock_->TimeInMilliseconds());
|
|
});
|
|
SendGenericPacket();
|
|
|
|
EXPECT_CALL(
|
|
mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(Pointee(AllOf(
|
|
Property(&RtpPacketToSend::GetExtension<RepairedRtpStreamId>, kRid),
|
|
Property(&RtpPacketToSend::HasExtension<RtpStreamId>, false))))));
|
|
rtp_sender_->ReSendPacket(kSeqNum);
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, MidAndRidNotIncludedOnSentPacketsAfterAck) {
|
|
const char kMid[] = "mid";
|
|
const char kRid[] = "f";
|
|
|
|
EnableMidSending(kMid);
|
|
EnableRidSending(kRid);
|
|
|
|
// This first packet should include both MID and RID.
|
|
EXPECT_CALL(
|
|
mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(Pointee(AllOf(
|
|
Property(&RtpPacketToSend::GetExtension<RtpMid>, kMid),
|
|
Property(&RtpPacketToSend::GetExtension<RtpStreamId>, kRid))))));
|
|
auto first_built_packet = SendGenericPacket();
|
|
rtp_sender_->OnReceivedAckOnSsrc(first_built_packet->SequenceNumber());
|
|
|
|
// The second packet should include neither since an ack was received.
|
|
EXPECT_CALL(
|
|
mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(Pointee(AllOf(
|
|
Property(&RtpPacketToSend::HasExtension<RtpMid>, false),
|
|
Property(&RtpPacketToSend::HasExtension<RtpStreamId>, false))))));
|
|
SendGenericPacket();
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, MidAndRidAlwaysIncludedOnSentPacketsWhenConfigured) {
|
|
SetUpRtpSender(false, /*always_send_mid_and_rid=*/true, nullptr);
|
|
const char kMid[] = "mid";
|
|
const char kRid[] = "f";
|
|
EnableMidSending(kMid);
|
|
EnableRidSending(kRid);
|
|
|
|
// Send two media packets: one before and one after the ack.
|
|
// Due to the configuration, both sent packets should contain MID and RID.
|
|
EXPECT_CALL(
|
|
mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(Pointee(
|
|
AllOf(Property(&RtpPacketToSend::GetExtension<RtpMid>, kMid),
|
|
Property(&RtpPacketToSend::GetExtension<RtpStreamId>, kRid))))))
|
|
.Times(2);
|
|
auto first_built_packet = SendGenericPacket();
|
|
rtp_sender_->OnReceivedAckOnSsrc(first_built_packet->SequenceNumber());
|
|
SendGenericPacket();
|
|
}
|
|
|
|
// Test that the first RTX packet includes both MID and RRID even if the packet
|
|
// being retransmitted did not have MID or RID. The MID and RID are needed on
|
|
// the first packets for a given SSRC, and RTX packets are sent on a separate
|
|
// SSRC.
|
|
TEST_F(RtpSenderTest, MidAndRidIncludedOnFirstRtxPacket) {
|
|
const char kMid[] = "mid";
|
|
const char kRid[] = "f";
|
|
|
|
EnableRtx();
|
|
EnableMidSending(kMid);
|
|
EnableRidSending(kRid);
|
|
|
|
// This first packet will include both MID and RID.
|
|
EXPECT_CALL(mock_paced_sender_, EnqueuePackets);
|
|
auto first_built_packet = SendGenericPacket();
|
|
rtp_sender_->OnReceivedAckOnSsrc(first_built_packet->SequenceNumber());
|
|
|
|
// The second packet will include neither since an ack was received, put
|
|
// it in the packet history for retransmission.
|
|
EXPECT_CALL(mock_paced_sender_, EnqueuePackets(SizeIs(1)))
|
|
.WillOnce([&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) {
|
|
packet_history_->PutRtpPacket(std::move(packets[0]),
|
|
clock_->TimeInMilliseconds());
|
|
});
|
|
auto second_built_packet = SendGenericPacket();
|
|
|
|
// The first RTX packet should include MID and RRID.
|
|
EXPECT_CALL(mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(Pointee(AllOf(
|
|
Property(&RtpPacketToSend::GetExtension<RtpMid>, kMid),
|
|
Property(&RtpPacketToSend::GetExtension<RepairedRtpStreamId>,
|
|
kRid))))));
|
|
rtp_sender_->ReSendPacket(second_built_packet->SequenceNumber());
|
|
}
|
|
|
|
// Test that the RTX packets sent after receving an ACK on the RTX SSRC does
|
|
// not include either MID or RRID even if the packet being retransmitted did
|
|
// had a MID or RID.
|
|
TEST_F(RtpSenderTest, MidAndRidNotIncludedOnRtxPacketsAfterAck) {
|
|
const char kMid[] = "mid";
|
|
const char kRid[] = "f";
|
|
|
|
EnableRtx();
|
|
EnableMidSending(kMid);
|
|
EnableRidSending(kRid);
|
|
|
|
// This first packet will include both MID and RID.
|
|
EXPECT_CALL(mock_paced_sender_, EnqueuePackets(SizeIs(1)))
|
|
.WillOnce([&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) {
|
|
packet_history_->PutRtpPacket(std::move(packets[0]),
|
|
clock_->TimeInMilliseconds());
|
|
});
|
|
auto first_built_packet = SendGenericPacket();
|
|
rtp_sender_->OnReceivedAckOnSsrc(first_built_packet->SequenceNumber());
|
|
|
|
// The second packet will include neither since an ack was received.
|
|
EXPECT_CALL(mock_paced_sender_, EnqueuePackets(SizeIs(1)))
|
|
.WillOnce([&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) {
|
|
packet_history_->PutRtpPacket(std::move(packets[0]),
|
|
clock_->TimeInMilliseconds());
|
|
});
|
|
auto second_built_packet = SendGenericPacket();
|
|
|
|
// The first RTX packet will include MID and RRID.
|
|
EXPECT_CALL(mock_paced_sender_, EnqueuePackets(SizeIs(1)))
|
|
.WillOnce([&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) {
|
|
rtp_sender_->OnReceivedAckOnRtxSsrc(packets[0]->SequenceNumber());
|
|
packet_history_->MarkPacketAsSent(
|
|
*packets[0]->retransmitted_sequence_number());
|
|
});
|
|
rtp_sender_->ReSendPacket(second_built_packet->SequenceNumber());
|
|
|
|
// The second and third RTX packets should not include MID nor RRID.
|
|
EXPECT_CALL(mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(Pointee(AllOf(
|
|
Property(&RtpPacketToSend::HasExtension<RtpMid>, false),
|
|
Property(&RtpPacketToSend::HasExtension<RepairedRtpStreamId>,
|
|
false))))))
|
|
.Times(2);
|
|
rtp_sender_->ReSendPacket(first_built_packet->SequenceNumber());
|
|
rtp_sender_->ReSendPacket(second_built_packet->SequenceNumber());
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, MidAndRidAlwaysIncludedOnRtxPacketsWhenConfigured) {
|
|
SetUpRtpSender(false, /*always_send_mid_and_rid=*/true, nullptr);
|
|
const char kMid[] = "mid";
|
|
const char kRid[] = "f";
|
|
EnableRtx();
|
|
EnableMidSending(kMid);
|
|
EnableRidSending(kRid);
|
|
|
|
// Send two media packets: one before and one after the ack.
|
|
EXPECT_CALL(
|
|
mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(Pointee(
|
|
AllOf(Property(&RtpPacketToSend::GetExtension<RtpMid>, kMid),
|
|
Property(&RtpPacketToSend::GetExtension<RtpStreamId>, kRid))))))
|
|
.Times(2)
|
|
.WillRepeatedly(
|
|
[&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) {
|
|
packet_history_->PutRtpPacket(std::move(packets[0]),
|
|
clock_->TimeInMilliseconds());
|
|
});
|
|
auto media_packet1 = SendGenericPacket();
|
|
rtp_sender_->OnReceivedAckOnSsrc(media_packet1->SequenceNumber());
|
|
auto media_packet2 = SendGenericPacket();
|
|
|
|
// Send three RTX packets with different combinations of orders w.r.t. the
|
|
// media and RTX acks.
|
|
// Due to the configuration, all sent packets should contain MID
|
|
// and either RID (media) or RRID (RTX).
|
|
EXPECT_CALL(mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(Pointee(AllOf(
|
|
Property(&RtpPacketToSend::GetExtension<RtpMid>, kMid),
|
|
Property(&RtpPacketToSend::GetExtension<RepairedRtpStreamId>,
|
|
kRid))))))
|
|
.Times(3)
|
|
.WillRepeatedly(
|
|
[&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) {
|
|
rtp_sender_->OnReceivedAckOnRtxSsrc(packets[0]->SequenceNumber());
|
|
packet_history_->MarkPacketAsSent(
|
|
*packets[0]->retransmitted_sequence_number());
|
|
});
|
|
rtp_sender_->ReSendPacket(media_packet2->SequenceNumber());
|
|
rtp_sender_->ReSendPacket(media_packet1->SequenceNumber());
|
|
rtp_sender_->ReSendPacket(media_packet2->SequenceNumber());
|
|
}
|
|
|
|
// Test that if the RtpState indicates an ACK has been received on that SSRC
|
|
// then neither the MID nor RID header extensions will be sent.
|
|
TEST_F(RtpSenderTest, MidAndRidNotIncludedOnSentPacketsAfterRtpStateRestored) {
|
|
const char kMid[] = "mid";
|
|
const char kRid[] = "f";
|
|
|
|
EnableMidSending(kMid);
|
|
EnableRidSending(kRid);
|
|
|
|
RtpState state = rtp_sender_->GetRtpState();
|
|
EXPECT_FALSE(state.ssrc_has_acked);
|
|
state.ssrc_has_acked = true;
|
|
rtp_sender_->SetRtpState(state);
|
|
|
|
EXPECT_CALL(
|
|
mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(Pointee(AllOf(
|
|
Property(&RtpPacketToSend::HasExtension<RtpMid>, false),
|
|
Property(&RtpPacketToSend::HasExtension<RtpStreamId>, false))))));
|
|
SendGenericPacket();
|
|
}
|
|
|
|
// Test that if the RTX RtpState indicates an ACK has been received on that
|
|
// RTX SSRC then neither the MID nor RRID header extensions will be sent on
|
|
// RTX packets.
|
|
TEST_F(RtpSenderTest, MidAndRridNotIncludedOnRtxPacketsAfterRtpStateRestored) {
|
|
const char kMid[] = "mid";
|
|
const char kRid[] = "f";
|
|
|
|
EnableRtx();
|
|
EnableMidSending(kMid);
|
|
EnableRidSending(kRid);
|
|
|
|
RtpState rtx_state = rtp_sender_->GetRtxRtpState();
|
|
EXPECT_FALSE(rtx_state.ssrc_has_acked);
|
|
rtx_state.ssrc_has_acked = true;
|
|
rtp_sender_->SetRtxRtpState(rtx_state);
|
|
|
|
EXPECT_CALL(mock_paced_sender_, EnqueuePackets(SizeIs(1)))
|
|
.WillOnce([&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) {
|
|
packet_history_->PutRtpPacket(std::move(packets[0]),
|
|
clock_->TimeInMilliseconds());
|
|
});
|
|
auto built_packet = SendGenericPacket();
|
|
|
|
EXPECT_CALL(
|
|
mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(Pointee(AllOf(
|
|
Property(&RtpPacketToSend::HasExtension<RtpMid>, false),
|
|
Property(&RtpPacketToSend::HasExtension<RtpStreamId>, false))))));
|
|
ASSERT_LT(0, rtp_sender_->ReSendPacket(built_packet->SequenceNumber()));
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, RespectsNackBitrateLimit) {
|
|
const int32_t kPacketSize = 1400;
|
|
const int32_t kNumPackets = 30;
|
|
|
|
retransmission_rate_limiter_.SetMaxRate(kPacketSize * kNumPackets * 8);
|
|
|
|
packet_history_->SetStorePacketsStatus(
|
|
RtpPacketHistory::StorageMode::kStoreAndCull, kNumPackets);
|
|
const uint16_t kStartSequenceNumber = rtp_sender_->SequenceNumber();
|
|
std::vector<uint16_t> sequence_numbers;
|
|
for (int32_t i = 0; i < kNumPackets; ++i) {
|
|
sequence_numbers.push_back(kStartSequenceNumber + i);
|
|
time_controller_.AdvanceTime(TimeDelta::Millis(1));
|
|
EXPECT_CALL(mock_paced_sender_, EnqueuePackets(SizeIs(1)))
|
|
.WillOnce([&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) {
|
|
packet_history_->PutRtpPacket(std::move(packets[0]),
|
|
clock_->TimeInMilliseconds());
|
|
});
|
|
SendPacket(clock_->TimeInMilliseconds(), kPacketSize);
|
|
}
|
|
|
|
time_controller_.AdvanceTime(TimeDelta::Millis(1000 - kNumPackets));
|
|
|
|
// Resending should work - brings the bandwidth up to the limit.
|
|
// NACK bitrate is capped to the same bitrate as the encoder, since the max
|
|
// protection overhead is 50% (see MediaOptimization::SetTargetRates).
|
|
EXPECT_CALL(mock_paced_sender_, EnqueuePackets(ElementsAre(Pointee(Property(
|
|
&RtpPacketToSend::packet_type,
|
|
RtpPacketMediaType::kRetransmission)))))
|
|
.Times(kNumPackets)
|
|
.WillRepeatedly(
|
|
[&](std::vector<std::unique_ptr<RtpPacketToSend>> packets) {
|
|
for (const auto& packet : packets) {
|
|
packet_history_->MarkPacketAsSent(
|
|
*packet->retransmitted_sequence_number());
|
|
}
|
|
});
|
|
rtp_sender_->OnReceivedNack(sequence_numbers, 0);
|
|
|
|
// Must be at least 5ms in between retransmission attempts.
|
|
time_controller_.AdvanceTime(TimeDelta::Millis(5));
|
|
|
|
// Resending should not work, bandwidth exceeded.
|
|
EXPECT_CALL(mock_paced_sender_, EnqueuePackets).Times(0);
|
|
rtp_sender_->OnReceivedNack(sequence_numbers, 0);
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, UpdatingCsrcsUpdatedOverhead) {
|
|
RtpRtcpInterface::Configuration config = GetDefaultConfig();
|
|
config.rtx_send_ssrc = {};
|
|
CreateSender(config);
|
|
|
|
// Base RTP overhead is 12B.
|
|
EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u);
|
|
|
|
// Adding two csrcs adds 2*4 bytes to the header.
|
|
rtp_sender_->SetCsrcs({1, 2});
|
|
EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 20u);
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, OnOverheadChanged) {
|
|
RtpRtcpInterface::Configuration config = GetDefaultConfig();
|
|
config.rtx_send_ssrc = {};
|
|
CreateSender(config);
|
|
|
|
// Base RTP overhead is 12B.
|
|
EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u);
|
|
|
|
rtp_sender_->RegisterRtpHeaderExtension(TransmissionOffset::kUri,
|
|
kTransmissionTimeOffsetExtensionId);
|
|
|
|
// TransmissionTimeOffset extension has a size of 3B, but with the addition
|
|
// of header index and rounding to 4 byte boundary we end up with 20B total.
|
|
EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 20u);
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, CountMidOnlyUntilAcked) {
|
|
RtpRtcpInterface::Configuration config = GetDefaultConfig();
|
|
config.rtx_send_ssrc = {};
|
|
CreateSender(config);
|
|
|
|
// Base RTP overhead is 12B.
|
|
EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u);
|
|
|
|
rtp_sender_->RegisterRtpHeaderExtension(RtpMid::kUri, kMidExtensionId);
|
|
rtp_sender_->RegisterRtpHeaderExtension(RtpStreamId::kUri, kRidExtensionId);
|
|
|
|
// Counted only if set.
|
|
EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u);
|
|
rtp_sender_->SetMid("foo");
|
|
EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 36u);
|
|
rtp_sender_->SetRid("bar");
|
|
EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 52u);
|
|
|
|
// Ack received, mid/rid no longer sent.
|
|
rtp_sender_->OnReceivedAckOnSsrc(0);
|
|
EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u);
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, DontCountVolatileExtensionsIntoOverhead) {
|
|
RtpRtcpInterface::Configuration config = GetDefaultConfig();
|
|
config.rtx_send_ssrc = {};
|
|
CreateSender(config);
|
|
|
|
// Base RTP overhead is 12B.
|
|
EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u);
|
|
|
|
rtp_sender_->RegisterRtpHeaderExtension(InbandComfortNoiseExtension::kUri, 1);
|
|
rtp_sender_->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::kUri,
|
|
2);
|
|
rtp_sender_->RegisterRtpHeaderExtension(VideoOrientation::kUri, 3);
|
|
rtp_sender_->RegisterRtpHeaderExtension(PlayoutDelayLimits::kUri, 4);
|
|
rtp_sender_->RegisterRtpHeaderExtension(VideoContentTypeExtension::kUri, 5);
|
|
rtp_sender_->RegisterRtpHeaderExtension(VideoTimingExtension::kUri, 6);
|
|
rtp_sender_->RegisterRtpHeaderExtension(RepairedRtpStreamId::kUri, 7);
|
|
rtp_sender_->RegisterRtpHeaderExtension(ColorSpaceExtension::kUri, 8);
|
|
|
|
// Still only 12B counted since can't count on above being sent.
|
|
EXPECT_EQ(rtp_sender_->ExpectedPerPacketOverhead(), 12u);
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, SendPacketHandlesRetransmissionHistory) {
|
|
packet_history_->SetStorePacketsStatus(
|
|
RtpPacketHistory::StorageMode::kStoreAndCull, 10);
|
|
|
|
// Ignore calls to EnqueuePackets() for this test.
|
|
EXPECT_CALL(mock_paced_sender_, EnqueuePackets).WillRepeatedly(Return());
|
|
|
|
// Build a media packet and put in the packet history.
|
|
std::unique_ptr<RtpPacketToSend> packet =
|
|
BuildRtpPacket(kPayload, true, 0, clock_->TimeInMilliseconds());
|
|
const uint16_t media_sequence_number = packet->SequenceNumber();
|
|
packet->set_allow_retransmission(true);
|
|
packet_history_->PutRtpPacket(std::move(packet),
|
|
clock_->TimeInMilliseconds());
|
|
|
|
// Simulate successful retransmission request.
|
|
time_controller_.AdvanceTime(TimeDelta::Millis(30));
|
|
EXPECT_THAT(rtp_sender_->ReSendPacket(media_sequence_number), Gt(0));
|
|
|
|
// Packet already pending, retransmission not allowed.
|
|
time_controller_.AdvanceTime(TimeDelta::Millis(30));
|
|
EXPECT_THAT(rtp_sender_->ReSendPacket(media_sequence_number), Eq(0));
|
|
|
|
// Simulate packet exiting pacer, mark as not longer pending.
|
|
packet_history_->MarkPacketAsSent(media_sequence_number);
|
|
|
|
// Retransmissions allowed again.
|
|
time_controller_.AdvanceTime(TimeDelta::Millis(30));
|
|
EXPECT_THAT(rtp_sender_->ReSendPacket(media_sequence_number), Gt(0));
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, MarksRetransmittedPackets) {
|
|
packet_history_->SetStorePacketsStatus(
|
|
RtpPacketHistory::StorageMode::kStoreAndCull, 10);
|
|
|
|
// Build a media packet and put in the packet history.
|
|
std::unique_ptr<RtpPacketToSend> packet =
|
|
BuildRtpPacket(kPayload, true, 0, clock_->TimeInMilliseconds());
|
|
const uint16_t media_sequence_number = packet->SequenceNumber();
|
|
packet->set_allow_retransmission(true);
|
|
packet_history_->PutRtpPacket(std::move(packet),
|
|
clock_->TimeInMilliseconds());
|
|
|
|
// Expect a retransmission packet marked with which packet it is a
|
|
// retransmit of.
|
|
EXPECT_CALL(
|
|
mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(AllOf(
|
|
Pointee(Property(&RtpPacketToSend::packet_type,
|
|
RtpPacketMediaType::kRetransmission)),
|
|
Pointee(Property(&RtpPacketToSend::retransmitted_sequence_number,
|
|
Eq(media_sequence_number)))))));
|
|
EXPECT_THAT(rtp_sender_->ReSendPacket(media_sequence_number), Gt(0));
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, GeneratedPaddingHasBweExtensions) {
|
|
// Min requested size in order to use RTX payload.
|
|
const size_t kMinPaddingSize = 50;
|
|
EnableRtx();
|
|
|
|
ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(
|
|
TransmissionOffset::kUri, kTransmissionTimeOffsetExtensionId));
|
|
ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(
|
|
AbsoluteSendTime::kUri, kAbsoluteSendTimeExtensionId));
|
|
ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(
|
|
TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
|
|
|
|
// Put a packet in the history, in order to facilitate payload padding.
|
|
std::unique_ptr<RtpPacketToSend> packet =
|
|
BuildRtpPacket(kPayload, true, 0, clock_->TimeInMilliseconds());
|
|
packet->set_allow_retransmission(true);
|
|
packet->SetPayloadSize(kMinPaddingSize);
|
|
packet->set_packet_type(RtpPacketMediaType::kVideo);
|
|
packet_history_->PutRtpPacket(std::move(packet),
|
|
clock_->TimeInMilliseconds());
|
|
|
|
// Generate a plain padding packet, check that extensions are registered.
|
|
std::vector<std::unique_ptr<RtpPacketToSend>> generated_packets =
|
|
GeneratePadding(/*target_size_bytes=*/1);
|
|
ASSERT_THAT(generated_packets, SizeIs(1));
|
|
auto& plain_padding = generated_packets.front();
|
|
EXPECT_GT(plain_padding->padding_size(), 0u);
|
|
EXPECT_TRUE(plain_padding->HasExtension<TransportSequenceNumber>());
|
|
EXPECT_TRUE(plain_padding->HasExtension<AbsoluteSendTime>());
|
|
EXPECT_TRUE(plain_padding->HasExtension<TransmissionOffset>());
|
|
EXPECT_GT(plain_padding->padding_size(), 0u);
|
|
|
|
// Generate a payload padding packets, check that extensions are registered.
|
|
generated_packets = GeneratePadding(kMinPaddingSize);
|
|
ASSERT_EQ(generated_packets.size(), 1u);
|
|
auto& payload_padding = generated_packets.front();
|
|
EXPECT_EQ(payload_padding->padding_size(), 0u);
|
|
EXPECT_TRUE(payload_padding->HasExtension<TransportSequenceNumber>());
|
|
EXPECT_TRUE(payload_padding->HasExtension<AbsoluteSendTime>());
|
|
EXPECT_TRUE(payload_padding->HasExtension<TransmissionOffset>());
|
|
EXPECT_GT(payload_padding->payload_size(), 0u);
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, GeneratePaddingResendsOldPacketsWithRtx) {
|
|
// Min requested size in order to use RTX payload.
|
|
const size_t kMinPaddingSize = 50;
|
|
|
|
rtp_sender_->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads);
|
|
rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload);
|
|
packet_history_->SetStorePacketsStatus(
|
|
RtpPacketHistory::StorageMode::kStoreAndCull, 1);
|
|
|
|
ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(
|
|
TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
|
|
|
|
const size_t kPayloadPacketSize = kMinPaddingSize;
|
|
std::unique_ptr<RtpPacketToSend> packet =
|
|
BuildRtpPacket(kPayload, true, 0, clock_->TimeInMilliseconds());
|
|
packet->set_allow_retransmission(true);
|
|
packet->SetPayloadSize(kPayloadPacketSize);
|
|
packet->set_packet_type(RtpPacketMediaType::kVideo);
|
|
packet_history_->PutRtpPacket(std::move(packet),
|
|
clock_->TimeInMilliseconds());
|
|
|
|
// Generated padding has large enough budget that the video packet should be
|
|
// retransmitted as padding.
|
|
std::vector<std::unique_ptr<RtpPacketToSend>> generated_packets =
|
|
GeneratePadding(kMinPaddingSize);
|
|
ASSERT_EQ(generated_packets.size(), 1u);
|
|
auto& padding_packet = generated_packets.front();
|
|
EXPECT_EQ(padding_packet->packet_type(), RtpPacketMediaType::kPadding);
|
|
EXPECT_EQ(padding_packet->Ssrc(), kRtxSsrc);
|
|
EXPECT_EQ(padding_packet->payload_size(),
|
|
kPayloadPacketSize + kRtxHeaderSize);
|
|
|
|
// Not enough budged for payload padding, use plain padding instead.
|
|
const size_t kPaddingBytesRequested = kMinPaddingSize - 1;
|
|
|
|
size_t padding_bytes_generated = 0;
|
|
generated_packets = GeneratePadding(kPaddingBytesRequested);
|
|
EXPECT_EQ(generated_packets.size(), 1u);
|
|
for (auto& packet : generated_packets) {
|
|
EXPECT_EQ(packet->packet_type(), RtpPacketMediaType::kPadding);
|
|
EXPECT_EQ(packet->Ssrc(), kRtxSsrc);
|
|
EXPECT_EQ(packet->payload_size(), 0u);
|
|
EXPECT_GT(packet->padding_size(), 0u);
|
|
padding_bytes_generated += packet->padding_size();
|
|
}
|
|
|
|
EXPECT_EQ(padding_bytes_generated, kMaxPaddingSize);
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, LimitsPayloadPaddingSize) {
|
|
// Limit RTX payload padding to 2x target size.
|
|
const double kFactor = 2.0;
|
|
field_trials_.SetMaxPaddingFactor(kFactor);
|
|
SetUpRtpSender(false, false, nullptr);
|
|
rtp_sender_->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads);
|
|
rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload);
|
|
packet_history_->SetStorePacketsStatus(
|
|
RtpPacketHistory::StorageMode::kStoreAndCull, 1);
|
|
|
|
ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(
|
|
TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
|
|
|
|
// Send a dummy video packet so it ends up in the packet history.
|
|
const size_t kPayloadPacketSize = 1234u;
|
|
std::unique_ptr<RtpPacketToSend> packet =
|
|
BuildRtpPacket(kPayload, true, 0, clock_->TimeInMilliseconds());
|
|
packet->set_allow_retransmission(true);
|
|
packet->SetPayloadSize(kPayloadPacketSize);
|
|
packet->set_packet_type(RtpPacketMediaType::kVideo);
|
|
packet_history_->PutRtpPacket(std::move(packet),
|
|
clock_->TimeInMilliseconds());
|
|
|
|
// Smallest target size that will result in the sent packet being returned as
|
|
// padding.
|
|
const size_t kMinTargerSizeForPayload =
|
|
(kPayloadPacketSize + kRtxHeaderSize) / kFactor;
|
|
|
|
// Generated padding has large enough budget that the video packet should be
|
|
// retransmitted as padding.
|
|
EXPECT_THAT(
|
|
GeneratePadding(kMinTargerSizeForPayload),
|
|
AllOf(Not(IsEmpty()),
|
|
Each(Pointee(Property(&RtpPacketToSend::padding_size, Eq(0u))))));
|
|
|
|
// If payload padding is > 2x requested size, plain padding is returned
|
|
// instead.
|
|
EXPECT_THAT(
|
|
GeneratePadding(kMinTargerSizeForPayload - 1),
|
|
AllOf(Not(IsEmpty()),
|
|
Each(Pointee(Property(&RtpPacketToSend::padding_size, Gt(0u))))));
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, GeneratePaddingCreatesPurePaddingWithoutRtx) {
|
|
packet_history_->SetStorePacketsStatus(
|
|
RtpPacketHistory::StorageMode::kStoreAndCull, 1);
|
|
ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(
|
|
TransmissionOffset::kUri, kTransmissionTimeOffsetExtensionId));
|
|
ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(
|
|
AbsoluteSendTime::kUri, kAbsoluteSendTimeExtensionId));
|
|
ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(
|
|
TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
|
|
|
|
const size_t kPayloadPacketSize = 1234;
|
|
// Send a dummy video packet so it ends up in the packet history. Since we
|
|
// are not using RTX, it should never be used as padding.
|
|
std::unique_ptr<RtpPacketToSend> packet =
|
|
BuildRtpPacket(kPayload, true, 0, clock_->TimeInMilliseconds());
|
|
packet->set_allow_retransmission(true);
|
|
packet->SetPayloadSize(kPayloadPacketSize);
|
|
packet->set_packet_type(RtpPacketMediaType::kVideo);
|
|
packet_history_->PutRtpPacket(std::move(packet),
|
|
clock_->TimeInMilliseconds());
|
|
|
|
// 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 =
|
|
GeneratePadding(kPaddingBytesRequested);
|
|
EXPECT_EQ(padding_packets.size(), kExpectedNumPaddingPackets);
|
|
for (auto& packet : padding_packets) {
|
|
EXPECT_EQ(packet->packet_type(), RtpPacketMediaType::kPadding);
|
|
EXPECT_EQ(packet->Ssrc(), kSsrc);
|
|
EXPECT_EQ(packet->payload_size(), 0u);
|
|
EXPECT_GT(packet->padding_size(), 0u);
|
|
padding_bytes_generated += packet->padding_size();
|
|
EXPECT_TRUE(packet->HasExtension<TransportSequenceNumber>());
|
|
EXPECT_TRUE(packet->HasExtension<AbsoluteSendTime>());
|
|
EXPECT_TRUE(packet->HasExtension<TransmissionOffset>());
|
|
}
|
|
|
|
EXPECT_EQ(padding_bytes_generated,
|
|
kExpectedNumPaddingPackets * kMaxPaddingSize);
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, SupportsPadding) {
|
|
bool kSendingMediaStats[] = {true, false};
|
|
bool kEnableRedundantPayloads[] = {true, false};
|
|
absl::string_view kBweExtensionUris[] = {
|
|
TransportSequenceNumber::kUri, TransportSequenceNumberV2::kUri,
|
|
AbsoluteSendTime::kUri, TransmissionOffset::kUri};
|
|
const int kExtensionsId = 7;
|
|
|
|
for (bool sending_media : kSendingMediaStats) {
|
|
rtp_sender_->SetSendingMediaStatus(sending_media);
|
|
for (bool redundant_payloads : kEnableRedundantPayloads) {
|
|
int rtx_mode = kRtxRetransmitted;
|
|
if (redundant_payloads) {
|
|
rtx_mode |= kRtxRedundantPayloads;
|
|
}
|
|
rtp_sender_->SetRtxStatus(rtx_mode);
|
|
|
|
for (auto extension_uri : kBweExtensionUris) {
|
|
EXPECT_FALSE(rtp_sender_->SupportsPadding());
|
|
rtp_sender_->RegisterRtpHeaderExtension(extension_uri, kExtensionsId);
|
|
if (!sending_media) {
|
|
EXPECT_FALSE(rtp_sender_->SupportsPadding());
|
|
} else {
|
|
EXPECT_TRUE(rtp_sender_->SupportsPadding());
|
|
if (redundant_payloads) {
|
|
EXPECT_TRUE(rtp_sender_->SupportsRtxPayloadPadding());
|
|
} else {
|
|
EXPECT_FALSE(rtp_sender_->SupportsRtxPayloadPadding());
|
|
}
|
|
}
|
|
rtp_sender_->DeregisterRtpHeaderExtension(extension_uri);
|
|
EXPECT_FALSE(rtp_sender_->SupportsPadding());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, SetsCaptureTimeOnRtxRetransmissions) {
|
|
EnableRtx();
|
|
|
|
// Put a packet in the packet history, with current time as capture time.
|
|
const int64_t start_time_ms = clock_->TimeInMilliseconds();
|
|
std::unique_ptr<RtpPacketToSend> packet =
|
|
BuildRtpPacket(kPayload, kMarkerBit, start_time_ms,
|
|
/*capture_time_ms=*/start_time_ms);
|
|
packet->set_allow_retransmission(true);
|
|
packet_history_->PutRtpPacket(std::move(packet), start_time_ms);
|
|
|
|
// Advance time, request an RTX retransmission. Capture timestamp should be
|
|
// preserved.
|
|
time_controller_.AdvanceTime(TimeDelta::Millis(10));
|
|
|
|
EXPECT_CALL(mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(Pointee(Property(
|
|
&RtpPacketToSend::capture_time_ms, start_time_ms)))));
|
|
EXPECT_GT(rtp_sender_->ReSendPacket(kSeqNum), 0);
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, ClearHistoryOnSequenceNumberCange) {
|
|
EnableRtx();
|
|
|
|
// Put a packet in the packet history.
|
|
const int64_t now_ms = clock_->TimeInMilliseconds();
|
|
std::unique_ptr<RtpPacketToSend> packet =
|
|
BuildRtpPacket(kPayload, kMarkerBit, now_ms, now_ms);
|
|
packet->set_allow_retransmission(true);
|
|
packet_history_->PutRtpPacket(std::move(packet), now_ms);
|
|
|
|
EXPECT_TRUE(packet_history_->GetPacketState(kSeqNum));
|
|
|
|
// Update the sequence number of the RTP module, verify packet has been
|
|
// removed.
|
|
rtp_sender_->SetSequenceNumber(rtp_sender_->SequenceNumber() - 1);
|
|
EXPECT_FALSE(packet_history_->GetPacketState(kSeqNum));
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, IgnoresNackAfterDisablingMedia) {
|
|
const int64_t kRtt = 10;
|
|
|
|
EnableRtx();
|
|
packet_history_->SetRtt(kRtt);
|
|
|
|
// Put a packet in the history.
|
|
const int64_t start_time_ms = clock_->TimeInMilliseconds();
|
|
std::unique_ptr<RtpPacketToSend> packet =
|
|
BuildRtpPacket(kPayload, kMarkerBit, start_time_ms,
|
|
/*capture_time_ms=*/start_time_ms);
|
|
packet->set_allow_retransmission(true);
|
|
packet_history_->PutRtpPacket(std::move(packet), start_time_ms);
|
|
|
|
// Disable media sending and try to retransmit the packet, it should fail.
|
|
rtp_sender_->SetSendingMediaStatus(false);
|
|
time_controller_.AdvanceTime(TimeDelta::Millis(kRtt));
|
|
EXPECT_LT(rtp_sender_->ReSendPacket(kSeqNum), 0);
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, DoesntFecProtectRetransmissions) {
|
|
// Set up retranmission without RTX, so that a plain copy of the old packet is
|
|
// re-sent instead.
|
|
const int64_t kRtt = 10;
|
|
rtp_sender_->SetSendingMediaStatus(true);
|
|
rtp_sender_->SetRtxStatus(kRtxOff);
|
|
packet_history_->SetStorePacketsStatus(
|
|
RtpPacketHistory::StorageMode::kStoreAndCull, 10);
|
|
packet_history_->SetRtt(kRtt);
|
|
|
|
// Put a fec protected packet in the history.
|
|
const int64_t start_time_ms = clock_->TimeInMilliseconds();
|
|
std::unique_ptr<RtpPacketToSend> packet =
|
|
BuildRtpPacket(kPayload, kMarkerBit, start_time_ms,
|
|
/*capture_time_ms=*/start_time_ms);
|
|
packet->set_allow_retransmission(true);
|
|
packet->set_fec_protect_packet(true);
|
|
packet_history_->PutRtpPacket(std::move(packet), start_time_ms);
|
|
|
|
// Re-send packet, the retransmitted packet should not have the FEC protection
|
|
// flag set.
|
|
EXPECT_CALL(mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(Pointee(
|
|
Property(&RtpPacketToSend::fec_protect_packet, false)))));
|
|
|
|
time_controller_.AdvanceTime(TimeDelta::Millis(kRtt));
|
|
EXPECT_GT(rtp_sender_->ReSendPacket(kSeqNum), 0);
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, MarksPacketsWithKeyframeStatus) {
|
|
FieldTrialBasedConfig field_trials;
|
|
RTPSenderVideo::Config video_config;
|
|
video_config.clock = clock_;
|
|
video_config.rtp_sender = rtp_sender_.get();
|
|
video_config.field_trials = &field_trials;
|
|
RTPSenderVideo rtp_sender_video(video_config);
|
|
|
|
const uint8_t kPayloadType = 127;
|
|
const absl::optional<VideoCodecType> kCodecType =
|
|
VideoCodecType::kVideoCodecGeneric;
|
|
|
|
const uint32_t kCaptureTimeMsToRtpTimestamp = 90; // 90 kHz clock
|
|
|
|
{
|
|
EXPECT_CALL(mock_paced_sender_,
|
|
EnqueuePackets(Each(
|
|
Pointee(Property(&RtpPacketToSend::is_key_frame, true)))))
|
|
.Times(AtLeast(1));
|
|
RTPVideoHeader video_header;
|
|
video_header.frame_type = VideoFrameType::kVideoFrameKey;
|
|
int64_t capture_time_ms = clock_->TimeInMilliseconds();
|
|
EXPECT_TRUE(rtp_sender_video.SendVideo(
|
|
kPayloadType, kCodecType,
|
|
capture_time_ms * kCaptureTimeMsToRtpTimestamp, capture_time_ms,
|
|
kPayloadData, video_header, kDefaultExpectedRetransmissionTimeMs));
|
|
|
|
time_controller_.AdvanceTime(TimeDelta::Millis(33));
|
|
}
|
|
|
|
{
|
|
EXPECT_CALL(mock_paced_sender_,
|
|
EnqueuePackets(Each(
|
|
Pointee(Property(&RtpPacketToSend::is_key_frame, false)))))
|
|
.Times(AtLeast(1));
|
|
RTPVideoHeader video_header;
|
|
video_header.frame_type = VideoFrameType::kVideoFrameDelta;
|
|
int64_t capture_time_ms = clock_->TimeInMilliseconds();
|
|
EXPECT_TRUE(rtp_sender_video.SendVideo(
|
|
kPayloadType, kCodecType,
|
|
capture_time_ms * kCaptureTimeMsToRtpTimestamp, capture_time_ms,
|
|
kPayloadData, video_header, kDefaultExpectedRetransmissionTimeMs));
|
|
|
|
time_controller_.AdvanceTime(TimeDelta::Millis(33));
|
|
}
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, PlainPaddingWithDeferredSequencing) {
|
|
CreateSenderWithDeferredSequencing(GetDefaultConfig());
|
|
|
|
EXPECT_THAT(
|
|
rtp_sender_->GeneratePadding(
|
|
/*target_size_bytes=*/500,
|
|
/*media_has_been_sent=*/true,
|
|
/*can_send_padding_on_media_ssrc=*/true),
|
|
Each(Pointee(AllOf(Property(&RtpPacketToSend::SequenceNumber, 0),
|
|
Property(&RtpPacketToSend::padding_size, Gt(0u)),
|
|
Property(&RtpPacketToSend::Ssrc, kSsrc)))));
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, PlainRtxPaddingWithDeferredSequencing) {
|
|
CreateSenderWithDeferredSequencing(GetDefaultConfig());
|
|
EnableRtx();
|
|
|
|
EXPECT_THAT(
|
|
rtp_sender_->GeneratePadding(
|
|
/*target_size_bytes=*/500,
|
|
/*media_has_been_sent=*/true,
|
|
/*can_send_padding_on_media_ssrc=*/true),
|
|
Each(Pointee(AllOf(Property(&RtpPacketToSend::SequenceNumber, 0),
|
|
Property(&RtpPacketToSend::padding_size, Gt(0u)),
|
|
Property(&RtpPacketToSend::Ssrc, kRtxSsrc)))));
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, PayloadPaddingWithDeferredSequencing) {
|
|
CreateSenderWithDeferredSequencing(GetDefaultConfig());
|
|
EnableRtx();
|
|
ASSERT_TRUE(rtp_sender_->RegisterRtpHeaderExtension(
|
|
TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
|
|
|
|
EXPECT_CALL(mock_paced_sender_, EnqueuePackets);
|
|
std::unique_ptr<RtpPacketToSend> media_packet =
|
|
SendPacket(clock_->TimeInMilliseconds(), /*payload_size=*/500);
|
|
packet_history_->PutRtpPacket(std::move(media_packet),
|
|
clock_->TimeInMilliseconds());
|
|
|
|
EXPECT_THAT(
|
|
rtp_sender_->GeneratePadding(
|
|
/*target_size_bytes=*/500,
|
|
/*media_has_been_sent=*/true,
|
|
/*can_send_padding_on_media_ssrc=*/true),
|
|
Each(Pointee(AllOf(Property(&RtpPacketToSend::SequenceNumber, 0),
|
|
Property(&RtpPacketToSend::payload_size, Gt(0u)),
|
|
Property(&RtpPacketToSend::Ssrc, kRtxSsrc)))));
|
|
}
|
|
|
|
TEST_F(RtpSenderTest, RtxRetransmissionWithDeferredSequencing) {
|
|
CreateSenderWithDeferredSequencing(GetDefaultConfig());
|
|
EnableRtx();
|
|
|
|
int64_t now_ms = clock_->TimeInMilliseconds();
|
|
auto packet = BuildRtpPacket(kPayload, kMarkerBit, kTimestamp, now_ms);
|
|
packet->SetSequenceNumber(kSeqNum);
|
|
packet->set_allow_retransmission(true);
|
|
packet_history_->PutRtpPacket(std::move(packet), now_ms);
|
|
|
|
EXPECT_CALL(mock_paced_sender_,
|
|
EnqueuePackets(ElementsAre(Pointee(
|
|
AllOf(Property(&RtpPacketToSend::Ssrc, kRtxSsrc),
|
|
Property(&RtpPacketToSend::SequenceNumber, 0u))))));
|
|
EXPECT_TRUE(rtp_sender_->ReSendPacket(kSeqNum));
|
|
}
|
|
|
|
} // namespace webrtc
|