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:
parent
26f22e0496
commit
aab1bdea11
@ -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();
|
||||||
|
|||||||
@ -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_;
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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());
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user