Add ability to flush packets from pacer queue on a key frame

Bug: webrtc:11340
Change-Id: I70a97ab3ea576e54f1b4cf02042af5e6d5d6c2de
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/300541
Auto-Submit: Erik Språng <sprang@webrtc.org>
Reviewed-by: Ying Wang <yinwa@webrtc.org>
Commit-Queue: Ying Wang <yinwa@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#39805}
This commit is contained in:
Erik Språng 2023-04-06 15:47:07 +02:00 committed by WebRTC LUCI CQ
parent 26f22e0496
commit aab1bdea11
6 changed files with 134 additions and 2 deletions

View File

@ -67,6 +67,8 @@ PacingController::PacingController(Clock* clock,
IsEnabled(field_trials_, "WebRTC-Pacer-IgnoreTransportOverhead")), IsEnabled(field_trials_, "WebRTC-Pacer-IgnoreTransportOverhead")),
fast_retransmissions_( fast_retransmissions_(
IsEnabled(field_trials_, "WebRTC-Pacer-FastRetransmissions")), IsEnabled(field_trials_, "WebRTC-Pacer-FastRetransmissions")),
keyframe_flushing_(
IsEnabled(field_trials_, "WebRTC-Pacer-KeyframeFlushing")),
transport_overhead_per_packet_(DataSize::Zero()), transport_overhead_per_packet_(DataSize::Zero()),
send_burst_interval_(TimeDelta::Zero()), send_burst_interval_(TimeDelta::Zero()),
last_timestamp_(clock_->CurrentTime()), last_timestamp_(clock_->CurrentTime()),
@ -188,6 +190,21 @@ void PacingController::EnqueuePacket(std::unique_ptr<RtpPacketToSend> packet) {
<< "SetPacingRate must be called before InsertPacket."; << "SetPacingRate must be called before InsertPacket.";
RTC_CHECK(packet->packet_type()); RTC_CHECK(packet->packet_type());
if (keyframe_flushing_ &&
packet->packet_type() == RtpPacketMediaType::kVideo &&
packet->is_key_frame() && packet->is_first_packet_of_frame() &&
!packet_queue_.HasKeyframePackets(packet->Ssrc())) {
// First packet of a keyframe (and no keyframe packets currently in the
// queue). Flush any pending packets currently in the queue for that stream
// in order to get the new keyframe out as quickly as possible.
packet_queue_.RemovePacketsForSsrc(packet->Ssrc());
absl::optional<uint32_t> rtx_ssrc =
packet_sender_->GetRtxSsrcForMedia(packet->Ssrc());
if (rtx_ssrc) {
packet_queue_.RemovePacketsForSsrc(*rtx_ssrc);
}
}
prober_.OnIncomingPacket(DataSize::Bytes(packet->payload_size())); prober_.OnIncomingPacket(DataSize::Bytes(packet->payload_size()));
const Timestamp now = CurrentTime(); const Timestamp now = CurrentTime();

View File

@ -205,6 +205,7 @@ class PacingController {
const bool pace_audio_; const bool pace_audio_;
const bool ignore_transport_overhead_; const bool ignore_transport_overhead_;
const bool fast_retransmissions_; const bool fast_retransmissions_;
const bool keyframe_flushing_;
DataSize transport_overhead_per_packet_; DataSize transport_overhead_per_packet_;
TimeDelta send_burst_interval_; TimeDelta send_burst_interval_;

View File

@ -2255,5 +2255,44 @@ TEST_F(PacingControllerTest, DoesNotPadIfProcessThreadIsBorked) {
EXPECT_LE(callback.padding_sent(), kMaxPadding.bytes<size_t>()); EXPECT_LE(callback.padding_sent(), kMaxPadding.bytes<size_t>());
} }
TEST_F(PacingControllerTest, FlushesPacketsOnKeyFrames) {
const uint32_t kSsrc = 12345;
const uint32_t kRtxSsrc = 12346;
const test::ExplicitKeyValueConfig trials(
"WebRTC-Pacer-KeyframeFlushing/Enabled/");
auto pacer = std::make_unique<PacingController>(&clock_, &callback_, trials);
EXPECT_CALL(callback_, GetRtxSsrcForMedia(kSsrc))
.WillRepeatedly(Return(kRtxSsrc));
pacer->SetPacingRates(kTargetRate, DataRate::Zero());
// Enqueue a video packet and a retransmission of that video stream.
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kVideo, kSsrc,
/*sequence_number=*/1, /*capture_time=*/1,
/*size_bytes=*/100));
pacer->EnqueuePacket(BuildPacket(RtpPacketMediaType::kRetransmission,
kRtxSsrc,
/*sequence_number=*/10, /*capture_time=*/1,
/*size_bytes=*/100));
EXPECT_EQ(pacer->QueueSizePackets(), 2u);
// Enqueue the first packet of a keyframe for said stream.
auto packet = BuildPacket(RtpPacketMediaType::kVideo, kSsrc,
/*sequence_number=*/2, /*capture_time=*/2,
/*size_bytes=*/1000);
packet->set_is_key_frame(true);
packet->set_first_packet_of_frame(true);
pacer->EnqueuePacket(std::move(packet));
// Only they new keyframe packet should be left in the queue.
EXPECT_EQ(pacer->QueueSizePackets(), 1u);
EXPECT_CALL(callback_, SendPacket(kSsrc, /*sequence_number=*/2,
/*timestamp=*/2, /*is_retrnamission=*/false,
/*is_padding=*/false));
AdvanceTimeUntil(pacer->NextSendTime());
pacer->ProcessPackets();
}
} // namespace } // namespace
} // namespace webrtc } // namespace webrtc

