/* * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "modules/rtp_rtcp/source/rtp_packet_history.h" #include #include #include #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "system_wrappers/include/clock.h" namespace webrtc { namespace { 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; } // namespace constexpr size_t RtpPacketHistory::kMaxCapacity; RtpPacketHistory::StoredPacket::StoredPacket() = default; RtpPacketHistory::StoredPacket::StoredPacket(StoredPacket&&) = default; RtpPacketHistory::StoredPacket& RtpPacketHistory::StoredPacket::operator=( RtpPacketHistory::StoredPacket&&) = default; RtpPacketHistory::StoredPacket::~StoredPacket() = default; RtpPacketHistory::RtpPacketHistory(Clock* clock) : clock_(clock), store_(false), prev_index_(0), 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); RTC_DCHECK_LE(number_to_store, kMaxCapacity); store_ = true; stored_packets_.resize(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 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 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 RtpPacketHistory::GetPacket(int index) const { const RtpPacketToSend& stored = *stored_packets_[index].packet; return std::unique_ptr(new RtpPacketToSend(stored)); } std::unique_ptr 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(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::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(i); } } return best_index; } void RtpPacketHistory::SetRtt(int64_t rtt_ms) { rtc::CritScope cs(&critsect_); RTC_DCHECK_GE(rtt_ms, 0); rtt_ms_ = rtt_ms; } } // namespace webrtc