chromeos: bluetooth: add BluetoothInputClient
[chromium-blink-merge.git] / webkit / media / buffered_data_source.cc
blobe20b941a045c769cf15fd457f0689a6103b3fb06
1 // Copyright (c) 2012 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 "webkit/media/buffered_data_source.h"
7 #include "base/bind.h"
8 #include "media/base/media_log.h"
9 #include "net/base/net_errors.h"
11 using WebKit::WebFrame;
13 namespace webkit_media {
15 // BufferedDataSource has an intermediate buffer, this value governs the initial
16 // size of that buffer. It is set to 32KB because this is a typical read size
17 // of FFmpeg.
18 static const int kInitialReadBufferSize = 32768;
20 // Number of cache misses we allow for a single Read() before signalling an
21 // error.
22 static const int kNumCacheMissRetries = 3;
24 BufferedDataSource::BufferedDataSource(
25 MessageLoop* render_loop,
26 WebFrame* frame,
27 media::MediaLog* media_log)
28 : total_bytes_(kPositionNotSpecified),
29 buffered_bytes_(0),
30 streaming_(false),
31 frame_(frame),
32 loader_(NULL),
33 is_downloading_data_(false),
34 read_position_(0),
35 read_size_(0),
36 read_buffer_(NULL),
37 intermediate_read_buffer_(new uint8[kInitialReadBufferSize]),
38 intermediate_read_buffer_size_(kInitialReadBufferSize),
39 render_loop_(render_loop),
40 stop_signal_received_(false),
41 stopped_on_render_loop_(false),
42 media_is_paused_(true),
43 media_has_played_(false),
44 preload_(media::AUTO),
45 using_range_request_(true),
46 cache_miss_retries_left_(kNumCacheMissRetries),
47 bitrate_(0),
48 playback_rate_(0.0),
49 media_log_(media_log) {
52 BufferedDataSource::~BufferedDataSource() {}
54 // A factory method to create BufferedResourceLoader using the read parameters.
55 // This method can be overrided to inject mock BufferedResourceLoader object
56 // for testing purpose.
57 BufferedResourceLoader* BufferedDataSource::CreateResourceLoader(
58 int64 first_byte_position, int64 last_byte_position) {
59 DCHECK(MessageLoop::current() == render_loop_);
61 return new BufferedResourceLoader(url_,
62 first_byte_position,
63 last_byte_position,
64 ChooseDeferStrategy(),
65 bitrate_,
66 playback_rate_,
67 media_log_);
70 void BufferedDataSource::set_host(media::DataSourceHost* host) {
71 DataSource::set_host(host);
73 if (loader_.get()) {
74 base::AutoLock auto_lock(lock_);
75 UpdateHostState_Locked();
79 void BufferedDataSource::Initialize(
80 const GURL& url,
81 const media::PipelineStatusCB& initialize_cb) {
82 DCHECK(MessageLoop::current() == render_loop_);
83 DCHECK(!initialize_cb.is_null());
84 DCHECK(!loader_.get());
85 url_ = url;
87 // This data source doesn't support data:// protocol so reject it.
88 if (url_.SchemeIs(kDataScheme)) {
89 initialize_cb.Run(media::DATASOURCE_ERROR_URL_NOT_SUPPORTED);
90 return;
93 initialize_cb_ = initialize_cb;
95 if (url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)) {
96 // Do an unbounded range request starting at the beginning. If the server
97 // responds with 200 instead of 206 we'll fall back into a streaming mode.
98 loader_.reset(CreateResourceLoader(0, kPositionNotSpecified));
99 loader_->Start(
100 base::Bind(&BufferedDataSource::HttpInitialStartCallback, this),
101 base::Bind(&BufferedDataSource::NetworkEventCallback, this),
102 frame_);
103 return;
106 // For all other protocols, assume they support range request. We fetch
107 // the full range of the resource to obtain the instance size because
108 // we won't be served HTTP headers.
109 loader_.reset(CreateResourceLoader(kPositionNotSpecified,
110 kPositionNotSpecified));
111 loader_->Start(
112 base::Bind(&BufferedDataSource::NonHttpInitialStartCallback, this),
113 base::Bind(&BufferedDataSource::NetworkEventCallback, this),
114 frame_);
117 /////////////////////////////////////////////////////////////////////////////
118 // media::Filter implementation.
119 void BufferedDataSource::Stop(const base::Closure& closure) {
121 base::AutoLock auto_lock(lock_);
122 stop_signal_received_ = true;
124 if (!closure.is_null())
125 closure.Run();
127 render_loop_->PostTask(FROM_HERE,
128 base::Bind(&BufferedDataSource::CleanupTask, this));
131 void BufferedDataSource::SetPlaybackRate(float playback_rate) {
132 render_loop_->PostTask(FROM_HERE, base::Bind(
133 &BufferedDataSource::SetPlaybackRateTask, this, playback_rate));
136 void BufferedDataSource::SetPreload(media::Preload preload) {
137 render_loop_->PostTask(FROM_HERE, base::Bind(
138 &BufferedDataSource::SetPreloadTask, this, preload));
141 void BufferedDataSource::SetBitrate(int bitrate) {
142 render_loop_->PostTask(FROM_HERE, base::Bind(
143 &BufferedDataSource::SetBitrateTask, this, bitrate));
146 /////////////////////////////////////////////////////////////////////////////
147 // media::DataSource implementation.
148 void BufferedDataSource::Read(
149 int64 position, size_t size, uint8* data,
150 const media::DataSource::ReadCallback& read_cb) {
151 DVLOG(1) << "Read: " << position << " offset, " << size << " bytes";
152 DCHECK(!read_cb.is_null());
155 base::AutoLock auto_lock(lock_);
156 DCHECK(read_cb_.is_null());
158 if (stop_signal_received_ || stopped_on_render_loop_) {
159 read_cb.Run(kReadError);
160 return;
163 read_cb_ = read_cb;
166 render_loop_->PostTask(FROM_HERE, base::Bind(
167 &BufferedDataSource::ReadTask, this,
168 position, static_cast<int>(size), data));
171 bool BufferedDataSource::GetSize(int64* size_out) {
172 if (total_bytes_ != kPositionNotSpecified) {
173 *size_out = total_bytes_;
174 return true;
176 *size_out = 0;
177 return false;
180 bool BufferedDataSource::IsStreaming() {
181 return streaming_;
184 bool BufferedDataSource::HasSingleOrigin() {
185 DCHECK(MessageLoop::current() == render_loop_);
186 return loader_.get() ? loader_->HasSingleOrigin() : true;
189 void BufferedDataSource::Abort() {
190 DCHECK(MessageLoop::current() == render_loop_);
192 CleanupTask();
193 frame_ = NULL;
196 /////////////////////////////////////////////////////////////////////////////
197 // Render thread tasks.
198 void BufferedDataSource::ReadTask(
199 int64 position,
200 int read_size,
201 uint8* buffer) {
202 DCHECK(MessageLoop::current() == render_loop_);
204 base::AutoLock auto_lock(lock_);
205 if (stopped_on_render_loop_)
206 return;
208 DCHECK(!read_cb_.is_null());
211 // Saves the read parameters.
212 read_position_ = position;
213 read_size_ = read_size;
214 read_buffer_ = buffer;
215 cache_miss_retries_left_ = kNumCacheMissRetries;
217 // Call to read internal to perform the actual read.
218 ReadInternal();
221 void BufferedDataSource::CleanupTask() {
222 DCHECK(MessageLoop::current() == render_loop_);
225 base::AutoLock auto_lock(lock_);
226 initialize_cb_.Reset();
227 if (stopped_on_render_loop_)
228 return;
230 // Signal that stop task has finished execution.
231 // NOTE: it's vital that this be set under lock, as that's how Read() tests
232 // before registering a new |read_cb_| (which is cleared below).
233 stopped_on_render_loop_ = true;
235 if (!read_cb_.is_null())
236 DoneRead_Locked(net::ERR_FAILED);
239 // We just need to stop the loader, so it stops activity.
240 if (loader_.get())
241 loader_->Stop();
243 // Reset the parameters of the current read request.
244 read_position_ = 0;
245 read_size_ = 0;
246 read_buffer_ = 0;
249 void BufferedDataSource::RestartLoadingTask() {
250 DCHECK(MessageLoop::current() == render_loop_);
251 if (stopped_on_render_loop_)
252 return;
255 // If there's no outstanding read then return early.
256 base::AutoLock auto_lock(lock_);
257 if (read_cb_.is_null())
258 return;
261 loader_.reset(CreateResourceLoader(read_position_, kPositionNotSpecified));
262 loader_->Start(
263 base::Bind(&BufferedDataSource::PartialReadStartCallback, this),
264 base::Bind(&BufferedDataSource::NetworkEventCallback, this),
265 frame_);
268 void BufferedDataSource::SetPlaybackRateTask(float playback_rate) {
269 DCHECK(MessageLoop::current() == render_loop_);
270 DCHECK(loader_.get());
272 playback_rate_ = playback_rate;
273 loader_->SetPlaybackRate(playback_rate);
275 bool previously_paused = media_is_paused_;
276 media_is_paused_ = (playback_rate == 0.0);
278 if (!media_has_played_ && previously_paused && !media_is_paused_)
279 media_has_played_ = true;
281 BufferedResourceLoader::DeferStrategy strategy = ChooseDeferStrategy();
282 loader_->UpdateDeferStrategy(strategy);
285 void BufferedDataSource::SetPreloadTask(media::Preload preload) {
286 DCHECK(MessageLoop::current() == render_loop_);
287 preload_ = preload;
290 void BufferedDataSource::SetBitrateTask(int bitrate) {
291 DCHECK(MessageLoop::current() == render_loop_);
292 DCHECK(loader_.get());
294 bitrate_ = bitrate;
295 loader_->SetBitrate(bitrate);
298 BufferedResourceLoader::DeferStrategy
299 BufferedDataSource::ChooseDeferStrategy() {
300 DCHECK(MessageLoop::current() == render_loop_);
301 // If the page indicated preload=metadata, then load exactly what is needed
302 // needed for starting playback.
303 if (!media_has_played_ && preload_ == media::METADATA)
304 return BufferedResourceLoader::kReadThenDefer;
306 // If the playback has started (at which point the preload value is ignored)
307 // and we're paused, then try to load as much as possible.
308 if (media_has_played_ && media_is_paused_)
309 return BufferedResourceLoader::kNeverDefer;
311 // If media is currently playing or the page indicated preload=auto,
312 // use threshold strategy to enable/disable deferring when the buffer
313 // is full/depleted.
314 return BufferedResourceLoader::kThresholdDefer;
317 // This method is the place where actual read happens, |loader_| must be valid
318 // prior to make this method call.
319 void BufferedDataSource::ReadInternal() {
320 DCHECK(MessageLoop::current() == render_loop_);
321 DCHECK(loader_.get());
323 // First we prepare the intermediate read buffer for BufferedResourceLoader
324 // to write to.
325 if (read_size_ > intermediate_read_buffer_size_) {
326 intermediate_read_buffer_.reset(new uint8[read_size_]);
329 // Perform the actual read with BufferedResourceLoader.
330 loader_->Read(read_position_, read_size_, intermediate_read_buffer_.get(),
331 base::Bind(&BufferedDataSource::ReadCallback, this));
334 // Method to report the results of the current read request. Also reset all
335 // the read parameters.
336 void BufferedDataSource::DoneRead_Locked(int error) {
337 DVLOG(1) << "DoneRead: " << error << " bytes";
339 DCHECK(MessageLoop::current() == render_loop_);
340 DCHECK(!read_cb_.is_null());
341 lock_.AssertAcquired();
343 if (error >= 0) {
344 read_cb_.Run(static_cast<size_t>(error));
345 } else {
346 read_cb_.Run(kReadError);
349 read_cb_.Reset();
350 read_position_ = 0;
351 read_size_ = 0;
352 read_buffer_ = 0;
355 void BufferedDataSource::DoneInitialization_Locked(
356 media::PipelineStatus status) {
357 DCHECK(MessageLoop::current() == render_loop_);
358 DCHECK(!initialize_cb_.is_null());
359 lock_.AssertAcquired();
361 initialize_cb_.Run(status);
362 initialize_cb_.Reset();
365 /////////////////////////////////////////////////////////////////////////////
366 // BufferedResourceLoader callback methods.
367 void BufferedDataSource::HttpInitialStartCallback(int error) {
368 DCHECK(MessageLoop::current() == render_loop_);
369 DCHECK(loader_.get());
371 int64 instance_size = loader_->instance_size();
372 bool success = error == net::OK;
374 bool initialize_cb_is_null = false;
376 base::AutoLock auto_lock(lock_);
377 initialize_cb_is_null = initialize_cb_.is_null();
379 if (initialize_cb_is_null) {
380 loader_->Stop();
381 return;
384 if (success) {
385 // TODO(hclam): Needs more thinking about supporting servers without range
386 // request or their partial response is not complete.
387 total_bytes_ = instance_size;
388 streaming_ = (instance_size == kPositionNotSpecified) ||
389 !loader_->range_supported();
390 } else {
391 // TODO(hclam): In case of failure, we can retry several times.
392 loader_->Stop();
395 if (error == net::ERR_INVALID_RESPONSE && using_range_request_) {
396 // Assuming that the Range header was causing the problem. Retry without
397 // the Range header.
398 using_range_request_ = false;
399 loader_.reset(CreateResourceLoader(kPositionNotSpecified,
400 kPositionNotSpecified));
401 loader_->Start(
402 base::Bind(&BufferedDataSource::HttpInitialStartCallback, this),
403 base::Bind(&BufferedDataSource::NetworkEventCallback, this),
404 frame_);
405 return;
408 // Reference to prevent destruction while inside the |initialize_cb_|
409 // call. This is a temporary fix to prevent crashes caused by holding the
410 // lock and running the destructor.
411 // TODO: Review locking in this class and figure out a way to run the callback
412 // w/o the lock.
413 scoped_refptr<BufferedDataSource> destruction_guard(this);
415 // We need to prevent calling to filter host and running the callback if
416 // we have received the stop signal. We need to lock down the whole callback
417 // method to prevent bad things from happening. The reason behind this is
418 // that we cannot guarantee tasks on render thread have completely stopped
419 // when we receive the Stop() method call. The only way to solve this is to
420 // let tasks on render thread to run but make sure they don't call outside
421 // this object when Stop() method is ever called. Locking this method is
422 // safe because |lock_| is only acquired in tasks on render thread.
423 base::AutoLock auto_lock(lock_);
424 if (stop_signal_received_)
425 return;
427 if (!success) {
428 DoneInitialization_Locked(media::PIPELINE_ERROR_NETWORK);
429 return;
432 UpdateHostState_Locked();
433 DoneInitialization_Locked(media::PIPELINE_OK);
437 void BufferedDataSource::NonHttpInitialStartCallback(int error) {
438 DCHECK(MessageLoop::current() == render_loop_);
439 DCHECK(loader_.get());
441 bool initialize_cb_is_null = false;
443 base::AutoLock auto_lock(lock_);
444 initialize_cb_is_null = initialize_cb_.is_null();
446 if (initialize_cb_is_null) {
447 loader_->Stop();
448 return;
451 int64 instance_size = loader_->instance_size();
452 bool success = error == net::OK && instance_size != kPositionNotSpecified;
454 if (success) {
455 total_bytes_ = instance_size;
456 buffered_bytes_ = total_bytes_;
457 } else {
458 loader_->Stop();
461 // Reference to prevent destruction while inside the |initialize_cb_|
462 // call. This is a temporary fix to prevent crashes caused by holding the
463 // lock and running the destructor.
464 // TODO: Review locking in this class and figure out a way to run the callback
465 // w/o the lock.
466 scoped_refptr<BufferedDataSource> destruction_guard(this);
468 // We need to prevent calling to filter host and running the callback if
469 // we have received the stop signal. We need to lock down the whole callback
470 // method to prevent bad things from happening. The reason behind this is
471 // that we cannot guarantee tasks on render thread have completely stopped
472 // when we receive the Stop() method call. The only way to solve this is to
473 // let tasks on render thread to run but make sure they don't call outside
474 // this object when Stop() method is ever called. Locking this method is
475 // safe because |lock_| is only acquired in tasks on render thread.
476 base::AutoLock auto_lock(lock_);
477 if (stop_signal_received_ || initialize_cb_.is_null())
478 return;
480 if (!success) {
481 DoneInitialization_Locked(media::PIPELINE_ERROR_NETWORK);
482 return;
485 UpdateHostState_Locked();
486 DoneInitialization_Locked(media::PIPELINE_OK);
490 void BufferedDataSource::PartialReadStartCallback(int error) {
491 DCHECK(MessageLoop::current() == render_loop_);
492 DCHECK(loader_.get());
494 if (error == net::OK) {
495 // Once the request has started successfully, we can proceed with
496 // reading from it.
497 ReadInternal();
498 return;
501 // Stop the resource loader since we have received an error.
502 loader_->Stop();
504 // We need to prevent calling to filter host and running the callback if
505 // we have received the stop signal. We need to lock down the whole callback
506 // method to prevent bad things from happening. The reason behind this is
507 // that we cannot guarantee tasks on render thread have completely stopped
508 // when we receive the Stop() method call. So only way to solve this is to
509 // let tasks on render thread to run but make sure they don't call outside
510 // this object when Stop() method is ever called. Locking this method is
511 // safe because |lock_| is only acquired in tasks on render thread.
512 base::AutoLock auto_lock(lock_);
513 if (stop_signal_received_)
514 return;
515 DoneRead_Locked(net::ERR_INVALID_RESPONSE);
518 void BufferedDataSource::ReadCallback(int error) {
519 DCHECK(MessageLoop::current() == render_loop_);
521 if (error < 0) {
522 DCHECK(loader_.get());
524 // Stop the resource load if it failed.
525 loader_->Stop();
527 if (error == net::ERR_CACHE_MISS && cache_miss_retries_left_ > 0) {
528 cache_miss_retries_left_--;
529 render_loop_->PostTask(FROM_HERE,
530 base::Bind(&BufferedDataSource::RestartLoadingTask, this));
531 return;
535 // We need to prevent calling to filter host and running the callback if
536 // we have received the stop signal. We need to lock down the whole callback
537 // method to prevent bad things from happening. The reason behind this is
538 // that we cannot guarantee tasks on render thread have completely stopped
539 // when we receive the Stop() method call. So only way to solve this is to
540 // let tasks on render thread to run but make sure they don't call outside
541 // this object when Stop() method is ever called. Locking this method is safe
542 // because |lock_| is only acquired in tasks on render thread.
543 base::AutoLock auto_lock(lock_);
544 if (stop_signal_received_)
545 return;
547 if (error > 0) {
548 // If a position error code is received, read was successful. So copy
549 // from intermediate read buffer to the target read buffer.
550 memcpy(read_buffer_, intermediate_read_buffer_.get(), error);
551 } else if (error == 0 && total_bytes_ == kPositionNotSpecified) {
552 // We've reached the end of the file and we didn't know the total size
553 // before. Update the total size so Read()s past the end of the file will
554 // fail like they would if we had known the file size at the beginning.
555 total_bytes_ = loader_->instance_size();
557 if (host() && total_bytes_ != kPositionNotSpecified) {
558 host()->SetTotalBytes(total_bytes_);
559 host()->SetBufferedBytes(total_bytes_);
562 DoneRead_Locked(error);
565 void BufferedDataSource::NetworkEventCallback() {
566 DCHECK(MessageLoop::current() == render_loop_);
567 DCHECK(loader_.get());
569 // In case of non-HTTP request we don't need to report network events,
570 // so return immediately.
571 if (!url_.SchemeIs(kHttpScheme) && !url_.SchemeIs(kHttpsScheme))
572 return;
574 bool is_downloading_data = loader_->is_downloading_data();
575 int64 buffered_position = loader_->GetBufferedPosition();
577 // If we get an unspecified value, return immediately.
578 if (buffered_position == kPositionNotSpecified)
579 return;
581 // We need to prevent calling to filter host and running the callback if
582 // we have received the stop signal. We need to lock down the whole callback
583 // method to prevent bad things from happening. The reason behind this is
584 // that we cannot guarantee tasks on render thread have completely stopped
585 // when we receive the Stop() method call. So only way to solve this is to
586 // let tasks on render thread to run but make sure they don't call outside
587 // this object when Stop() method is ever called. Locking this method is safe
588 // because |lock_| is only acquired in tasks on render thread.
589 base::AutoLock auto_lock(lock_);
590 if (stop_signal_received_)
591 return;
593 if (is_downloading_data != is_downloading_data_) {
594 is_downloading_data_ = is_downloading_data;
595 if (host())
596 host()->SetNetworkActivity(is_downloading_data);
599 buffered_bytes_ = buffered_position + 1;
600 if (host())
601 host()->SetBufferedBytes(buffered_bytes_);
604 void BufferedDataSource::UpdateHostState_Locked() {
605 // Called from various threads, under lock.
606 lock_.AssertAcquired();
608 if (!host())
609 return;
611 if (total_bytes_ != kPositionNotSpecified)
612 host()->SetTotalBytes(total_bytes_);
613 host()->SetBufferedBytes(buffered_bytes_);
616 } // namespace webkit_media