Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "media/blink/multibuffer_data_source.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/callback_helpers.h" | |
| 9 #include "base/location.h" | |
| 10 #include "base/single_thread_task_runner.h" | |
| 11 #include "media/base/media_log.h" | |
| 12 #include "media/blink/multibuffer_reader.h" | |
| 13 #include "net/base/net_errors.h" | |
| 14 | |
| 15 using blink::WebFrame; | |
| 16 | |
| 17 namespace { | |
| 18 | |
| 19 // Minimum preload buffer. | |
| 20 const int64 kMinBufferPreload = 2 << 20; // 2 Mb | |
| 21 // Maxmimum preload buffer. | |
| 22 const int64 kMaxBufferPreload = 20 << 20; // 20 Mb | |
| 23 | |
| 24 // Preload this much extra, then stop preloading until we fall below the | |
| 25 // kTargetSecondsBufferedAhead. | |
| 26 const int64 kPreloadHighExtra = 1 << 20; // 1 Mb | |
| 27 | |
| 28 // Total size of the pinned region in the cache. | |
| 29 const int64 kMaxBufferSize = 25 << 20; // 25 Mb | |
| 30 | |
| 31 // If bitrate is not known, use this. | |
| 32 const int64 kDefaultBitrate = 200 * 8 << 10; // 200 Kbps. | |
| 33 | |
| 34 // Maximum bitrate for buffer calculations. | |
| 35 const int64 kMaxBitrate = 20 * 8 << 20; // 20 Mbps. | |
| 36 | |
| 37 // Maximum playback rate for buffer calculations. | |
| 38 const double kMaxPlaybackRate = 25.0; | |
| 39 | |
| 40 // Preload this many seconds of data by default. | |
| 41 const int64 kTargetSecondsBufferedAhead = 10; | |
| 42 | |
| 43 // Keep this many seconds of data for going back by default. | |
| 44 const int64 kTargetSecondsBufferedBehind = 2; | |
| 45 | |
| 46 } // namespace | |
| 47 | |
| 48 namespace media { | |
| 49 | |
| 50 template<typename T> | |
| 51 T clamp(T value, T min, T max) { | |
| 52 return std::max(std::min(value, max), min); | |
| 53 } | |
| 54 | |
| 55 class MultibufferDataSource::ReadOperation { | |
| 56 public: | |
| 57 ReadOperation(int64 position, int size, uint8* data, | |
| 58 const DataSource::ReadCB& callback); | |
| 59 ~ReadOperation(); | |
| 60 | |
| 61 // Runs |callback_| with the given |result|, deleting the operation | |
| 62 // afterwards. | |
| 63 static void Run(scoped_ptr<ReadOperation> read_op, int result); | |
| 64 | |
| 65 int64 position() { return position_; } | |
| 66 int size() { return size_; } | |
| 67 uint8* data() { return data_; } | |
| 68 | |
| 69 private: | |
| 70 const int64 position_; | |
| 71 const int size_; | |
| 72 uint8* data_; | |
| 73 DataSource::ReadCB callback_; | |
| 74 | |
| 75 DISALLOW_IMPLICIT_CONSTRUCTORS(ReadOperation); | |
| 76 }; | |
| 77 | |
| 78 MultibufferDataSource::ReadOperation::ReadOperation( | |
| 79 int64 position, int size, uint8* data, | |
| 80 const DataSource::ReadCB& callback) | |
| 81 : position_(position), | |
| 82 size_(size), | |
| 83 data_(data), | |
| 84 callback_(callback) { | |
| 85 DCHECK(!callback_.is_null()); | |
| 86 } | |
| 87 | |
| 88 MultibufferDataSource::ReadOperation::~ReadOperation() { | |
| 89 DCHECK(callback_.is_null()); | |
| 90 } | |
| 91 | |
| 92 // static | |
| 93 void MultibufferDataSource::ReadOperation::Run( | |
| 94 scoped_ptr<ReadOperation> read_op, int result) { | |
| 95 base::ResetAndReturn(&read_op->callback_).Run(result); | |
| 96 } | |
| 97 | |
| 98 MultibufferDataSource::MultibufferDataSource( | |
| 99 const GURL& url, | |
| 100 UrlData::CORSMode cors_mode, | |
| 101 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | |
| 102 ResourceMultiBuffer* multibuffer, | |
| 103 WebFrame* frame, | |
| 104 MediaLog* media_log, | |
| 105 BufferedDataSourceHost* host, | |
| 106 const DownloadingCB& downloading_cb) | |
| 107 : cors_mode_(cors_mode), | |
| 108 total_bytes_(kPositionNotSpecified), | |
| 109 streaming_(false), | |
| 110 loading_(false), | |
| 111 render_task_runner_(task_runner), | |
| 112 multibuffer_(multibuffer), | |
| 113 frame_(frame), | |
| 114 stop_signal_received_(false), | |
| 115 media_has_played_(false), | |
| 116 single_origin_(true), | |
| 117 cancel_on_defer_(false), | |
| 118 preload_(AUTO), | |
| 119 bitrate_(0), | |
| 120 playback_rate_(0.0), | |
| 121 media_log_(media_log), | |
| 122 host_(host), | |
| 123 downloading_cb_(downloading_cb), | |
| 124 weak_factory_(this) { | |
| 125 weak_ptr_ = weak_factory_.GetWeakPtr(); | |
| 126 DCHECK(host_); | |
| 127 DCHECK(!downloading_cb_.is_null()); | |
| 128 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 129 url_data_ = multibuffer_->url_index()->GetByUrl(url, cors_mode_); | |
| 130 url_data_->Use(); | |
| 131 DCHECK(url_data_); | |
| 132 } | |
| 133 | |
| 134 MultibufferDataSource::~MultibufferDataSource() { | |
| 135 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 136 } | |
| 137 | |
| 138 bool MultibufferDataSource::media_has_played() const { | |
| 139 return media_has_played_; | |
| 140 } | |
| 141 | |
| 142 bool MultibufferDataSource::assume_fully_buffered() { | |
| 143 return !url_data_->url().SchemeIsHTTPOrHTTPS(); | |
| 144 } | |
| 145 | |
| 146 // A factory method to create BufferedResourceLoader using the read parameters. | |
|
liberato (no reviews please)
2015/10/16 21:50:36
comment is out of date. also, it doesn't create a
hubbe
2015/10/16 23:47:05
Removed the comment.
| |
| 147 void MultibufferDataSource::CreateResourceLoader( | |
| 148 int64 first_byte_position, int64 last_byte_position) { | |
| 149 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 150 | |
| 151 base::WeakPtr<MultibufferDataSource> weak_this = weak_factory_.GetWeakPtr(); | |
| 152 loader_.reset(new MultiBufferReader( | |
| 153 multibuffer_, | |
| 154 destination_url_data_ ? destination_url_data_ : url_data_, | |
| 155 first_byte_position, | |
| 156 last_byte_position, | |
| 157 base::Bind(&MultibufferDataSource::ProgressCallback, weak_this))); | |
| 158 UpdateBufferSizes(); | |
| 159 } | |
| 160 | |
| 161 void MultibufferDataSource::Initialize(const InitializeCB& init_cb) { | |
| 162 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 163 DCHECK(!init_cb.is_null()); | |
| 164 DCHECK(!loader_.get()); | |
| 165 | |
| 166 init_cb_ = init_cb; | |
| 167 | |
| 168 CreateResourceLoader(0, kPositionNotSpecified); | |
| 169 | |
| 170 base::WeakPtr<MultibufferDataSource> weak_this = weak_factory_.GetWeakPtr(); | |
| 171 | |
| 172 // We're not allowed to call Wait() if data is already available. | |
| 173 if (loader_->Available()) { | |
| 174 render_task_runner_->PostTask( | |
| 175 FROM_HERE, | |
| 176 base::Bind(&MultibufferDataSource::StartCallback, weak_this)); | |
| 177 } else { | |
| 178 loader_->Wait(1, base::Bind( | |
| 179 &MultibufferDataSource::StartCallback, weak_this)); | |
| 180 } | |
| 181 UpdateLoadingState(); | |
| 182 } | |
| 183 | |
| 184 void MultibufferDataSource::SetPreload(Preload preload) { | |
| 185 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 186 preload_ = preload; | |
| 187 UpdateBufferSizes(); | |
| 188 } | |
| 189 | |
| 190 bool MultibufferDataSource::HasSingleOrigin() { | |
| 191 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 192 DCHECK(init_cb_.is_null() && loader_.get()) | |
| 193 << "Initialize() must complete before calling HasSingleOrigin()"; | |
| 194 return single_origin_; | |
| 195 } | |
| 196 | |
| 197 bool MultibufferDataSource::DidPassCORSAccessCheck() const { | |
| 198 if (cors_mode_ == UrlData::kUnspecified) | |
| 199 return false; | |
| 200 // If init_cb is set, we initialization is not finished yet. | |
| 201 if (!init_cb_.is_null()) | |
| 202 return false; | |
| 203 // Loader will be false if there was a failure. | |
| 204 if (!loader_) | |
| 205 return false; | |
| 206 return true; | |
| 207 } | |
| 208 | |
| 209 void MultibufferDataSource::Abort() { | |
| 210 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 211 { | |
| 212 base::AutoLock auto_lock(lock_); | |
| 213 StopInternal_Locked(); | |
| 214 } | |
| 215 StopLoader(); | |
| 216 frame_ = NULL; | |
| 217 } | |
| 218 | |
| 219 void MultibufferDataSource::MediaPlaybackRateChanged(double playback_rate) { | |
| 220 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 221 DCHECK(loader_.get()); | |
| 222 | |
| 223 if (playback_rate < 0.0) | |
| 224 return; | |
| 225 | |
| 226 playback_rate_ = playback_rate; | |
| 227 cancel_on_defer_ = false; | |
| 228 UpdateBufferSizes(); | |
| 229 } | |
| 230 | |
| 231 void MultibufferDataSource::MediaIsPlaying() { | |
| 232 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 233 media_has_played_ = true; | |
| 234 cancel_on_defer_ = false; | |
| 235 paused_ = false; | |
| 236 preload_ = AUTO; | |
| 237 UpdateBufferSizes(); | |
| 238 } | |
| 239 | |
| 240 void MultibufferDataSource::MediaIsPaused() { | |
| 241 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 242 paused_ = true; | |
| 243 UpdateBufferSizes(); | |
| 244 } | |
| 245 | |
| 246 ///////////////////////////////////////////////////////////////////////////// | |
| 247 // DataSource implementation. | |
| 248 void MultibufferDataSource::Stop() { | |
| 249 { | |
| 250 base::AutoLock auto_lock(lock_); | |
| 251 StopInternal_Locked(); | |
| 252 } | |
| 253 | |
| 254 render_task_runner_->PostTask(FROM_HERE, | |
| 255 base::Bind(&MultibufferDataSource::StopLoader, | |
| 256 weak_factory_.GetWeakPtr())); | |
| 257 } | |
| 258 | |
| 259 void MultibufferDataSource::SetBitrate(int bitrate) { | |
| 260 render_task_runner_->PostTask(FROM_HERE, | |
| 261 base::Bind(&MultibufferDataSource::SetBitrateTask, | |
| 262 weak_factory_.GetWeakPtr(), | |
| 263 bitrate)); | |
| 264 } | |
| 265 | |
| 266 void MultibufferDataSource::OnBufferingHaveEnough() { | |
| 267 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 268 if (loader_ && preload_ == METADATA && !media_has_played_ && !IsStreaming()) { | |
| 269 cancel_on_defer_ = true; | |
| 270 if (!loading_) | |
| 271 loader_.reset(nullptr); | |
| 272 } | |
| 273 } | |
| 274 | |
| 275 void MultibufferDataSource::Read( | |
| 276 int64 position, int size, uint8* data, | |
| 277 const DataSource::ReadCB& read_cb) { | |
| 278 DVLOG(1) << "Read: " << position << " offset, " << size << " bytes"; | |
| 279 // Reading is not allowed until after initialization. | |
| 280 DCHECK(init_cb_.is_null()); | |
| 281 DCHECK(!read_cb.is_null()); | |
| 282 | |
| 283 { | |
| 284 base::AutoLock auto_lock(lock_); | |
| 285 DCHECK(!read_op_); | |
| 286 | |
| 287 if (stop_signal_received_) { | |
| 288 read_cb.Run(kReadError); | |
| 289 return; | |
| 290 } | |
| 291 | |
| 292 read_op_.reset(new ReadOperation(position, size, data, read_cb)); | |
| 293 } | |
| 294 | |
| 295 render_task_runner_->PostTask( | |
| 296 FROM_HERE, | |
| 297 base::Bind(&MultibufferDataSource::ReadTask, weak_factory_.GetWeakPtr())); | |
| 298 } | |
| 299 | |
| 300 bool MultibufferDataSource::GetSize(int64* size_out) { | |
| 301 if (destination_url_data_) { | |
| 302 *size_out = destination_url_data_->length(); | |
| 303 if (*size_out != kPositionNotSpecified) { | |
| 304 return true; | |
| 305 } | |
| 306 } | |
| 307 *size_out = 0; | |
| 308 return false; | |
| 309 } | |
| 310 | |
| 311 bool MultibufferDataSource::IsStreaming() { | |
| 312 return streaming_; | |
| 313 } | |
| 314 | |
| 315 ///////////////////////////////////////////////////////////////////////////// | |
| 316 // This method is the place where actual read happens, | |
| 317 void MultibufferDataSource::ReadTask() { | |
| 318 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 319 | |
| 320 base::AutoLock auto_lock(lock_); | |
| 321 int bytes_read = 0; | |
| 322 if (stop_signal_received_) | |
| 323 return; | |
| 324 DCHECK(read_op_); | |
| 325 DCHECK(read_op_->size()); | |
| 326 | |
| 327 if (!loader_) { | |
| 328 CreateResourceLoader(read_op_->position(), kPositionNotSpecified); | |
| 329 } | |
|
liberato (no reviews please)
2015/10/16 21:50:36
else?
hubbe
2015/10/16 23:47:05
Sure, why not?
| |
| 330 | |
| 331 loader_->Seek(read_op_->position()); | |
| 332 | |
| 333 int64_t available = loader_->Available(); | |
| 334 if (available < 0) { | |
| 335 // A failure has occured. | |
| 336 ReadOperation::Run(read_op_.Pass(), kReadError); | |
| 337 return; | |
| 338 } | |
| 339 if (available) { | |
| 340 bytes_read = static_cast<int>( | |
| 341 std::min<int64_t>(available, read_op_->size())); | |
| 342 bytes_read = loader_->TryRead(read_op_->data(), bytes_read); | |
| 343 ReadOperation::Run(read_op_.Pass(), bytes_read); | |
| 344 } else { | |
| 345 loader_->Wait(1, base::Bind(&MultibufferDataSource::ReadTask, | |
| 346 weak_factory_.GetWeakPtr())); | |
| 347 UpdateLoadingState(); | |
| 348 } | |
| 349 } | |
| 350 | |
| 351 void MultibufferDataSource::StopInternal_Locked() { | |
| 352 lock_.AssertAcquired(); | |
| 353 if (stop_signal_received_) | |
| 354 return; | |
| 355 | |
| 356 stop_signal_received_ = true; | |
| 357 | |
| 358 // Initialize() isn't part of the DataSource interface so don't call it in | |
| 359 // response to Stop(). | |
| 360 init_cb_.Reset(); | |
| 361 | |
| 362 if (read_op_) | |
| 363 ReadOperation::Run(read_op_.Pass(), kReadError); | |
| 364 } | |
| 365 | |
| 366 void MultibufferDataSource::StopLoader() { | |
| 367 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 368 loader_.reset(nullptr); | |
| 369 UpdateLoadingState(); | |
| 370 } | |
| 371 | |
| 372 void MultibufferDataSource::SetBitrateTask(int bitrate) { | |
| 373 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 374 DCHECK(loader_.get()); | |
| 375 | |
| 376 bitrate_ = bitrate; | |
| 377 UpdateBufferSizes(); | |
| 378 } | |
| 379 | |
| 380 ///////////////////////////////////////////////////////////////////////////// | |
| 381 // BufferedResourceLoader callback methods. | |
| 382 void MultibufferDataSource::StartCallback() { | |
| 383 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 384 DCHECK(loader_); | |
| 385 | |
| 386 bool init_cb_is_null = false; | |
| 387 { | |
| 388 base::AutoLock auto_lock(lock_); | |
| 389 init_cb_is_null = init_cb_.is_null(); | |
| 390 } | |
| 391 if (init_cb_is_null) { | |
| 392 loader_.reset(); | |
| 393 return; | |
| 394 } | |
| 395 | |
| 396 destination_url_data_ = loader_->GetUrlData(); | |
| 397 | |
| 398 // All responses must be successful. Resources that are assumed to be fully | |
| 399 // buffered must have a known content length. | |
| 400 bool success = | |
| 401 loader_->Available() > 0 && | |
| 402 destination_url_data_ && | |
| 403 (!assume_fully_buffered() || | |
| 404 destination_url_data_->length() != kPositionNotSpecified); | |
| 405 | |
| 406 if (success) { | |
| 407 total_bytes_ = destination_url_data_->length(); | |
| 408 streaming_ = | |
| 409 !assume_fully_buffered() && | |
| 410 (total_bytes_ == kPositionNotSpecified || | |
| 411 !destination_url_data_->range_supported()); | |
| 412 | |
| 413 media_log_->SetDoubleProperty("total_bytes", | |
| 414 static_cast<double>(total_bytes_)); | |
| 415 media_log_->SetBooleanProperty("streaming", streaming_); | |
| 416 } else { | |
| 417 loader_.reset(nullptr); | |
| 418 } | |
| 419 | |
| 420 // TODO(scherkus): we shouldn't have to lock to signal host(), see | |
| 421 // http://crbug.com/113712 for details. | |
| 422 base::AutoLock auto_lock(lock_); | |
| 423 if (stop_signal_received_) | |
| 424 return; | |
| 425 | |
| 426 if (success) { | |
| 427 if (total_bytes_ != kPositionNotSpecified) { | |
| 428 host_->SetTotalBytes(total_bytes_); | |
| 429 if (assume_fully_buffered()) | |
| 430 host_->AddBufferedByteRange(0, total_bytes_); | |
| 431 } | |
| 432 | |
| 433 // Progress callback might be called after the start callback, | |
| 434 // make sure that we update has_single_origin_ now. | |
|
liberato (no reviews please)
2015/10/16 21:50:36
single_origin_
hubbe
2015/10/16 23:47:05
Done.
| |
| 435 UpdateSingleOrigin(); | |
| 436 | |
| 437 media_log_->SetBooleanProperty("single_origin", single_origin_); | |
| 438 media_log_->SetBooleanProperty("passed_cors_access_check", | |
| 439 DidPassCORSAccessCheck()); | |
| 440 media_log_->SetBooleanProperty("range_header_supported", | |
| 441 destination_url_data_->range_supported()); | |
| 442 } | |
| 443 | |
| 444 UpdateLoadingState(); | |
| 445 render_task_runner_->PostTask( | |
| 446 FROM_HERE, base::Bind(base::ResetAndReturn(&init_cb_), success)); | |
| 447 } | |
| 448 | |
| 449 void MultibufferDataSource::UpdateSingleOrigin() { | |
| 450 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 451 if (loader_ && destination_url_data_) { | |
| 452 scoped_refptr<UrlData> new_url_data = loader_->GetUrlData(); | |
| 453 if (new_url_data && new_url_data != destination_url_data_) { | |
| 454 // A redirect has happened. | |
| 455 // Check if origin has changed. | |
| 456 if (destination_url_data_->url().GetOrigin() != | |
| 457 new_url_data->url().GetOrigin()) { | |
| 458 single_origin_ = false; | |
| 459 } | |
| 460 } | |
| 461 } | |
| 462 } | |
| 463 | |
| 464 void MultibufferDataSource::ProgressCallback(int64 begin, int64 end) { | |
| 465 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 466 | |
| 467 UpdateSingleOrigin(); | |
| 468 if (assume_fully_buffered()) | |
| 469 return; | |
| 470 | |
| 471 if (end > begin) { | |
| 472 // TODO(scherkus): we shouldn't have to lock to signal host(), see | |
| 473 // http://crbug.com/113712 for details. | |
| 474 base::AutoLock auto_lock(lock_); | |
| 475 if (stop_signal_received_) | |
| 476 return; | |
| 477 | |
| 478 host_->AddBufferedByteRange(begin, end); | |
| 479 } | |
| 480 | |
| 481 UpdateLoadingState(); | |
| 482 } | |
| 483 | |
| 484 void MultibufferDataSource::UpdateLoadingState() { | |
| 485 // Update loading state. | |
| 486 if ((!!loader_ && loader_->IsLoading()) != loading_) { | |
|
liberato (no reviews please)
2015/10/16 21:50:36
extra points to use s/!=/^ :)
hubbe
2015/10/16 23:47:05
Acknowledged.
| |
| 487 loading_ = !loading_; | |
| 488 | |
| 489 if (!loading_ && cancel_on_defer_) { | |
| 490 loader_.reset(nullptr); | |
| 491 } | |
| 492 | |
| 493 // Callback could kill us, be sure to call it last. | |
| 494 downloading_cb_.Run(loading_); | |
| 495 } | |
| 496 } | |
| 497 | |
| 498 void MultibufferDataSource::UpdateBufferSizes() { | |
| 499 if (!loader_) | |
| 500 return; | |
| 501 | |
| 502 if (!assume_fully_buffered()) { | |
| 503 // If the playback has started and we're paused, then try to load as much as | |
| 504 // possible, assuming that the file is cacheable. (If not, why bother?) | |
| 505 if (media_has_played_ && paused_ && | |
| 506 destination_url_data_ && | |
| 507 destination_url_data_->range_supported() && | |
| 508 destination_url_data_->cacheable()) { | |
| 509 loader_->SetPreload(1LL << 40, 1LL << 40); // 1 Tb | |
| 510 return; | |
| 511 } | |
| 512 } | |
| 513 | |
| 514 // Use a default bit rate if unknown and clamp to prevent overflow. | |
| 515 int64 bitrate = clamp<int64>(bitrate_, 0, kMaxBitrate); | |
| 516 if (bitrate == 0) | |
| 517 bitrate = kDefaultBitrate; | |
| 518 | |
| 519 // Only scale the buffer window for playback rates greater than 1.0 in | |
| 520 // magnitude and clamp to prevent overflow. | |
| 521 bool backward_playback = false; | |
| 522 double playback_rate = playback_rate_; | |
| 523 if (playback_rate < 0.0) { | |
| 524 backward_playback = true; | |
| 525 playback_rate *= -1.0; | |
| 526 } | |
| 527 | |
| 528 playback_rate = std::max(playback_rate, 1.0); | |
| 529 playback_rate = std::min(playback_rate, kMaxPlaybackRate); | |
| 530 | |
| 531 int64 bytes_per_second = (bitrate / 8.0) * playback_rate; | |
| 532 | |
| 533 int64 preload = clamp(kTargetSecondsBufferedAhead * bytes_per_second, | |
| 534 kMinBufferPreload, | |
| 535 kMaxBufferPreload); | |
| 536 int64 back_buffer = clamp(kTargetSecondsBufferedBehind * bytes_per_second, | |
| 537 kMinBufferPreload, | |
| 538 kMaxBufferPreload); | |
| 539 if (backward_playback) | |
| 540 std::swap(preload, back_buffer); | |
| 541 | |
| 542 int64 pin_forwards = kMaxBufferSize - back_buffer; | |
| 543 DCHECK_LE(preload_ + kPreloadHighExtra, pin_forwards); | |
| 544 loader_->SetMaxBuffer(back_buffer, pin_forwards); | |
| 545 | |
| 546 if (preload_ == METADATA) { | |
| 547 loader_->SetPreload(0, 0); | |
| 548 } else { | |
| 549 loader_->SetPreload(preload + kPreloadHighExtra, preload); | |
| 550 } | |
| 551 } | |
| 552 | |
| 553 } // namespace media | |
| OLD | NEW |