1 /* 2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "modules/congestion_controller/rtp/transport_feedback_adapter.h" 12 13 #include <stdlib.h> 14 15 #include <algorithm> 16 #include <cmath> 17 #include <utility> 18 19 #include "absl/algorithm/container.h" 20 #include "api/units/timestamp.h" 21 #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" 22 #include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" 23 #include "rtc_base/checks.h" 24 #include "rtc_base/logging.h" 25 #include "system_wrappers/include/field_trial.h" 26 27 namespace webrtc { 28 29 constexpr TimeDelta kSendTimeHistoryWindow = TimeDelta::Seconds(60); 30 AddInFlightPacketBytes(const PacketFeedback & packet)31 void InFlightBytesTracker::AddInFlightPacketBytes( 32 const PacketFeedback& packet) { 33 RTC_DCHECK(packet.sent.send_time.IsFinite()); 34 auto it = in_flight_data_.find(packet.network_route); 35 if (it != in_flight_data_.end()) { 36 it->second += packet.sent.size; 37 } else { 38 in_flight_data_.insert({packet.network_route, packet.sent.size}); 39 } 40 } 41 RemoveInFlightPacketBytes(const PacketFeedback & packet)42 void InFlightBytesTracker::RemoveInFlightPacketBytes( 43 const PacketFeedback& packet) { 44 if (packet.sent.send_time.IsInfinite()) 45 return; 46 auto it = in_flight_data_.find(packet.network_route); 47 if (it != in_flight_data_.end()) { 48 RTC_DCHECK_GE(it->second, packet.sent.size); 49 it->second -= packet.sent.size; 50 if (it->second.IsZero()) 51 in_flight_data_.erase(it); 52 } 53 } 54 GetOutstandingData(const rtc::NetworkRoute & network_route) const55 DataSize InFlightBytesTracker::GetOutstandingData( 56 const rtc::NetworkRoute& network_route) const { 57 auto it = in_flight_data_.find(network_route); 58 if (it != in_flight_data_.end()) { 59 return it->second; 60 } else { 61 return DataSize::Zero(); 62 } 63 } 64 65 // Comparator for consistent map with NetworkRoute as key. operator ()(const rtc::NetworkRoute & a,const rtc::NetworkRoute & b) const66 bool InFlightBytesTracker::NetworkRouteComparator::operator()( 67 const rtc::NetworkRoute& a, 68 const rtc::NetworkRoute& b) const { 69 if (a.local.network_id() != b.local.network_id()) 70 return a.local.network_id() < b.local.network_id(); 71 if (a.remote.network_id() != b.remote.network_id()) 72 return a.remote.network_id() < b.remote.network_id(); 73 74 if (a.local.adapter_id() != b.local.adapter_id()) 75 return a.local.adapter_id() < b.local.adapter_id(); 76 if (a.remote.adapter_id() != b.remote.adapter_id()) 77 return a.remote.adapter_id() < b.remote.adapter_id(); 78 79 if (a.local.uses_turn() != b.local.uses_turn()) 80 return a.local.uses_turn() < b.local.uses_turn(); 81 if (a.remote.uses_turn() != b.remote.uses_turn()) 82 return a.remote.uses_turn() < b.remote.uses_turn(); 83 84 return a.connected < b.connected; 85 } 86 87 TransportFeedbackAdapter::TransportFeedbackAdapter() = default; 88 89 AddPacket(const RtpPacketSendInfo & packet_info,size_t overhead_bytes,Timestamp creation_time)90 void TransportFeedbackAdapter::AddPacket(const RtpPacketSendInfo& packet_info, 91 size_t overhead_bytes, 92 Timestamp creation_time) { 93 PacketFeedback packet; 94 packet.creation_time = creation_time; 95 packet.sent.sequence_number = 96 seq_num_unwrapper_.Unwrap(packet_info.transport_sequence_number); 97 packet.sent.size = DataSize::Bytes(packet_info.length + overhead_bytes); 98 packet.sent.audio = packet_info.packet_type == RtpPacketMediaType::kAudio; 99 packet.network_route = network_route_; 100 packet.sent.pacing_info = packet_info.pacing_info; 101 102 while (!history_.empty() && 103 creation_time - history_.begin()->second.creation_time > 104 kSendTimeHistoryWindow) { 105 // TODO(sprang): Warn if erasing (too many) old items? 106 if (history_.begin()->second.sent.sequence_number > last_ack_seq_num_) 107 in_flight_.RemoveInFlightPacketBytes(history_.begin()->second); 108 history_.erase(history_.begin()); 109 } 110 history_.insert(std::make_pair(packet.sent.sequence_number, packet)); 111 } 112 ProcessSentPacket(const rtc::SentPacket & sent_packet)113 absl::optional<SentPacket> TransportFeedbackAdapter::ProcessSentPacket( 114 const rtc::SentPacket& sent_packet) { 115 auto send_time = Timestamp::Millis(sent_packet.send_time_ms); 116 // TODO(srte): Only use one way to indicate that packet feedback is used. 117 if (sent_packet.info.included_in_feedback || sent_packet.packet_id != -1) { 118 int64_t unwrapped_seq_num = 119 seq_num_unwrapper_.Unwrap(sent_packet.packet_id); 120 auto it = history_.find(unwrapped_seq_num); 121 if (it != history_.end()) { 122 bool packet_retransmit = it->second.sent.send_time.IsFinite(); 123 it->second.sent.send_time = send_time; 124 last_send_time_ = std::max(last_send_time_, send_time); 125 // TODO(srte): Don't do this on retransmit. 126 if (!pending_untracked_size_.IsZero()) { 127 if (send_time < last_untracked_send_time_) 128 RTC_LOG(LS_WARNING) 129 << "appending acknowledged data for out of order packet. (Diff: " 130 << ToString(last_untracked_send_time_ - send_time) << " ms.)"; 131 it->second.sent.prior_unacked_data += pending_untracked_size_; 132 pending_untracked_size_ = DataSize::Zero(); 133 } 134 if (!packet_retransmit) { 135 if (it->second.sent.sequence_number > last_ack_seq_num_) 136 in_flight_.AddInFlightPacketBytes(it->second); 137 it->second.sent.data_in_flight = GetOutstandingData(); 138 return it->second.sent; 139 } 140 } 141 } else if (sent_packet.info.included_in_allocation) { 142 if (send_time < last_send_time_) { 143 RTC_LOG(LS_WARNING) << "ignoring untracked data for out of order packet."; 144 } 145 pending_untracked_size_ += 146 DataSize::Bytes(sent_packet.info.packet_size_bytes); 147 last_untracked_send_time_ = std::max(last_untracked_send_time_, send_time); 148 } 149 return absl::nullopt; 150 } 151 152 absl::optional<TransportPacketsFeedback> ProcessTransportFeedback(const rtcp::TransportFeedback & feedback,Timestamp feedback_receive_time)153 TransportFeedbackAdapter::ProcessTransportFeedback( 154 const rtcp::TransportFeedback& feedback, 155 Timestamp feedback_receive_time) { 156 if (feedback.GetPacketStatusCount() == 0) { 157 RTC_LOG(LS_INFO) << "Empty transport feedback packet received."; 158 return absl::nullopt; 159 } 160 161 TransportPacketsFeedback msg; 162 msg.feedback_time = feedback_receive_time; 163 164 msg.prior_in_flight = in_flight_.GetOutstandingData(network_route_); 165 msg.packet_feedbacks = 166 ProcessTransportFeedbackInner(feedback, feedback_receive_time); 167 if (msg.packet_feedbacks.empty()) 168 return absl::nullopt; 169 170 auto it = history_.find(last_ack_seq_num_); 171 if (it != history_.end()) { 172 msg.first_unacked_send_time = it->second.sent.send_time; 173 } 174 msg.data_in_flight = in_flight_.GetOutstandingData(network_route_); 175 176 return msg; 177 } 178 SetNetworkRoute(const rtc::NetworkRoute & network_route)179 void TransportFeedbackAdapter::SetNetworkRoute( 180 const rtc::NetworkRoute& network_route) { 181 network_route_ = network_route; 182 } 183 GetOutstandingData() const184 DataSize TransportFeedbackAdapter::GetOutstandingData() const { 185 return in_flight_.GetOutstandingData(network_route_); 186 } 187 188 std::vector<PacketResult> ProcessTransportFeedbackInner(const rtcp::TransportFeedback & feedback,Timestamp feedback_receive_time)189 TransportFeedbackAdapter::ProcessTransportFeedbackInner( 190 const rtcp::TransportFeedback& feedback, 191 Timestamp feedback_receive_time) { 192 // Add timestamp deltas to a local time base selected on first packet arrival. 193 // This won't be the true time base, but makes it easier to manually inspect 194 // time stamps. 195 if (last_timestamp_.IsInfinite()) { 196 current_offset_ = feedback_receive_time; 197 } else { 198 // TODO(srte): We shouldn't need to do rounding here. 199 const TimeDelta delta = feedback.GetBaseDelta(last_timestamp_) 200 .RoundDownTo(TimeDelta::Millis(1)); 201 // Protect against assigning current_offset_ negative value. 202 if (delta < Timestamp::Zero() - current_offset_) { 203 RTC_LOG(LS_WARNING) << "Unexpected feedback timestamp received."; 204 current_offset_ = feedback_receive_time; 205 } else { 206 current_offset_ += delta; 207 } 208 } 209 last_timestamp_ = feedback.GetBaseTime(); 210 211 std::vector<PacketResult> packet_result_vector; 212 packet_result_vector.reserve(feedback.GetPacketStatusCount()); 213 214 size_t failed_lookups = 0; 215 size_t ignored = 0; 216 TimeDelta packet_offset = TimeDelta::Zero(); 217 for (const auto& packet : feedback.GetAllPackets()) { 218 int64_t seq_num = seq_num_unwrapper_.Unwrap(packet.sequence_number()); 219 220 if (seq_num > last_ack_seq_num_) { 221 // Starts at history_.begin() if last_ack_seq_num_ < 0, since any valid 222 // sequence number is >= 0. 223 for (auto it = history_.upper_bound(last_ack_seq_num_); 224 it != history_.upper_bound(seq_num); ++it) { 225 in_flight_.RemoveInFlightPacketBytes(it->second); 226 } 227 last_ack_seq_num_ = seq_num; 228 } 229 230 auto it = history_.find(seq_num); 231 if (it == history_.end()) { 232 ++failed_lookups; 233 continue; 234 } 235 236 if (it->second.sent.send_time.IsInfinite()) { 237 // TODO(srte): Fix the tests that makes this happen and make this a 238 // DCHECK. 239 RTC_DLOG(LS_ERROR) 240 << "Received feedback before packet was indicated as sent"; 241 continue; 242 } 243 244 PacketFeedback packet_feedback = it->second; 245 if (packet.received()) { 246 packet_offset += packet.delta(); 247 packet_feedback.receive_time = 248 current_offset_ + packet_offset.RoundDownTo(TimeDelta::Millis(1)); 249 // Note: Lost packets are not removed from history because they might be 250 // reported as received by a later feedback. 251 history_.erase(it); 252 } 253 if (packet_feedback.network_route == network_route_) { 254 PacketResult result; 255 result.sent_packet = packet_feedback.sent; 256 result.receive_time = packet_feedback.receive_time; 257 packet_result_vector.push_back(result); 258 } else { 259 ++ignored; 260 } 261 } 262 263 if (failed_lookups > 0) { 264 RTC_LOG(LS_WARNING) << "Failed to lookup send time for " << failed_lookups 265 << " packet" << (failed_lookups > 1 ? "s" : "") 266 << ". Send time history too small?"; 267 } 268 if (ignored > 0) { 269 RTC_LOG(LS_INFO) << "Ignoring " << ignored 270 << " packets because they were sent on a different route."; 271 } 272 273 return packet_result_vector; 274 } 275 276 } // namespace webrtc 277