View File

@ -50,10 +50,13 @@ DataSize PrioritizedPacketQueue::QueuedPacket::PacketSize() const {
} }
PrioritizedPacketQueue::StreamQueue::StreamQueue(Timestamp creation_time) PrioritizedPacketQueue::StreamQueue::StreamQueue(Timestamp creation_time)
: last_enqueue_time_(creation_time) {} : last_enqueue_time_(creation_time), num_keyframe_packets_(0) {}
bool PrioritizedPacketQueue::StreamQueue::EnqueuePacket(QueuedPacket packet, bool PrioritizedPacketQueue::StreamQueue::EnqueuePacket(QueuedPacket packet,
int priority_level) { int priority_level) {
if (packet.packet->is_key_frame()) {
++num_keyframe_packets_;
}
bool first_packet_at_level = packets_[priority_level].empty(); bool first_packet_at_level = packets_[priority_level].empty();
packets_[priority_level].push_back(std::move(packet)); packets_[priority_level].push_back(std::move(packet));
return first_packet_at_level; return first_packet_at_level;
@ -64,6 +67,10 @@ PrioritizedPacketQueue::StreamQueue::DequeuePacket(int priority_level) {
RTC_DCHECK(!packets_[priority_level].empty()); RTC_DCHECK(!packets_[priority_level].empty());
QueuedPacket packet = std::move(packets_[priority_level].front()); QueuedPacket packet = std::move(packets_[priority_level].front());
packets_[priority_level].pop_front(); packets_[priority_level].pop_front();
if (packet.packet->is_key_frame()) {
RTC_DCHECK_GT(num_keyframe_packets_, 0);
--num_keyframe_packets_;
}
return packet; return packet;
} }
@ -98,6 +105,7 @@ PrioritizedPacketQueue::StreamQueue::DequeueAll() {
for (int i = 0; i < kNumPriorityLevels; ++i) { for (int i = 0; i < kNumPriorityLevels; ++i) {
packets_by_prio[i].swap(packets_[i]); packets_by_prio[i].swap(packets_[i]);
} }
num_keyframe_packets_ = 0;
return packets_by_prio; return packets_by_prio;
} }
@ -292,6 +300,14 @@ void PrioritizedPacketQueue::RemovePacketsForSsrc(uint32_t ssrc) {
} }
} }
bool PrioritizedPacketQueue::HasKeyframePackets(uint32_t ssrc) const {
auto it = streams_.find(ssrc);
if (it != streams_.end()) {
return it->second->has_keyframe_packets();
}
return false;
}
void PrioritizedPacketQueue::DequeuePacketInternal(QueuedPacket& packet) { void PrioritizedPacketQueue::DequeuePacketInternal(QueuedPacket& packet) {
--size_packets_; --size_packets_;
RTC_DCHECK(packet.packet->packet_type().has_value()); RTC_DCHECK(packet.packet->packet_type().has_value());

View File

@ -85,6 +85,10 @@ class PrioritizedPacketQueue {
// Remove any packets matching the given SSRC. // Remove any packets matching the given SSRC.
void RemovePacketsForSsrc(uint32_t ssrc); void RemovePacketsForSsrc(uint32_t ssrc);
// Checks if the queue for the given SSRC has original (retransmissions not
// counted) video packets containing keyframe data.
bool HasKeyframePackets(uint32_t ssrc) const;
private: private:
static constexpr int kNumPriorityLevels = 4; static constexpr int kNumPriorityLevels = 4;
@ -118,12 +122,14 @@ class PrioritizedPacketQueue {
bool IsEmpty() const; bool IsEmpty() const;
Timestamp LeadingPacketEnqueueTime(int priority_level) const; Timestamp LeadingPacketEnqueueTime(int priority_level) const;
Timestamp LastEnqueueTime() const; Timestamp LastEnqueueTime() const;
bool has_keyframe_packets() const { return num_keyframe_packets_ > 0; }
std::array<std::deque<QueuedPacket>, kNumPriorityLevels> DequeueAll(); std::array<std::deque<QueuedPacket>, kNumPriorityLevels> DequeueAll();
private: private:
std::deque<QueuedPacket> packets_[kNumPriorityLevels]; std::deque<QueuedPacket> packets_[kNumPriorityLevels];
Timestamp last_enqueue_time_; Timestamp last_enqueue_time_;
int num_keyframe_packets_;
}; };
// Remove the packet from the internal state, e.g. queue time / size etc. // Remove the packet from the internal state, e.g. queue time / size etc.

View File

@ -27,12 +27,14 @@ constexpr int kDefaultPayloadSize = 789;
std::unique_ptr<RtpPacketToSend> CreatePacket(RtpPacketMediaType type, std::unique_ptr<RtpPacketToSend> CreatePacket(RtpPacketMediaType type,
uint16_t sequence_number, uint16_t sequence_number,
uint32_t ssrc = kDefaultSsrc) { uint32_t ssrc = kDefaultSsrc,
bool is_key_frame = false) {
auto packet = std::make_unique<RtpPacketToSend>(/*extensions=*/nullptr); auto packet = std::make_unique<RtpPacketToSend>(/*extensions=*/nullptr);
packet->set_packet_type(type); packet->set_packet_type(type);
packet->SetSsrc(ssrc); packet->SetSsrc(ssrc);
packet->SetSequenceNumber(sequence_number); packet->SetSequenceNumber(sequence_number);
packet->SetPayloadSize(kDefaultPayloadSize); packet->SetPayloadSize(kDefaultPayloadSize);
packet->set_is_key_frame(is_key_frame);
return packet; return packet;
} }
@ -360,4 +362,55 @@ TEST(PrioritizedPacketQueue, ClearPacketsAffectsOnlySpecifiedSsrc) {
EXPECT_TRUE(queue.Empty()); EXPECT_TRUE(queue.Empty());
} }
TEST(PrioritizedPacketQueue, ReportsKeyframePackets) {
Timestamp now = Timestamp::Zero();
PrioritizedPacketQueue queue(now);
const uint32_t kVideoSsrc1 = 1234;
const uint32_t kVideoSsrc2 = 2345;
EXPECT_FALSE(queue.HasKeyframePackets(kVideoSsrc1));
EXPECT_FALSE(queue.HasKeyframePackets(kVideoSsrc2));
queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/1,
kVideoSsrc1, /*is_key_frame=*/true));
queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/11,
kVideoSsrc2, /*is_key_frame=*/false));
EXPECT_TRUE(queue.HasKeyframePackets(kVideoSsrc1));
EXPECT_FALSE(queue.HasKeyframePackets(kVideoSsrc2));
queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/2,
kVideoSsrc1, /*is_key_frame=*/true));
queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/12,
kVideoSsrc2, /*is_key_frame=*/true));
EXPECT_TRUE(queue.HasKeyframePackets(kVideoSsrc1));
EXPECT_TRUE(queue.HasKeyframePackets(kVideoSsrc2));
queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/3,
kVideoSsrc1, /*is_key_frame=*/false));
queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/13,
kVideoSsrc2, /*is_key_frame=*/true));
EXPECT_TRUE(queue.HasKeyframePackets(kVideoSsrc1));
EXPECT_TRUE(queue.HasKeyframePackets(kVideoSsrc2));
EXPECT_EQ(queue.Pop()->SequenceNumber(), 1);
EXPECT_EQ(queue.Pop()->SequenceNumber(), 11);
EXPECT_TRUE(queue.HasKeyframePackets(kVideoSsrc1));
EXPECT_TRUE(queue.HasKeyframePackets(kVideoSsrc2));
EXPECT_EQ(queue.Pop()->SequenceNumber(), 2);
EXPECT_EQ(queue.Pop()->SequenceNumber(), 12);
EXPECT_FALSE(queue.HasKeyframePackets(kVideoSsrc1));
EXPECT_TRUE(queue.HasKeyframePackets(kVideoSsrc2));
queue.RemovePacketsForSsrc(kVideoSsrc2);
EXPECT_FALSE(queue.HasKeyframePackets(kVideoSsrc1));
EXPECT_FALSE(queue.HasKeyframePackets(kVideoSsrc2));
}
} // namespace webrtc } // namespace webrtc