| Index: webrtc/voice_engine/transport_feedback_packet_loss_tracker.cc
|
| diff --git a/webrtc/voice_engine/transport_feedback_packet_loss_tracker.cc b/webrtc/voice_engine/transport_feedback_packet_loss_tracker.cc
|
| index c05b044814d9f5a2916d985d40a1a7f8da7fe9ff..ea38f149da0d61a0184ad611f8a2f4fa479ae94b 100644
|
| --- a/webrtc/voice_engine/transport_feedback_packet_loss_tracker.cc
|
| +++ b/webrtc/voice_engine/transport_feedback_packet_loss_tracker.cc
|
| @@ -19,9 +19,6 @@
|
|
|
| namespace {
|
| constexpr uint16_t kSeqNumHalf = 0x8000u;
|
| -constexpr uint16_t kSeqNumQuarter = kSeqNumHalf / 2;
|
| -constexpr size_t kMaxConsecutiveOldReports = 4;
|
| -
|
| void UpdateCounter(size_t* counter, bool increment) {
|
| if (increment) {
|
| RTC_DCHECK_LT(*counter, std::numeric_limits<std::size_t>::max());
|
| @@ -37,25 +34,25 @@ void UpdateCounter(size_t* counter, bool increment) {
|
| namespace webrtc {
|
|
|
| TransportFeedbackPacketLossTracker::TransportFeedbackPacketLossTracker(
|
| - size_t max_window_size,
|
| - size_t plr_min_num_packets,
|
| - size_t rplr_min_num_pairs)
|
| - : max_window_size_(max_window_size),
|
| + size_t max_acked_packets,
|
| + size_t plr_min_num_acked_packets,
|
| + size_t rplr_min_num_acked_pairs)
|
| + : max_acked_packets_(max_acked_packets),
|
| ref_packet_status_(packet_status_window_.begin()),
|
| - plr_state_(plr_min_num_packets),
|
| - rplr_state_(rplr_min_num_pairs) {
|
| - RTC_DCHECK_GT(plr_min_num_packets, 0);
|
| - RTC_DCHECK_GE(max_window_size, plr_min_num_packets);
|
| - RTC_DCHECK_LE(max_window_size, kSeqNumHalf);
|
| - RTC_DCHECK_GT(rplr_min_num_pairs, 0);
|
| - RTC_DCHECK_GT(max_window_size, rplr_min_num_pairs);
|
| + plr_state_(plr_min_num_acked_packets),
|
| + rplr_state_(rplr_min_num_acked_pairs) {
|
| + RTC_DCHECK_GT(plr_min_num_acked_packets, 0);
|
| + RTC_DCHECK_GE(max_acked_packets, plr_min_num_acked_packets);
|
| + RTC_DCHECK_LE(max_acked_packets, kSeqNumHalf);
|
| + RTC_DCHECK_GT(rplr_min_num_acked_pairs, 0);
|
| + RTC_DCHECK_GT(max_acked_packets, rplr_min_num_acked_pairs);
|
| Reset();
|
| }
|
|
|
| void TransportFeedbackPacketLossTracker::Reset() {
|
| + acked_packets_ = 0;
|
| plr_state_.Reset();
|
| rplr_state_.Reset();
|
| - num_consecutive_old_reports_ = 0;
|
| packet_status_window_.clear();
|
| ref_packet_status_ = packet_status_window_.begin();
|
| }
|
| @@ -65,13 +62,33 @@ uint16_t TransportFeedbackPacketLossTracker::ReferenceSequenceNumber() const {
|
| return ref_packet_status_->first;
|
| }
|
|
|
| -bool TransportFeedbackPacketLossTracker::IsOldSequenceNumber(
|
| - uint16_t seq_num) const {
|
| - if (packet_status_window_.empty()) {
|
| - return false;
|
| +uint16_t TransportFeedbackPacketLossTracker::NewestSequenceNumber() const {
|
| + RTC_DCHECK(!packet_status_window_.empty());
|
| + return PreviousPacketStatus(packet_status_window_.end())->first;
|
| +}
|
| +
|
| +void TransportFeedbackPacketLossTracker::OnPacketAdded(uint16_t seq_num) {
|
| + if (packet_status_window_.find(seq_num) != packet_status_window_.end() ||
|
| + (!packet_status_window_.empty() &&
|
| + ForwardDiff(seq_num, NewestSequenceNumber()) <= kSeqNumHalf)) {
|
| + // The only way for these two to happen is when the stream lies dormant for
|
| + // long enough for the sequence numbers to wrap. Everything in the window in
|
| + // such a case would be too old to use.
|
| + Reset();
|
| + }
|
| +
|
| + // Shift older packets out of window.
|
| + while (!packet_status_window_.empty() &&
|
| + ForwardDiff(ref_packet_status_->first, seq_num) >= kSeqNumHalf) {
|
| + RemoveOldestPacketStatus();
|
| + }
|
| +
|
| + packet_status_window_.insert(packet_status_window_.end(),
|
| + std::make_pair(seq_num, PacketStatus::Unacked));
|
| +
|
| + if (packet_status_window_.size() == 1) {
|
| + ref_packet_status_ = packet_status_window_.cbegin();
|
| }
|
| - const uint16_t diff = ForwardDiff(ReferenceSequenceNumber(), seq_num);
|
| - return diff >= 3 * kSeqNumQuarter;
|
| }
|
|
|
| void TransportFeedbackPacketLossTracker::OnReceivedTransportFeedback(
|
| @@ -79,41 +96,16 @@ void TransportFeedbackPacketLossTracker::OnReceivedTransportFeedback(
|
| const auto& fb_vector = feedback.GetStatusVector();
|
| const uint16_t base_seq_num = feedback.GetBaseSequence();
|
|
|
| - if (IsOldSequenceNumber(base_seq_num)) {
|
| - ++num_consecutive_old_reports_;
|
| - if (num_consecutive_old_reports_ <= kMaxConsecutiveOldReports) {
|
| - // If the number consecutive old reports have not exceed a threshold, we
|
| - // consider this packet as a late arrival. We could consider adding it to
|
| - // |packet_status_window_|, but in current implementation, we simply
|
| - // ignore it.
|
| - return;
|
| - }
|
| - // If we see several consecutive older reports, we assume that we've not
|
| - // received reports for an exceedingly long time, and do a reset.
|
| - Reset();
|
| - RTC_DCHECK(!IsOldSequenceNumber(base_seq_num));
|
| - } else {
|
| - num_consecutive_old_reports_ = 0;
|
| - }
|
| -
|
| uint16_t seq_num = base_seq_num;
|
| for (size_t i = 0; i < fb_vector.size(); ++i, ++seq_num) {
|
| - // Remove the oldest feedbacks so that the distance between the oldest and
|
| - // the packet to be added does not exceed or equal to half of total sequence
|
| - // numbers.
|
| - while (!packet_status_window_.empty() &&
|
| - ForwardDiff(ReferenceSequenceNumber(), seq_num) >= kSeqNumHalf) {
|
| - RemoveOldestPacketStatus();
|
| - }
|
| -
|
| - const bool received =
|
| - fb_vector[i] !=
|
| - webrtc::rtcp::TransportFeedback::StatusSymbol::kNotReceived;
|
| - InsertPacketStatus(seq_num, received);
|
| -
|
| - while (packet_status_window_.size() > max_window_size_) {
|
| - // Make sure that the window holds at most |max_window_size_| items.
|
| - RemoveOldestPacketStatus();
|
| + const auto& it = packet_status_window_.find(seq_num);
|
| +
|
| + // Packets which aren't at least marked as unacked either do not belong to
|
| + // this media stream, or have been shifted out of window.
|
| + if (it != packet_status_window_.end()) {
|
| + const bool received = fb_vector[i] !=
|
| + webrtc::rtcp::TransportFeedback::StatusSymbol::kNotReceived;
|
| + RecordFeedback(it, received);
|
| }
|
| }
|
| }
|
| @@ -128,25 +120,38 @@ TransportFeedbackPacketLossTracker::GetRecoverablePacketLossRate() const {
|
| return rplr_state_.GetMetric();
|
| }
|
|
|
| -void TransportFeedbackPacketLossTracker::InsertPacketStatus(uint16_t seq_num,
|
| - bool received) {
|
| - const auto& ret =
|
| - packet_status_window_.insert(std::make_pair(seq_num, received));
|
| - if (!ret.second) {
|
| - if (!ret.first->second && received) {
|
| +void TransportFeedbackPacketLossTracker::RecordFeedback(
|
| + PacketStatusMap::iterator it,
|
| + bool received) {
|
| + if (it->second != PacketStatus::Unacked) {
|
| + // Normally, packets are sent (inserted into window as "unacked"), then we
|
| + // receive one feedback for them.
|
| + // But it is possible that a packet would receive two feedbacks. Then:
|
| + if (it->second == PacketStatus::Lost && received) {
|
| // If older status said that the packet was lost but newer one says it
|
| // is received, we take the newer one.
|
| - UpdateMetrics(ret.first, false);
|
| - ret.first->second = received;
|
| + UpdateMetrics(it, false);
|
| + it->second = PacketStatus::Unacked; // For clarity; overwritten shortly.
|
| } else {
|
| // If the value is unchanged or if older status said that the packet was
|
| // received but the newer one says it is lost, we ignore it.
|
| + // The standard allows for previously-reported packets to carry
|
| + // no report when the reports overlap, which also looks like the
|
| + // packet is being reported as lost.
|
| return;
|
| }
|
| }
|
| - UpdateMetrics(ret.first, true);
|
| - if (packet_status_window_.size() == 1)
|
| - ref_packet_status_ = ret.first;
|
| +
|
| + // Change from UNACKED to RECEIVED/LOST.
|
| + it->second = received ? PacketStatus::Received : PacketStatus::Lost;
|
| + UpdateMetrics(it, true);
|
| +
|
| + // Remove packets from the beginning of the window until maximum-acked
|
| + // is observed again. Note that multiple sent-but-unacked packets might
|
| + // be removed before we reach the first acked (whether as received or as
|
| + // lost) packet.
|
| + while (acked_packets_ > max_acked_packets_)
|
| + RemoveOldestPacketStatus();
|
| }
|
|
|
| void TransportFeedbackPacketLossTracker::RemoveOldestPacketStatus() {
|
| @@ -157,34 +162,54 @@ void TransportFeedbackPacketLossTracker::RemoveOldestPacketStatus() {
|
| }
|
|
|
| void TransportFeedbackPacketLossTracker::UpdateMetrics(
|
| - PacketStatusIterator it,
|
| + ConstPacketStatusIterator it,
|
| bool apply /* false = undo */) {
|
| RTC_DCHECK(it != packet_status_window_.end());
|
| + // Metrics are dependent on feedbacks from the other side. We don't want
|
| + // to update the metrics each time a packet is sent, except for the case
|
| + // when it shifts old sent-but-unacked-packets out of window.
|
| + RTC_DCHECK(!apply || it->second != PacketStatus::Unacked);
|
| +
|
| + if (it->second != PacketStatus::Unacked) {
|
| + UpdateCounter(&acked_packets_, apply);
|
| + }
|
| +
|
| UpdatePlr(it, apply);
|
| UpdateRplr(it, apply);
|
| }
|
|
|
| void TransportFeedbackPacketLossTracker::UpdatePlr(
|
| - PacketStatusIterator it,
|
| + ConstPacketStatusIterator it,
|
| bool apply /* false = undo */) {
|
| - // Record or undo reception status of currently handled packet.
|
| - if (it->second) {
|
| - UpdateCounter(&plr_state_.num_received_packets_, apply);
|
| - } else {
|
| - UpdateCounter(&plr_state_.num_lost_packets_, apply);
|
| + switch (it->second) {
|
| + case PacketStatus::Unacked:
|
| + return;
|
| + case PacketStatus::Received:
|
| + UpdateCounter(&plr_state_.num_received_packets_, apply);
|
| + break;
|
| + case PacketStatus::Lost:
|
| + UpdateCounter(&plr_state_.num_lost_packets_, apply);
|
| + break;
|
| + default:
|
| + RTC_NOTREACHED();
|
| }
|
| }
|
|
|
| void TransportFeedbackPacketLossTracker::UpdateRplr(
|
| - PacketStatusIterator it,
|
| + ConstPacketStatusIterator it,
|
| bool apply /* false = undo */) {
|
| - // Previous packet and current packet might compose a known pair.
|
| - // If so, the RPLR state needs to be updated accordingly.
|
| + if (it->second == PacketStatus::Unacked) {
|
| + // Unacked packets cannot compose a pair.
|
| + return;
|
| + }
|
| +
|
| + // Previous packet and current packet might compose a pair.
|
| if (it != ref_packet_status_) {
|
| const auto& prev = PreviousPacketStatus(it);
|
| - if (prev->first == static_cast<uint16_t>(it->first - 1)) {
|
| - UpdateCounter(&rplr_state_.num_known_pairs_, apply);
|
| - if (!prev->second && it->second) {
|
| + if (prev->second != PacketStatus::Unacked) {
|
| + UpdateCounter(&rplr_state_.num_acked_pairs_, apply);
|
| + if (prev->second == PacketStatus::Lost &&
|
| + it->second == PacketStatus::Received) {
|
| UpdateCounter(
|
| &rplr_state_.num_recoverable_losses_, apply);
|
| }
|
| @@ -192,20 +217,20 @@ void TransportFeedbackPacketLossTracker::UpdateRplr(
|
| }
|
|
|
| // Current packet and next packet might compose a pair.
|
| - // If so, the RPLR state needs to be updated accordingly.
|
| const auto& next = NextPacketStatus(it);
|
| if (next != packet_status_window_.end() &&
|
| - next->first == static_cast<uint16_t>(it->first + 1)) {
|
| - UpdateCounter(&rplr_state_.num_known_pairs_, apply);
|
| - if (!it->second && next->second) {
|
| + next->second != PacketStatus::Unacked) {
|
| + UpdateCounter(&rplr_state_.num_acked_pairs_, apply);
|
| + if (it->second == PacketStatus::Lost &&
|
| + next->second == PacketStatus::Received) {
|
| UpdateCounter(&rplr_state_.num_recoverable_losses_, apply);
|
| }
|
| }
|
| }
|
|
|
| -TransportFeedbackPacketLossTracker::PacketStatusIterator
|
| +TransportFeedbackPacketLossTracker::ConstPacketStatusIterator
|
| TransportFeedbackPacketLossTracker::PreviousPacketStatus(
|
| - PacketStatusIterator it) {
|
| + ConstPacketStatusIterator it) const {
|
| RTC_DCHECK(it != ref_packet_status_);
|
| if (it == packet_status_window_.end()) {
|
| // This is to make PreviousPacketStatus(packet_status_window_.end()) point
|
| @@ -221,8 +246,9 @@ TransportFeedbackPacketLossTracker::PreviousPacketStatus(
|
| return --it;
|
| }
|
|
|
| -TransportFeedbackPacketLossTracker::PacketStatusIterator
|
| -TransportFeedbackPacketLossTracker::NextPacketStatus(PacketStatusIterator it) {
|
| +TransportFeedbackPacketLossTracker::ConstPacketStatusIterator
|
| +TransportFeedbackPacketLossTracker::NextPacketStatus(
|
| + ConstPacketStatusIterator it) const {
|
| RTC_DCHECK(it != packet_status_window_.end());
|
| ++it;
|
| if (it == packet_status_window_.end()) {
|
| @@ -244,26 +270,35 @@ TransportFeedbackPacketLossTracker::NextPacketStatus(PacketStatusIterator it) {
|
| // error after long period, we can remove the fuzzer test, and move this method
|
| // to unit test.
|
| void TransportFeedbackPacketLossTracker::Validate() const { // Testing only!
|
| - RTC_CHECK_LE(packet_status_window_.size(), max_window_size_);
|
| - RTC_CHECK_EQ(packet_status_window_.size(),
|
| - plr_state_.num_lost_packets_ + plr_state_.num_received_packets_);
|
| + RTC_CHECK_EQ(plr_state_.num_received_packets_ + plr_state_.num_lost_packets_,
|
| + acked_packets_);
|
| + RTC_CHECK_LE(acked_packets_, packet_status_window_.size());
|
| + RTC_CHECK_LE(acked_packets_, max_acked_packets_);
|
| RTC_CHECK_LE(rplr_state_.num_recoverable_losses_,
|
| - rplr_state_.num_known_pairs_);
|
| - RTC_CHECK_LE(rplr_state_.num_known_pairs_,
|
| - packet_status_window_.size() - 1);
|
| + rplr_state_.num_acked_pairs_);
|
| + RTC_CHECK_LE(rplr_state_.num_acked_pairs_, acked_packets_ - 1);
|
|
|
| + size_t unacked_packets = 0;
|
| size_t received_packets = 0;
|
| size_t lost_packets = 0;
|
| - size_t known_status_pairs = 0;
|
| + size_t acked_pairs = 0;
|
| size_t recoverable_losses = 0;
|
|
|
| if (!packet_status_window_.empty()) {
|
| - PacketStatusIterator it = ref_packet_status_;
|
| + ConstPacketStatusIterator it = ref_packet_status_;
|
| do {
|
| - if (it->second) {
|
| - ++received_packets;
|
| - } else {
|
| - ++lost_packets;
|
| + switch (it->second) {
|
| + case PacketStatus::Unacked:
|
| + ++unacked_packets;
|
| + break;
|
| + case PacketStatus::Received:
|
| + ++received_packets;
|
| + break;
|
| + case PacketStatus::Lost:
|
| + ++lost_packets;
|
| + break;
|
| + default:
|
| + RTC_NOTREACHED();
|
| }
|
|
|
| auto next = std::next(it);
|
| @@ -271,9 +306,11 @@ void TransportFeedbackPacketLossTracker::Validate() const { // Testing only!
|
| next = packet_status_window_.begin();
|
|
|
| if (next != ref_packet_status_ &&
|
| - next->first == static_cast<uint16_t>(it->first + 1)) {
|
| - ++known_status_pairs;
|
| - if (!it->second && next->second)
|
| + it->second != PacketStatus::Unacked &&
|
| + next->second != PacketStatus::Unacked) {
|
| + ++acked_pairs;
|
| + if (it->second == PacketStatus::Lost &&
|
| + next->second == PacketStatus::Received)
|
| ++recoverable_losses;
|
| }
|
|
|
| @@ -286,14 +323,16 @@ void TransportFeedbackPacketLossTracker::Validate() const { // Testing only!
|
|
|
| RTC_CHECK_EQ(plr_state_.num_received_packets_, received_packets);
|
| RTC_CHECK_EQ(plr_state_.num_lost_packets_, lost_packets);
|
| - RTC_CHECK_EQ(rplr_state_.num_known_pairs_, known_status_pairs);
|
| + RTC_CHECK_EQ(packet_status_window_.size(),
|
| + unacked_packets + received_packets + lost_packets);
|
| + RTC_CHECK_EQ(rplr_state_.num_acked_pairs_, acked_pairs);
|
| RTC_CHECK_EQ(rplr_state_.num_recoverable_losses_, recoverable_losses);
|
| }
|
|
|
| rtc::Optional<float>
|
| TransportFeedbackPacketLossTracker::PlrState::GetMetric() const {
|
| const size_t total = num_lost_packets_ + num_received_packets_;
|
| - if (total < min_num_packets_) {
|
| + if (total < min_num_acked_packets_) {
|
| return rtc::Optional<float>();
|
| } else {
|
| return rtc::Optional<float>(
|
| @@ -303,11 +342,11 @@ TransportFeedbackPacketLossTracker::PlrState::GetMetric() const {
|
|
|
| rtc::Optional<float>
|
| TransportFeedbackPacketLossTracker::RplrState::GetMetric() const {
|
| - if (num_known_pairs_ < min_num_pairs_) {
|
| + if (num_acked_pairs_ < min_num_acked_pairs_) {
|
| return rtc::Optional<float>();
|
| } else {
|
| return rtc::Optional<float>(
|
| - static_cast<float>(num_recoverable_losses_) / num_known_pairs_);
|
| + static_cast<float>(num_recoverable_losses_) / num_acked_pairs_);
|
| }
|
| }
|
|
|
|
|