Remove pending packets from the pacer when an RTP module is removed.
This CL adds functionality to remove packets matching a given SSRC from the pacer queue, and calls that with any SSRCs used by an RTP module when that module is removed. Bug: chromium:1395081 Change-Id: I13c0285ddca600e784ad04a806727a508ede6dcc Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/287124 Reviewed-by: Jakob Ivarsson <jakobi@webrtc.org> Commit-Queue: Erik Språng <sprang@webrtc.org> Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Reviewed-by: Philip Eliasson <philipel@webrtc.org> Cr-Commit-Position: refs/heads/main@{#38880}
This commit is contained in:
parent
b02a8f5a7c
commit
1b11b58b56
@ -262,6 +262,11 @@ class RtpPacketSenderProxy : public RtpPacketSender {
|
|||||||
rtp_packet_pacer_->EnqueuePackets(std::move(packets));
|
rtp_packet_pacer_->EnqueuePackets(std::move(packets));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RemovePacketsForSsrc(uint32_t ssrc) override {
|
||||||
|
MutexLock lock(&mutex_);
|
||||||
|
rtp_packet_pacer_->RemovePacketsForSsrc(ssrc);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SequenceChecker thread_checker_;
|
SequenceChecker thread_checker_;
|
||||||
Mutex mutex_;
|
Mutex mutex_;
|
||||||
@ -565,6 +570,7 @@ void ChannelSend::StopSend() {
|
|||||||
|
|
||||||
RTC_DCHECK(packet_router_);
|
RTC_DCHECK(packet_router_);
|
||||||
packet_router_->RemoveSendRtpModule(rtp_rtcp_.get());
|
packet_router_->RemoveSendRtpModule(rtp_rtcp_.get());
|
||||||
|
rtp_packet_pacer_proxy_->RemovePacketsForSsrc(rtp_rtcp_->SSRC());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChannelSend::SetEncoder(int payload_type,
|
void ChannelSend::SetEncoder(int payload_type,
|
||||||
|
|||||||
@ -515,6 +515,17 @@ void RtpVideoSender::SetActiveModulesLocked(
|
|||||||
// prevent any stray packets in the pacer from asynchronously arriving
|
// prevent any stray packets in the pacer from asynchronously arriving
|
||||||
// to a disabled module.
|
// to a disabled module.
|
||||||
transport_->packet_router()->RemoveSendRtpModule(&rtp_module);
|
transport_->packet_router()->RemoveSendRtpModule(&rtp_module);
|
||||||
|
|
||||||
|
// Clear the pacer queue of any packets pertaining to this module.
|
||||||
|
transport_->packet_sender()->RemovePacketsForSsrc(rtp_module.SSRC());
|
||||||
|
if (rtp_module.RtxSsrc().has_value()) {
|
||||||
|
transport_->packet_sender()->RemovePacketsForSsrc(
|
||||||
|
*rtp_module.RtxSsrc());
|
||||||
|
}
|
||||||
|
if (rtp_module.FlexfecSsrc().has_value()) {
|
||||||
|
transport_->packet_sender()->RemovePacketsForSsrc(
|
||||||
|
*rtp_module.FlexfecSsrc());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If set to false this module won't send media.
|
// If set to false this module won't send media.
|
||||||
|
|||||||
@ -1098,4 +1098,81 @@ TEST(RtpVideoSenderTest, OverheadIsSubtractedFromTargetBitrate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(RtpVideoSenderTest, ClearsPendingPacketsOnInactivation) {
|
||||||
|
RtpVideoSenderTestFixture test({kSsrc1}, {kRtxSsrc1}, kPayloadType, {});
|
||||||
|
test.SetActiveModules({true});
|
||||||
|
|
||||||
|
RtpHeaderExtensionMap extensions;
|
||||||
|
extensions.Register<RtpDependencyDescriptorExtension>(
|
||||||
|
kDependencyDescriptorExtensionId);
|
||||||
|
std::vector<RtpPacket> sent_packets;
|
||||||
|
ON_CALL(test.transport(), SendRtp)
|
||||||
|
.WillByDefault([&](const uint8_t* packet, size_t length,
|
||||||
|
const PacketOptions& options) {
|
||||||
|
sent_packets.emplace_back(&extensions);
|
||||||
|
EXPECT_TRUE(sent_packets.back().Parse(packet, length));
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set a very low bitrate.
|
||||||
|
test.router()->OnBitrateUpdated(
|
||||||
|
CreateBitrateAllocationUpdate(/*rate_bps=*/30'000),
|
||||||
|
/*framerate=*/30);
|
||||||
|
|
||||||
|
// Create and send a large keyframe.
|
||||||
|
const size_t kImageSizeBytes = 10000;
|
||||||
|
constexpr uint8_t kPayload[kImageSizeBytes] = {'a'};
|
||||||
|
EncodedImage encoded_image;
|
||||||
|
encoded_image.SetTimestamp(1);
|
||||||
|
encoded_image.capture_time_ms_ = 2;
|
||||||
|
encoded_image._frameType = VideoFrameType::kVideoFrameKey;
|
||||||
|
encoded_image.SetEncodedData(
|
||||||
|
EncodedImageBuffer::Create(kPayload, sizeof(kPayload)));
|
||||||
|
EXPECT_EQ(test.router()
|
||||||
|
->OnEncodedImage(encoded_image, /*codec_specific=*/nullptr)
|
||||||
|
.error,
|
||||||
|
EncodedImageCallback::Result::OK);
|
||||||
|
|
||||||
|
// Advance time a small amount, check that sent data is only part of the
|
||||||
|
// image.
|
||||||
|
test.AdvanceTime(TimeDelta::Millis(5));
|
||||||
|
DataSize transmittedPayload = DataSize::Zero();
|
||||||
|
for (const RtpPacket& packet : sent_packets) {
|
||||||
|
transmittedPayload += DataSize::Bytes(packet.payload_size());
|
||||||
|
// Make sure we don't see the end of the frame.
|
||||||
|
EXPECT_FALSE(packet.Marker());
|
||||||
|
}
|
||||||
|
EXPECT_GT(transmittedPayload, DataSize::Zero());
|
||||||
|
EXPECT_LT(transmittedPayload, DataSize::Bytes(kImageSizeBytes / 4));
|
||||||
|
|
||||||
|
// Record the RTP timestamp of the first frame.
|
||||||
|
const uint32_t first_frame_timestamp = sent_packets[0].Timestamp();
|
||||||
|
sent_packets.clear();
|
||||||
|
|
||||||
|
// Disable the sending module and advance time slightly. No packets should be
|
||||||
|
// sent.
|
||||||
|
test.SetActiveModules({false});
|
||||||
|
test.AdvanceTime(TimeDelta::Millis(20));
|
||||||
|
EXPECT_TRUE(sent_packets.empty());
|
||||||
|
|
||||||
|
// Reactive the send module - any packets should have been removed, so nothing
|
||||||
|
// should be transmitted.
|
||||||
|
test.SetActiveModules({true});
|
||||||
|
test.AdvanceTime(TimeDelta::Millis(33));
|
||||||
|
EXPECT_TRUE(sent_packets.empty());
|
||||||
|
|
||||||
|
// Send a new frame.
|
||||||
|
encoded_image.SetTimestamp(3);
|
||||||
|
encoded_image.capture_time_ms_ = 4;
|
||||||
|
EXPECT_EQ(test.router()
|
||||||
|
->OnEncodedImage(encoded_image, /*codec_specific=*/nullptr)
|
||||||
|
.error,
|
||||||
|
EncodedImageCallback::Result::OK);
|
||||||
|
test.AdvanceTime(TimeDelta::Millis(33));
|
||||||
|
|
||||||
|
// Advance time, check we get new packets - but only for the second frame.
|
||||||
|
EXPECT_FALSE(sent_packets.empty());
|
||||||
|
EXPECT_NE(sent_packets[0].Timestamp(), first_frame_timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -148,6 +148,10 @@ void PacingController::SetCircuitBreakerThreshold(int num_iterations) {
|
|||||||
circuit_breaker_threshold_ = num_iterations;
|
circuit_breaker_threshold_ = num_iterations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PacingController::RemovePacketsForSsrc(uint32_t ssrc) {
|
||||||
|
packet_queue_.RemovePacketsForSsrc(ssrc);
|
||||||
|
}
|
||||||
|
|
||||||
bool PacingController::IsProbing() const {
|
bool PacingController::IsProbing() const {
|
||||||
return prober_.is_probing();
|
return prober_.is_probing();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -166,6 +166,9 @@ class PacingController {
|
|||||||
// is considered erroneous to exceed.
|
// is considered erroneous to exceed.
|
||||||
void SetCircuitBreakerThreshold(int num_iterations);
|
void SetCircuitBreakerThreshold(int num_iterations);
|
||||||
|
|
||||||
|
// Remove any pending packets matching this SSRC from the packet queue.
|
||||||
|
void RemovePacketsForSsrc(uint32_t ssrc);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TimeDelta UpdateTimeAndGetElapsed(Timestamp now);
|
TimeDelta UpdateTimeAndGetElapsed(Timestamp now);
|
||||||
bool ShouldSendKeepalive(Timestamp now) const;
|
bool ShouldSendKeepalive(Timestamp now) const;
|
||||||
|
|||||||
@ -60,7 +60,7 @@ bool PrioritizedPacketQueue::StreamQueue::EnqueuePacket(QueuedPacket packet,
|
|||||||
}
|
}
|
||||||
|
|
||||||
PrioritizedPacketQueue::QueuedPacket
|
PrioritizedPacketQueue::QueuedPacket
|
||||||
PrioritizedPacketQueue::StreamQueue::DequePacket(int priority_level) {
|
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();
|
||||||
@ -91,6 +91,16 @@ Timestamp PrioritizedPacketQueue::StreamQueue::LastEnqueueTime() const {
|
|||||||
return last_enqueue_time_;
|
return last_enqueue_time_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::array<std::deque<PrioritizedPacketQueue::QueuedPacket>,
|
||||||
|
PrioritizedPacketQueue::kNumPriorityLevels>
|
||||||
|
PrioritizedPacketQueue::StreamQueue::DequeueAll() {
|
||||||
|
std::array<std::deque<QueuedPacket>, kNumPriorityLevels> packets_by_prio;
|
||||||
|
for (int i = 0; i < kNumPriorityLevels; ++i) {
|
||||||
|
packets_by_prio[i].swap(packets_[i]);
|
||||||
|
}
|
||||||
|
return packets_by_prio;
|
||||||
|
}
|
||||||
|
|
||||||
PrioritizedPacketQueue::PrioritizedPacketQueue(Timestamp creation_time)
|
PrioritizedPacketQueue::PrioritizedPacketQueue(Timestamp creation_time)
|
||||||
: queue_time_sum_(TimeDelta::Zero()),
|
: queue_time_sum_(TimeDelta::Zero()),
|
||||||
pause_time_sum_(TimeDelta::Zero()),
|
pause_time_sum_(TimeDelta::Zero()),
|
||||||
@ -162,54 +172,16 @@ std::unique_ptr<RtpPacketToSend> PrioritizedPacketQueue::Pop() {
|
|||||||
|
|
||||||
RTC_DCHECK_GE(top_active_prio_level_, 0);
|
RTC_DCHECK_GE(top_active_prio_level_, 0);
|
||||||
StreamQueue& stream_queue = *streams_by_prio_[top_active_prio_level_].front();
|
StreamQueue& stream_queue = *streams_by_prio_[top_active_prio_level_].front();
|
||||||
QueuedPacket packet = stream_queue.DequePacket(top_active_prio_level_);
|
QueuedPacket packet = stream_queue.DequeuePacket(top_active_prio_level_);
|
||||||
--size_packets_;
|
DequeuePacketInternal(packet);
|
||||||
RTC_DCHECK(packet.packet->packet_type().has_value());
|
|
||||||
RtpPacketMediaType packet_type = packet.packet->packet_type().value();
|
|
||||||
--size_packets_per_media_type_[static_cast<size_t>(packet_type)];
|
|
||||||
RTC_DCHECK_GE(size_packets_per_media_type_[static_cast<size_t>(packet_type)],
|
|
||||||
0);
|
|
||||||
size_payload_ -= packet.PacketSize();
|
|
||||||
|
|
||||||
// Calculate the total amount of time spent by this packet in the queue
|
|
||||||
// while in a non-paused state. Note that the `pause_time_sum_ms_` was
|
|
||||||
// subtracted from `packet.enqueue_time_ms` when the packet was pushed, and
|
|
||||||
// by subtracting it now we effectively remove the time spent in in the
|
|
||||||
// queue while in a paused state.
|
|
||||||
TimeDelta time_in_non_paused_state =
|
|
||||||
last_update_time_ - packet.enqueue_time - pause_time_sum_;
|
|
||||||
queue_time_sum_ -= time_in_non_paused_state;
|
|
||||||
|
|
||||||
// Set the time spent in the send queue, which is the per-packet equivalent of
|
|
||||||
// totalPacketSendDelay. The notion of being paused is an implementation
|
|
||||||
// detail that we do not want to expose, so it makes sense to report the
|
|
||||||
// metric excluding the pause time. This also avoids spikes in the metric.
|
|
||||||
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-totalpacketsenddelay
|
|
||||||
packet.packet->set_time_in_send_queue(time_in_non_paused_state);
|
|
||||||
|
|
||||||
RTC_DCHECK(size_packets_ > 0 || queue_time_sum_ == TimeDelta::Zero());
|
|
||||||
|
|
||||||
RTC_CHECK(packet.enqueue_time_iterator != enqueue_times_.end());
|
|
||||||
enqueue_times_.erase(packet.enqueue_time_iterator);
|
|
||||||
|
|
||||||
// Remove StreamQueue from head of fifo-queue for this prio level, and
|
// Remove StreamQueue from head of fifo-queue for this prio level, and
|
||||||
// and add it to the end if it still has packets.
|
// and add it to the end if it still has packets.
|
||||||
streams_by_prio_[top_active_prio_level_].pop_front();
|
streams_by_prio_[top_active_prio_level_].pop_front();
|
||||||
if (stream_queue.HasPacketsAtPrio(top_active_prio_level_)) {
|
if (stream_queue.HasPacketsAtPrio(top_active_prio_level_)) {
|
||||||
streams_by_prio_[top_active_prio_level_].push_back(&stream_queue);
|
streams_by_prio_[top_active_prio_level_].push_back(&stream_queue);
|
||||||
} else if (streams_by_prio_[top_active_prio_level_].empty()) {
|
|
||||||
// No stream queues have packets at this prio level, find top priority
|
|
||||||
// that is not empty.
|
|
||||||
if (size_packets_ == 0) {
|
|
||||||
top_active_prio_level_ = -1;
|
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < kNumPriorityLevels; ++i) {
|
MaybeUpdateTopPrioLevel();
|
||||||
if (!streams_by_prio_[i].empty()) {
|
|
||||||
top_active_prio_level_ = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::move(packet.packet);
|
return std::move(packet.packet);
|
||||||
@ -276,4 +248,96 @@ void PrioritizedPacketQueue::SetPauseState(bool paused, Timestamp now) {
|
|||||||
paused_ = paused;
|
paused_ = paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PrioritizedPacketQueue::RemovePacketsForSsrc(uint32_t ssrc) {
|
||||||
|
auto kv = streams_.find(ssrc);
|
||||||
|
if (kv != streams_.end()) {
|
||||||
|
// Dequeue all packets from the queue for this SSRC.
|
||||||
|
StreamQueue& queue = *kv->second;
|
||||||
|
std::array<std::deque<QueuedPacket>, kNumPriorityLevels> packets_by_prio =
|
||||||
|
queue.DequeueAll();
|
||||||
|
for (int i = 0; i < kNumPriorityLevels; ++i) {
|
||||||
|
std::deque<QueuedPacket>& packet_queue = packets_by_prio[i];
|
||||||
|
if (packet_queue.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First erase all packets at this prio level.
|
||||||
|
while (!packet_queue.empty()) {
|
||||||
|
QueuedPacket packet = std::move(packet_queue.front());
|
||||||
|
packet_queue.pop_front();
|
||||||
|
DequeuePacketInternal(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, deregister this `StreamQueue` from the round-robin tables.
|
||||||
|
RTC_DCHECK(!streams_by_prio_[i].empty());
|
||||||
|
if (streams_by_prio_[i].size() == 1) {
|
||||||
|
// This is the last and only queue that had packets for this prio level.
|
||||||
|
// Update the global top prio level if neccessary.
|
||||||
|
RTC_DCHECK(streams_by_prio_[i].front() == &queue);
|
||||||
|
streams_by_prio_[i].pop_front();
|
||||||
|
if (i == top_active_prio_level_) {
|
||||||
|
MaybeUpdateTopPrioLevel();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// More than stream had packets at this prio level, filter this one out.
|
||||||
|
std::deque<StreamQueue*> filtered_queue;
|
||||||
|
for (StreamQueue* queue_ptr : streams_by_prio_[i]) {
|
||||||
|
if (queue_ptr != &queue) {
|
||||||
|
filtered_queue.push_back(queue_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
streams_by_prio_[i].swap(filtered_queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrioritizedPacketQueue::DequeuePacketInternal(QueuedPacket& packet) {
|
||||||
|
--size_packets_;
|
||||||
|
RTC_DCHECK(packet.packet->packet_type().has_value());
|
||||||
|
RtpPacketMediaType packet_type = packet.packet->packet_type().value();
|
||||||
|
--size_packets_per_media_type_[static_cast<size_t>(packet_type)];
|
||||||
|
RTC_DCHECK_GE(size_packets_per_media_type_[static_cast<size_t>(packet_type)],
|
||||||
|
0);
|
||||||
|
size_payload_ -= packet.PacketSize();
|
||||||
|
|
||||||
|
// Calculate the total amount of time spent by this packet in the queue
|
||||||
|
// while in a non-paused state. Note that the `pause_time_sum_ms_` was
|
||||||
|
// subtracted from `packet.enqueue_time_ms` when the packet was pushed, and
|
||||||
|
// by subtracting it now we effectively remove the time spent in in the
|
||||||
|
// queue while in a paused state.
|
||||||
|
TimeDelta time_in_non_paused_state =
|
||||||
|
last_update_time_ - packet.enqueue_time - pause_time_sum_;
|
||||||
|
queue_time_sum_ -= time_in_non_paused_state;
|
||||||
|
|
||||||
|
// Set the time spent in the send queue, which is the per-packet equivalent of
|
||||||
|
// totalPacketSendDelay. The notion of being paused is an implementation
|
||||||
|
// detail that we do not want to expose, so it makes sense to report the
|
||||||
|
// metric excluding the pause time. This also avoids spikes in the metric.
|
||||||
|
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-totalpacketsenddelay
|
||||||
|
packet.packet->set_time_in_send_queue(time_in_non_paused_state);
|
||||||
|
|
||||||
|
RTC_DCHECK(size_packets_ > 0 || queue_time_sum_ == TimeDelta::Zero());
|
||||||
|
|
||||||
|
RTC_CHECK(packet.enqueue_time_iterator != enqueue_times_.end());
|
||||||
|
enqueue_times_.erase(packet.enqueue_time_iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrioritizedPacketQueue::MaybeUpdateTopPrioLevel() {
|
||||||
|
if (streams_by_prio_[top_active_prio_level_].empty()) {
|
||||||
|
// No stream queues have packets at this prio level, find top priority
|
||||||
|
// that is not empty.
|
||||||
|
if (size_packets_ == 0) {
|
||||||
|
top_active_prio_level_ = -1;
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < kNumPriorityLevels; ++i) {
|
||||||
|
if (!streams_by_prio_[i].empty()) {
|
||||||
|
top_active_prio_level_ = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -13,10 +13,12 @@
|
|||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "api/units/data_size.h"
|
#include "api/units/data_size.h"
|
||||||
#include "api/units/time_delta.h"
|
#include "api/units/time_delta.h"
|
||||||
@ -80,6 +82,9 @@ class PrioritizedPacketQueue {
|
|||||||
// Set the pause state, while `paused` is true queuing time is not counted.
|
// Set the pause state, while `paused` is true queuing time is not counted.
|
||||||
void SetPauseState(bool paused, Timestamp now);
|
void SetPauseState(bool paused, Timestamp now);
|
||||||
|
|
||||||
|
// Remove any packets matching the given SSRC.
|
||||||
|
void RemovePacketsForSsrc(uint32_t ssrc);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr int kNumPriorityLevels = 4;
|
static constexpr int kNumPriorityLevels = 4;
|
||||||
|
|
||||||
@ -107,18 +112,27 @@ class PrioritizedPacketQueue {
|
|||||||
// count for that priority level went from zero to non-zero.
|
// count for that priority level went from zero to non-zero.
|
||||||
bool EnqueuePacket(QueuedPacket packet, int priority_level);
|
bool EnqueuePacket(QueuedPacket packet, int priority_level);
|
||||||
|
|
||||||
QueuedPacket DequePacket(int priority_level);
|
QueuedPacket DequeuePacket(int priority_level);
|
||||||
|
|
||||||
bool HasPacketsAtPrio(int priority_level) const;
|
bool HasPacketsAtPrio(int priority_level) const;
|
||||||
bool IsEmpty() const;
|
bool IsEmpty() const;
|
||||||
Timestamp LeadingPacketEnqueueTime(int priority_level) const;
|
Timestamp LeadingPacketEnqueueTime(int priority_level) const;
|
||||||
Timestamp LastEnqueueTime() const;
|
Timestamp LastEnqueueTime() const;
|
||||||
|
|
||||||
|
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_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Remove the packet from the internal state, e.g. queue time / size etc.
|
||||||
|
void DequeuePacketInternal(QueuedPacket& packet);
|
||||||
|
|
||||||
|
// Check if the queue pointed to by `top_active_prio_level_` is empty and
|
||||||
|
// if so move it to the lowest non-empty index.
|
||||||
|
void MaybeUpdateTopPrioLevel();
|
||||||
|
|
||||||
// Cumulative sum, over all packets, of time spent in the queue.
|
// Cumulative sum, over all packets, of time spent in the queue.
|
||||||
TimeDelta queue_time_sum_;
|
TimeDelta queue_time_sum_;
|
||||||
// Cumulative sum of time the queue has spent in a paused state.
|
// Cumulative sum of time the queue has spent in a paused state.
|
||||||
|
|||||||
@ -306,4 +306,58 @@ TEST(PrioritizedPacketQueue,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(PrioritizedPacketQueue, ClearsPackets) {
|
||||||
|
Timestamp now = Timestamp::Zero();
|
||||||
|
PrioritizedPacketQueue queue(now);
|
||||||
|
const uint32_t kSsrc = 1;
|
||||||
|
|
||||||
|
// Add two packets of each type, all using the same SSRC.
|
||||||
|
int sequence_number = 0;
|
||||||
|
for (size_t i = 0; i < kNumMediaTypes; ++i) {
|
||||||
|
queue.Push(now, CreatePacket(static_cast<RtpPacketMediaType>(i),
|
||||||
|
sequence_number++, kSsrc));
|
||||||
|
queue.Push(now, CreatePacket(static_cast<RtpPacketMediaType>(i),
|
||||||
|
sequence_number++, kSsrc));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(queue.SizeInPackets(), 2 * int{kNumMediaTypes});
|
||||||
|
|
||||||
|
// Remove all of them.
|
||||||
|
queue.RemovePacketsForSsrc(kSsrc);
|
||||||
|
EXPECT_TRUE(queue.Empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PrioritizedPacketQueue, ClearPacketsAffectsOnlySpecifiedSsrc) {
|
||||||
|
Timestamp now = Timestamp::Zero();
|
||||||
|
PrioritizedPacketQueue queue(now);
|
||||||
|
const uint32_t kRemovingSsrc = 1;
|
||||||
|
const uint32_t kStayingSsrc = 2;
|
||||||
|
|
||||||
|
// Add an audio packet and a retransmission for the SSRC we will remove,
|
||||||
|
// ensuring they are first in line.
|
||||||
|
queue.Push(
|
||||||
|
now, CreatePacket(RtpPacketMediaType::kAudio, /*seq=*/1, kRemovingSsrc));
|
||||||
|
queue.Push(now, CreatePacket(RtpPacketMediaType::kRetransmission, /*seq=*/2,
|
||||||
|
kRemovingSsrc));
|
||||||
|
|
||||||
|
// Add a video packet and a retransmission for the SSRC that will remain.
|
||||||
|
// The retransmission packets now both have pointers to their respective qeues
|
||||||
|
// from the same prio level.
|
||||||
|
queue.Push(now,
|
||||||
|
CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/3, kStayingSsrc));
|
||||||
|
queue.Push(now, CreatePacket(RtpPacketMediaType::kRetransmission, /*seq=*/4,
|
||||||
|
kStayingSsrc));
|
||||||
|
|
||||||
|
EXPECT_EQ(queue.SizeInPackets(), 4);
|
||||||
|
|
||||||
|
// Clear the first two packets.
|
||||||
|
queue.RemovePacketsForSsrc(kRemovingSsrc);
|
||||||
|
EXPECT_EQ(queue.SizeInPackets(), 2);
|
||||||
|
|
||||||
|
// We should get the single remaining retransmission first, then the video
|
||||||
|
// packet.
|
||||||
|
EXPECT_EQ(queue.Pop()->SequenceNumber(), 4);
|
||||||
|
EXPECT_EQ(queue.Pop()->SequenceNumber(), 3);
|
||||||
|
EXPECT_TRUE(queue.Empty());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -181,6 +181,14 @@ void TaskQueuePacedSender::EnqueuePackets(
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TaskQueuePacedSender::RemovePacketsForSsrc(uint32_t ssrc) {
|
||||||
|
task_queue_.RunOrPost([this, ssrc]() {
|
||||||
|
RTC_DCHECK_RUN_ON(&task_queue_);
|
||||||
|
pacing_controller_.RemovePacketsForSsrc(ssrc);
|
||||||
|
MaybeProcessPackets(Timestamp::MinusInfinity());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void TaskQueuePacedSender::SetAccountForAudioPackets(bool account_for_audio) {
|
void TaskQueuePacedSender::SetAccountForAudioPackets(bool account_for_audio) {
|
||||||
task_queue_.RunOrPost([this, account_for_audio]() {
|
task_queue_.RunOrPost([this, account_for_audio]() {
|
||||||
RTC_DCHECK_RUN_ON(&task_queue_);
|
RTC_DCHECK_RUN_ON(&task_queue_);
|
||||||
|
|||||||
@ -70,6 +70,8 @@ class TaskQueuePacedSender : public RtpPacketPacer, public RtpPacketSender {
|
|||||||
// PacingController::PacketSender::SendPacket() when it's time to send.
|
// PacingController::PacketSender::SendPacket() when it's time to send.
|
||||||
void EnqueuePackets(
|
void EnqueuePackets(
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>> packets) override;
|
std::vector<std::unique_ptr<RtpPacketToSend>> packets) override;
|
||||||
|
// Remove any pending packets matching this SSRC from the packet queue.
|
||||||
|
void RemovePacketsForSsrc(uint32_t ssrc) override;
|
||||||
|
|
||||||
// Methods implementing RtpPacketPacer.
|
// Methods implementing RtpPacketPacer.
|
||||||
|
|
||||||
|
|||||||
@ -28,6 +28,11 @@ class RtpPacketSender {
|
|||||||
// packets and the current target send rate.
|
// packets and the current target send rate.
|
||||||
virtual void EnqueuePackets(
|
virtual void EnqueuePackets(
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>> packets) = 0;
|
std::vector<std::unique_ptr<RtpPacketToSend>> packets) = 0;
|
||||||
|
|
||||||
|
// Clear any pending packets with the given SSRC from the queue.
|
||||||
|
// TODO(crbug.com/1395081): Make pure virtual when downstream code has been
|
||||||
|
// updated.
|
||||||
|
virtual void RemovePacketsForSsrc(uint32_t ssrc) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -43,6 +43,7 @@ class DEPRECATED_RtpSenderEgress {
|
|||||||
|
|
||||||
void EnqueuePackets(
|
void EnqueuePackets(
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>> packets) override;
|
std::vector<std::unique_ptr<RtpPacketToSend>> packets) override;
|
||||||
|
void RemovePacketsForSsrc(uint32_t ssrc) override {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint16_t transport_sequence_number_;
|
uint16_t transport_sequence_number_;
|
||||||
|
|||||||
@ -49,6 +49,8 @@ class RtpSenderEgress {
|
|||||||
|
|
||||||
void EnqueuePackets(
|
void EnqueuePackets(
|
||||||
std::vector<std::unique_ptr<RtpPacketToSend>> packets) override;
|
std::vector<std::unique_ptr<RtpPacketToSend>> packets) override;
|
||||||
|
// Since we don't pace packets, there's no pending packets to remove.
|
||||||
|
void RemovePacketsForSsrc(uint32_t ssrc) override {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void PrepareForSend(RtpPacketToSend* packet);
|
void PrepareForSend(RtpPacketToSend* packet);
|
||||||
|
|||||||
@ -102,6 +102,7 @@ class MockRtpPacketPacer : public RtpPacketSender {
|
|||||||
EnqueuePackets,
|
EnqueuePackets,
|
||||||
(std::vector<std::unique_ptr<RtpPacketToSend>>),
|
(std::vector<std::unique_ptr<RtpPacketToSend>>),
|
||||||
(override));
|
(override));
|
||||||
|
MOCK_METHOD(void, RemovePacketsForSsrc, (uint32_t), (override));
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user