diff --git a/modules/rtp_rtcp/source/rtp_packet_history.cc b/modules/rtp_rtcp/source/rtp_packet_history.cc index b3649f8a39..2bd06c48be 100644 --- a/modules/rtp_rtcp/source/rtp_packet_history.cc +++ b/modules/rtp_rtcp/source/rtp_packet_history.cc @@ -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& 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 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; +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 packet, + StorageType type, + rtc::Optional 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 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(*packet.packet); +} + +rtc::Optional 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 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::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(*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 RtpPacketHistory::RemovePacket( + StoredPacketIterator packet_it) { + // Move the packet out from the StoredPacket container. + std::unique_ptr 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 diff --git a/modules/rtp_rtcp/source/rtp_packet_history.h b/modules/rtp_rtcp/source/rtp_packet_history.h index e9d5808b37..bfcdcc8f1a 100644 --- a/modules/rtp_rtcp/source/rtp_packet_history.h +++ b/modules/rtp_rtcp/source/rtp_packet_history.h @@ -11,6 +11,7 @@ #ifndef MODULES_RTP_RTCP_SOURCE_RTP_PACKET_HISTORY_H_ #define MODULES_RTP_RTCP_SOURCE_RTP_PACKET_HISTORY_H_ +#include #include #include @@ -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 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 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 packet, + StorageType type, + rtc::Optional 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 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 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 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 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 packet; }; - std::unique_ptr 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::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 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 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 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 stored_packets_ RTC_GUARDED_BY(critsect_); - int64_t rtt_ms_ RTC_GUARDED_BY(critsect_); RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RtpPacketHistory); }; } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc b/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc index cdfd3a0a25..c7cd18085a 100644 --- a/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc @@ -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(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 packet = CreateRtpPacket(kSeqNum); - hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, false); + EXPECT_EQ(StorageMode::kDisabled, hist_.GetStorageMode()); + std::unique_ptr 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 packet = CreateRtpPacket(kSeqNum); + hist_.SetStorePacketsStatus(StorageMode::kStore, 10); + std::unique_ptr 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 packet = CreateRtpPacket(kSeqNum); + std::unique_ptr 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 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 packet = CreateRtpPacket(kSeqNum); + std::unique_ptr 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 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 packet = CreateRtpPacket(kSeqNum); + std::unique_ptr 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 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 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 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 packet = CreateRtpPacket(kSeqNum); + std::unique_ptr 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 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 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 packet = CreateRtpPacket(kSeqNum); - rtc::CopyOnWriteBuffer buffer = packet->Buffer(); - hist_.PutRtpPacket(std::move(packet), kAllowRetransmission, false); + std::unique_ptr 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc index f8ef1300dd..5b5a85dbcf 100644 --- a/modules/rtp_rtcp/source/rtp_sender.cc +++ b/modules/rtp_rtcp/source/rtp_sender.cc @@ -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 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 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(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 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 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 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;