sprang | 5e023eb | 2015-09-14 06:42:43 -0700 | [diff] [blame] | 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 | |
Stefan Holmer | 280de9e | 2016-09-30 10:06:51 +0200 | [diff] [blame] | 11 | #include "webrtc/modules/congestion_controller/transport_feedback_adapter.h" |
mflodman | 0e7e259 | 2015-11-12 21:02:42 -0800 | [diff] [blame] | 12 | |
stefan | 9e117c5e1 | 2017-08-16 08:16:25 -0700 | [diff] [blame] | 13 | #include <algorithm> |
| 14 | |
Stefan Holmer | 280de9e | 2016-09-30 10:06:51 +0200 | [diff] [blame] | 15 | #include "webrtc/modules/congestion_controller/delay_based_bwe.h" |
sprang | 5e023eb | 2015-09-14 06:42:43 -0700 | [diff] [blame] | 16 | #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" |
Edward Lemur | c20978e | 2017-07-06 19:44:34 +0200 | [diff] [blame] | 17 | #include "webrtc/rtc_base/checks.h" |
| 18 | #include "webrtc/rtc_base/logging.h" |
| 19 | #include "webrtc/rtc_base/mod_ops.h" |
nisse | 284542b | 2017-01-10 08:58:32 -0800 | [diff] [blame] | 20 | #include "webrtc/system_wrappers/include/field_trial.h" |
sprang | 5e023eb | 2015-09-14 06:42:43 -0700 | [diff] [blame] | 21 | |
| 22 | namespace webrtc { |
| 23 | |
| 24 | const int64_t kNoTimestamp = -1; |
stefan | e9ad271 | 2017-02-10 06:09:28 -0800 | [diff] [blame] | 25 | const int64_t kSendTimeHistoryWindowMs = 60000; |
sprang | 5e023eb | 2015-09-14 06:42:43 -0700 | [diff] [blame] | 26 | const int64_t kBaseTimestampScaleFactor = |
| 27 | rtcp::TransportFeedback::kDeltaScaleFactor * (1 << 8); |
| 28 | const int64_t kBaseTimestampRangeSizeUs = kBaseTimestampScaleFactor * (1 << 24); |
| 29 | |
elad.alon | 61ce37e | 2017-03-09 07:09:31 -0800 | [diff] [blame] | 30 | TransportFeedbackAdapter::TransportFeedbackAdapter(const Clock* clock) |
sprang | c1b57a1 | 2017-02-28 08:50:47 -0800 | [diff] [blame] | 31 | : send_side_bwe_with_overhead_( |
| 32 | webrtc::field_trial::IsEnabled("WebRTC-SendSideBwe-WithOverhead")), |
elad.alon | 0fe1216 | 2017-01-31 05:48:37 -0800 | [diff] [blame] | 33 | transport_overhead_bytes_per_packet_(0), |
michaelt | f4caaab | 2017-01-16 23:55:07 -0800 | [diff] [blame] | 34 | send_time_history_(clock, kSendTimeHistoryWindowMs), |
sprang | 5e023eb | 2015-09-14 06:42:43 -0700 | [diff] [blame] | 35 | clock_(clock), |
| 36 | current_offset_ms_(kNoTimestamp), |
Stefan Holmer | 9ea46b5 | 2017-03-15 12:40:25 +0100 | [diff] [blame] | 37 | last_timestamp_us_(kNoTimestamp), |
| 38 | local_net_id_(0), |
| 39 | remote_net_id_(0) {} |
sprang | 5e023eb | 2015-09-14 06:42:43 -0700 | [diff] [blame] | 40 | |
elad.alon | d12a8e1 | 2017-03-23 11:04:48 -0700 | [diff] [blame] | 41 | TransportFeedbackAdapter::~TransportFeedbackAdapter() { |
| 42 | RTC_DCHECK(observers_.empty()); |
| 43 | } |
sprang | 5e023eb | 2015-09-14 06:42:43 -0700 | [diff] [blame] | 44 | |
elad.alon | d12a8e1 | 2017-03-23 11:04:48 -0700 | [diff] [blame] | 45 | void TransportFeedbackAdapter::RegisterPacketFeedbackObserver( |
| 46 | PacketFeedbackObserver* observer) { |
| 47 | rtc::CritScope cs(&observers_lock_); |
| 48 | RTC_DCHECK(observer); |
| 49 | RTC_DCHECK(std::find(observers_.begin(), observers_.end(), observer) == |
| 50 | observers_.end()); |
| 51 | observers_.push_back(observer); |
| 52 | } |
| 53 | |
| 54 | void TransportFeedbackAdapter::DeRegisterPacketFeedbackObserver( |
| 55 | PacketFeedbackObserver* observer) { |
| 56 | rtc::CritScope cs(&observers_lock_); |
| 57 | RTC_DCHECK(observer); |
| 58 | const auto it = std::find(observers_.begin(), observers_.end(), observer); |
| 59 | RTC_DCHECK(it != observers_.end()); |
| 60 | observers_.erase(it); |
| 61 | } |
| 62 | |
| 63 | void TransportFeedbackAdapter::AddPacket(uint32_t ssrc, |
| 64 | uint16_t sequence_number, |
stefan | bbe876f | 2015-10-23 02:05:40 -0700 | [diff] [blame] | 65 | size_t length, |
philipel | 8aadd50 | 2017-02-23 02:56:13 -0800 | [diff] [blame] | 66 | const PacedPacketInfo& pacing_info) { |
elad.alon | d12a8e1 | 2017-03-23 11:04:48 -0700 | [diff] [blame] | 67 | { |
| 68 | rtc::CritScope cs(&lock_); |
| 69 | if (send_side_bwe_with_overhead_) { |
| 70 | length += transport_overhead_bytes_per_packet_; |
| 71 | } |
| 72 | const int64_t creation_time_ms = clock_->TimeInMilliseconds(); |
| 73 | send_time_history_.AddAndRemoveOld( |
| 74 | PacketFeedback(creation_time_ms, sequence_number, length, local_net_id_, |
| 75 | remote_net_id_, pacing_info)); |
nisse | 284542b | 2017-01-10 08:58:32 -0800 | [diff] [blame] | 76 | } |
elad.alon | d12a8e1 | 2017-03-23 11:04:48 -0700 | [diff] [blame] | 77 | |
| 78 | { |
| 79 | rtc::CritScope cs(&observers_lock_); |
| 80 | for (auto observer : observers_) { |
| 81 | observer->OnPacketAdded(ssrc, sequence_number); |
| 82 | } |
| 83 | } |
sprang | 5e023eb | 2015-09-14 06:42:43 -0700 | [diff] [blame] | 84 | } |
| 85 | |
stefan | bbe876f | 2015-10-23 02:05:40 -0700 | [diff] [blame] | 86 | void TransportFeedbackAdapter::OnSentPacket(uint16_t sequence_number, |
| 87 | int64_t send_time_ms) { |
stefan | c1aeaf0 | 2015-10-15 07:26:07 -0700 | [diff] [blame] | 88 | rtc::CritScope cs(&lock_); |
stefan | bbe876f | 2015-10-23 02:05:40 -0700 | [diff] [blame] | 89 | send_time_history_.OnSentPacket(sequence_number, send_time_ms); |
stefan | c1aeaf0 | 2015-10-15 07:26:07 -0700 | [diff] [blame] | 90 | } |
| 91 | |
nisse | 284542b | 2017-01-10 08:58:32 -0800 | [diff] [blame] | 92 | void TransportFeedbackAdapter::SetTransportOverhead( |
| 93 | int transport_overhead_bytes_per_packet) { |
| 94 | rtc::CritScope cs(&lock_); |
| 95 | transport_overhead_bytes_per_packet_ = transport_overhead_bytes_per_packet; |
| 96 | } |
| 97 | |
Stefan Holmer | 9ea46b5 | 2017-03-15 12:40:25 +0100 | [diff] [blame] | 98 | void TransportFeedbackAdapter::SetNetworkIds(uint16_t local_id, |
| 99 | uint16_t remote_id) { |
philipel | cb9ba30 | 2017-03-07 06:30:59 -0800 | [diff] [blame] | 100 | rtc::CritScope cs(&lock_); |
Stefan Holmer | 9ea46b5 | 2017-03-15 12:40:25 +0100 | [diff] [blame] | 101 | local_net_id_ = local_id; |
| 102 | remote_net_id_ = remote_id; |
philipel | cb9ba30 | 2017-03-07 06:30:59 -0800 | [diff] [blame] | 103 | } |
| 104 | |
elad.alon | f949000 | 2017-03-06 05:32:21 -0800 | [diff] [blame] | 105 | std::vector<PacketFeedback> TransportFeedbackAdapter::GetPacketFeedbackVector( |
sprang | 5e023eb | 2015-09-14 06:42:43 -0700 | [diff] [blame] | 106 | const rtcp::TransportFeedback& feedback) { |
| 107 | int64_t timestamp_us = feedback.GetBaseTimeUs(); |
stefan | 9e117c5e1 | 2017-08-16 08:16:25 -0700 | [diff] [blame] | 108 | int64_t now_ms = clock_->TimeInMilliseconds(); |
sprang | 5e023eb | 2015-09-14 06:42:43 -0700 | [diff] [blame] | 109 | // Add timestamp deltas to a local time base selected on first packet arrival. |
| 110 | // This won't be the true time base, but makes it easier to manually inspect |
| 111 | // time stamps. |
| 112 | if (last_timestamp_us_ == kNoTimestamp) { |
stefan | 9e117c5e1 | 2017-08-16 08:16:25 -0700 | [diff] [blame] | 113 | current_offset_ms_ = now_ms; |
sprang | 5e023eb | 2015-09-14 06:42:43 -0700 | [diff] [blame] | 114 | } else { |
| 115 | int64_t delta = timestamp_us - last_timestamp_us_; |
| 116 | |
| 117 | // Detect and compensate for wrap-arounds in base time. |
| 118 | if (std::abs(delta - kBaseTimestampRangeSizeUs) < std::abs(delta)) { |
| 119 | delta -= kBaseTimestampRangeSizeUs; // Wrap backwards. |
| 120 | } else if (std::abs(delta + kBaseTimestampRangeSizeUs) < std::abs(delta)) { |
| 121 | delta += kBaseTimestampRangeSizeUs; // Wrap forwards. |
| 122 | } |
| 123 | |
| 124 | current_offset_ms_ += delta / 1000; |
| 125 | } |
| 126 | last_timestamp_us_ = timestamp_us; |
| 127 | |
elad.alon | f949000 | 2017-03-06 05:32:21 -0800 | [diff] [blame] | 128 | std::vector<PacketFeedback> packet_feedback_vector; |
danilchap | c1b693c | 2017-04-20 08:23:41 -0700 | [diff] [blame] | 129 | if (feedback.GetPacketStatusCount() == 0) { |
stefan | e3a5567 | 2017-02-13 09:08:22 -0800 | [diff] [blame] | 130 | LOG(LS_INFO) << "Empty transport feedback packet received."; |
| 131 | return packet_feedback_vector; |
| 132 | } |
danilchap | c1b693c | 2017-04-20 08:23:41 -0700 | [diff] [blame] | 133 | packet_feedback_vector.reserve(feedback.GetPacketStatusCount()); |
stefan | 9e117c5e1 | 2017-08-16 08:16:25 -0700 | [diff] [blame] | 134 | int64_t feedback_rtt = -1; |
sprang | 5e023eb | 2015-09-14 06:42:43 -0700 | [diff] [blame] | 135 | { |
| 136 | rtc::CritScope cs(&lock_); |
| 137 | size_t failed_lookups = 0; |
| 138 | int64_t offset_us = 0; |
stefan | e3a5567 | 2017-02-13 09:08:22 -0800 | [diff] [blame] | 139 | int64_t timestamp_ms = 0; |
elad.alon | ec304f9 | 2017-03-08 05:03:53 -0800 | [diff] [blame] | 140 | uint16_t seq_num = feedback.GetBaseSequence(); |
danilchap | c1b693c | 2017-04-20 08:23:41 -0700 | [diff] [blame] | 141 | for (const auto& packet : feedback.GetReceivedPackets()) { |
elad.alon | ec304f9 | 2017-03-08 05:03:53 -0800 | [diff] [blame] | 142 | // Insert into the vector those unreceived packets which precede this |
| 143 | // iteration's received packet. |
| 144 | for (; seq_num != packet.sequence_number(); ++seq_num) { |
| 145 | PacketFeedback packet_feedback(PacketFeedback::kNotReceived, seq_num); |
| 146 | // Note: Element not removed from history because it might be reported |
| 147 | // as received by another feedback. |
| 148 | if (!send_time_history_.GetFeedback(&packet_feedback, false)) |
| 149 | ++failed_lookups; |
Stefan Holmer | 9ea46b5 | 2017-03-15 12:40:25 +0100 | [diff] [blame] | 150 | if (packet_feedback.local_net_id == local_net_id_ && |
| 151 | packet_feedback.remote_net_id == remote_net_id_) { |
| 152 | packet_feedback_vector.push_back(packet_feedback); |
| 153 | } |
elad.alon | ec304f9 | 2017-03-08 05:03:53 -0800 | [diff] [blame] | 154 | } |
| 155 | |
| 156 | // Handle this iteration's received packet. |
danilchap | 4a0c764 | 2017-01-18 02:40:30 -0800 | [diff] [blame] | 157 | offset_us += packet.delta_us(); |
stefan | e3a5567 | 2017-02-13 09:08:22 -0800 | [diff] [blame] | 158 | timestamp_ms = current_offset_ms_ + (offset_us / 1000); |
elad.alon | f949000 | 2017-03-06 05:32:21 -0800 | [diff] [blame] | 159 | PacketFeedback packet_feedback(timestamp_ms, packet.sequence_number()); |
| 160 | if (!send_time_history_.GetFeedback(&packet_feedback, true)) |
danilchap | 4a0c764 | 2017-01-18 02:40:30 -0800 | [diff] [blame] | 161 | ++failed_lookups; |
Stefan Holmer | 9ea46b5 | 2017-03-15 12:40:25 +0100 | [diff] [blame] | 162 | if (packet_feedback.local_net_id == local_net_id_ && |
| 163 | packet_feedback.remote_net_id == remote_net_id_) { |
stefan | 9e117c5e1 | 2017-08-16 08:16:25 -0700 | [diff] [blame] | 164 | if (packet_feedback.send_time_ms >= 0) { |
| 165 | int64_t rtt = now_ms - packet_feedback.send_time_ms; |
| 166 | // max() is used to account for feedback being delayed by the |
| 167 | // receiver. |
| 168 | feedback_rtt = std::max(rtt, feedback_rtt); |
| 169 | } |
Stefan Holmer | 9ea46b5 | 2017-03-15 12:40:25 +0100 | [diff] [blame] | 170 | packet_feedback_vector.push_back(packet_feedback); |
| 171 | } |
elad.alon | ec304f9 | 2017-03-08 05:03:53 -0800 | [diff] [blame] | 172 | |
| 173 | ++seq_num; |
sprang | 5e023eb | 2015-09-14 06:42:43 -0700 | [diff] [blame] | 174 | } |
elad.alon | ec304f9 | 2017-03-08 05:03:53 -0800 | [diff] [blame] | 175 | |
sprang | 5e023eb | 2015-09-14 06:42:43 -0700 | [diff] [blame] | 176 | if (failed_lookups > 0) { |
| 177 | LOG(LS_WARNING) << "Failed to lookup send time for " << failed_lookups |
| 178 | << " packet" << (failed_lookups > 1 ? "s" : "") |
| 179 | << ". Send time history too small?"; |
| 180 | } |
stefan | 9e117c5e1 | 2017-08-16 08:16:25 -0700 | [diff] [blame] | 181 | if (feedback_rtt > -1) { |
| 182 | feedback_rtts_.push_back(feedback_rtt); |
| 183 | const size_t kFeedbackRttWindow = 32; |
| 184 | if (feedback_rtts_.size() > kFeedbackRttWindow) |
| 185 | feedback_rtts_.pop_front(); |
| 186 | min_feedback_rtt_.emplace( |
| 187 | *std::min_element(feedback_rtts_.begin(), feedback_rtts_.end())); |
| 188 | } |
sprang | 5e023eb | 2015-09-14 06:42:43 -0700 | [diff] [blame] | 189 | } |
stefan | c3de033 | 2016-08-02 07:22:17 -0700 | [diff] [blame] | 190 | return packet_feedback_vector; |
| 191 | } |
Erik Språng | 6b8d355 | 2015-09-24 15:06:57 +0200 | [diff] [blame] | 192 | |
stefan | c3de033 | 2016-08-02 07:22:17 -0700 | [diff] [blame] | 193 | void TransportFeedbackAdapter::OnTransportFeedback( |
| 194 | const rtcp::TransportFeedback& feedback) { |
Stefan Holmer | 60e4346 | 2016-09-07 09:58:20 +0200 | [diff] [blame] | 195 | last_packet_feedback_vector_ = GetPacketFeedbackVector(feedback); |
elad.alon | d12a8e1 | 2017-03-23 11:04:48 -0700 | [diff] [blame] | 196 | { |
| 197 | rtc::CritScope cs(&observers_lock_); |
| 198 | for (auto observer : observers_) { |
| 199 | observer->OnPacketFeedbackVector(last_packet_feedback_vector_); |
| 200 | } |
| 201 | } |
Stefan Holmer | 60e4346 | 2016-09-07 09:58:20 +0200 | [diff] [blame] | 202 | } |
| 203 | |
elad.alon | f949000 | 2017-03-06 05:32:21 -0800 | [diff] [blame] | 204 | std::vector<PacketFeedback> |
| 205 | TransportFeedbackAdapter::GetTransportFeedbackVector() const { |
Stefan Holmer | 60e4346 | 2016-09-07 09:58:20 +0200 | [diff] [blame] | 206 | return last_packet_feedback_vector_; |
sprang | 5e023eb | 2015-09-14 06:42:43 -0700 | [diff] [blame] | 207 | } |
stefan | 9e117c5e1 | 2017-08-16 08:16:25 -0700 | [diff] [blame] | 208 | |
| 209 | rtc::Optional<int64_t> TransportFeedbackAdapter::GetMinFeedbackLoopRtt() const { |
| 210 | rtc::CritScope cs(&lock_); |
| 211 | return min_feedback_rtt_; |
| 212 | } |
| 213 | |
| 214 | size_t TransportFeedbackAdapter::GetOutstandingBytes() const { |
| 215 | rtc::CritScope cs(&lock_); |
| 216 | return send_time_history_.GetOutstandingBytes(local_net_id_, remote_net_id_); |
| 217 | } |
sprang | 5e023eb | 2015-09-14 06:42:43 -0700 | [diff] [blame] | 218 | } // namespace webrtc |