| 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/buffered_data_source.h" | |
| 6 | |
| 7 #include <utility> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/callback_helpers.h" | |
| 11 #include "base/location.h" | |
| 12 #include "base/macros.h" | |
| 13 #include "base/single_thread_task_runner.h" | |
| 14 #include "build/build_config.h" | |
| 15 #include "media/base/media_log.h" | |
| 16 #include "net/base/net_errors.h" | |
| 17 | |
| 18 using blink::WebFrame; | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 // BufferedDataSource has an intermediate buffer, this value governs the initial | |
| 23 // size of that buffer. It is set to 32KB because this is a typical read size | |
| 24 // of FFmpeg. | |
| 25 const int kInitialReadBufferSize = 32768; | |
| 26 | |
| 27 // The number of milliseconds to wait before retrying a failed load. | |
| 28 const int kLoaderFailedRetryDelayMs = 250; | |
| 29 | |
| 30 // Each retry, add this many MS to the delay. | |
| 31 // total delay is: | |
| 32 // (kLoaderPartialRetryDelayMs + | |
| 33 // kAdditionalDelayPerRetryMs * (kMaxRetries - 1) / 2) * kLoaderRetries | |
| 34 // = 29250 ms | |
| 35 const int kAdditionalDelayPerRetryMs = 50; | |
| 36 | |
| 37 } // namespace | |
| 38 | |
| 39 namespace media { | |
| 40 | |
| 41 class BufferedDataSource::ReadOperation { | |
| 42 public: | |
| 43 ReadOperation(int64_t position, | |
| 44 int size, | |
| 45 uint8_t* data, | |
| 46 const DataSource::ReadCB& callback); | |
| 47 ~ReadOperation(); | |
| 48 | |
| 49 // Runs |callback_| with the given |result|, deleting the operation | |
| 50 // afterwards. | |
| 51 static void Run(std::unique_ptr<ReadOperation> read_op, int result); | |
| 52 | |
| 53 // State for the number of times this read operation has been retried. | |
| 54 int retries() { return retries_; } | |
| 55 void IncrementRetries() { ++retries_; } | |
| 56 | |
| 57 int64_t position() { return position_; } | |
| 58 int size() { return size_; } | |
| 59 uint8_t* data() { return data_; } | |
| 60 | |
| 61 private: | |
| 62 int retries_; | |
| 63 | |
| 64 const int64_t position_; | |
| 65 const int size_; | |
| 66 uint8_t* data_; | |
| 67 DataSource::ReadCB callback_; | |
| 68 | |
| 69 DISALLOW_IMPLICIT_CONSTRUCTORS(ReadOperation); | |
| 70 }; | |
| 71 | |
| 72 BufferedDataSource::ReadOperation::ReadOperation( | |
| 73 int64_t position, | |
| 74 int size, | |
| 75 uint8_t* data, | |
| 76 const DataSource::ReadCB& callback) | |
| 77 : retries_(0), | |
| 78 position_(position), | |
| 79 size_(size), | |
| 80 data_(data), | |
| 81 callback_(callback) { | |
| 82 DCHECK(!callback_.is_null()); | |
| 83 } | |
| 84 | |
| 85 BufferedDataSource::ReadOperation::~ReadOperation() { | |
| 86 DCHECK(callback_.is_null()); | |
| 87 } | |
| 88 | |
| 89 // static | |
| 90 void BufferedDataSource::ReadOperation::Run( | |
| 91 std::unique_ptr<ReadOperation> read_op, | |
| 92 int result) { | |
| 93 base::ResetAndReturn(&read_op->callback_).Run(result); | |
| 94 } | |
| 95 | |
| 96 BufferedDataSource::BufferedDataSource( | |
| 97 const GURL& url, | |
| 98 BufferedResourceLoader::CORSMode cors_mode, | |
| 99 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | |
| 100 WebFrame* frame, | |
| 101 MediaLog* media_log, | |
| 102 BufferedDataSourceHost* host, | |
| 103 const DownloadingCB& downloading_cb) | |
| 104 : url_(url), | |
| 105 cors_mode_(cors_mode), | |
| 106 total_bytes_(kPositionNotSpecified), | |
| 107 streaming_(false), | |
| 108 frame_(frame), | |
| 109 intermediate_read_buffer_(kInitialReadBufferSize), | |
| 110 render_task_runner_(task_runner), | |
| 111 stop_signal_received_(false), | |
| 112 media_has_played_(false), | |
| 113 buffering_strategy_(BUFFERING_STRATEGY_NORMAL), | |
| 114 preload_(AUTO), | |
| 115 bitrate_(0), | |
| 116 playback_rate_(0.0), | |
| 117 media_log_(media_log), | |
| 118 host_(host), | |
| 119 downloading_cb_(downloading_cb), | |
| 120 weak_factory_(this) { | |
| 121 weak_ptr_ = weak_factory_.GetWeakPtr(); | |
| 122 DCHECK(host_); | |
| 123 DCHECK(!downloading_cb_.is_null()); | |
| 124 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 125 } | |
| 126 | |
| 127 BufferedDataSource::~BufferedDataSource() { | |
| 128 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 129 } | |
| 130 | |
| 131 bool BufferedDataSource::media_has_played() const { | |
| 132 return media_has_played_; | |
| 133 } | |
| 134 | |
| 135 bool BufferedDataSource::assume_fully_buffered() { | |
| 136 return !url_.SchemeIsHTTPOrHTTPS(); | |
| 137 } | |
| 138 | |
| 139 // A factory method to create BufferedResourceLoader using the read parameters. | |
| 140 // This method can be overridden to inject mock BufferedResourceLoader object | |
| 141 // for testing purpose. | |
| 142 BufferedResourceLoader* BufferedDataSource::CreateResourceLoader( | |
| 143 int64_t first_byte_position, | |
| 144 int64_t last_byte_position) { | |
| 145 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 146 | |
| 147 BufferedResourceLoader::DeferStrategy strategy = preload_ == METADATA ? | |
| 148 BufferedResourceLoader::kReadThenDefer : | |
| 149 BufferedResourceLoader::kCapacityDefer; | |
| 150 | |
| 151 return new BufferedResourceLoader(url_, | |
| 152 cors_mode_, | |
| 153 first_byte_position, | |
| 154 last_byte_position, | |
| 155 strategy, | |
| 156 bitrate_, | |
| 157 playback_rate_, | |
| 158 media_log_.get()); | |
| 159 } | |
| 160 | |
| 161 void BufferedDataSource::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 if (url_.SchemeIsHTTPOrHTTPS()) { | |
| 169 // Do an unbounded range request starting at the beginning. If the server | |
| 170 // responds with 200 instead of 206 we'll fall back into a streaming mode. | |
| 171 loader_.reset(CreateResourceLoader(0, kPositionNotSpecified)); | |
| 172 } else { | |
| 173 // For all other protocols, assume they support range request. We fetch | |
| 174 // the full range of the resource to obtain the instance size because | |
| 175 // we won't be served HTTP headers. | |
| 176 loader_.reset(CreateResourceLoader(kPositionNotSpecified, | |
| 177 kPositionNotSpecified)); | |
| 178 } | |
| 179 | |
| 180 base::WeakPtr<BufferedDataSource> weak_this = weak_factory_.GetWeakPtr(); | |
| 181 loader_->Start( | |
| 182 base::Bind(&BufferedDataSource::StartCallback, weak_this), | |
| 183 base::Bind(&BufferedDataSource::LoadingStateChangedCallback, weak_this), | |
| 184 base::Bind(&BufferedDataSource::ProgressCallback, weak_this), | |
| 185 frame_); | |
| 186 } | |
| 187 | |
| 188 void BufferedDataSource::SetPreload(Preload preload) { | |
| 189 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 190 preload_ = preload; | |
| 191 } | |
| 192 | |
| 193 void BufferedDataSource::SetBufferingStrategy( | |
| 194 BufferingStrategy buffering_strategy) { | |
| 195 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 196 buffering_strategy_ = buffering_strategy; | |
| 197 UpdateDeferStrategy(); | |
| 198 } | |
| 199 | |
| 200 bool BufferedDataSource::HasSingleOrigin() { | |
| 201 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 202 DCHECK(init_cb_.is_null() && loader_.get()) | |
| 203 << "Initialize() must complete before calling HasSingleOrigin()"; | |
| 204 return loader_->HasSingleOrigin(); | |
| 205 } | |
| 206 | |
| 207 bool BufferedDataSource::DidPassCORSAccessCheck() const { | |
| 208 return loader_.get() && loader_->DidPassCORSAccessCheck(); | |
| 209 } | |
| 210 | |
| 211 void BufferedDataSource::Abort() { | |
| 212 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 213 { | |
| 214 base::AutoLock auto_lock(lock_); | |
| 215 StopInternal_Locked(); | |
| 216 } | |
| 217 StopLoader(); | |
| 218 frame_ = NULL; | |
| 219 } | |
| 220 | |
| 221 void BufferedDataSource::MediaPlaybackRateChanged(double playback_rate) { | |
| 222 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 223 DCHECK(loader_.get()); | |
| 224 | |
| 225 if (playback_rate < 0.0) | |
| 226 return; | |
| 227 | |
| 228 playback_rate_ = playback_rate; | |
| 229 loader_->SetPlaybackRate(playback_rate); | |
| 230 } | |
| 231 | |
| 232 void BufferedDataSource::MediaIsPlaying() { | |
| 233 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 234 media_has_played_ = true; | |
| 235 UpdateDeferStrategy(); | |
| 236 } | |
| 237 | |
| 238 ///////////////////////////////////////////////////////////////////////////// | |
| 239 // DataSource implementation. | |
| 240 void BufferedDataSource::Stop() { | |
| 241 { | |
| 242 base::AutoLock auto_lock(lock_); | |
| 243 StopInternal_Locked(); | |
| 244 } | |
| 245 | |
| 246 render_task_runner_->PostTask( | |
| 247 FROM_HERE, | |
| 248 base::Bind(&BufferedDataSource::StopLoader, weak_factory_.GetWeakPtr())); | |
| 249 } | |
| 250 | |
| 251 void BufferedDataSource::SetBitrate(int bitrate) { | |
| 252 render_task_runner_->PostTask(FROM_HERE, | |
| 253 base::Bind(&BufferedDataSource::SetBitrateTask, | |
| 254 weak_factory_.GetWeakPtr(), | |
| 255 bitrate)); | |
| 256 } | |
| 257 | |
| 258 void BufferedDataSource::OnBufferingHaveEnough(bool always_cancel) { | |
| 259 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 260 if (loader_ && (always_cancel || (preload_ == METADATA && | |
| 261 !media_has_played_ && !IsStreaming()))) { | |
| 262 loader_->CancelUponDeferral(); | |
| 263 } | |
| 264 } | |
| 265 | |
| 266 int64_t BufferedDataSource::GetMemoryUsage() const { | |
| 267 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 268 return loader_ ? loader_->GetMemoryUsage() : 0; | |
| 269 } | |
| 270 | |
| 271 GURL BufferedDataSource::GetUrlAfterRedirects() const { | |
| 272 return response_original_url_; | |
| 273 } | |
| 274 | |
| 275 void BufferedDataSource::Read(int64_t position, | |
| 276 int size, | |
| 277 uint8_t* data, | |
| 278 const DataSource::ReadCB& read_cb) { | |
| 279 DVLOG(1) << "Read: " << position << " offset, " << size << " bytes"; | |
| 280 DCHECK(!read_cb.is_null()); | |
| 281 | |
| 282 { | |
| 283 base::AutoLock auto_lock(lock_); | |
| 284 DCHECK(!read_op_); | |
| 285 | |
| 286 if (stop_signal_received_) { | |
| 287 read_cb.Run(kReadError); | |
| 288 return; | |
| 289 } | |
| 290 | |
| 291 read_op_.reset(new ReadOperation(position, size, data, read_cb)); | |
| 292 } | |
| 293 | |
| 294 render_task_runner_->PostTask( | |
| 295 FROM_HERE, | |
| 296 base::Bind(&BufferedDataSource::ReadTask, weak_factory_.GetWeakPtr())); | |
| 297 } | |
| 298 | |
| 299 bool BufferedDataSource::GetSize(int64_t* size_out) { | |
| 300 if (total_bytes_ != kPositionNotSpecified) { | |
| 301 *size_out = total_bytes_; | |
| 302 return true; | |
| 303 } | |
| 304 *size_out = 0; | |
| 305 return false; | |
| 306 } | |
| 307 | |
| 308 bool BufferedDataSource::IsStreaming() { | |
| 309 return streaming_; | |
| 310 } | |
| 311 | |
| 312 ///////////////////////////////////////////////////////////////////////////// | |
| 313 // Render thread tasks. | |
| 314 void BufferedDataSource::ReadTask() { | |
| 315 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 316 ReadInternal(); | |
| 317 } | |
| 318 | |
| 319 void BufferedDataSource::StopInternal_Locked() { | |
| 320 lock_.AssertAcquired(); | |
| 321 if (stop_signal_received_) | |
| 322 return; | |
| 323 | |
| 324 stop_signal_received_ = true; | |
| 325 | |
| 326 // Initialize() isn't part of the DataSource interface so don't call it in | |
| 327 // response to Stop(). | |
| 328 init_cb_.Reset(); | |
| 329 | |
| 330 if (read_op_) | |
| 331 ReadOperation::Run(std::move(read_op_), kReadError); | |
| 332 } | |
| 333 | |
| 334 void BufferedDataSource::StopLoader() { | |
| 335 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 336 | |
| 337 if (loader_) | |
| 338 loader_->Stop(); | |
| 339 } | |
| 340 | |
| 341 void BufferedDataSource::SetBitrateTask(int bitrate) { | |
| 342 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 343 DCHECK(loader_.get()); | |
| 344 | |
| 345 bitrate_ = bitrate; | |
| 346 loader_->SetBitrate(bitrate); | |
| 347 } | |
| 348 | |
| 349 // This method is the place where actual read happens, |loader_| must be valid | |
| 350 // prior to make this method call. | |
| 351 void BufferedDataSource::ReadInternal() { | |
| 352 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 353 int64_t position = 0; | |
| 354 int size = 0; | |
| 355 { | |
| 356 base::AutoLock auto_lock(lock_); | |
| 357 if (stop_signal_received_) | |
| 358 return; | |
| 359 | |
| 360 position = read_op_->position(); | |
| 361 size = read_op_->size(); | |
| 362 } | |
| 363 | |
| 364 // First we prepare the intermediate read buffer for BufferedResourceLoader | |
| 365 // to write to. | |
| 366 if (static_cast<int>(intermediate_read_buffer_.size()) < size) | |
| 367 intermediate_read_buffer_.resize(size); | |
| 368 | |
| 369 // Perform the actual read with BufferedResourceLoader. | |
| 370 DCHECK(!intermediate_read_buffer_.empty()); | |
| 371 loader_->Read(position, | |
| 372 size, | |
| 373 &intermediate_read_buffer_[0], | |
| 374 base::Bind(&BufferedDataSource::ReadCallback, | |
| 375 weak_factory_.GetWeakPtr())); | |
| 376 } | |
| 377 | |
| 378 | |
| 379 ///////////////////////////////////////////////////////////////////////////// | |
| 380 // BufferedResourceLoader callback methods. | |
| 381 void BufferedDataSource::StartCallback( | |
| 382 BufferedResourceLoader::Status status) { | |
| 383 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 384 DCHECK(loader_.get()); | |
| 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_->Stop(); | |
| 393 return; | |
| 394 } | |
| 395 | |
| 396 response_original_url_ = loader_->response_original_url(); | |
| 397 #if defined(OS_ANDROID) | |
| 398 // The response original url is the URL of this resource after following | |
| 399 // redirects. Update |url_| to this so that we only follow redirects once. | |
| 400 // We do this on Android only to preserve the behavior we had before the | |
| 401 // unified media pipeline. This behavior will soon exist on all platforms | |
| 402 // as we switch to MultiBufferDataSource (http://crbug.com/514719). | |
| 403 // If the response URL is empty (which happens when it's from a Service | |
| 404 // Worker), keep the original one. | |
| 405 if (!response_original_url_.is_empty()) | |
| 406 url_ = response_original_url_; | |
| 407 #endif // defined(OS_ANDROID) | |
| 408 | |
| 409 // All responses must be successful. Resources that are assumed to be fully | |
| 410 // buffered must have a known content length. | |
| 411 bool success = status == BufferedResourceLoader::kOk && | |
| 412 (!assume_fully_buffered() || | |
| 413 loader_->instance_size() != kPositionNotSpecified); | |
| 414 | |
| 415 if (success) { | |
| 416 total_bytes_ = loader_->instance_size(); | |
| 417 streaming_ = | |
| 418 !assume_fully_buffered() && | |
| 419 (total_bytes_ == kPositionNotSpecified || !loader_->range_supported()); | |
| 420 | |
| 421 media_log_->SetDoubleProperty("total_bytes", | |
| 422 static_cast<double>(total_bytes_)); | |
| 423 media_log_->SetBooleanProperty("streaming", streaming_); | |
| 424 } else { | |
| 425 loader_->Stop(); | |
| 426 } | |
| 427 | |
| 428 // TODO(scherkus): we shouldn't have to lock to signal host(), see | |
| 429 // http://crbug.com/113712 for details. | |
| 430 base::AutoLock auto_lock(lock_); | |
| 431 if (stop_signal_received_) | |
| 432 return; | |
| 433 | |
| 434 if (success) { | |
| 435 if (total_bytes_ != kPositionNotSpecified) { | |
| 436 host_->SetTotalBytes(total_bytes_); | |
| 437 if (assume_fully_buffered()) | |
| 438 host_->AddBufferedByteRange(0, total_bytes_); | |
| 439 } | |
| 440 | |
| 441 media_log_->SetBooleanProperty("single_origin", loader_->HasSingleOrigin()); | |
| 442 media_log_->SetBooleanProperty("passed_cors_access_check", | |
| 443 loader_->DidPassCORSAccessCheck()); | |
| 444 media_log_->SetBooleanProperty("range_header_supported", | |
| 445 loader_->range_supported()); | |
| 446 } | |
| 447 | |
| 448 render_task_runner_->PostTask( | |
| 449 FROM_HERE, base::Bind(base::ResetAndReturn(&init_cb_), success)); | |
| 450 } | |
| 451 | |
| 452 void BufferedDataSource::PartialReadStartCallback( | |
| 453 BufferedResourceLoader::Status status) { | |
| 454 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 455 DCHECK(loader_.get()); | |
| 456 if (status == BufferedResourceLoader::kOk && | |
| 457 CheckPartialResponseURL(loader_->response_original_url())) { | |
| 458 // Once the request has started successfully, we can proceed with | |
| 459 // reading from it. | |
| 460 ReadInternal(); | |
| 461 return; | |
| 462 } | |
| 463 | |
| 464 // Stop the resource loader since we have received an error. | |
| 465 loader_->Stop(); | |
| 466 | |
| 467 // TODO(scherkus): we shouldn't have to lock to signal host(), see | |
| 468 // http://crbug.com/113712 for details. | |
| 469 base::AutoLock auto_lock(lock_); | |
| 470 if (stop_signal_received_) | |
| 471 return; | |
| 472 ReadOperation::Run(std::move(read_op_), kReadError); | |
| 473 } | |
| 474 | |
| 475 bool BufferedDataSource::CheckPartialResponseURL( | |
| 476 const GURL& partial_response_original_url) const { | |
| 477 // We check the redirected URL of partial responses in case malicious | |
| 478 // attackers scan the bytes of other origin resources by mixing their | |
| 479 // generated bytes and the target response. See http://crbug.com/489060#c32 | |
| 480 // for details. | |
| 481 // If the origin of the new response is different from the first response we | |
| 482 // deny the redirected response unless the crossorigin attribute has been set. | |
| 483 if ((response_original_url_.GetOrigin() == | |
| 484 partial_response_original_url.GetOrigin()) || | |
| 485 DidPassCORSAccessCheck()) { | |
| 486 return true; | |
| 487 } | |
| 488 | |
| 489 MEDIA_LOG(ERROR, media_log_) << "BufferedDataSource: origin has changed"; | |
| 490 return false; | |
| 491 } | |
| 492 | |
| 493 void BufferedDataSource::ReadCallback( | |
| 494 BufferedResourceLoader::Status status, | |
| 495 int bytes_read) { | |
| 496 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 497 | |
| 498 // TODO(scherkus): we shouldn't have to lock to signal host(), see | |
| 499 // http://crbug.com/113712 for details. | |
| 500 base::AutoLock auto_lock(lock_); | |
| 501 if (stop_signal_received_) | |
| 502 return; | |
| 503 | |
| 504 if (status != BufferedResourceLoader::kOk) { | |
| 505 // Stop the resource load if it failed. | |
| 506 loader_->Stop(); | |
| 507 | |
| 508 if (read_op_->retries() < kLoaderRetries) { | |
| 509 // Allow some resiliency against sporadic network failures or intentional | |
| 510 // cancellations due to a system suspend / resume. Here we treat failed | |
| 511 // reads as a cache miss so long as we haven't exceeded max retries. | |
| 512 if (status == BufferedResourceLoader::kFailed) { | |
| 513 render_task_runner_->PostDelayedTask( | |
| 514 FROM_HERE, base::Bind(&BufferedDataSource::ReadCallback, | |
| 515 weak_factory_.GetWeakPtr(), | |
| 516 BufferedResourceLoader::kCacheMiss, 0), | |
| 517 base::TimeDelta::FromMilliseconds(kLoaderFailedRetryDelayMs + | |
| 518 read_op_->retries() * | |
| 519 kAdditionalDelayPerRetryMs)); | |
| 520 return; | |
| 521 } | |
| 522 | |
| 523 read_op_->IncrementRetries(); | |
| 524 | |
| 525 // Recreate a loader starting from where we last left off until the | |
| 526 // end of the resource. | |
| 527 loader_.reset(CreateResourceLoader( | |
| 528 read_op_->position(), kPositionNotSpecified)); | |
| 529 | |
| 530 base::WeakPtr<BufferedDataSource> weak_this = weak_factory_.GetWeakPtr(); | |
| 531 loader_->Start( | |
| 532 base::Bind(&BufferedDataSource::PartialReadStartCallback, weak_this), | |
| 533 base::Bind(&BufferedDataSource::LoadingStateChangedCallback, | |
| 534 weak_this), | |
| 535 base::Bind(&BufferedDataSource::ProgressCallback, weak_this), | |
| 536 frame_); | |
| 537 return; | |
| 538 } | |
| 539 | |
| 540 ReadOperation::Run(std::move(read_op_), kReadError); | |
| 541 return; | |
| 542 } | |
| 543 | |
| 544 if (bytes_read > 0) { | |
| 545 DCHECK(!intermediate_read_buffer_.empty()); | |
| 546 memcpy(read_op_->data(), &intermediate_read_buffer_[0], bytes_read); | |
| 547 } else if (bytes_read == 0 && total_bytes_ == kPositionNotSpecified) { | |
| 548 // We've reached the end of the file and we didn't know the total size | |
| 549 // before. Update the total size so Read()s past the end of the file will | |
| 550 // fail like they would if we had known the file size at the beginning. | |
| 551 total_bytes_ = loader_->instance_size(); | |
| 552 | |
| 553 if (total_bytes_ != kPositionNotSpecified) { | |
| 554 host_->SetTotalBytes(total_bytes_); | |
| 555 host_->AddBufferedByteRange(loader_->first_byte_position(), | |
| 556 total_bytes_); | |
| 557 } | |
| 558 } | |
| 559 ReadOperation::Run(std::move(read_op_), bytes_read); | |
| 560 } | |
| 561 | |
| 562 void BufferedDataSource::LoadingStateChangedCallback( | |
| 563 BufferedResourceLoader::LoadingState state) { | |
| 564 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 565 | |
| 566 if (assume_fully_buffered()) | |
| 567 return; | |
| 568 | |
| 569 bool is_downloading_data = false; | |
| 570 switch (state) { | |
| 571 case BufferedResourceLoader::kLoading: | |
| 572 is_downloading_data = true; | |
| 573 break; | |
| 574 case BufferedResourceLoader::kLoadingDeferred: | |
| 575 case BufferedResourceLoader::kLoadingFinished: | |
| 576 is_downloading_data = false; | |
| 577 break; | |
| 578 | |
| 579 // TODO(scherkus): we don't signal network activity changes when loads | |
| 580 // fail to preserve existing behaviour when deferring is toggled, however | |
| 581 // we should consider changing DownloadingCB to also propagate loading | |
| 582 // state. For example there isn't any signal today to notify the client that | |
| 583 // loading has failed (we only get errors on subsequent reads). | |
| 584 case BufferedResourceLoader::kLoadingFailed: | |
| 585 return; | |
| 586 } | |
| 587 | |
| 588 downloading_cb_.Run(is_downloading_data); | |
| 589 } | |
| 590 | |
| 591 void BufferedDataSource::ProgressCallback(int64_t position) { | |
| 592 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
| 593 | |
| 594 if (assume_fully_buffered()) | |
| 595 return; | |
| 596 | |
| 597 // TODO(scherkus): we shouldn't have to lock to signal host(), see | |
| 598 // http://crbug.com/113712 for details. | |
| 599 base::AutoLock auto_lock(lock_); | |
| 600 if (stop_signal_received_) | |
| 601 return; | |
| 602 | |
| 603 host_->AddBufferedByteRange(loader_->first_byte_position(), position); | |
| 604 } | |
| 605 | |
| 606 void BufferedDataSource::UpdateDeferStrategy() { | |
| 607 if (!loader_) | |
| 608 return; | |
| 609 | |
| 610 // No need to aggressively buffer when we are assuming the resource is fully | |
| 611 // buffered. | |
| 612 if (assume_fully_buffered()) { | |
| 613 loader_->UpdateDeferStrategy(BufferedResourceLoader::kCapacityDefer); | |
| 614 return; | |
| 615 } | |
| 616 | |
| 617 // If the playback has started (at which point the preload value is ignored) | |
| 618 // and the strategy is aggressive, then try to load as much as possible (the | |
| 619 // loader will fall back to kCapacityDefer if it knows the current response | |
| 620 // won't be useful from the cache in the future). | |
| 621 bool aggressive = (buffering_strategy_ == BUFFERING_STRATEGY_AGGRESSIVE); | |
| 622 if (media_has_played_ && aggressive && loader_->range_supported()) { | |
| 623 loader_->UpdateDeferStrategy(BufferedResourceLoader::kNeverDefer); | |
| 624 return; | |
| 625 } | |
| 626 | |
| 627 // If media is currently playing or the page indicated preload=auto or the | |
| 628 // the server does not support the byte range request or we do not want to go | |
| 629 // too far ahead of the read head, use threshold strategy to enable/disable | |
| 630 // deferring when the buffer is full/depleted. | |
| 631 loader_->UpdateDeferStrategy(BufferedResourceLoader::kCapacityDefer); | |
| 632 } | |
| 633 | |
| 634 } // namespace media | |
| OLD | NEW |