Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(516)

Side by Side Diff: webrtc/modules/video_coding/frame_buffer2.cc

Issue 2322263002: Frame continuity is now tested as soon as a frame is inserted into the FrameBuffer. (Closed)
Patch Set: Feedback fixes Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license 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 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 6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may 7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree. 8 * be found in the AUTHORS file in the root of the source tree.
9 */ 9 */
10 10
11 #include "webrtc/modules/video_coding/frame_buffer2.h" 11 #include "webrtc/modules/video_coding/frame_buffer2.h"
12 12
13 #include <algorithm> 13 #include <algorithm>
14 #include <cstring>
15 #include <queue>
14 16
15 #include "webrtc/base/checks.h" 17 #include "webrtc/base/checks.h"
16 #include "webrtc/modules/video_coding/frame_object.h" 18 #include "webrtc/base/logging.h"
17 #include "webrtc/modules/video_coding/jitter_estimator.h" 19 #include "webrtc/modules/video_coding/jitter_estimator.h"
18 #include "webrtc/modules/video_coding/sequence_number_util.h"
19 #include "webrtc/modules/video_coding/timing.h" 20 #include "webrtc/modules/video_coding/timing.h"
20 #include "webrtc/system_wrappers/include/clock.h" 21 #include "webrtc/system_wrappers/include/clock.h"
21 22
22 namespace webrtc { 23 namespace webrtc {
23 namespace video_coding { 24 namespace video_coding {
24 25
25 namespace { 26 namespace {
26 // The maximum age of decoded frames tracked by frame buffer, compared to 27 // Max number of frames the buffer will hold.
27 // |newest_picture_id_|. 28 constexpr int kMaxFramesBuffered = 1000;
28 constexpr int kMaxFrameAge = 4096;
29 29
30 // The maximum number of decoded frames being tracked by the frame buffer. 30 // Max number of decoded frame info that will be saved.
31 constexpr int kMaxNumHistoryFrames = 256; 31 constexpr int kMaxFramesHistory = 20;
32 } // namespace 32 } // namespace
33 33
34 bool FrameBuffer::FrameComp::operator()(const FrameKey& f1,
35 const FrameKey& f2) const {
36 // first = picture id
37 // second = spatial layer
38 if (f1.first == f2.first)
39 return f1.second < f2.second;
40 return AheadOf(f2.first, f1.first);
41 }
42
43 FrameBuffer::FrameBuffer(Clock* clock, 34 FrameBuffer::FrameBuffer(Clock* clock,
44 VCMJitterEstimator* jitter_estimator, 35 VCMJitterEstimator* jitter_estimator,
45 VCMTiming* timing) 36 VCMTiming* timing)
46 : clock_(clock), 37 : frames_(),
danilchap 2016/09/19 12:21:50 may be do not mention member if you create it with
philipel 2016/09/19 15:36:57 Done.
47 frame_inserted_event_(false, false), 38 clock_(clock),
39 new_countinuous_frame_event_(false, false),
48 jitter_estimator_(jitter_estimator), 40 jitter_estimator_(jitter_estimator),
49 timing_(timing), 41 timing_(timing),
50 inter_frame_delay_(clock_->TimeInMilliseconds()), 42 inter_frame_delay_(clock_->TimeInMilliseconds()),
51 newest_picture_id_(-1), 43 last_decoded_frame_it_(frames_.end()),
44 last_continuous_frame_it_(frames_.end()),
45 num_frames_history_(0),
46 num_frames_buffered_(0),
52 stopped_(false), 47 stopped_(false),
53 protection_mode_(kProtectionNack) {} 48 protection_mode_(kProtectionNack) {}
54 49
55 FrameBuffer::ReturnReason FrameBuffer::NextFrame( 50 FrameBuffer::ReturnReason FrameBuffer::NextFrame(
56 int64_t max_wait_time_ms, 51 int64_t max_wait_time_ms,
57 std::unique_ptr<FrameObject>* frame_out) { 52 std::unique_ptr<FrameObject>* frame_out) {
58 int64_t latest_return_time = clock_->TimeInMilliseconds() + max_wait_time_ms; 53 int64_t latest_return_time = clock_->TimeInMilliseconds() + max_wait_time_ms;
59 int64_t now = clock_->TimeInMilliseconds(); 54 int64_t now_ms = clock_->TimeInMilliseconds();
60 int64_t wait_ms = max_wait_time_ms; 55 int64_t wait_ms = max_wait_time_ms;
56 FrameMap::iterator next_frame_it;
57
61 while (true) { 58 while (true) {
62 std::map<FrameKey, std::unique_ptr<FrameObject>, FrameComp>::iterator
63 next_frame_it;
64 { 59 {
65 rtc::CritScope lock(&crit_); 60 rtc::CritScope lock(&crit_);
66 frame_inserted_event_.Reset(); 61 new_countinuous_frame_event_.Reset();
67 if (stopped_) 62 if (stopped_)
68 return kStopped; 63 return kStopped;
69 64
70 now = clock_->TimeInMilliseconds(); 65 now_ms = clock_->TimeInMilliseconds();
71 wait_ms = max_wait_time_ms; 66 wait_ms = max_wait_time_ms;
67
68 // Need to hold |crit_| in order to use |frames_|, therefore we
69 // set it here in the loop instead of outside the loop in order to not
70 // acquire the look unnecesserily.
danilchap 2016/09/19 12:21:50 s/look/lock/
philipel 2016/09/19 15:36:57 Done.
72 next_frame_it = frames_.end(); 71 next_frame_it = frames_.end();
73 for (auto frame_it = frames_.begin(); frame_it != frames_.end();
74 ++frame_it) {
75 const FrameObject& frame = *frame_it->second;
76 if (IsContinuous(frame)) {
77 next_frame_it = frame_it;
78 int64_t render_time =
79 next_frame_it->second->RenderTime() == -1
80 ? timing_->RenderTimeMs(frame.timestamp, now)
81 : next_frame_it->second->RenderTime();
82 wait_ms = timing_->MaxWaitingTime(render_time, now);
83 frame_it->second->SetRenderTime(render_time);
84 72
85 // This will cause the frame buffer to prefer high framerate rather 73 // |frame_it| points to the first frame after the
86 // than high resolution in the case of the decoder not decoding fast 74 // |last_decoded_frame_it_|.
87 // enough and the stream has multiple spatial and temporal layers. 75 auto frame_it = frames_.end();
88 if (wait_ms == 0) 76 if (last_decoded_frame_it_ == frames_.end()) {
danilchap 2016/09/19 12:21:50 you use this construction twice, may be add a smal
philipel 2016/09/19 15:36:57 I guess you are thinking of line 263? It's similar
89 continue; 77 frame_it = frames_.begin();
78 } else {
79 frame_it = last_decoded_frame_it_;
80 ++frame_it;
81 }
90 82
91 break; 83 // |continuous_end_it| point to the first frame after the
84 // |last_continuous_frame_it_|.
85 auto continuous_end_it = last_continuous_frame_it_;
86 if (continuous_end_it != frames_.end())
87 ++continuous_end_it;
88
89 while (frame_it != continuous_end_it) {
danilchap 2016/09/19 12:21:50 since you ++frame_it before both continue, may be
philipel 2016/09/19 15:36:57 Done.
90 if (frame_it->second.num_missing_decodable > 0) {
91 ++frame_it;
92 continue;
92 } 93 }
94
95 FrameObject* frame = frame_it->second.frame.get();
96 next_frame_it = frame_it;
97 int64_t render_time =
98 frame->RenderTime() == -1
99 ? timing_->RenderTimeMs(frame->timestamp, now_ms)
100 : frame->RenderTime();
101 wait_ms = timing_->MaxWaitingTime(render_time, now_ms);
102 frame->SetRenderTime(render_time);
103
104 // This will cause the frame buffer to prefer high framerate rather
105 // than high resolution in the case of the decoder not decoding fast
106 // enough and the stream has multiple spatial and temporal layers.
107 if (wait_ms == 0) {
108 ++frame_it;
109 continue;
110 }
111
112 break;
93 } 113 }
94 } 114 } // rtc::Critscope lock(&crit_);
95 115
96 wait_ms = std::min<int64_t>(wait_ms, latest_return_time - now); 116 wait_ms = std::min<int64_t>(wait_ms, latest_return_time - now_ms);
97 wait_ms = std::max<int64_t>(wait_ms, 0); 117 wait_ms = std::max<int64_t>(wait_ms, 0);
98 // If the timeout occurs, return. Otherwise a new frame has been inserted 118 // If the timeout occurs, return. Otherwise a new frame has been inserted
99 // and the best frame to decode next will be selected again. 119 // and the best frame to decode next will be selected again.
100 if (!frame_inserted_event_.Wait(wait_ms)) { 120 if (!new_countinuous_frame_event_.Wait(wait_ms)) {
danilchap 2016/09/19 12:21:50 though unrelated to this cl, may be cleaner to tur
philipel 2016/09/19 15:36:57 Much cleaner, thank you!
101 rtc::CritScope lock(&crit_); 121 rtc::CritScope lock(&crit_);
102 if (next_frame_it != frames_.end()) { 122 if (next_frame_it != frames_.end()) {
103 int64_t received_timestamp = next_frame_it->second->ReceivedTime(); 123 std::unique_ptr<FrameObject> frame =
104 uint32_t timestamp = next_frame_it->second->Timestamp(); 124 std::move(next_frame_it->second.frame);
125 int64_t received_timestamp = frame->ReceivedTime();
126 uint32_t timestamp = frame->Timestamp();
105 127
106 int64_t frame_delay; 128 int64_t frame_delay;
107 if (inter_frame_delay_.CalculateDelay(timestamp, &frame_delay, 129 if (inter_frame_delay_.CalculateDelay(timestamp, &frame_delay,
108 received_timestamp)) { 130 received_timestamp)) {
109 jitter_estimator_->UpdateEstimate(frame_delay, 131 jitter_estimator_->UpdateEstimate(frame_delay, frame->size);
110 next_frame_it->second->size);
111 } 132 }
112 float rtt_mult = protection_mode_ == kProtectionNackFEC ? 0.0 : 1.0; 133 float rtt_mult = protection_mode_ == kProtectionNackFEC ? 0.0 : 1.0;
113 timing_->SetJitterDelay(jitter_estimator_->GetJitterEstimate(rtt_mult)); 134 timing_->SetJitterDelay(jitter_estimator_->GetJitterEstimate(rtt_mult));
114 timing_->UpdateCurrentDelay(next_frame_it->second->RenderTime(), 135 timing_->UpdateCurrentDelay(frame->RenderTime(),
115 clock_->TimeInMilliseconds()); 136 clock_->TimeInMilliseconds());
116 137
117 decoded_frames_.insert(next_frame_it->first); 138 PropagateDecodability(next_frame_it->second);
118 std::unique_ptr<FrameObject> frame = std::move(next_frame_it->second); 139 AdvanceLastDecodedFrame(next_frame_it);
119 frames_.erase(frames_.begin(), ++next_frame_it);
120 *frame_out = std::move(frame); 140 *frame_out = std::move(frame);
121 return kFrameFound; 141 return kFrameFound;
122 } else { 142 } else {
123 return kTimeout; 143 return kTimeout;
124 } 144 }
125 } 145 }
126 } 146 }
127 } 147 }
128 148
129 void FrameBuffer::SetProtectionMode(VCMVideoProtection mode) { 149 void FrameBuffer::SetProtectionMode(VCMVideoProtection mode) {
130 rtc::CritScope lock(&crit_); 150 rtc::CritScope lock(&crit_);
131 protection_mode_ = mode; 151 protection_mode_ = mode;
132 } 152 }
133 153
134 void FrameBuffer::Start() { 154 void FrameBuffer::Start() {
135 rtc::CritScope lock(&crit_); 155 rtc::CritScope lock(&crit_);
136 stopped_ = false; 156 stopped_ = false;
137 } 157 }
138 158
139 void FrameBuffer::Stop() { 159 void FrameBuffer::Stop() {
140 rtc::CritScope lock(&crit_); 160 rtc::CritScope lock(&crit_);
141 stopped_ = true; 161 stopped_ = true;
142 frame_inserted_event_.Set(); 162 new_countinuous_frame_event_.Set();
143 } 163 }
144 164
145 void FrameBuffer::InsertFrame(std::unique_ptr<FrameObject> frame) { 165 int FrameBuffer::InsertFrame(std::unique_ptr<FrameObject> frame) {
146 rtc::CritScope lock(&crit_); 166 rtc::CritScope lock(&crit_);
147 // If |newest_picture_id_| is -1 then this is the first frame we received. 167 FrameKey key(frame->picture_id, frame->spatial_layer);
148 if (newest_picture_id_ == -1) 168 int last_continuous_picture_id =
149 newest_picture_id_ = frame->picture_id; 169 last_continuous_frame_it_ == frames_.end()
170 ? -1
171 : last_continuous_frame_it_->first.picture_id;
150 172
151 if (AheadOf<uint16_t>(frame->picture_id, newest_picture_id_)) 173 if (num_frames_buffered_ >= kMaxFramesBuffered) {
152 newest_picture_id_ = frame->picture_id; 174 LOG(LS_INFO) << "Frame with (picture_id:spatial_id) (" << key.picture_id
175 << ":" << int(key.spatial_layer)
176 << ") could not be inserted due to the frame "
177 << "buffer being full, dropping frame.";
178 return last_continuous_picture_id;
179 }
153 180
154 // Remove frames as long as we have too many, |kMaxNumHistoryFrames|. 181 if (frame->inter_layer_predicted && frame->spatial_layer == 0) {
155 while (decoded_frames_.size() > kMaxNumHistoryFrames) 182 LOG(LS_INFO) << "Frame with (picture_id:spatial_id) (" << key.picture_id
156 decoded_frames_.erase(decoded_frames_.begin()); 183 << ":" << int(key.spatial_layer)
184 << ") is marked as inter layer predicted, dropping frame.";
185 return last_continuous_picture_id;
186 }
157 187
158 // Remove frames that are too old. 188 if (last_decoded_frame_it_ != frames_.end() &&
159 uint16_t old_picture_id = Subtract<1 << 16>(newest_picture_id_, kMaxFrameAge); 189 key < last_decoded_frame_it_->first) {
160 auto old_decoded_it = 190 LOG(LS_INFO) << "Frame with (picture_id:spatial_id) (" << key.picture_id
161 decoded_frames_.lower_bound(FrameKey(old_picture_id, 0)); 191 << ":" << int(key.spatial_layer) << ") inserted after frame ("
162 decoded_frames_.erase(decoded_frames_.begin(), old_decoded_it); 192 << last_decoded_frame_it_->first.picture_id << ":"
193 << int(last_decoded_frame_it_->first.spatial_layer)
194 << ") was handed off for decoding, dropping frame.";
195 return last_continuous_picture_id;
196 }
163 197
164 FrameKey key(frame->picture_id, frame->spatial_layer); 198 auto info = UpdateFrameInfo(*frame.get());
danilchap 2016/09/19 12:21:50 no need to .get(): UpdateFrameInfo(*frame); should
philipel 2016/09/19 15:36:57 Done.
165 frames_[key] = std::move(frame); 199
166 frame_inserted_event_.Set(); 200 if (info == frames_.end()) {
201 LOG(LS_INFO) << "Frame with (picture_id:spatial_id) (" << key.picture_id
danilchap 2016/09/19 12:21:50 may be move this log inside UpdateFrameInfo, close
philipel 2016/09/19 15:36:57 UpdateFrameInfoWithIncomingFrame now returns false
202 << ":" << int(key.spatial_layer)
203 << " depends on a non-decoded frame more previous than "
204 << "the last decoded frame, dropping frame.";
205 return last_continuous_picture_id;
206 }
207
208 info->second.frame = std::move(frame);
209 ++num_frames_buffered_;
210
211 if (info->second.num_missing_continuous == 0) {
212 info->second.continuous = true;
213 PropagateContinuity(info);
214 last_continuous_picture_id = last_continuous_frame_it_->first.picture_id;
215
216 // Since we now have new continuous frames there might be a better frame
217 // to return from NextFrame. Signal that thread so that it again can choose
218 // which frame to return.
219 new_countinuous_frame_event_.Set();
220 }
221
222 return last_continuous_picture_id;
167 } 223 }
168 224
169 bool FrameBuffer::IsContinuous(const FrameObject& frame) const { 225 void FrameBuffer::PropagateContinuity(const FrameMap::iterator& start) {
170 // If a frame with an earlier picture id was inserted compared to the last 226 RTC_DCHECK(start->second.continuous);
171 // decoded frames picture id then that frame arrived too late. 227 if (last_continuous_frame_it_ == frames_.end())
172 if (!decoded_frames_.empty() && 228 last_continuous_frame_it_ = start;
173 AheadOf(decoded_frames_.rbegin()->first, frame.picture_id)) { 229
174 return false; 230 std::queue<FrameMap::iterator> continuous_frames_;
danilchap 2016/09/19 12:21:50 remove last '_' in the variable name.
philipel 2016/09/19 15:36:57 Done.
231 continuous_frames_.push(start);
232
233 while (!continuous_frames_.empty()) {
234 auto frame = continuous_frames_.front();
235 continuous_frames_.pop();
236
237 if (last_continuous_frame_it_->first < frame->first)
238 last_continuous_frame_it_ = frame;
239
240 // Loop through all dependent frames, and if that frame no longer has
241 // any unfulfilled dependencies then that frame is continuous as well.
242 for (size_t d = 0; d < frame->second.num_dependent_frames; ++d) {
243 auto frame_ref = frames_.find(frame->second.dependent_frames[d]);
244 --frame_ref->second.num_missing_continuous;
245
246 if (frame_ref->second.num_missing_continuous == 0) {
247 frame_ref->second.continuous = true;
248 continuous_frames_.push(frame_ref);
249 }
250 }
251 }
252 }
253
254 void FrameBuffer::PropagateDecodability(const FrameInfo& info) {
255 for (size_t d = 0; d < info.num_dependent_frames; ++d) {
256 auto ref_info = frames_.find(info.dependent_frames[d]);
257 RTC_DCHECK_GT(ref_info->second.num_missing_decodable, 0U);
258 --ref_info->second.num_missing_decodable;
259 }
260 }
261
262 void FrameBuffer::AdvanceLastDecodedFrame(const FrameMap::iterator& decoded) {
263 if (last_decoded_frame_it_ == frames_.end()) {
264 last_decoded_frame_it_ = frames_.begin();
265 } else {
266 ++last_decoded_frame_it_;
danilchap 2016/09/19 12:21:50 May be add DCHECK(last_decoded_frame_it_->first <
philipel 2016/09/19 15:36:56 Done.
267 }
268 --num_frames_buffered_;
danilchap 2016/09/19 12:21:50 probably join this with another lonely ++num_frame
philipel 2016/09/19 15:36:57 Done.
269
270 // First, delete non-decoded frames from the history.
271 while (last_decoded_frame_it_ != decoded) {
danilchap 2016/09/19 12:21:50 may be do not use this variable to iterate over un
philipel 2016/09/19 15:36:56 I don't think this is unexpected considering the f
272 last_decoded_frame_it_ = frames_.erase(last_decoded_frame_it_);
273 --num_frames_buffered_;
175 } 274 }
176 275
177 // Have we decoded all frames that this frame depend on? 276 // Then remove old history if we have too much history saved.
277 ++num_frames_history_;
278 if (num_frames_history_ > kMaxFramesHistory) {
279 frames_.erase(frames_.begin());
280 --num_frames_history_;
281 }
282 }
283
284 FrameBuffer::FrameMap::iterator FrameBuffer::UpdateFrameInfo(
285 const FrameObject& frame) {
286 FrameKey key(frame.picture_id, frame.spatial_layer);
287 auto info = frames_.insert(std::make_pair(key, FrameInfo())).first;
288 info->second.num_missing_continuous = frame.num_references;
289 info->second.num_missing_decodable = frame.num_references;
290
291 RTC_DCHECK(last_decoded_frame_it_ == frames_.end() ||
292 last_decoded_frame_it_->first < info->first);
293
294 // Check how many dependencies that has already been fulfilled.
178 for (size_t r = 0; r < frame.num_references; ++r) { 295 for (size_t r = 0; r < frame.num_references; ++r) {
179 FrameKey ref_key(frame.references[r], frame.spatial_layer); 296 FrameKey ref_key(frame.references[r], frame.spatial_layer);
180 if (decoded_frames_.find(ref_key) == decoded_frames_.end()) 297 auto ref_info = frames_.find(ref_key);
181 return false; 298
299 // Does |frame| depend on a frame earlier than the last decoded frame?
300 if (last_decoded_frame_it_ != frames_.end() &&
301 (ref_key < last_decoded_frame_it_->first ||
danilchap 2016/09/19 12:21:50 may be !(last_decoded_frame_it_->first < ref_key)
philipel 2016/09/19 15:36:57 Done.
302 ref_key == last_decoded_frame_it_->first)) {
303 // Do we depend on a frame that was never decoded?
danilchap 2016/09/19 12:21:50 s/Do we/Does |frame|/
philipel 2016/09/19 15:36:57 Done.
304 if (ref_info == frames_.end())
305 return frames_.end();
306
307 --info->second.num_missing_continuous;
308 --info->second.num_missing_decodable;
309
310 } else {
311 if (ref_info == frames_.end())
312 ref_info = frames_.insert(std::make_pair(ref_key, FrameInfo())).first;
313
314 if (ref_info->second.continuous)
315 --info->second.num_missing_continuous;
316
317 // Add backwards reference so |frame| can be updated when new
318 // frames are inserted or decoded.
319 ref_info->second.dependent_frames[ref_info->second.num_dependent_frames] =
320 key;
321 ++ref_info->second.num_dependent_frames;
322 }
182 } 323 }
183 324
184 // If this is a layer frame, have we decoded the lower layer of this 325 // Check if we have the lower spatial layer frame.
185 // super frame.
186 if (frame.inter_layer_predicted) { 326 if (frame.inter_layer_predicted) {
187 RTC_DCHECK_GT(frame.spatial_layer, 0); 327 ++info->second.num_missing_continuous;
188 FrameKey ref_key(frame.picture_id, frame.spatial_layer - 1); 328 ++info->second.num_missing_decodable;
189 if (decoded_frames_.find(ref_key) == decoded_frames_.end()) 329
190 return false; 330 FrameKey ref_key = FrameKey(frame.picture_id, frame.spatial_layer - 1);
danilchap 2016/09/19 12:21:50 FrameKey ref_key(frame.picture_id, frame.spatial_l
philipel 2016/09/19 15:36:57 Done.
331 auto ref_info = frames_.insert(std::make_pair(ref_key, FrameInfo())).first;
danilchap 2016/09/19 12:21:50 this ref_info is not used outside as iterator, may
philipel 2016/09/19 15:36:57 Using an iterator is more in style with the rest o
332 if (ref_info->second.continuous)
333 --info->second.num_missing_continuous;
334
335 ref_info->second.dependent_frames[ref_info->second.num_dependent_frames] =
336 key;
337 ++ref_info->second.num_dependent_frames;
191 } 338 }
192 339
193 return true; 340 return info;
194 } 341 }
195 342
196 } // namespace video_coding 343 } // namespace video_coding
197 } // namespace webrtc 344 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698