webapps: fix theme color on status bar for Galaxy devices
[chromium-blink-merge.git] / media / blink / buffered_data_source.cc
blobeb4c984995ace670230b3ab660fdc6881ab0789d
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.
5 #include "media/blink/buffered_data_source.h"
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 "net/base/net_errors.h"
14 using blink::WebFrame;
16 namespace {
18 // BufferedDataSource has an intermediate buffer, this value governs the initial
19 // size of that buffer. It is set to 32KB because this is a typical read size
20 // of FFmpeg.
21 const int kInitialReadBufferSize = 32768;
23 // Number of cache misses or read failures we allow for a single Read() before
24 // signaling an error.
25 const int kLoaderRetries = 3;
27 // The number of milliseconds to wait before retrying a failed load.
28 const int kLoaderFailedRetryDelayMs = 250;
30 } // namespace
32 namespace media {
34 class BufferedDataSource::ReadOperation {
35 public:
36 ReadOperation(int64 position, int size, uint8* data,
37 const DataSource::ReadCB& callback);
38 ~ReadOperation();
40 // Runs |callback_| with the given |result|, deleting the operation
41 // afterwards.
42 static void Run(scoped_ptr<ReadOperation> read_op, int result);
44 // State for the number of times this read operation has been retried.
45 int retries() { return retries_; }
46 void IncrementRetries() { ++retries_; }
48 int64 position() { return position_; }
49 int size() { return size_; }
50 uint8* data() { return data_; }
52 private:
53 int retries_;
55 const int64 position_;
56 const int size_;
57 uint8* data_;
58 DataSource::ReadCB callback_;
60 DISALLOW_IMPLICIT_CONSTRUCTORS(ReadOperation);
63 BufferedDataSource::ReadOperation::ReadOperation(
64 int64 position, int size, uint8* data,
65 const DataSource::ReadCB& callback)
66 : retries_(0),
67 position_(position),
68 size_(size),
69 data_(data),
70 callback_(callback) {
71 DCHECK(!callback_.is_null());
74 BufferedDataSource::ReadOperation::~ReadOperation() {
75 DCHECK(callback_.is_null());
78 // static
79 void BufferedDataSource::ReadOperation::Run(
80 scoped_ptr<ReadOperation> read_op, int result) {
81 base::ResetAndReturn(&read_op->callback_).Run(result);
84 BufferedDataSource::BufferedDataSource(
85 const GURL& url,
86 BufferedResourceLoader::CORSMode cors_mode,
87 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
88 WebFrame* frame,
89 MediaLog* media_log,
90 BufferedDataSourceHost* host,
91 const DownloadingCB& downloading_cb)
92 : url_(url),
93 cors_mode_(cors_mode),
94 total_bytes_(kPositionNotSpecified),
95 streaming_(false),
96 frame_(frame),
97 intermediate_read_buffer_(kInitialReadBufferSize),
98 render_task_runner_(task_runner),
99 stop_signal_received_(false),
100 media_has_played_(false),
101 preload_(AUTO),
102 bitrate_(0),
103 playback_rate_(0.0),
104 media_log_(media_log),
105 host_(host),
106 downloading_cb_(downloading_cb),
107 weak_factory_(this) {
108 weak_ptr_ = weak_factory_.GetWeakPtr();
109 DCHECK(host_);
110 DCHECK(!downloading_cb_.is_null());
111 DCHECK(render_task_runner_->BelongsToCurrentThread());
114 BufferedDataSource::~BufferedDataSource() {
115 DCHECK(render_task_runner_->BelongsToCurrentThread());
118 // A factory method to create BufferedResourceLoader using the read parameters.
119 // This method can be overridden to inject mock BufferedResourceLoader object
120 // for testing purpose.
121 BufferedResourceLoader* BufferedDataSource::CreateResourceLoader(
122 int64 first_byte_position, int64 last_byte_position) {
123 DCHECK(render_task_runner_->BelongsToCurrentThread());
125 BufferedResourceLoader::DeferStrategy strategy = preload_ == METADATA ?
126 BufferedResourceLoader::kReadThenDefer :
127 BufferedResourceLoader::kCapacityDefer;
129 return new BufferedResourceLoader(url_,
130 cors_mode_,
131 first_byte_position,
132 last_byte_position,
133 strategy,
134 bitrate_,
135 playback_rate_,
136 media_log_.get());
139 void BufferedDataSource::Initialize(const InitializeCB& init_cb) {
140 DCHECK(render_task_runner_->BelongsToCurrentThread());
141 DCHECK(!init_cb.is_null());
142 DCHECK(!loader_.get());
144 init_cb_ = init_cb;
146 if (url_.SchemeIsHTTPOrHTTPS()) {
147 // Do an unbounded range request starting at the beginning. If the server
148 // responds with 200 instead of 206 we'll fall back into a streaming mode.
149 loader_.reset(CreateResourceLoader(0, kPositionNotSpecified));
150 } else {
151 // For all other protocols, assume they support range request. We fetch
152 // the full range of the resource to obtain the instance size because
153 // we won't be served HTTP headers.
154 loader_.reset(CreateResourceLoader(kPositionNotSpecified,
155 kPositionNotSpecified));
158 base::WeakPtr<BufferedDataSource> weak_this = weak_factory_.GetWeakPtr();
159 loader_->Start(
160 base::Bind(&BufferedDataSource::StartCallback, weak_this),
161 base::Bind(&BufferedDataSource::LoadingStateChangedCallback, weak_this),
162 base::Bind(&BufferedDataSource::ProgressCallback, weak_this),
163 frame_);
166 void BufferedDataSource::SetPreload(Preload preload) {
167 DCHECK(render_task_runner_->BelongsToCurrentThread());
168 preload_ = preload;
171 bool BufferedDataSource::HasSingleOrigin() {
172 DCHECK(render_task_runner_->BelongsToCurrentThread());
173 DCHECK(init_cb_.is_null() && loader_.get())
174 << "Initialize() must complete before calling HasSingleOrigin()";
175 return loader_->HasSingleOrigin();
178 bool BufferedDataSource::DidPassCORSAccessCheck() const {
179 return loader_.get() && loader_->DidPassCORSAccessCheck();
182 void BufferedDataSource::Abort() {
183 DCHECK(render_task_runner_->BelongsToCurrentThread());
185 base::AutoLock auto_lock(lock_);
186 StopInternal_Locked();
188 StopLoader();
189 frame_ = NULL;
192 void BufferedDataSource::MediaPlaybackRateChanged(double playback_rate) {
193 DCHECK(render_task_runner_->BelongsToCurrentThread());
194 DCHECK(loader_.get());
196 if (playback_rate < 0.0)
197 return;
199 playback_rate_ = playback_rate;
200 loader_->SetPlaybackRate(playback_rate);
203 void BufferedDataSource::MediaIsPlaying() {
204 DCHECK(render_task_runner_->BelongsToCurrentThread());
205 media_has_played_ = true;
206 UpdateDeferStrategy(false);
209 void BufferedDataSource::MediaIsPaused() {
210 DCHECK(render_task_runner_->BelongsToCurrentThread());
211 UpdateDeferStrategy(true);
214 /////////////////////////////////////////////////////////////////////////////
215 // DataSource implementation.
216 void BufferedDataSource::Stop() {
218 base::AutoLock auto_lock(lock_);
219 StopInternal_Locked();
222 render_task_runner_->PostTask(
223 FROM_HERE,
224 base::Bind(&BufferedDataSource::StopLoader, weak_factory_.GetWeakPtr()));
227 void BufferedDataSource::SetBitrate(int bitrate) {
228 render_task_runner_->PostTask(FROM_HERE,
229 base::Bind(&BufferedDataSource::SetBitrateTask,
230 weak_factory_.GetWeakPtr(),
231 bitrate));
234 void BufferedDataSource::OnBufferingHaveEnough() {
235 DCHECK(render_task_runner_->BelongsToCurrentThread());
236 if (loader_ && preload_ == METADATA && !media_has_played_ && !IsStreaming())
237 loader_->CancelUponDeferral();
240 void BufferedDataSource::Read(
241 int64 position, int size, uint8* data,
242 const DataSource::ReadCB& read_cb) {
243 DVLOG(1) << "Read: " << position << " offset, " << size << " bytes";
244 DCHECK(!read_cb.is_null());
247 base::AutoLock auto_lock(lock_);
248 DCHECK(!read_op_);
250 if (stop_signal_received_) {
251 read_cb.Run(kReadError);
252 return;
255 read_op_.reset(new ReadOperation(position, size, data, read_cb));
258 render_task_runner_->PostTask(
259 FROM_HERE,
260 base::Bind(&BufferedDataSource::ReadTask, weak_factory_.GetWeakPtr()));
263 bool BufferedDataSource::GetSize(int64* size_out) {
264 if (total_bytes_ != kPositionNotSpecified) {
265 *size_out = total_bytes_;
266 return true;
268 *size_out = 0;
269 return false;
272 bool BufferedDataSource::IsStreaming() {
273 return streaming_;
276 /////////////////////////////////////////////////////////////////////////////
277 // Render thread tasks.
278 void BufferedDataSource::ReadTask() {
279 DCHECK(render_task_runner_->BelongsToCurrentThread());
280 ReadInternal();
283 void BufferedDataSource::StopInternal_Locked() {
284 lock_.AssertAcquired();
285 if (stop_signal_received_)
286 return;
288 stop_signal_received_ = true;
290 // Initialize() isn't part of the DataSource interface so don't call it in
291 // response to Stop().
292 init_cb_.Reset();
294 if (read_op_)
295 ReadOperation::Run(read_op_.Pass(), kReadError);
298 void BufferedDataSource::StopLoader() {
299 DCHECK(render_task_runner_->BelongsToCurrentThread());
301 if (loader_)
302 loader_->Stop();
305 void BufferedDataSource::SetBitrateTask(int bitrate) {
306 DCHECK(render_task_runner_->BelongsToCurrentThread());
307 DCHECK(loader_.get());
309 bitrate_ = bitrate;
310 loader_->SetBitrate(bitrate);
313 // This method is the place where actual read happens, |loader_| must be valid
314 // prior to make this method call.
315 void BufferedDataSource::ReadInternal() {
316 DCHECK(render_task_runner_->BelongsToCurrentThread());
317 int64 position = 0;
318 int size = 0;
320 base::AutoLock auto_lock(lock_);
321 if (stop_signal_received_)
322 return;
324 position = read_op_->position();
325 size = read_op_->size();
328 // First we prepare the intermediate read buffer for BufferedResourceLoader
329 // to write to.
330 if (static_cast<int>(intermediate_read_buffer_.size()) < size)
331 intermediate_read_buffer_.resize(size);
333 // Perform the actual read with BufferedResourceLoader.
334 DCHECK(!intermediate_read_buffer_.empty());
335 loader_->Read(position,
336 size,
337 &intermediate_read_buffer_[0],
338 base::Bind(&BufferedDataSource::ReadCallback,
339 weak_factory_.GetWeakPtr()));
343 /////////////////////////////////////////////////////////////////////////////
344 // BufferedResourceLoader callback methods.
345 void BufferedDataSource::StartCallback(
346 BufferedResourceLoader::Status status) {
347 DCHECK(render_task_runner_->BelongsToCurrentThread());
348 DCHECK(loader_.get());
350 bool init_cb_is_null = false;
352 base::AutoLock auto_lock(lock_);
353 init_cb_is_null = init_cb_.is_null();
355 if (init_cb_is_null) {
356 loader_->Stop();
357 return;
359 response_original_url_ = loader_->response_original_url();
361 // All responses must be successful. Resources that are assumed to be fully
362 // buffered must have a known content length.
363 bool success = status == BufferedResourceLoader::kOk &&
364 (!assume_fully_buffered() ||
365 loader_->instance_size() != kPositionNotSpecified);
367 if (success) {
368 total_bytes_ = loader_->instance_size();
369 streaming_ =
370 !assume_fully_buffered() &&
371 (total_bytes_ == kPositionNotSpecified || !loader_->range_supported());
373 media_log_->SetDoubleProperty("total_bytes",
374 static_cast<double>(total_bytes_));
375 media_log_->SetBooleanProperty("streaming", streaming_);
376 } else {
377 loader_->Stop();
380 // TODO(scherkus): we shouldn't have to lock to signal host(), see
381 // http://crbug.com/113712 for details.
382 base::AutoLock auto_lock(lock_);
383 if (stop_signal_received_)
384 return;
386 if (success) {
387 if (total_bytes_ != kPositionNotSpecified) {
388 host_->SetTotalBytes(total_bytes_);
389 if (assume_fully_buffered())
390 host_->AddBufferedByteRange(0, total_bytes_);
393 media_log_->SetBooleanProperty("single_origin", loader_->HasSingleOrigin());
394 media_log_->SetBooleanProperty("passed_cors_access_check",
395 loader_->DidPassCORSAccessCheck());
396 media_log_->SetBooleanProperty("range_header_supported",
397 loader_->range_supported());
400 render_task_runner_->PostTask(
401 FROM_HERE, base::Bind(base::ResetAndReturn(&init_cb_), success));
404 void BufferedDataSource::PartialReadStartCallback(
405 BufferedResourceLoader::Status status) {
406 DCHECK(render_task_runner_->BelongsToCurrentThread());
407 DCHECK(loader_.get());
408 if (status == BufferedResourceLoader::kOk &&
409 CheckPartialResponseURL(loader_->response_original_url())) {
410 // Once the request has started successfully, we can proceed with
411 // reading from it.
412 ReadInternal();
413 return;
416 // Stop the resource loader since we have received an error.
417 loader_->Stop();
419 // TODO(scherkus): we shouldn't have to lock to signal host(), see
420 // http://crbug.com/113712 for details.
421 base::AutoLock auto_lock(lock_);
422 if (stop_signal_received_)
423 return;
424 ReadOperation::Run(read_op_.Pass(), kReadError);
427 bool BufferedDataSource::CheckPartialResponseURL(
428 const GURL& partial_response_original_url) const {
429 // We check the redirected URL of partial responses in case malicious
430 // attackers scan the bytes of other origin resources by mixing their
431 // generated bytes and the target response. See http://crbug.com/489060#c32
432 // for details.
433 // If the origin of the new response is different from the first response we
434 // deny the redirected response.
435 return response_original_url_.GetOrigin() ==
436 partial_response_original_url.GetOrigin();
439 void BufferedDataSource::ReadCallback(
440 BufferedResourceLoader::Status status,
441 int bytes_read) {
442 DCHECK(render_task_runner_->BelongsToCurrentThread());
444 // TODO(scherkus): we shouldn't have to lock to signal host(), see
445 // http://crbug.com/113712 for details.
446 base::AutoLock auto_lock(lock_);
447 if (stop_signal_received_)
448 return;
450 if (status != BufferedResourceLoader::kOk) {
451 // Stop the resource load if it failed.
452 loader_->Stop();
454 if (read_op_->retries() < kLoaderRetries) {
455 // Allow some resiliency against sporadic network failures or intentional
456 // cancellations due to a system suspend / resume. Here we treat failed
457 // reads as a cache miss so long as we haven't exceeded max retries.
458 if (status == BufferedResourceLoader::kFailed) {
459 render_task_runner_->PostDelayedTask(
460 FROM_HERE, base::Bind(&BufferedDataSource::ReadCallback,
461 weak_factory_.GetWeakPtr(),
462 BufferedResourceLoader::kCacheMiss, 0),
463 base::TimeDelta::FromMilliseconds(kLoaderFailedRetryDelayMs));
464 return;
467 read_op_->IncrementRetries();
469 // Recreate a loader starting from where we last left off until the
470 // end of the resource.
471 loader_.reset(CreateResourceLoader(
472 read_op_->position(), kPositionNotSpecified));
474 base::WeakPtr<BufferedDataSource> weak_this = weak_factory_.GetWeakPtr();
475 loader_->Start(
476 base::Bind(&BufferedDataSource::PartialReadStartCallback, weak_this),
477 base::Bind(&BufferedDataSource::LoadingStateChangedCallback,
478 weak_this),
479 base::Bind(&BufferedDataSource::ProgressCallback, weak_this),
480 frame_);
481 return;
484 ReadOperation::Run(read_op_.Pass(), kReadError);
485 return;
488 if (bytes_read > 0) {
489 DCHECK(!intermediate_read_buffer_.empty());
490 memcpy(read_op_->data(), &intermediate_read_buffer_[0], bytes_read);
491 } else if (bytes_read == 0 && total_bytes_ == kPositionNotSpecified) {
492 // We've reached the end of the file and we didn't know the total size
493 // before. Update the total size so Read()s past the end of the file will
494 // fail like they would if we had known the file size at the beginning.
495 total_bytes_ = loader_->instance_size();
497 if (total_bytes_ != kPositionNotSpecified) {
498 host_->SetTotalBytes(total_bytes_);
499 host_->AddBufferedByteRange(loader_->first_byte_position(),
500 total_bytes_);
503 ReadOperation::Run(read_op_.Pass(), bytes_read);
506 void BufferedDataSource::LoadingStateChangedCallback(
507 BufferedResourceLoader::LoadingState state) {
508 DCHECK(render_task_runner_->BelongsToCurrentThread());
510 if (assume_fully_buffered())
511 return;
513 bool is_downloading_data;
514 switch (state) {
515 case BufferedResourceLoader::kLoading:
516 is_downloading_data = true;
517 break;
518 case BufferedResourceLoader::kLoadingDeferred:
519 case BufferedResourceLoader::kLoadingFinished:
520 is_downloading_data = false;
521 break;
523 // TODO(scherkus): we don't signal network activity changes when loads
524 // fail to preserve existing behaviour when deferring is toggled, however
525 // we should consider changing DownloadingCB to also propagate loading
526 // state. For example there isn't any signal today to notify the client that
527 // loading has failed (we only get errors on subsequent reads).
528 case BufferedResourceLoader::kLoadingFailed:
529 return;
532 downloading_cb_.Run(is_downloading_data);
535 void BufferedDataSource::ProgressCallback(int64 position) {
536 DCHECK(render_task_runner_->BelongsToCurrentThread());
538 if (assume_fully_buffered())
539 return;
541 // TODO(scherkus): we shouldn't have to lock to signal host(), see
542 // http://crbug.com/113712 for details.
543 base::AutoLock auto_lock(lock_);
544 if (stop_signal_received_)
545 return;
547 host_->AddBufferedByteRange(loader_->first_byte_position(), position);
550 void BufferedDataSource::UpdateDeferStrategy(bool paused) {
551 // No need to aggressively buffer when we are assuming the resource is fully
552 // buffered.
553 if (assume_fully_buffered()) {
554 loader_->UpdateDeferStrategy(BufferedResourceLoader::kCapacityDefer);
555 return;
558 // If the playback has started (at which point the preload value is ignored)
559 // and we're paused, then try to load as much as possible (the loader will
560 // fall back to kCapacityDefer if it knows the current response won't be
561 // useful from the cache in the future).
562 if (media_has_played_ && paused && loader_->range_supported()) {
563 loader_->UpdateDeferStrategy(BufferedResourceLoader::kNeverDefer);
564 return;
567 // If media is currently playing or the page indicated preload=auto or the
568 // the server does not support the byte range request or we do not want to go
569 // too far ahead of the read head, use threshold strategy to enable/disable
570 // deferring when the buffer is full/depleted.
571 loader_->UpdateDeferStrategy(BufferedResourceLoader::kCapacityDefer);
574 } // namespace media