OLD | NEW |
---|---|
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 |
OLD | NEW |