Add field trial for fast retransmissions.
This adds a (default off) flag which makes retransmissions be processed immediately, just like audio packets normally are. This might increase send rates and thus losses in some cases, but will also reduce retranmission delays especially when timer slack or bursting is used. Usefuleness TBD via experiment. Bug: chromium:1354491 Change-Id: Icaa83125bfb30826ce72e6e786963d411e05ea57 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/272483 Commit-Queue: Erik Språng <sprang@webrtc.org> Reviewed-by: Henrik Boström <hbos@webrtc.org> Cr-Commit-Position: refs/heads/main@{#37926}
This commit is contained in:

committed by
WebRTC LUCI CQ

parent
1d0b0aed97
commit
c18a8fd8d1
@ -67,6 +67,8 @@ PacingController::PacingController(Clock* clock,
|
|||||||
pace_audio_(IsEnabled(field_trials_, "WebRTC-Pacer-BlockAudio")),
|
pace_audio_(IsEnabled(field_trials_, "WebRTC-Pacer-BlockAudio")),
|
||||||
ignore_transport_overhead_(
|
ignore_transport_overhead_(
|
||||||
IsEnabled(field_trials_, "WebRTC-Pacer-IgnoreTransportOverhead")),
|
IsEnabled(field_trials_, "WebRTC-Pacer-IgnoreTransportOverhead")),
|
||||||
|
fast_retransmissions_(
|
||||||
|
IsEnabled(field_trials_, "WebRTC-Pacer-FastRetransmissions")),
|
||||||
min_packet_limit_(kDefaultMinPacketLimit),
|
min_packet_limit_(kDefaultMinPacketLimit),
|
||||||
transport_overhead_per_packet_(DataSize::Zero()),
|
transport_overhead_per_packet_(DataSize::Zero()),
|
||||||
send_burst_interval_(TimeDelta::Zero()),
|
send_burst_interval_(TimeDelta::Zero()),
|
||||||
@ -313,13 +315,11 @@ Timestamp PacingController::NextSendTime() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not pacing audio, if leading packet is audio its target send
|
// If queue contains a packet which should not be paced, its target send time
|
||||||
// time is the time at which it was enqueued.
|
// is the time at which it was enqueued.
|
||||||
Timestamp unpaced_audio_time =
|
Timestamp unpaced_send_time = NextUnpacedSendTime();
|
||||||
pace_audio_ ? Timestamp::PlusInfinity()
|
if (unpaced_send_time.IsFinite()) {
|
||||||
: packet_queue_.LeadingAudioPacketEnqueueTime();
|
return unpaced_send_time;
|
||||||
if (unpaced_audio_time.IsFinite()) {
|
|
||||||
return unpaced_audio_time;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (congested_ || !seen_first_packet_) {
|
if (congested_ || !seen_first_packet_) {
|
||||||
@ -582,11 +582,8 @@ std::unique_ptr<RtpPacketToSend> PacingController::GetPendingPacket(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// First, check if there is any reason _not_ to send the next queued packet.
|
// First, check if there is any reason _not_ to send the next queued packet.
|
||||||
|
// Unpaced packets and probes are exempted from send checks.
|
||||||
// Unpaced audio packets and probes are exempted from send checks.
|
if (NextUnpacedSendTime().IsInfinite() && !is_probe) {
|
||||||
bool unpaced_audio_packet =
|
|
||||||
!pace_audio_ && packet_queue_.LeadingAudioPacketEnqueueTime().IsFinite();
|
|
||||||
if (!unpaced_audio_packet && !is_probe) {
|
|
||||||
if (congested_) {
|
if (congested_) {
|
||||||
// Don't send anything if congested.
|
// Don't send anything if congested.
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -667,4 +664,23 @@ void PacingController::MaybeUpdateMediaRateDueToLongQueue(Timestamp now) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timestamp PacingController::NextUnpacedSendTime() const {
|
||||||
|
if (!pace_audio_) {
|
||||||
|
Timestamp leading_audio_send_time =
|
||||||
|
packet_queue_.LeadingPacketEnqueueTime(RtpPacketMediaType::kAudio);
|
||||||
|
if (leading_audio_send_time.IsFinite()) {
|
||||||
|
return leading_audio_send_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fast_retransmissions_) {
|
||||||
|
Timestamp leading_retransmission_send_time =
|
||||||
|
packet_queue_.LeadingPacketEnqueueTime(
|
||||||
|
RtpPacketMediaType::kRetransmission);
|
||||||
|
if (leading_retransmission_send_time.IsFinite()) {
|
||||||
|
return leading_retransmission_send_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Timestamp::MinusInfinity();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -176,6 +176,12 @@ class PacingController {
|
|||||||
|
|
||||||
Timestamp CurrentTime() const;
|
Timestamp CurrentTime() const;
|
||||||
|
|
||||||
|
// Helper methods for packet that may not be paced. Returns a finite Timestamp
|
||||||
|
// if a packet type is configured to not be paced and the packet queue has at
|
||||||
|
// least one packet of that type. Otherwise returns
|
||||||
|
// Timestamp::MinusInfinity().
|
||||||
|
Timestamp NextUnpacedSendTime() const;
|
||||||
|
|
||||||
Clock* const clock_;
|
Clock* const clock_;
|
||||||
PacketSender* const packet_sender_;
|
PacketSender* const packet_sender_;
|
||||||
const FieldTrialsView& field_trials_;
|
const FieldTrialsView& field_trials_;
|
||||||
@ -184,6 +190,7 @@ class PacingController {
|
|||||||
const bool send_padding_if_silent_;
|
const bool send_padding_if_silent_;
|
||||||
const bool pace_audio_;
|
const bool pace_audio_;
|
||||||
const bool ignore_transport_overhead_;
|
const bool ignore_transport_overhead_;
|
||||||
|
const bool fast_retransmissions_;
|
||||||
|
|
||||||
TimeDelta min_packet_limit_;
|
TimeDelta min_packet_limit_;
|
||||||
DataSize transport_overhead_per_packet_;
|
DataSize transport_overhead_per_packet_;
|
||||||
|
@ -2061,5 +2061,31 @@ TEST_F(PacingControllerTest, RespectsQueueTimeLimit) {
|
|||||||
EXPECT_EQ(pacer.pacing_rate(), kNominalPacingRate);
|
EXPECT_EQ(pacer.pacing_rate(), kNominalPacingRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(PacingControllerTest, BudgetDoesNotAffectRetransmissionInsTrial) {
|
||||||
|
const DataSize kPacketSize = DataSize::Bytes(1000);
|
||||||
|
|
||||||
|
EXPECT_CALL(callback_, SendPadding).Times(0);
|
||||||
|
const test::ExplicitKeyValueConfig trials(
|
||||||
|
"WebRTC-Pacer-FastRetransmissions/Enabled/");
|
||||||
|
PacingController pacer(&clock_, &callback_, trials);
|
||||||
|
pacer.SetPacingRates(kTargetRate, /*padding_rate=*/DataRate::Zero());
|
||||||
|
|
||||||
|
// Send a video packet so that we have a bit debt.
|
||||||
|
pacer.EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo, kVideoSsrc,
|
||||||
|
/*sequence_number=*/1,
|
||||||
|
/*capture_time=*/1, kPacketSize.bytes()));
|
||||||
|
EXPECT_CALL(callback_, SendPacket);
|
||||||
|
pacer.ProcessPackets();
|
||||||
|
EXPECT_GT(pacer.NextSendTime(), clock_.CurrentTime());
|
||||||
|
|
||||||
|
// A retransmission packet should still be immediately processed.
|
||||||
|
EXPECT_CALL(callback_, SendPacket);
|
||||||
|
pacer.EnqueuePacket(BuildPacket(RtpPacketMediaType::kRetransmission,
|
||||||
|
kVideoSsrc,
|
||||||
|
/*sequence_number=*/1,
|
||||||
|
/*capture_time=*/1, kPacketSize.bytes()));
|
||||||
|
pacer.ProcessPackets();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -81,10 +81,10 @@ bool PrioritizedPacketQueue::StreamQueue::IsEmpty() const {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Timestamp PrioritizedPacketQueue::StreamQueue::LeadingAudioPacketEnqueueTime()
|
Timestamp PrioritizedPacketQueue::StreamQueue::LeadingPacketEnqueueTime(
|
||||||
const {
|
int priority_level) const {
|
||||||
RTC_DCHECK(!packets_[kAudioPrioLevel].empty());
|
RTC_DCHECK(!packets_[priority_level].empty());
|
||||||
return packets_[kAudioPrioLevel].begin()->enqueue_time;
|
return packets_[priority_level].begin()->enqueue_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
Timestamp PrioritizedPacketQueue::StreamQueue::LastEnqueueTime() const {
|
Timestamp PrioritizedPacketQueue::StreamQueue::LastEnqueueTime() const {
|
||||||
@ -225,13 +225,14 @@ PrioritizedPacketQueue::SizeInPacketsPerRtpPacketMediaType() const {
|
|||||||
return size_packets_per_media_type_;
|
return size_packets_per_media_type_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Timestamp PrioritizedPacketQueue::LeadingAudioPacketEnqueueTime() const {
|
Timestamp PrioritizedPacketQueue::LeadingPacketEnqueueTime(
|
||||||
if (streams_by_prio_[kAudioPrioLevel].empty()) {
|
RtpPacketMediaType type) const {
|
||||||
|
const int priority_level = GetPriorityForType(type);
|
||||||
|
if (streams_by_prio_[priority_level].empty()) {
|
||||||
return Timestamp::MinusInfinity();
|
return Timestamp::MinusInfinity();
|
||||||
}
|
}
|
||||||
return streams_by_prio_[kAudioPrioLevel]
|
return streams_by_prio_[priority_level].front()->LeadingPacketEnqueueTime(
|
||||||
.front()
|
priority_level);
|
||||||
->LeadingAudioPacketEnqueueTime();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Timestamp PrioritizedPacketQueue::OldestEnqueueTime() const {
|
Timestamp PrioritizedPacketQueue::OldestEnqueueTime() const {
|
||||||
|
@ -57,9 +57,10 @@ class PrioritizedPacketQueue {
|
|||||||
const std::array<int, kNumMediaTypes>& SizeInPacketsPerRtpPacketMediaType()
|
const std::array<int, kNumMediaTypes>& SizeInPacketsPerRtpPacketMediaType()
|
||||||
const;
|
const;
|
||||||
|
|
||||||
// The enqueue time of the next audio packet this queue will return via the
|
// The enqueue time of the next packet this queue will return via the Pop()
|
||||||
// Pop() method. If queue has no audio packets, returns MinusInfinity().
|
// method, for the given packet type. If queue has no packets, of that type,
|
||||||
Timestamp LeadingAudioPacketEnqueueTime() const;
|
// returns Timestamp::MinusInfinity().
|
||||||
|
Timestamp LeadingPacketEnqueueTime(RtpPacketMediaType type) const;
|
||||||
|
|
||||||
// Enqueue time of the oldest packet in the queue,
|
// Enqueue time of the oldest packet in the queue,
|
||||||
// Timestamp::MinusInfinity() if queue is empty.
|
// Timestamp::MinusInfinity() if queue is empty.
|
||||||
@ -110,7 +111,7 @@ class PrioritizedPacketQueue {
|
|||||||
|
|
||||||
bool HasPacketsAtPrio(int priority_level) const;
|
bool HasPacketsAtPrio(int priority_level) const;
|
||||||
bool IsEmpty() const;
|
bool IsEmpty() const;
|
||||||
Timestamp LeadingAudioPacketEnqueueTime() const;
|
Timestamp LeadingPacketEnqueueTime(int priority_level) const;
|
||||||
Timestamp LastEnqueueTime() const;
|
Timestamp LastEnqueueTime() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -214,21 +214,39 @@ TEST(PrioritizedPacketQueue, SubtractsPusedTimeFromAverageQueueTime) {
|
|||||||
EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Millis(750));
|
EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Millis(750));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrioritizedPacketQueue, ReportsLeadingAudioEnqueueTime) {
|
TEST(PrioritizedPacketQueue, ReportsLeadingPacketEnqueueTime) {
|
||||||
PrioritizedPacketQueue queue(/*creation_time=*/Timestamp::Zero());
|
PrioritizedPacketQueue queue(/*creation_time=*/Timestamp::Zero());
|
||||||
EXPECT_EQ(queue.LeadingAudioPacketEnqueueTime(), Timestamp::MinusInfinity());
|
EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kAudio),
|
||||||
|
Timestamp::MinusInfinity());
|
||||||
|
EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kVideo),
|
||||||
|
Timestamp::MinusInfinity());
|
||||||
|
|
||||||
queue.Push(Timestamp::Millis(10),
|
queue.Push(Timestamp::Millis(10),
|
||||||
CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/1));
|
CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/1));
|
||||||
EXPECT_EQ(queue.LeadingAudioPacketEnqueueTime(), Timestamp::MinusInfinity());
|
EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kAudio),
|
||||||
|
Timestamp::MinusInfinity());
|
||||||
|
EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kVideo),
|
||||||
|
Timestamp::Millis(10));
|
||||||
|
|
||||||
queue.Push(Timestamp::Millis(20),
|
queue.Push(Timestamp::Millis(20),
|
||||||
CreatePacket(RtpPacketMediaType::kAudio, /*seq=*/2));
|
CreatePacket(RtpPacketMediaType::kAudio, /*seq=*/2));
|
||||||
|
|
||||||
EXPECT_EQ(queue.LeadingAudioPacketEnqueueTime(), Timestamp::Millis(20));
|
EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kAudio),
|
||||||
|
Timestamp::Millis(20));
|
||||||
|
EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kVideo),
|
||||||
|
Timestamp::Millis(10));
|
||||||
|
|
||||||
queue.Pop(); // Pop audio packet.
|
queue.Pop(); // Pop audio packet.
|
||||||
EXPECT_EQ(queue.LeadingAudioPacketEnqueueTime(), Timestamp::MinusInfinity());
|
EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kAudio),
|
||||||
|
Timestamp::MinusInfinity());
|
||||||
|
EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kVideo),
|
||||||
|
Timestamp::Millis(10));
|
||||||
|
|
||||||
|
queue.Pop(); // Pop video packet.
|
||||||
|
EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kAudio),
|
||||||
|
Timestamp::MinusInfinity());
|
||||||
|
EXPECT_EQ(queue.LeadingPacketEnqueueTime(RtpPacketMediaType::kVideo),
|
||||||
|
Timestamp::MinusInfinity());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrioritizedPacketQueue,
|
TEST(PrioritizedPacketQueue,
|
||||||
|
Reference in New Issue
Block a user