OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright (c) 2016 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 <algorithm> | |
12 #include <limits> | |
13 | |
14 #include "webrtc/base/checks.h" | |
15 #include "webrtc/base/mod_ops.h" | |
16 #include "webrtc/modules/video_coding/nack_module.h" | |
17 #include "webrtc/modules/utility/include/process_thread.h" | |
18 | |
19 namespace webrtc { | |
20 | |
21 NackModule::NackInfo::NackInfo() | |
22 : seq_num(0), send_at_seq_num(0), send_at_time(0), retries(0) {} | |
23 | |
24 NackModule::NackInfo::NackInfo(uint16_t seq_num, uint16_t send_at_seq_num) | |
25 : seq_num(seq_num), | |
26 send_at_seq_num(send_at_seq_num), | |
27 send_at_time(0), | |
28 retries(0) {} | |
29 | |
30 NackModule::NackModule(Clock* clock, | |
31 VCMNackSender* nack_sender, | |
32 VCMKeyFrameRequestSender* keyframe_request_sender) | |
33 : clock_(clock), | |
34 nack_sender_(nack_sender), | |
35 keyframe_request_sender_(keyframe_request_sender), | |
36 running_(true), | |
37 initialized_(false), | |
38 rtt_ms_(kDefaultRttMs), | |
39 last_seq_num_(0), | |
40 last_process_time_ms_(-1), | |
41 reordering_count_(0) { | |
42 memset(reordering_occurences_, -1, sizeof(reordering_occurences_)); | |
43 memset(reordering_buckets_, 0, sizeof(reordering_buckets_)); | |
44 RTC_DCHECK(clock_); | |
45 RTC_DCHECK(nack_sender_); | |
46 RTC_DCHECK(keyframe_request_sender_); | |
47 } | |
48 | |
49 void NackModule::OnReceivedPacket(const VCMPacket& packet) { | |
50 rtc::CritScope lock(&crit_); | |
51 if (!running_) | |
52 return; | |
53 uint16_t seq_num = packet.seqNum; | |
54 // TODO(philipel): When the packet includes information whether it is | |
55 // retransmitted or not, use that value instead. For | |
56 // now set it to true, which will cause the reordering | |
57 // statistics to never be updated. | |
58 bool is_retransmitted = true; | |
59 bool is_keyframe = packet.isFirstPacket && packet.frameType == kVideoFrameKey; | |
60 | |
61 if (!initialized_) { | |
62 last_seq_num_ = seq_num; | |
63 if (is_keyframe) seq_num_keyframes_.emplace(unwrapper_.Unwrap(seq_num)); | |
stefan-webrtc
2016/02/26 09:26:55
Maybe we should be unwrapping all sequence numbers
philipel
2016/02/26 15:12:07
Implemented custom comparator.
| |
64 initialized_ = true; | |
65 return; | |
66 } | |
67 | |
68 if (seq_num == last_seq_num_) | |
69 return; | |
70 | |
71 if (AheadOf(seq_num, last_seq_num_)) { | |
72 AddPacketsToNack(last_seq_num_ + 1, seq_num); | |
73 last_seq_num_ = seq_num; | |
74 | |
75 int64_t seq_num_unwrapped = unwrapper_.Unwrap(seq_num); | |
76 | |
77 // Keep track of new keyframes. | |
78 if (is_keyframe) | |
79 seq_num_keyframes_.emplace(seq_num_unwrapped); | |
80 | |
81 // And remove old ones so we don't accumulate keyframes. | |
82 auto it = seq_num_keyframes_.lower_bound(seq_num_unwrapped - | |
83 kMaxNackPackets); | |
84 if (it != seq_num_keyframes_.begin()) | |
85 seq_num_keyframes_.erase(seq_num_keyframes_.begin(), it); | |
86 | |
87 // Are there any nacks that are waiting for this seq_num. | |
88 std::vector<uint16_t> nack_batch = GetNackBatch(kSeqNumOnly); | |
89 if (!nack_batch.empty()) | |
90 nack_sender_->SendNack(nack_batch.data(), nack_batch.size()); | |
stefan-webrtc
2016/02/26 09:26:55
Is there any risk that we end up deadlocking here?
philipel
2016/02/26 15:12:07
I have not experienced any deadlocks when testing
stefan-webrtc
2016/03/01 08:52:09
Acknowledged.
| |
91 } else { | |
92 // An out of order packet has been received. | |
93 RemovePacketFromNack(seq_num); | |
94 if (!is_retransmitted) | |
95 UpdateReorderingStatistics(seq_num); | |
96 } | |
stefan-webrtc
2016/02/26 09:26:55
move the else to line 70 and make an early return:
philipel
2016/02/26 15:12:06
Done.
| |
97 } | |
98 | |
99 void NackModule::UpdateRtt(int64_t rtt_ms) { | |
100 rtc::CritScope lock(&crit_); | |
101 rtt_ms_ = rtt_ms; | |
102 } | |
103 | |
104 void NackModule::Stop() { | |
105 rtc::CritScope lock(&crit_); | |
106 running_ = false; | |
107 } | |
108 | |
109 int64_t NackModule::TimeUntilNextProcess() { | |
110 rtc::CritScope lock(&crit_); | |
111 return std::max( | |
112 last_process_time_ms_ + | |
113 kProcessIntervalMs - | |
114 clock_->TimeInMilliseconds(), | |
115 0l); | |
stefan-webrtc
2016/02/26 09:26:55
0ll is what you want, I think? Or just do 0 as I t
philipel
2016/02/26 15:12:07
Compiler complains if I use 0ll :)
stefan-webrtc
2016/03/01 08:52:09
Acknowledged.
| |
116 } | |
117 | |
118 int32_t NackModule::Process() { | |
119 rtc::CritScope lock(&crit_); | |
120 if (!running_) | |
121 return 0; | |
122 | |
123 // Update the last_process_time_ms_ in intervals to achieve | |
124 // the targeted frequency over time. Also add multiple intervals | |
125 // in case of a skip in time as to not make uneccessary | |
126 // calls to Process in order to catch up. | |
127 int64_t now_ms = clock_->TimeInMilliseconds(); | |
128 if (last_process_time_ms_ == 0) { | |
129 last_process_time_ms_ = now_ms + kProcessIntervalMs; | |
130 } else { | |
131 last_process_time_ms_ = last_process_time_ms_ + | |
132 kProcessIntervalMs + | |
133 (now_ms - 1 - last_process_time_ms_) / | |
134 kProcessIntervalMs * kProcessIntervalMs; | |
135 } | |
136 | |
137 std::vector<uint16_t> nack_batch = GetNackBatch(kTimeOnly); | |
138 if (!nack_batch.empty() && nack_sender_ != nullptr) | |
139 nack_sender_->SendNack(nack_batch.data(), nack_batch.size()); | |
140 | |
141 return 0; | |
142 } | |
143 | |
144 bool NackModule::RemovePacketsUntilKeyframe() { | |
145 while (!seq_num_keyframes_.empty()) { | |
146 int64_t kf_unwrapped_seq_num = *seq_num_keyframes_.begin(); | |
147 auto it = nack_list_.lower_bound(kf_unwrapped_seq_num); | |
148 | |
149 // If this keyframe is so old it does not remove any | |
150 // packets from the list, remove if from the list of | |
151 // keyframes and try the next keyframe. | |
152 if (it == nack_list_.begin()) { | |
153 seq_num_keyframes_.erase(seq_num_keyframes_.begin()); | |
154 continue; | |
155 } | |
156 | |
157 // We have found a keyframe that actually is newer than | |
158 // atleast one packet in the nack list. | |
159 nack_list_.erase(nack_list_.begin(), it); | |
160 return true; | |
161 } | |
162 return false; | |
163 } | |
164 | |
165 void NackModule::AddPacketsToNack(uint16_t seq_num_start, | |
166 uint16_t seq_num_end) { | |
167 // If the nack list is too large, remove packets from the nack list until | |
168 // the latest first packet of a keyframe. If the list is still too large, | |
169 // clear it and request a keyframe. | |
170 uint16_t num_new_nacks = ForwardDiff(seq_num_start, seq_num_end); | |
171 if (nack_list_.size() + num_new_nacks > kMaxNackPackets) { | |
172 while (RemovePacketsUntilKeyframe() && | |
173 nack_list_.size() + num_new_nacks > kMaxNackPackets) {} | |
174 | |
175 if (nack_list_.size() + num_new_nacks > kMaxNackPackets) { | |
176 nack_list_.clear(); | |
177 keyframe_request_sender_->RequestKeyFrame(); | |
178 return; | |
179 } | |
180 } | |
181 | |
182 for (uint16_t seq_num = seq_num_start; seq_num != seq_num_end; ++seq_num) { | |
183 NackInfo nack_info(seq_num, seq_num + WaitNumberOfPackets(0.5)); | |
184 int64_t seq_num_unwrapped = unwrapper_.Unwrap(seq_num); | |
185 nack_list_[seq_num_unwrapped] = nack_info; | |
186 } | |
187 } | |
188 | |
189 void NackModule::RemovePacketFromNack(uint16_t seq_num) { | |
190 int64_t seq_num_unwrapped = unwrapper_.Unwrap(seq_num); | |
191 nack_list_.erase(seq_num_unwrapped); | |
192 } | |
193 | |
194 std::vector<uint16_t> NackModule::GetNackBatch(NackFilterOptions options) { | |
195 bool consider_seq_num = options != kTimeOnly; | |
196 bool consider_timestamp = options != kSeqNumOnly; | |
197 int64_t now_ms = clock_->TimeInMilliseconds(); | |
198 std::vector<uint16_t> nack_batch; | |
199 auto it = nack_list_.begin(); | |
200 while (it != nack_list_.end()) { | |
201 if (consider_seq_num && it->second.send_at_time == 0 && | |
202 AheadOrAt(last_seq_num_, it->second.send_at_seq_num)) { | |
203 nack_batch.emplace_back(it->second.seq_num); | |
204 ++it->second.retries; | |
205 it->second.send_at_time = now_ms + rtt_ms_; | |
206 if (it->second.retries >= kMaxNackRetries) { | |
207 it = nack_list_.erase(it); | |
208 } else { | |
209 ++it; | |
210 } | |
211 continue; | |
212 } | |
213 | |
214 if (consider_timestamp && it->second.send_at_time > 0 && | |
215 it->second.send_at_time <= now_ms) { | |
216 nack_batch.emplace_back(it->second.seq_num); | |
217 ++it->second.retries; | |
218 it->second.send_at_time = now_ms + rtt_ms_; | |
219 if (it->second.retries >= kMaxNackRetries) { | |
220 it = nack_list_.erase(it); | |
221 } else { | |
222 ++it; | |
223 } | |
224 continue; | |
225 } | |
226 ++it; | |
227 } | |
228 return nack_batch; | |
229 } | |
230 | |
231 void NackModule::UpdateReorderingStatistics(uint16_t seq_num) { | |
232 RTC_DCHECK(AheadOf(last_seq_num_, seq_num)); | |
233 | |
234 int remove_from_bucket = reordering_occurences_[reordering_index_]; | |
235 if (remove_from_bucket != -1) | |
236 reordering_buckets_[remove_from_bucket] -= 1; | |
237 | |
238 uint16_t diff = ReverseDiff(last_seq_num_, seq_num); | |
239 uint16_t add_to_bucket = | |
240 std::min(static_cast<uint16_t>(kNumReorderingBuckets - 1), | |
241 static_cast<uint16_t>(diff - 1)); | |
242 reordering_buckets_[add_to_bucket] += 1; | |
243 if (reordering_occurences_[reordering_index_] == -1) | |
244 ++reordering_count_; | |
245 reordering_occurences_[reordering_index_] = add_to_bucket; | |
246 reordering_index_ = Add<kMaxReorderedPackets>(reordering_index_, 1); | |
247 } | |
248 | |
249 int NackModule::WaitNumberOfPackets(float probability) const { | |
250 RTC_DCHECK_GE(probability, 0.f); | |
251 RTC_DCHECK_LE(probability, 1.f); | |
252 | |
253 // Until there are enough values we default to 0. Also | |
254 // protect against divide by zero later in the code. | |
255 if (reordering_count_ < 20) | |
256 return 0; | |
257 | |
258 int bucket = 0; | |
259 float accumulated_probability = 0; | |
260 while (accumulated_probability < probability && | |
261 bucket < kNumReorderingBuckets) { | |
262 accumulated_probability += | |
263 static_cast<float>(reordering_buckets_[bucket]) / reordering_count_; | |
264 ++bucket; | |
265 } | |
266 return bucket + 1; | |
267 } | |
268 | |
269 } // namespace webrtc | |
OLD | NEW |