OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/cast/sender/vp8_encoder.h" | 5 #include "media/cast/sender/vp8_encoder.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "media/base/video_frame.h" | 10 #include "media/base/video_frame.h" |
11 #include "media/cast/cast_defines.h" | 11 #include "media/cast/cast_defines.h" |
12 #include "media/cast/net/cast_transport_config.h" | 12 #include "media/cast/net/cast_transport_config.h" |
13 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h" | 13 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h" |
14 | 14 |
15 namespace media { | 15 namespace media { |
16 namespace cast { | 16 namespace cast { |
17 | 17 |
18 static const uint32 kMinIntra = 300; | 18 static const uint32 kMinIntra = 300; |
19 | 19 |
20 static int ComputeMaxNumOfRepeatedBuffers(int max_unacked_frames) { | |
21 if (max_unacked_frames > kNumberOfVp8VideoBuffers) | |
22 return (max_unacked_frames - 1) / kNumberOfVp8VideoBuffers; | |
23 | |
24 return 0; | |
25 } | |
26 | |
27 Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config, | 20 Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config, |
28 int max_unacked_frames) | 21 int max_unacked_frames) |
29 : cast_config_(video_config), | 22 : cast_config_(video_config), |
30 use_multiple_video_buffers_( | 23 use_multiple_video_buffers_( |
31 cast_config_.max_number_of_video_buffers_used == | 24 cast_config_.max_number_of_video_buffers_used == |
32 kNumberOfVp8VideoBuffers), | 25 kNumberOfVp8VideoBuffers), |
33 max_number_of_repeated_buffers_in_a_row_( | |
34 ComputeMaxNumOfRepeatedBuffers(max_unacked_frames)), | |
35 key_frame_requested_(true), | 26 key_frame_requested_(true), |
36 first_frame_received_(false), | 27 first_frame_received_(false), |
37 last_encoded_frame_id_(kStartFrameId), | 28 last_encoded_frame_id_(kStartFrameId), |
38 number_of_repeated_buffers_(0) { | 29 last_acked_frame_id_(kStartFrameId), |
39 // TODO(pwestin): we need to figure out how to synchronize the acking with the | 30 frame_id_to_reference_(kStartFrameId - 1), |
40 // internal state of the encoder, ideally the encoder will tell if we can | 31 undroppable_frames_(0) { |
41 // send another frame. | |
42 DCHECK(!use_multiple_video_buffers_ || | |
43 max_number_of_repeated_buffers_in_a_row_ == 0) | |
44 << "Invalid config"; | |
45 | |
46 // VP8 have 3 buffers available for prediction, with | 32 // VP8 have 3 buffers available for prediction, with |
47 // max_number_of_video_buffers_used set to 1 we maximize the coding efficiency | 33 // max_number_of_video_buffers_used set to 1 we maximize the coding efficiency |
48 // however in this mode we can not skip frames in the receiver to catch up | 34 // however in this mode we can not skip frames in the receiver to catch up |
49 // after a temporary network outage; with max_number_of_video_buffers_used | 35 // after a temporary network outage; with max_number_of_video_buffers_used |
50 // set to 3 we allow 2 frames to be skipped by the receiver without error | 36 // set to 3 we allow 2 frames to be skipped by the receiver without error |
51 // propagation. | 37 // propagation. |
52 DCHECK(cast_config_.max_number_of_video_buffers_used == 1 || | 38 DCHECK(cast_config_.max_number_of_video_buffers_used == 1 || |
53 cast_config_.max_number_of_video_buffers_used == | 39 cast_config_.max_number_of_video_buffers_used == |
54 kNumberOfVp8VideoBuffers) | 40 kNumberOfVp8VideoBuffers) |
55 << "Invalid argument"; | 41 << "Invalid argument"; |
(...skipping 11 matching lines...) Expand all Loading... |
67 config_.reset(new vpx_codec_enc_cfg_t()); | 53 config_.reset(new vpx_codec_enc_cfg_t()); |
68 encoder_.reset(new vpx_codec_ctx_t()); | 54 encoder_.reset(new vpx_codec_ctx_t()); |
69 | 55 |
70 // Creating a wrapper to the image - setting image data to NULL. Actual | 56 // Creating a wrapper to the image - setting image data to NULL. Actual |
71 // pointer will be set during encode. Setting align to 1, as it is | 57 // pointer will be set during encode. Setting align to 1, as it is |
72 // meaningless (actual memory is not allocated). | 58 // meaningless (actual memory is not allocated). |
73 raw_image_ = vpx_img_wrap( | 59 raw_image_ = vpx_img_wrap( |
74 NULL, IMG_FMT_I420, cast_config_.width, cast_config_.height, 1, NULL); | 60 NULL, IMG_FMT_I420, cast_config_.width, cast_config_.height, 1, NULL); |
75 | 61 |
76 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { | 62 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { |
77 acked_frame_buffers_[i] = true; | 63 buffer_state_[i].frame_id = kStartFrameId; |
78 used_buffers_frame_id_[i] = kStartFrameId; | 64 buffer_state_[i].state = kBufferStartState; |
79 } | 65 } |
80 InitEncode(cast_config_.number_of_encode_threads); | 66 InitEncode(cast_config_.number_of_encode_threads); |
81 } | 67 } |
82 | 68 |
83 void Vp8Encoder::InitEncode(int number_of_encode_threads) { | 69 void Vp8Encoder::InitEncode(int number_of_encode_threads) { |
84 DCHECK(thread_checker_.CalledOnValidThread()); | 70 DCHECK(thread_checker_.CalledOnValidThread()); |
85 // Populate encoder configuration with default values. | 71 // Populate encoder configuration with default values. |
86 if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), config_.get(), 0)) { | 72 if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), config_.get(), 0)) { |
87 DCHECK(false) << "Invalid return value"; | 73 DCHECK(false) << "Invalid return value"; |
88 } | 74 } |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
153 vpx_codec_flags_t flags = 0; | 139 vpx_codec_flags_t flags = 0; |
154 if (key_frame_requested_) { | 140 if (key_frame_requested_) { |
155 flags = VPX_EFLAG_FORCE_KF; | 141 flags = VPX_EFLAG_FORCE_KF; |
156 // Self reference. | 142 // Self reference. |
157 latest_frame_id_to_reference = last_encoded_frame_id_ + 1; | 143 latest_frame_id_to_reference = last_encoded_frame_id_ + 1; |
158 // We can pick any buffer as buffer_to_update since we update | 144 // We can pick any buffer as buffer_to_update since we update |
159 // them all. | 145 // them all. |
160 buffer_to_update = kLastBuffer; | 146 buffer_to_update = kLastBuffer; |
161 } else { | 147 } else { |
162 // Reference all acked frames (buffers). | 148 // Reference all acked frames (buffers). |
163 latest_frame_id_to_reference = GetLatestFrameIdToReference(); | 149 latest_frame_id_to_reference = GetCodecReferenceFlags(&flags); |
164 GetCodecReferenceFlags(&flags); | |
165 buffer_to_update = GetNextBufferToUpdate(); | 150 buffer_to_update = GetNextBufferToUpdate(); |
166 GetCodecUpdateFlags(buffer_to_update, &flags); | 151 GetCodecUpdateFlags(buffer_to_update, &flags); |
167 } | 152 } |
168 | 153 |
169 // Note: The duration does not reflect the real time between frames. This is | 154 // Note: The duration does not reflect the real time between frames. This is |
170 // done to keep the encoder happy. | 155 // done to keep the encoder happy. |
171 // | 156 // |
172 // TODO(miu): This is a semi-hack. We should consider using | 157 // TODO(miu): This is a semi-hack. We should consider using |
173 // |video_frame->timestamp()| instead. | 158 // |video_frame->timestamp()| instead. |
174 uint32 duration = kVideoFrequency / cast_config_.max_frame_rate; | 159 uint32 duration = kVideoFrequency / cast_config_.max_frame_rate; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
207 is_key_frame = !!(pkt->data.frame.flags & VPX_FRAME_IS_KEY); | 192 is_key_frame = !!(pkt->data.frame.flags & VPX_FRAME_IS_KEY); |
208 break; // Done, since all data is provided in one CX_FRAME_PKT packet. | 193 break; // Done, since all data is provided in one CX_FRAME_PKT packet. |
209 } | 194 } |
210 // Don't update frame_id for zero size frames. | 195 // Don't update frame_id for zero size frames. |
211 if (encoded_image->data.empty()) | 196 if (encoded_image->data.empty()) |
212 return true; | 197 return true; |
213 | 198 |
214 // Populate the encoded frame. | 199 // Populate the encoded frame. |
215 encoded_image->frame_id = ++last_encoded_frame_id_; | 200 encoded_image->frame_id = ++last_encoded_frame_id_; |
216 if (is_key_frame) { | 201 if (is_key_frame) { |
| 202 // TODO(Hubbe): Replace "dependency" with a "bool is_key_frame". |
217 encoded_image->dependency = EncodedFrame::KEY; | 203 encoded_image->dependency = EncodedFrame::KEY; |
218 encoded_image->referenced_frame_id = encoded_image->frame_id; | 204 encoded_image->referenced_frame_id = encoded_image->frame_id; |
219 } else { | 205 } else { |
220 encoded_image->dependency = EncodedFrame::DEPENDENT; | 206 encoded_image->dependency = EncodedFrame::DEPENDENT; |
221 encoded_image->referenced_frame_id = latest_frame_id_to_reference; | 207 encoded_image->referenced_frame_id = latest_frame_id_to_reference; |
222 } | 208 } |
223 | 209 |
224 DVLOG(1) << "VP8 encoded frame_id " << encoded_image->frame_id | 210 DVLOG(1) << "VP8 encoded frame_id " << encoded_image->frame_id |
225 << ", sized:" << encoded_image->data.size(); | 211 << ", sized:" << encoded_image->data.size(); |
226 | 212 |
227 if (is_key_frame) { | 213 if (is_key_frame) { |
228 key_frame_requested_ = false; | 214 key_frame_requested_ = false; |
229 | 215 |
230 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { | 216 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { |
231 used_buffers_frame_id_[i] = encoded_image->frame_id; | 217 buffer_state_[i].state = kBufferSent; |
| 218 buffer_state_[i].frame_id = encoded_image->frame_id; |
232 } | 219 } |
233 // We can pick any buffer as last_used_vp8_buffer_ since we update | |
234 // them all. | |
235 last_used_vp8_buffer_ = buffer_to_update; | |
236 } else { | 220 } else { |
237 if (buffer_to_update != kNoBuffer) { | 221 if (buffer_to_update != kNoBuffer) { |
238 acked_frame_buffers_[buffer_to_update] = false; | 222 buffer_state_[buffer_to_update].state = kBufferSent; |
239 used_buffers_frame_id_[buffer_to_update] = encoded_image->frame_id; | 223 buffer_state_[buffer_to_update].frame_id = encoded_image->frame_id; |
240 last_used_vp8_buffer_ = buffer_to_update; | |
241 } | 224 } |
242 } | 225 } |
243 return true; | 226 return true; |
244 } | 227 } |
245 | 228 |
246 void Vp8Encoder::GetCodecReferenceFlags(vpx_codec_flags_t* flags) { | 229 uint32 Vp8Encoder::GetCodecReferenceFlags(vpx_codec_flags_t* flags) { |
247 if (!use_multiple_video_buffers_) | 230 if (!use_multiple_video_buffers_) |
248 return; | 231 return last_encoded_frame_id_ + 1; |
249 | 232 |
250 // We need to reference something. | 233 const uint32 kMagicFrameOffset = 512; |
251 DCHECK(acked_frame_buffers_[kAltRefBuffer] || | 234 // We set latest_frame_to_reference to an old frame so that |
252 acked_frame_buffers_[kGoldenBuffer] || | 235 // IsNewerFrameId will work correctly. |
253 acked_frame_buffers_[kLastBuffer]) | 236 uint32 latest_frame_to_reference = |
254 << "Invalid state"; | 237 last_encoded_frame_id_ - kMagicFrameOffset; |
255 | 238 |
256 if (!acked_frame_buffers_[kAltRefBuffer]) { | 239 // Reference all acked frames. |
257 *flags |= VP8_EFLAG_NO_REF_ARF; | 240 // TODO(hubbe): We may also want to allow references to the |
258 } | 241 // last encoded frame, if that frame was assigned to a buffer. |
259 if (!acked_frame_buffers_[kGoldenBuffer]) { | 242 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { |
260 *flags |= VP8_EFLAG_NO_REF_GF; | 243 if (buffer_state_[i].state == kBufferAcked) { |
261 } | 244 if (IsNewerFrameId(buffer_state_[i].frame_id, |
262 if (!acked_frame_buffers_[kLastBuffer]) { | 245 latest_frame_to_reference)) { |
263 *flags |= VP8_EFLAG_NO_REF_LAST; | 246 latest_frame_to_reference = buffer_state_[i].frame_id; |
264 } | 247 } |
265 } | |
266 | |
267 uint32 Vp8Encoder::GetLatestFrameIdToReference() { | |
268 if (!use_multiple_video_buffers_) | |
269 return last_encoded_frame_id_; | |
270 | |
271 int latest_frame_id_to_reference = -1; | |
272 if (acked_frame_buffers_[kAltRefBuffer]) { | |
273 latest_frame_id_to_reference = used_buffers_frame_id_[kAltRefBuffer]; | |
274 } | |
275 if (acked_frame_buffers_[kGoldenBuffer]) { | |
276 if (latest_frame_id_to_reference == -1) { | |
277 latest_frame_id_to_reference = used_buffers_frame_id_[kGoldenBuffer]; | |
278 } else { | 248 } else { |
279 if (IsNewerFrameId(used_buffers_frame_id_[kGoldenBuffer], | 249 switch (i) { |
280 latest_frame_id_to_reference)) { | 250 case kAltRefBuffer: |
281 latest_frame_id_to_reference = used_buffers_frame_id_[kGoldenBuffer]; | 251 *flags |= VP8_EFLAG_NO_REF_ARF; |
| 252 break; |
| 253 case kGoldenBuffer: |
| 254 *flags |= VP8_EFLAG_NO_REF_GF; |
| 255 break; |
| 256 case kLastBuffer: |
| 257 *flags |= VP8_EFLAG_NO_REF_LAST; |
| 258 break; |
282 } | 259 } |
283 } | 260 } |
284 } | 261 } |
285 if (acked_frame_buffers_[kLastBuffer]) { | 262 |
286 if (latest_frame_id_to_reference == -1) { | 263 if (latest_frame_to_reference == |
287 latest_frame_id_to_reference = used_buffers_frame_id_[kLastBuffer]; | 264 last_encoded_frame_id_ - kMagicFrameOffset) { |
288 } else { | 265 // We have nothing to reference, it's kind of like a key frame, |
289 if (IsNewerFrameId(used_buffers_frame_id_[kLastBuffer], | 266 // but doesn't reset buffers. |
290 latest_frame_id_to_reference)) { | 267 latest_frame_to_reference = last_encoded_frame_id_ + 1; |
291 latest_frame_id_to_reference = used_buffers_frame_id_[kLastBuffer]; | |
292 } | |
293 } | |
294 } | 268 } |
295 DCHECK(latest_frame_id_to_reference != -1) << "Invalid state"; | 269 |
296 return static_cast<uint32>(latest_frame_id_to_reference); | 270 return latest_frame_to_reference; |
297 } | 271 } |
298 | 272 |
299 Vp8Encoder::Vp8Buffers Vp8Encoder::GetNextBufferToUpdate() { | 273 Vp8Encoder::Vp8Buffers Vp8Encoder::GetNextBufferToUpdate() { |
300 if (!use_multiple_video_buffers_) | 274 if (!use_multiple_video_buffers_) |
301 return kNoBuffer; | 275 return kNoBuffer; |
302 | 276 |
303 // Update at most one buffer, except for key-frames. | 277 // The goal here is to make sure that we always keep one ACKed |
| 278 // buffer while trying to get an ACK for a newer buffer as we go. |
| 279 // Here are the rules for which buffer to select for update: |
| 280 // 1. If there is a buffer in state kStartState, use it. |
| 281 // 2. If there is a buffer other than the oldest buffer |
| 282 // which is Acked, use the oldest buffer. |
| 283 // 3. If there are Sent buffers which are older than |
| 284 // latest_acked_frame_, use the oldest one. |
| 285 // 4. If all else fails, just overwrite the newest buffer, |
| 286 // but no more than 3 times in a row. |
| 287 // TODO(hubbe): Figure out if 3 is optimal. |
| 288 // Note, rule 1-3 describe cases where there is a "free" buffer |
| 289 // that we can use. Rule 4 describes what happens when there is |
| 290 // no free buffer available. |
304 | 291 |
305 Vp8Buffers buffer_to_update = kNoBuffer; | 292 // Buffers, sorted from oldest frame to newest. |
306 if (number_of_repeated_buffers_ < max_number_of_repeated_buffers_in_a_row_) { | 293 Vp8Encoder::Vp8Buffers buffers[kNumberOfVp8VideoBuffers]; |
307 // TODO(pwestin): experiment with this. The issue with only this change is | 294 |
308 // that we can end up with only 4 frames in flight when we expect 6. | 295 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { |
309 // buffer_to_update = last_used_vp8_buffer_; | 296 Vp8Encoder::Vp8Buffers buffer = static_cast<Vp8Encoder::Vp8Buffers>(i); |
310 buffer_to_update = kNoBuffer; | 297 |
311 ++number_of_repeated_buffers_; | 298 // Rule 1 |
312 } else { | 299 if (buffer_state_[buffer].state == kBufferStartState) { |
313 number_of_repeated_buffers_ = 0; | 300 undroppable_frames_ = 0; |
314 switch (last_used_vp8_buffer_) { | 301 return buffer; |
315 case kAltRefBuffer: | 302 } |
316 buffer_to_update = kLastBuffer; | 303 buffers[buffer] = buffer; |
317 VLOG(1) << "VP8 update last buffer"; | 304 } |
318 break; | 305 |
319 case kLastBuffer: | 306 // Sorting three elements with selection sort. |
320 buffer_to_update = kGoldenBuffer; | 307 for (int i = 0; i < kNumberOfVp8VideoBuffers - 1; i++) { |
321 VLOG(1) << "VP8 update golden buffer"; | 308 for (int j = i + 1; j < kNumberOfVp8VideoBuffers; j++) { |
322 break; | 309 if (IsOlderFrameId(buffer_state_[buffers[j]].frame_id, |
323 case kGoldenBuffer: | 310 buffer_state_[buffers[i]].frame_id)) { |
324 buffer_to_update = kAltRefBuffer; | 311 std::swap(buffers[i], buffers[j]); |
325 VLOG(1) << "VP8 update alt-ref buffer"; | 312 } |
326 break; | |
327 case kNoBuffer: | |
328 DCHECK(false) << "Invalid state"; | |
329 break; | |
330 } | 313 } |
331 } | 314 } |
332 return buffer_to_update; | 315 |
| 316 // Rule 2 |
| 317 if (buffer_state_[buffers[1]].state == kBufferAcked || |
| 318 buffer_state_[buffers[2]].state == kBufferAcked) { |
| 319 undroppable_frames_ = 0; |
| 320 return buffers[0]; |
| 321 } |
| 322 |
| 323 // Rule 3 |
| 324 for (int i = 0; i < kNumberOfVp8VideoBuffers; i++) { |
| 325 if (buffer_state_[buffers[i]].state == kBufferSent && |
| 326 IsOlderFrameId(buffer_state_[buffers[i]].frame_id, |
| 327 last_acked_frame_id_)) { |
| 328 undroppable_frames_ = 0; |
| 329 return buffers[i]; |
| 330 } |
| 331 } |
| 332 |
| 333 // Rule 4 |
| 334 if (undroppable_frames_ >= 3) { |
| 335 undroppable_frames_ = 0; |
| 336 return kNoBuffer; |
| 337 } else { |
| 338 undroppable_frames_++; |
| 339 return buffers[kNumberOfVp8VideoBuffers - 1]; |
| 340 } |
333 } | 341 } |
334 | 342 |
335 void Vp8Encoder::GetCodecUpdateFlags(Vp8Buffers buffer_to_update, | 343 void Vp8Encoder::GetCodecUpdateFlags(Vp8Buffers buffer_to_update, |
336 vpx_codec_flags_t* flags) { | 344 vpx_codec_flags_t* flags) { |
337 if (!use_multiple_video_buffers_) | 345 if (!use_multiple_video_buffers_) |
338 return; | 346 return; |
339 | 347 |
340 // Update at most one buffer, except for key-frames. | 348 // Update at most one buffer, except for key-frames. |
341 switch (buffer_to_update) { | 349 switch (buffer_to_update) { |
342 case kAltRefBuffer: | 350 case kAltRefBuffer: |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
374 } | 382 } |
375 } | 383 } |
376 | 384 |
377 void Vp8Encoder::LatestFrameIdToReference(uint32 frame_id) { | 385 void Vp8Encoder::LatestFrameIdToReference(uint32 frame_id) { |
378 DCHECK(thread_checker_.CalledOnValidThread()); | 386 DCHECK(thread_checker_.CalledOnValidThread()); |
379 if (!use_multiple_video_buffers_) | 387 if (!use_multiple_video_buffers_) |
380 return; | 388 return; |
381 | 389 |
382 VLOG(1) << "VP8 ok to reference frame:" << static_cast<int>(frame_id); | 390 VLOG(1) << "VP8 ok to reference frame:" << static_cast<int>(frame_id); |
383 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { | 391 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { |
384 if (frame_id == used_buffers_frame_id_[i]) { | 392 if (frame_id == buffer_state_[i].frame_id) { |
385 acked_frame_buffers_[i] = true; | 393 buffer_state_[i].state = kBufferAcked; |
| 394 break; |
386 } | 395 } |
387 } | 396 } |
| 397 if (IsOlderFrameId(last_acked_frame_id_, frame_id)) { |
| 398 last_acked_frame_id_ = frame_id; |
| 399 } |
388 } | 400 } |
389 | 401 |
390 void Vp8Encoder::GenerateKeyFrame() { | 402 void Vp8Encoder::GenerateKeyFrame() { |
391 DCHECK(thread_checker_.CalledOnValidThread()); | 403 DCHECK(thread_checker_.CalledOnValidThread()); |
392 key_frame_requested_ = true; | 404 key_frame_requested_ = true; |
393 } | 405 } |
394 | 406 |
395 // Calculate the max size of the key frame relative to a normal delta frame. | 407 // Calculate the max size of the key frame relative to a normal delta frame. |
396 uint32 Vp8Encoder::MaxIntraTarget(uint32 optimal_buffer_size_ms) const { | 408 uint32 Vp8Encoder::MaxIntraTarget(uint32 optimal_buffer_size_ms) const { |
397 // Set max to the optimal buffer level (normalized by target BR), | 409 // Set max to the optimal buffer level (normalized by target BR), |
398 // and scaled by a scale_parameter. | 410 // and scaled by a scale_parameter. |
399 // Max target size = scalePar * optimalBufferSize * targetBR[Kbps]. | 411 // Max target size = scalePar * optimalBufferSize * targetBR[Kbps]. |
400 // This values is presented in percentage of perFrameBw: | 412 // This values is presented in percentage of perFrameBw: |
401 // perFrameBw = targetBR[Kbps] * 1000 / frameRate. | 413 // perFrameBw = targetBR[Kbps] * 1000 / frameRate. |
402 // The target in % is as follows: | 414 // The target in % is as follows: |
403 | 415 |
404 float scale_parameter = 0.5; | 416 float scale_parameter = 0.5; |
405 uint32 target_pct = optimal_buffer_size_ms * scale_parameter * | 417 uint32 target_pct = optimal_buffer_size_ms * scale_parameter * |
406 cast_config_.max_frame_rate / 10; | 418 cast_config_.max_frame_rate / 10; |
407 | 419 |
408 // Don't go below 3 times the per frame bandwidth. | 420 // Don't go below 3 times the per frame bandwidth. |
409 return std::max(target_pct, kMinIntra); | 421 return std::max(target_pct, kMinIntra); |
410 } | 422 } |
411 | 423 |
412 } // namespace cast | 424 } // namespace cast |
413 } // namespace media | 425 } // namespace media |
OLD | NEW |