Reland "Rework rtp packet history"
This is a reland of 6328d7cbbc8a72fdc81a766c0bf4039e1e2e7887 Original change's description: > Rework rtp packet history > > This CL rewrites the history from the ground up, but keeps the logic > (mostly) intact. It does however lay the groundwork for adding a new > mode where TransportFeedback messages can be used to remove packets > from the history as we know the remote end has received them. > > This should both reduce memory usage and make the payload based padding > a little more likely to be useful. > > My tests show a reduction of ca 500-800kB reduction in memory usage per > rtp module. So with simulcast and/or fec this will increase. Lossy > links and long RTT will use more memory. > > I've also slightly update the interface to make usage with/without > pacer less unintuitive, and avoid making a copy of the entire RTP > packet just to find the ssrc and sequence number to put into the pacer. > > The more aggressive culling is not enabled by default. I will > wire that up in a follow-up CL, as there's some interface refactoring > required. > > Bug: webrtc:8975 > Change-Id: I0c1bb528f32eeed0fb276b4ae77ae3235656980f > Reviewed-on: https://webrtc-review.googlesource.com/59441 > Commit-Queue: Erik Språng <sprang@webrtc.org> > Reviewed-by: Danil Chapovalov <danilchap@webrtc.org> > Cr-Commit-Position: refs/heads/master@{#22347} Bug: webrtc:8975 Change-Id: Ibbdbcc3c13bd58d994ad66f789a95ef9bd9bc19b Reviewed-on: https://webrtc-review.googlesource.com/60900 Reviewed-by: Danil Chapovalov <danilchap@webrtc.org> Commit-Queue: Erik Språng <sprang@webrtc.org> Cr-Commit-Position: refs/heads/master@{#22356}
This commit is contained in:
@ -17,17 +17,33 @@
|
||||
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/ptr_util.h"
|
||||
#include "system_wrappers/include/clock.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
// Min packet size for BestFittingPacket() to honor.
|
||||
constexpr size_t kMinPacketRequestBytes = 50;
|
||||
// Don't overwrite a packet within one second, or three RTTs, after transmission
|
||||
// whichever is larger. Instead try to dynamically expand history.
|
||||
constexpr int64_t kMinPacketDurationMs = 1000;
|
||||
constexpr int kMinPacketDurationRtt = 3;
|
||||
|
||||
// Utility function to get the absolute difference in size between the provided
|
||||
// target size and the size of packet.
|
||||
size_t SizeDiff(const std::unique_ptr<RtpPacketToSend>& packet, size_t size) {
|
||||
size_t packet_size = packet->size();
|
||||
if (packet_size > size) {
|
||||
return packet_size - size;
|
||||
}
|
||||
return size - packet_size;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
constexpr size_t RtpPacketHistory::kMaxCapacity;
|
||||
constexpr int64_t RtpPacketHistory::kMinPacketDurationMs;
|
||||
constexpr int RtpPacketHistory::kMinPacketDurationRtt;
|
||||
constexpr int RtpPacketHistory::kPacketCullingDelayFactor;
|
||||
|
||||
RtpPacketHistory::PacketState::PacketState() = default;
|
||||
RtpPacketHistory::PacketState::PacketState(const PacketState&) = default;
|
||||
RtpPacketHistory::PacketState::~PacketState() = default;
|
||||
|
||||
RtpPacketHistory::StoredPacket::StoredPacket() = default;
|
||||
RtpPacketHistory::StoredPacket::StoredPacket(StoredPacket&&) = default;
|
||||
@ -36,209 +52,239 @@ RtpPacketHistory::StoredPacket& RtpPacketHistory::StoredPacket::operator=(
|
||||
RtpPacketHistory::StoredPacket::~StoredPacket() = default;
|
||||
|
||||
RtpPacketHistory::RtpPacketHistory(Clock* clock)
|
||||
: clock_(clock), store_(false), prev_index_(0), rtt_ms_(-1) {}
|
||||
: clock_(clock),
|
||||
number_to_store_(0),
|
||||
mode_(StorageMode::kDisabled),
|
||||
rtt_ms_(-1) {}
|
||||
|
||||
RtpPacketHistory::~RtpPacketHistory() {}
|
||||
|
||||
void RtpPacketHistory::SetStorePacketsStatus(bool enable,
|
||||
uint16_t number_to_store) {
|
||||
rtc::CritScope cs(&critsect_);
|
||||
if (enable) {
|
||||
if (store_) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "Purging packet history in order to re-set status.";
|
||||
Free();
|
||||
}
|
||||
RTC_DCHECK(!store_);
|
||||
Allocate(number_to_store);
|
||||
} else {
|
||||
Free();
|
||||
}
|
||||
}
|
||||
|
||||
void RtpPacketHistory::Allocate(size_t number_to_store) {
|
||||
RTC_DCHECK_GT(number_to_store, 0);
|
||||
void RtpPacketHistory::SetStorePacketsStatus(StorageMode mode,
|
||||
size_t number_to_store) {
|
||||
RTC_DCHECK_LE(number_to_store, kMaxCapacity);
|
||||
store_ = true;
|
||||
stored_packets_.resize(number_to_store);
|
||||
rtc::CritScope cs(&lock_);
|
||||
if (mode != StorageMode::kDisabled && mode_ != StorageMode::kDisabled) {
|
||||
RTC_LOG(LS_WARNING) << "Purging packet history in order to re-set status.";
|
||||
}
|
||||
Reset();
|
||||
mode_ = mode;
|
||||
number_to_store_ = std::min(kMaxCapacity, number_to_store);
|
||||
}
|
||||
|
||||
void RtpPacketHistory::Free() {
|
||||
if (!store_) {
|
||||
return;
|
||||
}
|
||||
|
||||
stored_packets_.clear();
|
||||
|
||||
store_ = false;
|
||||
prev_index_ = 0;
|
||||
}
|
||||
|
||||
bool RtpPacketHistory::StorePackets() const {
|
||||
rtc::CritScope cs(&critsect_);
|
||||
return store_;
|
||||
}
|
||||
|
||||
void RtpPacketHistory::PutRtpPacket(std::unique_ptr<RtpPacketToSend> packet,
|
||||
StorageType type,
|
||||
bool sent) {
|
||||
RTC_DCHECK(packet);
|
||||
rtc::CritScope cs(&critsect_);
|
||||
if (!store_) {
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
|
||||
// If index we're about to overwrite contains a packet that has not
|
||||
// yet been sent (probably pending in paced sender), or if the send time is
|
||||
// less than 3 round trip times ago, expand the buffer to avoid overwriting
|
||||
// valid data.
|
||||
StoredPacket* stored_packet = &stored_packets_[prev_index_];
|
||||
int64_t packet_duration_ms =
|
||||
std::max(kMinPacketDurationRtt * rtt_ms_, kMinPacketDurationMs);
|
||||
if (stored_packet->packet &&
|
||||
(stored_packet->send_time == 0 ||
|
||||
(rtt_ms_ >= 0 &&
|
||||
now_ms - stored_packet->send_time <= packet_duration_ms))) {
|
||||
size_t current_size = stored_packets_.size();
|
||||
if (current_size < kMaxCapacity) {
|
||||
size_t expanded_size = std::max(current_size * 3 / 2, current_size + 1);
|
||||
expanded_size = std::min(expanded_size, kMaxCapacity);
|
||||
Allocate(expanded_size);
|
||||
// Causes discontinuity, but that's OK-ish. FindSeqNum() will still work,
|
||||
// but may be slower - at least until buffer has wrapped around once.
|
||||
prev_index_ = current_size;
|
||||
stored_packet = &stored_packets_[prev_index_];
|
||||
}
|
||||
}
|
||||
|
||||
// Store packet.
|
||||
if (packet->capture_time_ms() <= 0)
|
||||
packet->set_capture_time_ms(now_ms);
|
||||
stored_packet->sequence_number = packet->SequenceNumber();
|
||||
stored_packet->send_time = sent ? now_ms : 0;
|
||||
stored_packet->storage_type = type;
|
||||
stored_packet->has_been_retransmitted = false;
|
||||
stored_packet->packet = std::move(packet);
|
||||
|
||||
prev_index_ = (prev_index_ + 1) % stored_packets_.size();
|
||||
}
|
||||
|
||||
bool RtpPacketHistory::HasRtpPacket(uint16_t sequence_number) const {
|
||||
rtc::CritScope cs(&critsect_);
|
||||
if (!store_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int unused_index = 0;
|
||||
return FindSeqNum(sequence_number, &unused_index);
|
||||
}
|
||||
|
||||
std::unique_ptr<RtpPacketToSend> RtpPacketHistory::GetPacketAndSetSendTime(
|
||||
uint16_t sequence_number,
|
||||
int64_t min_elapsed_time_ms,
|
||||
bool retransmit) {
|
||||
rtc::CritScope cs(&critsect_);
|
||||
if (!store_) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
if (!FindSeqNum(sequence_number, &index)) {
|
||||
RTC_LOG(LS_WARNING) << "No match for getting seqNum " << sequence_number;
|
||||
return nullptr;
|
||||
}
|
||||
RTC_DCHECK_EQ(sequence_number,
|
||||
stored_packets_[index].packet->SequenceNumber());
|
||||
|
||||
// Verify elapsed time since last retrieve, but only for retransmissions and
|
||||
// always send packet upon first retransmission request.
|
||||
int64_t now = clock_->TimeInMilliseconds();
|
||||
if (min_elapsed_time_ms > 0 && retransmit &&
|
||||
stored_packets_[index].has_been_retransmitted &&
|
||||
((now - stored_packets_[index].send_time) < min_elapsed_time_ms)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (retransmit) {
|
||||
if (stored_packets_[index].storage_type == kDontRetransmit) {
|
||||
// No bytes copied since this packet shouldn't be retransmitted.
|
||||
return nullptr;
|
||||
}
|
||||
stored_packets_[index].has_been_retransmitted = true;
|
||||
}
|
||||
stored_packets_[index].send_time = clock_->TimeInMilliseconds();
|
||||
return GetPacket(index);
|
||||
}
|
||||
|
||||
std::unique_ptr<RtpPacketToSend> RtpPacketHistory::GetPacket(int index) const {
|
||||
const RtpPacketToSend& stored = *stored_packets_[index].packet;
|
||||
return std::unique_ptr<RtpPacketToSend>(new RtpPacketToSend(stored));
|
||||
}
|
||||
|
||||
std::unique_ptr<RtpPacketToSend> RtpPacketHistory::GetBestFittingPacket(
|
||||
size_t packet_length) const {
|
||||
rtc::CritScope cs(&critsect_);
|
||||
if (!store_)
|
||||
return nullptr;
|
||||
int index = FindBestFittingPacket(packet_length);
|
||||
if (index < 0)
|
||||
return nullptr;
|
||||
return GetPacket(index);
|
||||
}
|
||||
|
||||
bool RtpPacketHistory::FindSeqNum(uint16_t sequence_number, int* index) const {
|
||||
if (prev_index_ > 0) {
|
||||
*index = prev_index_ - 1;
|
||||
} else {
|
||||
*index = stored_packets_.size() - 1; // Wrap.
|
||||
}
|
||||
uint16_t temp_sequence_number = stored_packets_[*index].sequence_number;
|
||||
|
||||
int idx = *index - (temp_sequence_number - sequence_number);
|
||||
if (idx >= 0 && idx < static_cast<int>(stored_packets_.size())) {
|
||||
*index = idx;
|
||||
temp_sequence_number = stored_packets_[*index].sequence_number;
|
||||
}
|
||||
|
||||
if (temp_sequence_number != sequence_number) {
|
||||
// We did not found a match, search all.
|
||||
for (uint16_t m = 0; m < stored_packets_.size(); m++) {
|
||||
if (stored_packets_[m].sequence_number == sequence_number) {
|
||||
*index = m;
|
||||
temp_sequence_number = stored_packets_[*index].sequence_number;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return temp_sequence_number == sequence_number &&
|
||||
stored_packets_[*index].packet;
|
||||
}
|
||||
|
||||
int RtpPacketHistory::FindBestFittingPacket(size_t size) const {
|
||||
if (size < kMinPacketRequestBytes || stored_packets_.empty())
|
||||
return -1;
|
||||
size_t min_diff = std::numeric_limits<size_t>::max();
|
||||
int best_index = -1; // Returned unchanged if we don't find anything.
|
||||
for (size_t i = 0; i < stored_packets_.size(); ++i) {
|
||||
if (!stored_packets_[i].packet)
|
||||
continue;
|
||||
size_t stored_size = stored_packets_[i].packet->size();
|
||||
size_t diff =
|
||||
(stored_size > size) ? (stored_size - size) : (size - stored_size);
|
||||
if (diff < min_diff) {
|
||||
min_diff = diff;
|
||||
best_index = static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
return best_index;
|
||||
RtpPacketHistory::StorageMode RtpPacketHistory::GetStorageMode() const {
|
||||
rtc::CritScope cs(&lock_);
|
||||
return mode_;
|
||||
}
|
||||
|
||||
void RtpPacketHistory::SetRtt(int64_t rtt_ms) {
|
||||
rtc::CritScope cs(&critsect_);
|
||||
rtc::CritScope cs(&lock_);
|
||||
RTC_DCHECK_GE(rtt_ms, 0);
|
||||
rtt_ms_ = rtt_ms;
|
||||
}
|
||||
|
||||
void RtpPacketHistory::PutRtpPacket(std::unique_ptr<RtpPacketToSend> packet,
|
||||
StorageType type,
|
||||
rtc::Optional<int64_t> send_time_ms) {
|
||||
RTC_DCHECK(packet);
|
||||
rtc::CritScope cs(&lock_);
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
if (mode_ == StorageMode::kDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
CullOldPackets(now_ms);
|
||||
|
||||
// Store packet.
|
||||
const uint16_t rtp_seq_no = packet->SequenceNumber();
|
||||
StoredPacket& stored_packet = packet_history_[rtp_seq_no];
|
||||
RTC_DCHECK(stored_packet.packet == nullptr);
|
||||
stored_packet.packet = std::move(packet);
|
||||
|
||||
if (stored_packet.packet->capture_time_ms() <= 0) {
|
||||
stored_packet.packet->set_capture_time_ms(now_ms);
|
||||
}
|
||||
stored_packet.send_time_ms = send_time_ms;
|
||||
stored_packet.storage_type = type;
|
||||
stored_packet.times_retransmitted = 0;
|
||||
|
||||
if (!start_seqno_) {
|
||||
start_seqno_ = rtp_seq_no;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<RtpPacketToSend> RtpPacketHistory::GetPacketAndSetSendTime(
|
||||
uint16_t sequence_number,
|
||||
bool verify_rtt) {
|
||||
rtc::CritScope cs(&lock_);
|
||||
if (mode_ == StorageMode::kDisabled) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
StoredPacketIterator rtp_it = packet_history_.find(sequence_number);
|
||||
if (rtp_it == packet_history_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
StoredPacket& packet = rtp_it->second;
|
||||
if (verify_rtt && !VerifyRtt(rtp_it->second, now_ms)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (packet.send_time_ms) {
|
||||
++packet.times_retransmitted;
|
||||
}
|
||||
|
||||
// Update send-time and return copy of packet instance.
|
||||
packet.send_time_ms = now_ms;
|
||||
|
||||
if (packet.storage_type == StorageType::kDontRetransmit) {
|
||||
// Non retransmittable packet, so call must come from paced sender.
|
||||
// Remove from history and return actual packet instance.
|
||||
return RemovePacket(rtp_it);
|
||||
}
|
||||
return rtc::MakeUnique<RtpPacketToSend>(*packet.packet);
|
||||
}
|
||||
|
||||
rtc::Optional<RtpPacketHistory::PacketState> RtpPacketHistory::GetPacketState(
|
||||
uint16_t sequence_number,
|
||||
bool verify_rtt) const {
|
||||
rtc::CritScope cs(&lock_);
|
||||
if (mode_ == StorageMode::kDisabled) {
|
||||
return rtc::nullopt;
|
||||
}
|
||||
|
||||
auto rtp_it = packet_history_.find(sequence_number);
|
||||
if (rtp_it == packet_history_.end()) {
|
||||
return rtc::nullopt;
|
||||
}
|
||||
|
||||
if (verify_rtt && !VerifyRtt(rtp_it->second, clock_->TimeInMilliseconds())) {
|
||||
return rtc::nullopt;
|
||||
}
|
||||
|
||||
return StoredPacketToPacketState(rtp_it->second);
|
||||
}
|
||||
|
||||
bool RtpPacketHistory::VerifyRtt(const RtpPacketHistory::StoredPacket& packet,
|
||||
int64_t now_ms) const {
|
||||
if (packet.send_time_ms) {
|
||||
// Send-time already set, this check must be for a retransmission.
|
||||
if (packet.times_retransmitted > 0 &&
|
||||
now_ms < *packet.send_time_ms + rtt_ms_) {
|
||||
// This packet has already been retransmitted once, and the time since
|
||||
// that even is lower than on RTT. Ignore request as this packet is
|
||||
// likely already in the network pipe.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<RtpPacketToSend> RtpPacketHistory::GetBestFittingPacket(
|
||||
size_t packet_length) const {
|
||||
// TODO(sprang): Make this smarter, taking retransmit count etc into account.
|
||||
rtc::CritScope cs(&lock_);
|
||||
if (packet_length < kMinPacketRequestBytes || packet_history_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t min_diff = std::numeric_limits<size_t>::max();
|
||||
RtpPacketToSend* best_packet = nullptr;
|
||||
for (auto& it : packet_history_) {
|
||||
size_t diff = SizeDiff(it.second.packet, packet_length);
|
||||
if (!min_diff || diff < min_diff) {
|
||||
min_diff = diff;
|
||||
best_packet = it.second.packet.get();
|
||||
if (diff == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rtc::MakeUnique<RtpPacketToSend>(*best_packet);
|
||||
}
|
||||
|
||||
void RtpPacketHistory::Reset() {
|
||||
packet_history_.clear();
|
||||
start_seqno_.reset();
|
||||
}
|
||||
|
||||
void RtpPacketHistory::CullOldPackets(int64_t now_ms) {
|
||||
int64_t packet_duration_ms =
|
||||
std::max(kMinPacketDurationRtt * rtt_ms_, kMinPacketDurationMs);
|
||||
while (!packet_history_.empty()) {
|
||||
auto stored_packet_it = packet_history_.find(*start_seqno_);
|
||||
RTC_DCHECK(stored_packet_it != packet_history_.end());
|
||||
|
||||
if (packet_history_.size() >= kMaxCapacity) {
|
||||
// We have reached the absolute max capacity, remove one packet
|
||||
// unconditionally.
|
||||
RemovePacket(stored_packet_it);
|
||||
continue;
|
||||
}
|
||||
|
||||
const StoredPacket& stored_packet = stored_packet_it->second;
|
||||
if (!stored_packet.send_time_ms) {
|
||||
// Don't remove packets that have not been sent.
|
||||
return;
|
||||
}
|
||||
|
||||
if (*stored_packet.send_time_ms + packet_duration_ms > now_ms) {
|
||||
// Don't cull packets too early to avoid failed retransmission requests.
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet_history_.size() >= number_to_store_ ||
|
||||
(mode_ == StorageMode::kStoreAndCull &&
|
||||
*stored_packet.send_time_ms +
|
||||
(packet_duration_ms * kPacketCullingDelayFactor) <=
|
||||
now_ms)) {
|
||||
// Too many packets in history, or this packet has timed out. Remove it
|
||||
// and continue.
|
||||
RemovePacket(stored_packet_it);
|
||||
} else {
|
||||
// No more packets can be removed right now.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<RtpPacketToSend> RtpPacketHistory::RemovePacket(
|
||||
StoredPacketIterator packet_it) {
|
||||
// Move the packet out from the StoredPacket container.
|
||||
std::unique_ptr<RtpPacketToSend> rtp_packet =
|
||||
std::move(packet_it->second.packet);
|
||||
// Erase the packet from the map, and capture iterator to the next one.
|
||||
StoredPacketIterator next_it = packet_history_.erase(packet_it);
|
||||
|
||||
// |next_it| now points to the next element, or to the end. If the end,
|
||||
// check if we can wrap around.
|
||||
if (next_it == packet_history_.end()) {
|
||||
next_it = packet_history_.begin();
|
||||
}
|
||||
|
||||
// Update |start_seq_no| to the new oldest item.
|
||||
if (next_it != packet_history_.end()) {
|
||||
start_seqno_ = next_it->first;
|
||||
} else {
|
||||
start_seqno_.reset();
|
||||
}
|
||||
|
||||
return rtp_packet;
|
||||
}
|
||||
|
||||
RtpPacketHistory::PacketState RtpPacketHistory::StoredPacketToPacketState(
|
||||
const RtpPacketHistory::StoredPacket& stored_packet) {
|
||||
RtpPacketHistory::PacketState state;
|
||||
state.rtp_sequence_number = stored_packet.packet->SequenceNumber();
|
||||
state.send_time_ms = stored_packet.send_time_ms;
|
||||
state.capture_time_ms = stored_packet.packet->capture_time_ms();
|
||||
state.ssrc = stored_packet.packet->Ssrc();
|
||||
state.payload_size = stored_packet.packet->size();
|
||||
state.times_retransmitted = stored_packet.times_retransmitted;
|
||||
return state;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#ifndef MODULES_RTP_RTCP_SOURCE_RTP_PACKET_HISTORY_H_
|
||||
#define MODULES_RTP_RTCP_SOURCE_RTP_PACKET_HISTORY_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
@ -27,66 +28,120 @@ class RtpPacketToSend;
|
||||
|
||||
class RtpPacketHistory {
|
||||
public:
|
||||
enum class StorageMode {
|
||||
kDisabled, // Don't store any packets.
|
||||
kStore, // Store and keep at least |number_to_store| packets.
|
||||
kStoreAndCull // Store up to |number_to_store| packets, but try to remove
|
||||
// packets as they time out or as signaled as received.
|
||||
};
|
||||
|
||||
// Snapshot indicating the state of a packet in the history.
|
||||
struct PacketState {
|
||||
PacketState();
|
||||
PacketState(const PacketState&);
|
||||
~PacketState();
|
||||
|
||||
uint16_t rtp_sequence_number = 0;
|
||||
rtc::Optional<int64_t> send_time_ms;
|
||||
int64_t capture_time_ms = 0;
|
||||
uint32_t ssrc = 0;
|
||||
size_t payload_size = 0;
|
||||
// Number of times RE-transmitted, ie not including the first transmission.
|
||||
size_t times_retransmitted = 0;
|
||||
};
|
||||
|
||||
// Maximum number of packets we ever allow in the history.
|
||||
static constexpr size_t kMaxCapacity = 9600;
|
||||
// Don't remove packets within max(1000ms, 3x RTT).
|
||||
static constexpr int64_t kMinPacketDurationMs = 1000;
|
||||
static constexpr int kMinPacketDurationRtt = 3;
|
||||
// With kStoreAndCull, always remove packets after 3x max(1000ms, 3x rtt).
|
||||
static constexpr int kPacketCullingDelayFactor = 3;
|
||||
|
||||
explicit RtpPacketHistory(Clock* clock);
|
||||
~RtpPacketHistory();
|
||||
|
||||
void SetStorePacketsStatus(bool enable, uint16_t number_to_store);
|
||||
bool StorePackets() const;
|
||||
|
||||
void PutRtpPacket(std::unique_ptr<RtpPacketToSend> packet,
|
||||
StorageType type,
|
||||
bool sent);
|
||||
// Set/get storage mode. Note that setting the state will clear the history,
|
||||
// even if setting the same state as is currently used.
|
||||
void SetStorePacketsStatus(StorageMode mode, size_t number_to_store);
|
||||
StorageMode GetStorageMode() const;
|
||||
|
||||
// Set RTT, used to avoid premature retransmission and to prevent over-writing
|
||||
// a packet in the history before we are reasonably sure it has been received.
|
||||
void SetRtt(int64_t rtt_ms);
|
||||
|
||||
// If |send_time| is set, packet was sent without using pacer, so state will
|
||||
// be set accordingly.
|
||||
void PutRtpPacket(std::unique_ptr<RtpPacketToSend> packet,
|
||||
StorageType type,
|
||||
rtc::Optional<int64_t> send_time_ms);
|
||||
|
||||
// Gets stored RTP packet corresponding to the input |sequence number|.
|
||||
// Returns nullptr if packet is not found.
|
||||
// |min_elapsed_time_ms| is the minimum time that must have elapsed since
|
||||
// the last time the packet was resent (parameter is ignored if set to zero).
|
||||
// If the packet is found but the minimum time has not elapsed, returns
|
||||
// nullptr.
|
||||
// Returns nullptr if packet is not found. If |verify_rtt| is true, doesn't
|
||||
// return packet that was (re)sent too recently.
|
||||
std::unique_ptr<RtpPacketToSend> GetPacketAndSetSendTime(
|
||||
uint16_t sequence_number,
|
||||
int64_t min_elapsed_time_ms,
|
||||
bool retransmit);
|
||||
bool verify_rtt);
|
||||
|
||||
// Similar to GetPacketAndSetSendTime(), but only returns a snapshot of the
|
||||
// current state for packet, and never updates internal state.
|
||||
rtc::Optional<PacketState> GetPacketState(uint16_t sequence_number,
|
||||
bool verify_rtt) const;
|
||||
|
||||
// Get the packet (if any) from the history, with size closest to
|
||||
// |packet_size|. The exact size of the packet is not guaranteed.
|
||||
std::unique_ptr<RtpPacketToSend> GetBestFittingPacket(
|
||||
size_t packet_size) const;
|
||||
|
||||
bool HasRtpPacket(uint16_t sequence_number) const;
|
||||
|
||||
private:
|
||||
struct StoredPacket {
|
||||
StoredPacket();
|
||||
StoredPacket(StoredPacket&&);
|
||||
StoredPacket& operator=(StoredPacket&&);
|
||||
~StoredPacket();
|
||||
uint16_t sequence_number = 0;
|
||||
int64_t send_time = 0;
|
||||
StorageType storage_type = kDontRetransmit;
|
||||
bool has_been_retransmitted = false;
|
||||
|
||||
// The time of last transmission, including retransmissions.
|
||||
rtc::Optional<int64_t> send_time_ms;
|
||||
|
||||
// Number of times RE-transmitted, ie excluding the first transmission.
|
||||
size_t times_retransmitted = 0;
|
||||
|
||||
// Storing a packet with |storage_type| = kDontRetransmit indicates this is
|
||||
// only used as temporary storage until sent by the pacer sender.
|
||||
StorageType storage_type = kDontRetransmit;
|
||||
|
||||
// The actual packet.
|
||||
std::unique_ptr<RtpPacketToSend> packet;
|
||||
};
|
||||
|
||||
std::unique_ptr<RtpPacketToSend> GetPacket(int index) const
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(critsect_);
|
||||
void Allocate(size_t number_to_store) RTC_EXCLUSIVE_LOCKS_REQUIRED(critsect_);
|
||||
void Free() RTC_EXCLUSIVE_LOCKS_REQUIRED(critsect_);
|
||||
bool FindSeqNum(uint16_t sequence_number, int* index) const
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(critsect_);
|
||||
int FindBestFittingPacket(size_t size) const
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(critsect_);
|
||||
using StoredPacketIterator = std::map<uint16_t, StoredPacket>::iterator;
|
||||
|
||||
// Helper method used by GetPacketAndSetSendTime() and GetPacketState() to
|
||||
// check if packet has too recently been sent.
|
||||
bool VerifyRtt(const StoredPacket& packet, int64_t now_ms) const
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_);
|
||||
void Reset() RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_);
|
||||
void CullOldPackets(int64_t now_ms) RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_);
|
||||
// Removes the packet from the history, and context/mapping that has been
|
||||
// stored. Returns the RTP packet instance contained within the StoredPacket.
|
||||
std::unique_ptr<RtpPacketToSend> RemovePacket(StoredPacketIterator packet)
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_);
|
||||
static PacketState StoredPacketToPacketState(
|
||||
const StoredPacket& stored_packet);
|
||||
|
||||
Clock* const clock_;
|
||||
rtc::CriticalSection lock_;
|
||||
size_t number_to_store_ RTC_GUARDED_BY(lock_);
|
||||
StorageMode mode_ RTC_GUARDED_BY(lock_);
|
||||
int64_t rtt_ms_ RTC_GUARDED_BY(lock_);
|
||||
|
||||
// Map from rtp sequence numbers to stored packet.
|
||||
std::map<uint16_t, StoredPacket> packet_history_ RTC_GUARDED_BY(lock_);
|
||||
|
||||
// The earliest packet in the history. This might not be the lowest sequence
|
||||
// number, in case there is a wraparound.
|
||||
rtc::Optional<uint16_t> start_seqno_ RTC_GUARDED_BY(lock_);
|
||||
|
||||
Clock* clock_;
|
||||
rtc::CriticalSection critsect_;
|
||||
bool store_ RTC_GUARDED_BY(critsect_);
|
||||
size_t prev_index_ RTC_GUARDED_BY(critsect_);
|
||||
std::vector<StoredPacket> stored_packets_ RTC_GUARDED_BY(critsect_);
|
||||
int64_t rtt_ms_ RTC_GUARDED_BY(critsect_);
|
||||
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RtpPacketHistory);
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
@ -20,11 +20,20 @@
|
||||
#include "typedefs.h" // NOLINT(build/include)
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
// Set a high sequence number so we'll suffer a wrap-around.
|
||||
constexpr uint16_t kStartSeqNum = 65534u;
|
||||
|
||||
// Utility method for truncating sequence numbers to uint16.
|
||||
uint16_t To16u(size_t sequence_number) {
|
||||
return static_cast<uint16_t>(sequence_number & 0xFFFF);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
using StorageMode = RtpPacketHistory::StorageMode;
|
||||
|
||||
class RtpPacketHistoryTest : public ::testing::Test {
|
||||
protected:
|
||||
static constexpr uint16_t kSeqNum = 88;
|
||||
|
||||
RtpPacketHistoryTest() : fake_clock_(123456), hist_(&fake_clock_) {}
|
||||
|
||||
SimulatedClock fake_clock_;
|
||||
@ -40,256 +49,444 @@ class RtpPacketHistoryTest : public ::testing::Test {
|
||||
};
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, SetStoreStatus) {
|
||||
EXPECT_FALSE(hist_.StorePackets());
|
||||
hist_.SetStorePacketsStatus(true, 10);
|
||||
EXPECT_TRUE(hist_.StorePackets());
|
||||
hist_.SetStorePacketsStatus(false, 0);
|
||||
EXPECT_FALSE(hist_.StorePackets());
|
||||
EXPECT_EQ(StorageMode::kDisabled, hist_.GetStorageMode());
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStore, 10);
|
||||
EXPECT_EQ(StorageMode::kStore, hist_.GetStorageMode());
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10);
|
||||
EXPECT_EQ(StorageMode::kStoreAndCull, hist_.GetStorageMode());
|
||||
hist_.SetStorePacketsStatus(StorageMode::kDisabled, 0);
|
||||
EXPECT_EQ(StorageMode::kDisabled, hist_.GetStorageMode());
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, ClearsHistoryAfterSetStoreStatus) {
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStore, 10);
|
||||
// Store a packet, but with send-time. It should then not be removed.
|
||||
hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), kAllowRetransmission,
|
||||
rtc::nullopt);
|
||||
EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
|
||||
// Changing store status, even to the current one, will clear the history.
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStore, 10);
|
||||
EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, StartSeqResetAfterReset) {
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10);
|
||||
// Store a packet, but with send-time. It should then not be removed.
|
||||
hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), kAllowRetransmission,
|
||||
rtc::nullopt);
|
||||
EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
|
||||
// Changing store status, to clear the history.
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10);
|
||||
EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
|
||||
// Add a new packet.
|
||||
hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum + 1), kAllowRetransmission,
|
||||
rtc::nullopt);
|
||||
EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum + 1, false));
|
||||
|
||||
// Advance time past where packet expires.
|
||||
fake_clock_.AdvanceTimeMilliseconds(
|
||||
RtpPacketHistory::kPacketCullingDelayFactor *
|
||||
RtpPacketHistory::kMinPacketDurationMs);
|
||||
|
||||
// Add one more packet and verify no state left from packet before reset.
|
||||
hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 2)),
|
||||
kAllowRetransmission, rtc::nullopt);
|
||||
EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum + 1, false));
|
||||
EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2), false));
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, NoStoreStatus) {
|
||||
EXPECT_FALSE(hist_.StorePackets());
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kSeqNum);
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, false);
|
||||
EXPECT_EQ(StorageMode::kDisabled, hist_.GetStorageMode());
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kStartSeqNum);
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, rtc::nullopt);
|
||||
// Packet should not be stored.
|
||||
EXPECT_FALSE(hist_.GetPacketAndSetSendTime(kSeqNum, 0, false));
|
||||
EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, GetRtpPacket_NotStored) {
|
||||
hist_.SetStorePacketsStatus(true, 10);
|
||||
EXPECT_FALSE(hist_.GetPacketAndSetSendTime(0, 0, false));
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStore, 10);
|
||||
EXPECT_FALSE(hist_.GetPacketState(0, false));
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, PutRtpPacket) {
|
||||
hist_.SetStorePacketsStatus(true, 10);
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kSeqNum);
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStore, 10);
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kStartSeqNum);
|
||||
|
||||
EXPECT_FALSE(hist_.HasRtpPacket(kSeqNum));
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, false);
|
||||
EXPECT_TRUE(hist_.HasRtpPacket(kSeqNum));
|
||||
EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, rtc::nullopt);
|
||||
EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, GetRtpPacket) {
|
||||
hist_.SetStorePacketsStatus(true, 10);
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStore, 10);
|
||||
int64_t capture_time_ms = 1;
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kSeqNum);
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kStartSeqNum);
|
||||
packet->set_capture_time_ms(capture_time_ms);
|
||||
rtc::CopyOnWriteBuffer buffer = packet->Buffer();
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, false);
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, rtc::nullopt);
|
||||
|
||||
std::unique_ptr<RtpPacketToSend> packet_out =
|
||||
hist_.GetPacketAndSetSendTime(kSeqNum, 0, false);
|
||||
hist_.GetPacketAndSetSendTime(kStartSeqNum, false);
|
||||
EXPECT_TRUE(packet_out);
|
||||
EXPECT_EQ(buffer, packet_out->Buffer());
|
||||
EXPECT_EQ(capture_time_ms, packet_out->capture_time_ms());
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, NoCaptureTime) {
|
||||
hist_.SetStorePacketsStatus(true, 10);
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStore, 10);
|
||||
fake_clock_.AdvanceTimeMilliseconds(1);
|
||||
int64_t capture_time_ms = fake_clock_.TimeInMilliseconds();
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kSeqNum);
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kStartSeqNum);
|
||||
packet->set_capture_time_ms(-1);
|
||||
rtc::CopyOnWriteBuffer buffer = packet->Buffer();
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, false);
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, rtc::nullopt);
|
||||
|
||||
std::unique_ptr<RtpPacketToSend> packet_out =
|
||||
hist_.GetPacketAndSetSendTime(kSeqNum, 0, false);
|
||||
hist_.GetPacketAndSetSendTime(kStartSeqNum, false);
|
||||
EXPECT_TRUE(packet_out);
|
||||
EXPECT_EQ(buffer, packet_out->Buffer());
|
||||
EXPECT_EQ(capture_time_ms, packet_out->capture_time_ms());
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, DontRetransmit) {
|
||||
hist_.SetStorePacketsStatus(true, 10);
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStore, 10);
|
||||
int64_t capture_time_ms = fake_clock_.TimeInMilliseconds();
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kSeqNum);
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kStartSeqNum);
|
||||
rtc::CopyOnWriteBuffer buffer = packet->Buffer();
|
||||
hist_.PutRtpPacket(std::move(packet), kDontRetransmit, false);
|
||||
hist_.PutRtpPacket(std::move(packet), kDontRetransmit, rtc::nullopt);
|
||||
|
||||
// Get the packet and verify data.
|
||||
std::unique_ptr<RtpPacketToSend> packet_out;
|
||||
packet_out = hist_.GetPacketAndSetSendTime(kSeqNum, 0, true);
|
||||
EXPECT_FALSE(packet_out);
|
||||
|
||||
packet_out = hist_.GetPacketAndSetSendTime(kSeqNum, 0, false);
|
||||
EXPECT_TRUE(packet_out);
|
||||
|
||||
packet_out = hist_.GetPacketAndSetSendTime(kStartSeqNum, false);
|
||||
ASSERT_TRUE(packet_out);
|
||||
EXPECT_EQ(buffer.size(), packet_out->size());
|
||||
EXPECT_EQ(capture_time_ms, packet_out->capture_time_ms());
|
||||
|
||||
// Non-retransmittable packets are immediately removed, so getting in again
|
||||
// should fail.
|
||||
EXPECT_FALSE(hist_.GetPacketAndSetSendTime(kStartSeqNum, false));
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, MinResendTime) {
|
||||
TEST_F(RtpPacketHistoryTest, PacketStateIsCorrect) {
|
||||
const uint32_t kSsrc = 92384762;
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10);
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kStartSeqNum);
|
||||
packet->SetSsrc(kSsrc);
|
||||
packet->SetPayloadSize(1234);
|
||||
const size_t packet_size = packet->size();
|
||||
|
||||
hist_.PutRtpPacket(std::move(packet), StorageType::kAllowRetransmission,
|
||||
fake_clock_.TimeInMilliseconds());
|
||||
|
||||
rtc::Optional<RtpPacketHistory::PacketState> state =
|
||||
hist_.GetPacketState(kStartSeqNum, false);
|
||||
ASSERT_TRUE(state);
|
||||
EXPECT_EQ(state->rtp_sequence_number, kStartSeqNum);
|
||||
EXPECT_EQ(state->send_time_ms, fake_clock_.TimeInMilliseconds());
|
||||
EXPECT_EQ(state->capture_time_ms, fake_clock_.TimeInMilliseconds());
|
||||
EXPECT_EQ(state->ssrc, kSsrc);
|
||||
EXPECT_EQ(state->payload_size, packet_size);
|
||||
EXPECT_EQ(state->times_retransmitted, 0u);
|
||||
|
||||
fake_clock_.AdvanceTimeMilliseconds(1);
|
||||
EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum, false));
|
||||
|
||||
state = hist_.GetPacketState(kStartSeqNum, false);
|
||||
ASSERT_TRUE(state);
|
||||
EXPECT_EQ(state->times_retransmitted, 1u);
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, MinResendTimeWithPacer) {
|
||||
static const int64_t kMinRetransmitIntervalMs = 100;
|
||||
|
||||
hist_.SetStorePacketsStatus(true, 10);
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStore, 10);
|
||||
hist_.SetRtt(kMinRetransmitIntervalMs);
|
||||
int64_t capture_time_ms = fake_clock_.TimeInMilliseconds();
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kSeqNum);
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kStartSeqNum);
|
||||
size_t len = packet->size();
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, false);
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, rtc::nullopt);
|
||||
|
||||
// First transmission: TimeToSendPacket() call from pacer.
|
||||
EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kSeqNum, 0, false));
|
||||
EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum, false));
|
||||
|
||||
fake_clock_.AdvanceTimeMilliseconds(kMinRetransmitIntervalMs);
|
||||
// Time has elapsed.
|
||||
std::unique_ptr<RtpPacketToSend> packet_out =
|
||||
hist_.GetPacketAndSetSendTime(kSeqNum, kMinRetransmitIntervalMs, true);
|
||||
EXPECT_TRUE(packet_out);
|
||||
EXPECT_EQ(len, packet_out->size());
|
||||
EXPECT_EQ(capture_time_ms, packet_out->capture_time_ms());
|
||||
// First retransmission - allow early retransmission.
|
||||
fake_clock_.AdvanceTimeMilliseconds(1);
|
||||
|
||||
// With pacer there's two calls to history:
|
||||
// 1) When the NACK request arrived, use GetPacketState() to see if the
|
||||
// packet is there and verify RTT constraints. Then we use the ssrc
|
||||
// and sequence number to enqueue the retransmission in the pacer
|
||||
// 2) When the pacer determines that it is time to send the packet, it calls
|
||||
// GetPacketAndSetSendTime(). This time we do not need to verify RTT as
|
||||
// has that has already been done.
|
||||
rtc::Optional<RtpPacketHistory::PacketState> packet_state =
|
||||
hist_.GetPacketState(kStartSeqNum, /*verify_rtt=*/true);
|
||||
EXPECT_TRUE(packet_state);
|
||||
EXPECT_EQ(len, packet_state->payload_size);
|
||||
EXPECT_EQ(capture_time_ms, packet_state->capture_time_ms);
|
||||
|
||||
// Retransmission was allowed, next send it from pacer.
|
||||
EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum,
|
||||
/*verify_rtt=*/false));
|
||||
|
||||
// Second retransmission - advance time to just before retransmission OK.
|
||||
fake_clock_.AdvanceTimeMilliseconds(kMinRetransmitIntervalMs - 1);
|
||||
// Time has not elapsed. Packet should be found, but no bytes copied.
|
||||
EXPECT_TRUE(hist_.HasRtpPacket(kSeqNum));
|
||||
EXPECT_FALSE(
|
||||
hist_.GetPacketAndSetSendTime(kSeqNum, kMinRetransmitIntervalMs, true));
|
||||
EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum, /*verify_rtt=*/true));
|
||||
|
||||
// Advance time to just after retransmission OK.
|
||||
fake_clock_.AdvanceTimeMilliseconds(1);
|
||||
EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum, /*verify_rtt=*/true));
|
||||
EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum, false));
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, EarlyFirstResend) {
|
||||
TEST_F(RtpPacketHistoryTest, MinResendTimeWithoutPacer) {
|
||||
static const int64_t kMinRetransmitIntervalMs = 100;
|
||||
|
||||
hist_.SetStorePacketsStatus(true, 10);
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStore, 10);
|
||||
hist_.SetRtt(kMinRetransmitIntervalMs);
|
||||
int64_t capture_time_ms = fake_clock_.TimeInMilliseconds();
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kSeqNum);
|
||||
rtc::CopyOnWriteBuffer buffer = packet->Buffer();
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, false);
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kStartSeqNum);
|
||||
size_t len = packet->size();
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission,
|
||||
fake_clock_.TimeInMilliseconds());
|
||||
|
||||
// First transmission: TimeToSendPacket() call from pacer.
|
||||
EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kSeqNum, 0, false));
|
||||
// First retransmission - allow early retransmission.
|
||||
fake_clock_.AdvanceTimeMilliseconds(1);
|
||||
packet = hist_.GetPacketAndSetSendTime(kStartSeqNum, true);
|
||||
EXPECT_TRUE(packet);
|
||||
EXPECT_EQ(len, packet->size());
|
||||
EXPECT_EQ(capture_time_ms, packet->capture_time_ms());
|
||||
|
||||
// Second retransmission - advance time to just before retransmission OK.
|
||||
fake_clock_.AdvanceTimeMilliseconds(kMinRetransmitIntervalMs - 1);
|
||||
// Time has not elapsed, but this is the first retransmission request so
|
||||
// allow anyway.
|
||||
std::unique_ptr<RtpPacketToSend> packet_out =
|
||||
hist_.GetPacketAndSetSendTime(kSeqNum, kMinRetransmitIntervalMs, true);
|
||||
EXPECT_TRUE(packet_out);
|
||||
EXPECT_EQ(buffer, packet_out->Buffer());
|
||||
EXPECT_EQ(capture_time_ms, packet_out->capture_time_ms());
|
||||
EXPECT_FALSE(hist_.GetPacketAndSetSendTime(kStartSeqNum, true));
|
||||
|
||||
fake_clock_.AdvanceTimeMilliseconds(kMinRetransmitIntervalMs - 1);
|
||||
// Time has not elapsed. Packet should be found, but no bytes copied.
|
||||
EXPECT_TRUE(hist_.HasRtpPacket(kSeqNum));
|
||||
EXPECT_FALSE(
|
||||
hist_.GetPacketAndSetSendTime(kSeqNum, kMinRetransmitIntervalMs, true));
|
||||
// Advance time to just after retransmission OK.
|
||||
fake_clock_.AdvanceTimeMilliseconds(1);
|
||||
EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum, true));
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, DynamicExpansion) {
|
||||
hist_.SetStorePacketsStatus(true, 10);
|
||||
TEST_F(RtpPacketHistoryTest, RemovesOldestSentPacketWhenAtMaxSize) {
|
||||
const size_t kMaxNumPackets = 10;
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStore, kMaxNumPackets);
|
||||
|
||||
// Add 4 packets, and then send them.
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kSeqNum + i);
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, false);
|
||||
}
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kSeqNum + i, 100, false));
|
||||
}
|
||||
fake_clock_.AdvanceTimeMilliseconds(33);
|
||||
// History does not allow removing packets within kMinPacketDurationMs,
|
||||
// so in order to test capacity, make sure insertion spans this time.
|
||||
const int64_t kPacketIntervalMs =
|
||||
RtpPacketHistory::kMinPacketDurationMs / kMaxNumPackets;
|
||||
|
||||
// Add 16 packets, and then send them. History should expand to make this
|
||||
// work.
|
||||
for (int i = 4; i < 20; ++i) {
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kSeqNum + i);
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, false);
|
||||
}
|
||||
for (int i = 4; i < 20; ++i) {
|
||||
EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kSeqNum + i, 100, false));
|
||||
// Add packets until the buffer is full.
|
||||
for (size_t i = 0; i < kMaxNumPackets; ++i) {
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kStartSeqNum + i);
|
||||
// Immediate mark packet as sent.
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission,
|
||||
fake_clock_.TimeInMilliseconds());
|
||||
fake_clock_.AdvanceTimeMilliseconds(kPacketIntervalMs);
|
||||
}
|
||||
|
||||
fake_clock_.AdvanceTimeMilliseconds(100);
|
||||
// First packet should still be there.
|
||||
EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
|
||||
// Retransmit last 16 packets.
|
||||
for (int i = 4; i < 20; ++i) {
|
||||
EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kSeqNum + i, 100, false));
|
||||
}
|
||||
// History is full, oldest one should be overwritten.
|
||||
std::unique_ptr<RtpPacketToSend> packet =
|
||||
CreateRtpPacket(To16u(kStartSeqNum + kMaxNumPackets));
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission,
|
||||
fake_clock_.TimeInMilliseconds());
|
||||
|
||||
// Oldest packet should be gone, but packet after than one still present.
|
||||
EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum + 1, false));
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, FullExpansion) {
|
||||
static const int kSendSidePacketHistorySize = 600;
|
||||
hist_.SetStorePacketsStatus(true, kSendSidePacketHistorySize);
|
||||
for (size_t i = 0; i < RtpPacketHistory::kMaxCapacity + 1; ++i) {
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kSeqNum + i);
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, false);
|
||||
TEST_F(RtpPacketHistoryTest, RemovesOldestPacketWhenAtMaxCapacity) {
|
||||
// Tests the absolute upper bound on number of stored packets. Don't allow
|
||||
// storing more than this, even if packets have not yet been sent.
|
||||
const size_t kMaxNumPackets = RtpPacketHistory::kMaxCapacity;
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStore,
|
||||
RtpPacketHistory::kMaxCapacity);
|
||||
|
||||
// Add packets until the buffer is full.
|
||||
for (size_t i = 0; i < kMaxNumPackets; ++i) {
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kStartSeqNum + i);
|
||||
// Don't mark packets as sent, preventing them from being removed.
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, rtc::nullopt);
|
||||
}
|
||||
|
||||
fake_clock_.AdvanceTimeMilliseconds(100);
|
||||
// First packet should still be there.
|
||||
EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
|
||||
// Retransmit all packets currently in buffer.
|
||||
for (size_t i = 1; i < RtpPacketHistory::kMaxCapacity + 1; ++i) {
|
||||
EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kSeqNum + i, 100, false));
|
||||
}
|
||||
// History is full, oldest one should be overwritten.
|
||||
std::unique_ptr<RtpPacketToSend> packet =
|
||||
CreateRtpPacket(To16u(kStartSeqNum + kMaxNumPackets));
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission,
|
||||
fake_clock_.TimeInMilliseconds());
|
||||
|
||||
// Oldest packet should be gone, but packet after than one still present.
|
||||
EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum + 1, false));
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, DontExpandIfPacketIsOldEnough) {
|
||||
const size_t kSendSidePacketHistorySize = 600;
|
||||
const int64_t kRttMs = 334;
|
||||
hist_.SetStorePacketsStatus(true, kSendSidePacketHistorySize);
|
||||
TEST_F(RtpPacketHistoryTest, DontRemoveUnsentPackets) {
|
||||
const size_t kMaxNumPackets = 10;
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStore, kMaxNumPackets);
|
||||
|
||||
// Add packets until the buffer is full.
|
||||
for (size_t i = 0; i < kMaxNumPackets; ++i) {
|
||||
// Mark packets as unsent.
|
||||
hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + i)),
|
||||
kAllowRetransmission, rtc::nullopt);
|
||||
}
|
||||
fake_clock_.AdvanceTimeMilliseconds(RtpPacketHistory::kMinPacketDurationMs);
|
||||
|
||||
// First packet should still be there.
|
||||
EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
|
||||
// History is full, but old packets not sent, so allow expansion.
|
||||
hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + kMaxNumPackets)),
|
||||
kAllowRetransmission, fake_clock_.TimeInMilliseconds());
|
||||
EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
|
||||
// Set all packet as sent and advance time past min packet duration time,
|
||||
// otherwise packets till still be prevented from being removed.
|
||||
for (size_t i = 0; i <= kMaxNumPackets; ++i) {
|
||||
EXPECT_TRUE(hist_.GetPacketAndSetSendTime(To16u(kStartSeqNum + i), false));
|
||||
}
|
||||
fake_clock_.AdvanceTimeMilliseconds(RtpPacketHistory::kMinPacketDurationMs);
|
||||
// Add a new packet, this means the two oldest ones will be culled.
|
||||
hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + kMaxNumPackets + 1)),
|
||||
kAllowRetransmission, fake_clock_.TimeInMilliseconds());
|
||||
EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum + 1, false));
|
||||
EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2), false));
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, DontRemoveTooRecentlyTransmittedPackets) {
|
||||
// Set size to remove old packets as soon as possible.
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStore, 1);
|
||||
|
||||
// Add a packet, marked as send, and advance time to just before removal time.
|
||||
hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), kAllowRetransmission,
|
||||
fake_clock_.TimeInMilliseconds());
|
||||
fake_clock_.AdvanceTimeMilliseconds(RtpPacketHistory::kMinPacketDurationMs -
|
||||
1);
|
||||
|
||||
// Add a new packet to trigger culling.
|
||||
hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum + 1), kAllowRetransmission,
|
||||
fake_clock_.TimeInMilliseconds());
|
||||
// First packet should still be there.
|
||||
EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
|
||||
// Advance time to where packet will be eligible for removal and try again.
|
||||
fake_clock_.AdvanceTimeMilliseconds(1);
|
||||
hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 2)),
|
||||
kAllowRetransmission, fake_clock_.TimeInMilliseconds());
|
||||
// First packet should no be gone, but next one still there.
|
||||
EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum + 1, false));
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, DontRemoveTooRecentlyTransmittedPacketsHighRtt) {
|
||||
const int64_t kRttMs = RtpPacketHistory::kMinPacketDurationMs * 2;
|
||||
const int64_t kPacketTimeoutMs =
|
||||
kRttMs * RtpPacketHistory::kMinPacketDurationRtt;
|
||||
|
||||
// Set size to remove old packets as soon as possible.
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStore, 1);
|
||||
hist_.SetRtt(kRttMs);
|
||||
|
||||
// Fill up the buffer with packets.
|
||||
for (size_t i = 0; i < kSendSidePacketHistorySize; ++i) {
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kSeqNum + i);
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, false);
|
||||
EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kSeqNum + i, 100, false));
|
||||
}
|
||||
// Add a packet, marked as send, and advance time to just before removal time.
|
||||
hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), kAllowRetransmission,
|
||||
fake_clock_.TimeInMilliseconds());
|
||||
fake_clock_.AdvanceTimeMilliseconds(kPacketTimeoutMs - 1);
|
||||
|
||||
// Move clock forward past expiration time.
|
||||
fake_clock_.AdvanceTimeMilliseconds(kRttMs * 3 + 1);
|
||||
// Add a new packet to trigger culling.
|
||||
hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum + 1), kAllowRetransmission,
|
||||
fake_clock_.TimeInMilliseconds());
|
||||
// First packet should still be there.
|
||||
EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
|
||||
// Insert a new packet and check that the old one for this index has been
|
||||
// overwritten.
|
||||
std::unique_ptr<RtpPacketToSend> packet =
|
||||
CreateRtpPacket(kSeqNum + kSendSidePacketHistorySize);
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, true);
|
||||
EXPECT_FALSE(hist_.HasRtpPacket(kSeqNum));
|
||||
// Advance time to where packet will be eligible for removal and try again.
|
||||
fake_clock_.AdvanceTimeMilliseconds(1);
|
||||
hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 2)),
|
||||
kAllowRetransmission, fake_clock_.TimeInMilliseconds());
|
||||
// First packet should no be gone, but next one still there.
|
||||
EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum + 1, false));
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, ExpandIfPacketTooRecentlyTransmitted) {
|
||||
const size_t kSendSidePacketHistorySize = 600;
|
||||
const int64_t kRttMs = 334;
|
||||
hist_.SetStorePacketsStatus(true, kSendSidePacketHistorySize);
|
||||
hist_.SetRtt(kRttMs);
|
||||
TEST_F(RtpPacketHistoryTest, RemovesOldWithCulling) {
|
||||
const size_t kMaxNumPackets = 10;
|
||||
// Enable culling. Even without feedback, this can trigger early removal.
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, kMaxNumPackets);
|
||||
|
||||
// Fill up the buffer with packets.
|
||||
for (size_t i = 0; i < kSendSidePacketHistorySize; ++i) {
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kSeqNum + i);
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, false);
|
||||
EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kSeqNum + i, kRttMs, false));
|
||||
}
|
||||
hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), kAllowRetransmission,
|
||||
fake_clock_.TimeInMilliseconds());
|
||||
|
||||
// Move clock forward to just before expiration time.
|
||||
fake_clock_.AdvanceTimeMilliseconds(kRttMs * 3);
|
||||
int64_t kMaxPacketDurationMs = RtpPacketHistory::kMinPacketDurationMs *
|
||||
RtpPacketHistory::kPacketCullingDelayFactor;
|
||||
fake_clock_.AdvanceTimeMilliseconds(kMaxPacketDurationMs - 1);
|
||||
|
||||
// Insert a new packet and verify that the old one for this index still
|
||||
// exists - ie the buffer has been expanded.
|
||||
std::unique_ptr<RtpPacketToSend> packet =
|
||||
CreateRtpPacket(kSeqNum + kSendSidePacketHistorySize);
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, true);
|
||||
EXPECT_TRUE(hist_.HasRtpPacket(kSeqNum));
|
||||
// First packet should still be there.
|
||||
EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
|
||||
// Advance to where packet can be culled, even if buffer is not full.
|
||||
fake_clock_.AdvanceTimeMilliseconds(1);
|
||||
hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum + 1), kAllowRetransmission,
|
||||
fake_clock_.TimeInMilliseconds());
|
||||
|
||||
EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, ExpandIfPacketTooRecentlyTransmittedOnFastLink) {
|
||||
const size_t kSendSidePacketHistorySize = 600;
|
||||
const int64_t kRttMs = 5;
|
||||
hist_.SetStorePacketsStatus(true, kSendSidePacketHistorySize);
|
||||
TEST_F(RtpPacketHistoryTest, RemovesOldWithCullingHighRtt) {
|
||||
const size_t kMaxNumPackets = 10;
|
||||
const int64_t kRttMs = RtpPacketHistory::kMinPacketDurationMs * 2;
|
||||
// Enable culling. Even without feedback, this can trigger early removal.
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, kMaxNumPackets);
|
||||
hist_.SetRtt(kRttMs);
|
||||
|
||||
// Fill up the buffer with packets.
|
||||
for (size_t i = 0; i < kSendSidePacketHistorySize; ++i) {
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kSeqNum + i);
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, false);
|
||||
EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kSeqNum + i, kRttMs, false));
|
||||
}
|
||||
hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), kAllowRetransmission,
|
||||
fake_clock_.TimeInMilliseconds());
|
||||
|
||||
// Move clock forward after expiration time based on RTT, but before
|
||||
// expiration time based on absolute time.
|
||||
fake_clock_.AdvanceTimeMilliseconds(999);
|
||||
int64_t kMaxPacketDurationMs = kRttMs *
|
||||
RtpPacketHistory::kMinPacketDurationRtt *
|
||||
RtpPacketHistory::kPacketCullingDelayFactor;
|
||||
fake_clock_.AdvanceTimeMilliseconds(kMaxPacketDurationMs - 1);
|
||||
|
||||
// Insert a new packet and verify that the old one for this index still
|
||||
// exists - ie the buffer has been expanded.
|
||||
std::unique_ptr<RtpPacketToSend> packet =
|
||||
CreateRtpPacket(kSeqNum + kSendSidePacketHistorySize);
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, true);
|
||||
EXPECT_TRUE(hist_.HasRtpPacket(kSeqNum));
|
||||
// First packet should still be there.
|
||||
EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
|
||||
// Advance to where packet can be culled, even if buffer is not full.
|
||||
fake_clock_.AdvanceTimeMilliseconds(1);
|
||||
hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum + 1), kAllowRetransmission,
|
||||
fake_clock_.TimeInMilliseconds());
|
||||
|
||||
EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum, false));
|
||||
}
|
||||
|
||||
TEST_F(RtpPacketHistoryTest, GetBestFittingPacket) {
|
||||
const size_t kTargetSize = 500;
|
||||
hist_.SetStorePacketsStatus(StorageMode::kStore, 10);
|
||||
|
||||
// Add three packets of various sizes.
|
||||
std::unique_ptr<RtpPacketToSend> packet = CreateRtpPacket(kStartSeqNum);
|
||||
packet->SetPayloadSize(kTargetSize);
|
||||
const size_t target_packet_size = packet->size();
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission,
|
||||
fake_clock_.TimeInMilliseconds());
|
||||
packet = CreateRtpPacket(kStartSeqNum + 1);
|
||||
packet->SetPayloadSize(kTargetSize - 1);
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission,
|
||||
fake_clock_.TimeInMilliseconds());
|
||||
packet = CreateRtpPacket(To16u(kStartSeqNum + 2));
|
||||
packet->SetPayloadSize(kTargetSize + 1);
|
||||
hist_.PutRtpPacket(std::move(packet), kAllowRetransmission,
|
||||
fake_clock_.TimeInMilliseconds());
|
||||
|
||||
EXPECT_EQ(target_packet_size,
|
||||
hist_.GetBestFittingPacket(target_packet_size)->size());
|
||||
}
|
||||
} // namespace webrtc
|
||||
|
||||
@ -153,7 +153,8 @@ RTPSender::RTPSender(
|
||||
// be found when paced.
|
||||
if (flexfec_sender) {
|
||||
flexfec_packet_history_.SetStorePacketsStatus(
|
||||
true, kMinFlexfecPacketsToStoreForPacing);
|
||||
RtpPacketHistory::StorageMode::kStore,
|
||||
kMinFlexfecPacketsToStoreForPacing);
|
||||
}
|
||||
}
|
||||
|
||||
@ -600,39 +601,63 @@ size_t RTPSender::SendPadData(size_t bytes,
|
||||
}
|
||||
|
||||
void RTPSender::SetStorePacketsStatus(bool enable, uint16_t number_to_store) {
|
||||
packet_history_.SetStorePacketsStatus(enable, number_to_store);
|
||||
RtpPacketHistory::StorageMode mode =
|
||||
enable ? RtpPacketHistory::StorageMode::kStore
|
||||
: RtpPacketHistory::StorageMode::kDisabled;
|
||||
packet_history_.SetStorePacketsStatus(mode, number_to_store);
|
||||
}
|
||||
|
||||
bool RTPSender::StorePackets() const {
|
||||
return packet_history_.StorePackets();
|
||||
return packet_history_.GetStorageMode() !=
|
||||
RtpPacketHistory::StorageMode::kDisabled;
|
||||
}
|
||||
|
||||
int32_t RTPSender::ReSendPacket(uint16_t packet_id, int64_t min_resend_time) {
|
||||
// Try to find packet in RTP packet history. Also verify RTT here, so that we
|
||||
// don't retransmit too often.
|
||||
|
||||
RTC_DCHECK(retransmission_rate_limiter_);
|
||||
if (paced_sender_) {
|
||||
/// If paced sender is used, don't update send state - that will be done in
|
||||
// the TimeToSendPacket() call.
|
||||
rtc::Optional<RtpPacketHistory::PacketState> stored_packet =
|
||||
packet_history_.GetPacketState(packet_id, true);
|
||||
if (!stored_packet) {
|
||||
// Packet not found.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if we're overusing retransmission bitrate.
|
||||
// TODO(sprang): Add histograms for nack success or failure reasons.
|
||||
if (!retransmission_rate_limiter_->TryUseRate(
|
||||
stored_packet->payload_size)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Convert from TickTime to Clock since capture_time_ms is based on
|
||||
// TickTime.
|
||||
int64_t corrected_capture_tims_ms =
|
||||
stored_packet->capture_time_ms + clock_delta_ms_;
|
||||
paced_sender_->InsertPacket(
|
||||
RtpPacketSender::kNormalPriority, stored_packet->ssrc,
|
||||
stored_packet->rtp_sequence_number, corrected_capture_tims_ms,
|
||||
stored_packet->payload_size, true);
|
||||
|
||||
return stored_packet->payload_size;
|
||||
}
|
||||
|
||||
std::unique_ptr<RtpPacketToSend> packet =
|
||||
packet_history_.GetPacketAndSetSendTime(packet_id, min_resend_time, true);
|
||||
packet_history_.GetPacketAndSetSendTime(packet_id, true);
|
||||
if (!packet) {
|
||||
// Packet not found.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if we're overusing retransmission bitrate.
|
||||
// TODO(sprang): Add histograms for nack success or failure reasons.
|
||||
RTC_DCHECK(retransmission_rate_limiter_);
|
||||
if (!retransmission_rate_limiter_->TryUseRate(packet->size()))
|
||||
if (!retransmission_rate_limiter_->TryUseRate(packet->size())) {
|
||||
return -1;
|
||||
|
||||
if (paced_sender_) {
|
||||
// Convert from TickTime to Clock since capture_time_ms is based on
|
||||
// TickTime.
|
||||
int64_t corrected_capture_tims_ms =
|
||||
packet->capture_time_ms() + clock_delta_ms_;
|
||||
paced_sender_->InsertPacket(RtpPacketSender::kNormalPriority,
|
||||
packet->Ssrc(), packet->SequenceNumber(),
|
||||
corrected_capture_tims_ms,
|
||||
packet->payload_size(), true);
|
||||
|
||||
return packet->size();
|
||||
}
|
||||
|
||||
bool rtx = (RtxStatus() & kRtxRetransmitted) > 0;
|
||||
int32_t packet_size = static_cast<int32_t>(packet->size());
|
||||
if (!PrepareAndSendPacket(std::move(packet), rtx, true, PacedPacketInfo()))
|
||||
@ -710,12 +735,13 @@ bool RTPSender::TimeToSendPacket(uint32_t ssrc,
|
||||
return true;
|
||||
|
||||
std::unique_ptr<RtpPacketToSend> packet;
|
||||
// No need to verify RTT here, it has already been checked before putting the
|
||||
// packet into the pacer. But _do_ update the send time.
|
||||
if (ssrc == SSRC()) {
|
||||
packet = packet_history_.GetPacketAndSetSendTime(sequence_number, 0,
|
||||
retransmission);
|
||||
packet = packet_history_.GetPacketAndSetSendTime(sequence_number, false);
|
||||
} else if (ssrc == FlexfecSsrc()) {
|
||||
packet = flexfec_packet_history_.GetPacketAndSetSendTime(sequence_number, 0,
|
||||
retransmission);
|
||||
packet =
|
||||
flexfec_packet_history_.GetPacketAndSetSendTime(sequence_number, false);
|
||||
}
|
||||
|
||||
if (!packet) {
|
||||
@ -894,9 +920,10 @@ bool RTPSender::SendToNetwork(std::unique_ptr<RtpPacketToSend> packet,
|
||||
if (ssrc == flexfec_ssrc) {
|
||||
// Store FlexFEC packets in the history here, so they can be found
|
||||
// when the pacer calls TimeToSendPacket.
|
||||
flexfec_packet_history_.PutRtpPacket(std::move(packet), storage, false);
|
||||
flexfec_packet_history_.PutRtpPacket(std::move(packet), storage,
|
||||
rtc::nullopt);
|
||||
} else {
|
||||
packet_history_.PutRtpPacket(std::move(packet), storage, false);
|
||||
packet_history_.PutRtpPacket(std::move(packet), storage, rtc::nullopt);
|
||||
}
|
||||
|
||||
paced_sender_->InsertPacket(priority, ssrc, seq_no, corrected_time_ms,
|
||||
@ -937,7 +964,7 @@ bool RTPSender::SendToNetwork(std::unique_ptr<RtpPacketToSend> packet,
|
||||
// packet history (even if send failed).
|
||||
if (storage == kAllowRetransmission) {
|
||||
RTC_DCHECK_EQ(ssrc, SSRC());
|
||||
packet_history_.PutRtpPacket(std::move(packet), storage, true);
|
||||
packet_history_.PutRtpPacket(std::move(packet), storage, now_ms);
|
||||
}
|
||||
|
||||
return sent;
|
||||
|
||||
Reference in New Issue
Block a user