Introduce RtpTransportConfig:allow_bandwidht_estimation_probe_without_media

If allow_bandwidht_estimation_probe_without_media is true and a writable
video rtp stream with RTX exist, a probe can be sent immediately without
waiting for a large media packet.

Bug: webrtc:14928
Change-Id: Ie2204734f9fe3e6bff9aed4a1f7f8995956d35cb
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/336000
Commit-Queue: Per Kjellander <perkj@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41626}
This commit is contained in:
Per K 2024-01-26 12:57:01 +01:00 committed by WebRTC LUCI CQ
parent 89db1c5827
commit 98db63cfb6
13 changed files with 127 additions and 17 deletions

View File

@ -38,6 +38,10 @@ struct RtpTransportConfig {
// The burst interval of the pacer, see TaskQueuePacedSender constructor.
absl::optional<TimeDelta> pacer_burst_interval;
// A bandwith estimation probe may be sent on a writable Rtp stream that have
// RTX configured. It can be sent without first sending media packets.
bool allow_bandwidth_estimation_probe_without_media = false;
};
} // namespace webrtc

View File

@ -70,6 +70,8 @@ bool IsRelayed(const rtc::NetworkRoute& route) {
RtpTransportControllerSend::RtpTransportControllerSend(
const RtpTransportConfig& config)
: env_(config.env),
allow_bandwidth_estimation_probe_without_media_(
config.allow_bandwidth_estimation_probe_without_media),
task_queue_(TaskQueueBase::Current()),
bitrate_configurator_(config.bitrate_config),
pacer_started_(false),
@ -165,6 +167,9 @@ void RtpTransportControllerSend::RegisterSendingRtpStream(
// Allow pacer to send packets using this module.
packet_router_.AddSendRtpModule(&rtp_module,
/*remb_candidate=*/true);
pacer_.SetAllowProbeWithoutMediaPacket(
allow_bandwidth_estimation_probe_without_media_ &&
packet_router_.SupportsRtxPayloadPadding());
}
void RtpTransportControllerSend::DeRegisterSendingRtpStream(
@ -182,6 +187,9 @@ void RtpTransportControllerSend::DeRegisterSendingRtpStream(
if (rtp_module.FlexfecSsrc().has_value()) {
pacer_.RemovePacketsForSsrc(*rtp_module.FlexfecSsrc());
}
pacer_.SetAllowProbeWithoutMediaPacket(
allow_bandwidth_estimation_probe_without_media_ &&
packet_router_.SupportsRtxPayloadPadding());
}
void RtpTransportControllerSend::UpdateControlState() {

View File

@ -150,6 +150,7 @@ class RtpTransportControllerSend final
const Environment env_;
SequenceChecker sequence_checker_;
const bool allow_bandwidth_estimation_probe_without_media_;
TaskQueueBase* task_queue_;
PacketRouter packet_router_;
std::vector<std::unique_ptr<RtpVideoSenderInterface>> video_rtp_senders_

View File

@ -52,6 +52,18 @@ void BitrateProber::SetEnabled(bool enable) {
}
}
void BitrateProber::SetAllowProbeWithoutMediaPacket(bool allow) {
config_.allow_start_probing_immediately = allow;
MaybeSetActiveState(/*packet_size=*/DataSize::Zero());
}
void BitrateProber::MaybeSetActiveState(DataSize packet_size) {
if (ReadyToSetActiveState(packet_size)) {
next_probe_time_ = Timestamp::MinusInfinity();
probing_state_ = ProbingState::kActive;
}
}
bool BitrateProber::ReadyToSetActiveState(DataSize packet_size) const {
if (clusters_.empty()) {
RTC_DCHECK(probing_state_ == ProbingState::kDisabled ||
@ -63,19 +75,19 @@ bool BitrateProber::ReadyToSetActiveState(DataSize packet_size) const {
case ProbingState::kActive:
return false;
case ProbingState::kInactive:
// If config_.min_packet_size > 0, a "large enough" packet must be sent
// first, before a probe can be generated and sent. Otherwise, send the
// probe asap.
if (config_.allow_start_probing_immediately) {
return true;
}
// If config_.min_packet_size > 0, a "large enough" packet must be
// sent first, before a probe can be generated and sent. Otherwise,
// send the probe asap.
return packet_size >=
std::min(RecommendedMinProbeSize(), config_.min_packet_size.Get());
}
}
void BitrateProber::OnIncomingPacket(DataSize packet_size) {
if (ReadyToSetActiveState(packet_size)) {
next_probe_time_ = Timestamp::MinusInfinity();
probing_state_ = ProbingState::kActive;
}
MaybeSetActiveState(packet_size);
}
void BitrateProber::CreateProbeCluster(
@ -101,10 +113,8 @@ void BitrateProber::CreateProbeCluster(
cluster.pace_info.probe_cluster_id = cluster_config.id;
clusters_.push(cluster);
if (ReadyToSetActiveState(/*packet_size=*/DataSize::Zero())) {
next_probe_time_ = Timestamp::MinusInfinity();
probing_state_ = ProbingState::kActive;
}
MaybeSetActiveState(/*packet_size=*/DataSize::Zero());
RTC_DCHECK(probing_state_ == ProbingState::kActive ||
probing_state_ == ProbingState::kInactive);

View File

@ -38,6 +38,9 @@ struct BitrateProberConfig {
// This defines the max min packet size, meaning that on high bitrates
// a packet of at least this size is needed to trigger sending a probe.
FieldTrialParameter<DataSize> min_packet_size;
// If true, `min_packet_size` is ignored.
bool allow_start_probing_immediately = false;
};
// Note that this class isn't thread-safe by itself and therefore relies
@ -48,6 +51,7 @@ class BitrateProber {
~BitrateProber() = default;
void SetEnabled(bool enable);
void SetAllowProbeWithoutMediaPacket(bool allow);
// Returns true if the prober is in a probing session, i.e., it currently
// wants packets to be sent out according to the time returned by
@ -105,6 +109,8 @@ class BitrateProber {
};
Timestamp CalculateNextProbeTime(const ProbeCluster& cluster) const;
void MaybeSetActiveState(DataSize packet_size);
bool ReadyToSetActiveState(DataSize packet_size) const;
ProbingState probing_state_;

View File

@ -252,6 +252,10 @@ void PacingController::SetSendBurstInterval(TimeDelta burst_interval) {
send_burst_interval_ = burst_interval;
}
void PacingController::SetAllowProbeWithoutMediaPacket(bool allow) {
prober_.SetAllowProbeWithoutMediaPacket(allow);
}
TimeDelta PacingController::ExpectedQueueTime() const {
RTC_DCHECK_GT(adjusted_media_rate_, DataRate::Zero());
return QueueSizeData() / adjusted_media_rate_;

View File

@ -160,6 +160,9 @@ class PacingController {
// 'burst_interval'.
void SetSendBurstInterval(TimeDelta burst_interval);
// A probe may be sent without first waing for a media packet.
void SetAllowProbeWithoutMediaPacket(bool allow);
// Returns the time when the oldest packet was queued.
Timestamp OldestPacketEnqueueTime() const;

View File

@ -1366,10 +1366,9 @@ TEST_F(PacingControllerTest, CanProbeWithPaddingBeforeFirstMediaPacket) {
const int kInitialBitrateBps = 300000;
PacingControllerProbing packet_sender;
const test::ExplicitKeyValueConfig trials(
"WebRTC-Bwe-ProbingBehavior/min_packet_size:0/");
auto pacer =
std::make_unique<PacingController>(&clock_, &packet_sender, trials);
std::make_unique<PacingController>(&clock_, &packet_sender, trials_);
pacer->SetAllowProbeWithoutMediaPacket(true);
std::vector<ProbeClusterConfig> probe_clusters = {
{.at_time = clock_.CurrentTime(),
.target_data_rate = kFirstClusterRate,
@ -1393,16 +1392,46 @@ TEST_F(PacingControllerTest, CanProbeWithPaddingBeforeFirstMediaPacket) {
EXPECT_GT(packet_sender.padding_packets_sent(), 5);
}
TEST_F(PacingControllerTest, ProbeSentAfterSetAllowProbeWithoutMediaPacket) {
const int kInitialBitrateBps = 300000;
PacingControllerProbing packet_sender;
auto pacer =
std::make_unique<PacingController>(&clock_, &packet_sender, trials_);
std::vector<ProbeClusterConfig> probe_clusters = {
{.at_time = clock_.CurrentTime(),
.target_data_rate = kFirstClusterRate,
.target_duration = TimeDelta::Millis(15),
.target_probe_count = 5,
.id = 0}};
pacer->CreateProbeClusters(probe_clusters);
pacer->SetPacingRates(
DataRate::BitsPerSec(kInitialBitrateBps * kPaceMultiplier),
DataRate::Zero());
pacer->SetAllowProbeWithoutMediaPacket(true);
Timestamp start = clock_.CurrentTime();
Timestamp next_process = pacer->NextSendTime();
while (clock_.CurrentTime() < start + TimeDelta::Millis(100) &&
next_process.IsFinite()) {
AdvanceTimeUntil(next_process);
pacer->ProcessPackets();
next_process = pacer->NextSendTime();
}
EXPECT_GT(packet_sender.padding_packets_sent(), 5);
}
TEST_F(PacingControllerTest, CanNotProbeWithPaddingIfGeneratePaddingFails) {
// const size_t kPacketSize = 1200;
const int kInitialBitrateBps = 300000;
PacingControllerProbing packet_sender;
packet_sender.SetCanGeneratePadding(false);
const test::ExplicitKeyValueConfig trials(
"WebRTC-Bwe-ProbingBehavior/min_packet_size:0/");
auto pacer =
std::make_unique<PacingController>(&clock_, &packet_sender, trials);
std::make_unique<PacingController>(&clock_, &packet_sender, trials_);
pacer->SetAllowProbeWithoutMediaPacket(true);
std::vector<ProbeClusterConfig> probe_clusters = {
{.at_time = clock_.CurrentTime(),
.target_data_rate = kFirstClusterRate,

View File

@ -65,6 +65,16 @@ void PacketRouter::AddSendRtpModule(RtpRtcpInterface* rtp_module,
}
}
bool PacketRouter::SupportsRtxPayloadPadding() const {
RTC_DCHECK_RUN_ON(&thread_checker_);
for (RtpRtcpInterface* rtp_module : send_modules_list_) {
if (rtp_module->SupportsRtxPayloadPadding()) {
return true;
}
}
return false;
}
void PacketRouter::AddSendRtpModuleToMap(RtpRtcpInterface* rtp_module,
uint32_t ssrc) {
RTC_DCHECK_RUN_ON(&thread_checker_);

View File

@ -50,6 +50,8 @@ class PacketRouter : public PacingController::PacketSender {
void AddSendRtpModule(RtpRtcpInterface* rtp_module, bool remb_candidate);
void RemoveSendRtpModule(RtpRtcpInterface* rtp_module);
bool SupportsRtxPayloadPadding() const;
void AddReceiveRtpModule(RtcpFeedbackSenderInterface* rtcp_sender,
bool remb_candidate);
void RemoveReceiveRtpModule(RtcpFeedbackSenderInterface* rtcp_sender);

View File

@ -125,6 +125,31 @@ TEST_F(PacketRouterTest, GeneratePaddingPrioritizesRtx) {
packet_router_.RemoveSendRtpModule(&rtp_2);
}
TEST_F(PacketRouterTest, SupportsRtxPayloadPaddingFalseIfNoRtxSendModule) {
EXPECT_FALSE(packet_router_.SupportsRtxPayloadPadding());
NiceMock<MockRtpRtcpInterface> none_rtx_module;
ON_CALL(none_rtx_module, SupportsRtxPayloadPadding())
.WillByDefault(Return(false));
packet_router_.AddSendRtpModule(&none_rtx_module, false);
EXPECT_FALSE(packet_router_.SupportsRtxPayloadPadding());
packet_router_.RemoveSendRtpModule(&none_rtx_module);
EXPECT_FALSE(packet_router_.SupportsRtxPayloadPadding());
}
TEST_F(PacketRouterTest, SupportsRtxPayloadPaddingTrueIfRtxSendModule) {
NiceMock<MockRtpRtcpInterface> rtx_module;
ON_CALL(rtx_module, SupportsRtxPayloadPadding()).WillByDefault(Return(true));
packet_router_.AddSendRtpModule(&rtx_module, false);
EXPECT_TRUE(packet_router_.SupportsRtxPayloadPadding());
packet_router_.RemoveSendRtpModule(&rtx_module);
EXPECT_FALSE(packet_router_.SupportsRtxPayloadPadding());
}
TEST_F(PacketRouterTest, GeneratePaddingPrioritizesVideo) {
// Two RTP modules. Neither support RTX, both support padding,
// but the first one is for audio and second for video.

View File

@ -52,6 +52,11 @@ void TaskQueuePacedSender::SetSendBurstInterval(TimeDelta burst_interval) {
pacing_controller_.SetSendBurstInterval(burst_interval);
}
void TaskQueuePacedSender::SetAllowProbeWithoutMediaPacket(bool allow) {
RTC_DCHECK_RUN_ON(task_queue_);
pacing_controller_.SetAllowProbeWithoutMediaPacket(allow);
}
void TaskQueuePacedSender::EnsureStarted() {
RTC_DCHECK_RUN_ON(task_queue_);
is_started_ = true;

View File

@ -60,6 +60,9 @@ class TaskQueuePacedSender : public RtpPacketPacer, public RtpPacketSender {
// 'burst_interval'.
void SetSendBurstInterval(TimeDelta burst_interval);
// A probe may be sent without first waing for a media packet.
void SetAllowProbeWithoutMediaPacket(bool allow);
// Ensure that necessary delayed tasks are scheduled.
void EnsureStarted();