Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / media / blink / buffered_data_source.cc
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 "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"
13
14 using blink::WebFrame;
15
16 namespace {
17
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;
22
23 // Number of cache misses we allow for a single Read() before signaling an
24 // error.
25 const int kNumCacheMissRetries = 3;
26
27 }  // namespace
28
29 namespace media {
30
31 class BufferedDataSource::ReadOperation {
32  public:
33   ReadOperation(int64 position, int size, uint8* data,
34                 const DataSource::ReadCB& callback);
35   ~ReadOperation();
36
37   // Runs |callback_| with the given |result|, deleting the operation
38   // afterwards.
39   static void Run(scoped_ptr<ReadOperation> read_op, int result);
40
41   // State for the number of times this read operation has been retried.
42   int retries() { return retries_; }
43   void IncrementRetries() { ++retries_; }
44
45   int64 position() { return position_; }
46   int size() { return size_; }
47   uint8* data() { return data_; }
48
49  private:
50   int retries_;
51
52   const int64 position_;
53   const int size_;
54   uint8* data_;
55   DataSource::ReadCB callback_;
56
57   DISALLOW_IMPLICIT_CONSTRUCTORS(ReadOperation);
58 };
59
60 BufferedDataSource::ReadOperation::ReadOperation(
61     int64 position, int size, uint8* data,
62     const DataSource::ReadCB& callback)
63     : retries_(0),
64       position_(position),
65       size_(size),
66       data_(data),
67       callback_(callback) {
68   DCHECK(!callback_.is_null());
69 }
70
71 BufferedDataSource::ReadOperation::~ReadOperation() {
72   DCHECK(callback_.is_null());
73 }
74
75 // static
76 void BufferedDataSource::ReadOperation::Run(
77     scoped_ptr<ReadOperation> read_op, int result) {
78   base::ResetAndReturn(&read_op->callback_).Run(result);
79 }
80
81 BufferedDataSource::BufferedDataSource(
82     const GURL& url,
83     BufferedResourceLoader::CORSMode cors_mode,
84     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
85     WebFrame* frame,
86     MediaLog* media_log,
87     BufferedDataSourceHost* host,
88     const DownloadingCB& downloading_cb)
89     : url_(url),
90       cors_mode_(cors_mode),
91       total_bytes_(kPositionNotSpecified),
92       streaming_(false),
93       frame_(frame),
94       intermediate_read_buffer_(kInitialReadBufferSize),
95       render_task_runner_(task_runner),
96       stop_signal_received_(false),
97       media_has_played_(false),
98       preload_(AUTO),
99       bitrate_(0),
100       playback_rate_(0.0),
101       media_log_(media_log),
102       host_(host),
103       downloading_cb_(downloading_cb),
104       weak_factory_(this) {
105   DCHECK(host_);
106   DCHECK(!downloading_cb_.is_null());
107 }
108
109 BufferedDataSource::~BufferedDataSource() {}
110
111 // A factory method to create BufferedResourceLoader using the read parameters.
112 // This method can be overridden to inject mock BufferedResourceLoader object
113 // for testing purpose.
114 BufferedResourceLoader* BufferedDataSource::CreateResourceLoader(
115     int64 first_byte_position, int64 last_byte_position) {
116   DCHECK(render_task_runner_->BelongsToCurrentThread());
117
118   BufferedResourceLoader::DeferStrategy strategy = preload_ == METADATA ?
119       BufferedResourceLoader::kReadThenDefer :
120       BufferedResourceLoader::kCapacityDefer;
121
122   return new BufferedResourceLoader(url_,
123                                     cors_mode_,
124                                     first_byte_position,
125                                     last_byte_position,
126                                     strategy,
127                                     bitrate_,
128                                     playback_rate_,
129                                     media_log_.get());
130 }
131
132 void BufferedDataSource::Initialize(const InitializeCB& init_cb) {
133   DCHECK(render_task_runner_->BelongsToCurrentThread());
134   DCHECK(!init_cb.is_null());
135   DCHECK(!loader_.get());
136
137   init_cb_ = init_cb;
138
139   if (url_.SchemeIsHTTPOrHTTPS()) {
140     // Do an unbounded range request starting at the beginning.  If the server
141     // responds with 200 instead of 206 we'll fall back into a streaming mode.
142     loader_.reset(CreateResourceLoader(0, kPositionNotSpecified));
143   } else {
144     // For all other protocols, assume they support range request. We fetch
145     // the full range of the resource to obtain the instance size because
146     // we won't be served HTTP headers.
147     loader_.reset(CreateResourceLoader(kPositionNotSpecified,
148                                        kPositionNotSpecified));
149   }
150
151   base::WeakPtr<BufferedDataSource> weak_this = weak_factory_.GetWeakPtr();
152   loader_->Start(
153       base::Bind(&BufferedDataSource::StartCallback, weak_this),
154       base::Bind(&BufferedDataSource::LoadingStateChangedCallback, weak_this),
155       base::Bind(&BufferedDataSource::ProgressCallback, weak_this),
156       frame_);
157 }
158
159 void BufferedDataSource::SetPreload(Preload preload) {
160   DCHECK(render_task_runner_->BelongsToCurrentThread());
161   preload_ = preload;
162 }
163
164 bool BufferedDataSource::HasSingleOrigin() {
165   DCHECK(render_task_runner_->BelongsToCurrentThread());
166   DCHECK(init_cb_.is_null() && loader_.get())
167       << "Initialize() must complete before calling HasSingleOrigin()";
168   return loader_->HasSingleOrigin();
169 }
170
171 bool BufferedDataSource::DidPassCORSAccessCheck() const {
172   return loader_.get() && loader_->DidPassCORSAccessCheck();
173 }
174
175 void BufferedDataSource::Abort() {
176   DCHECK(render_task_runner_->BelongsToCurrentThread());
177   {
178     base::AutoLock auto_lock(lock_);
179     StopInternal_Locked();
180   }
181   StopLoader();
182   frame_ = NULL;
183 }
184
185 void BufferedDataSource::MediaPlaybackRateChanged(float playback_rate) {
186   DCHECK(render_task_runner_->BelongsToCurrentThread());
187   DCHECK(loader_.get());
188
189   if (playback_rate < 0.0f)
190     return;
191
192   playback_rate_ = playback_rate;
193   loader_->SetPlaybackRate(playback_rate);
194 }
195
196 void BufferedDataSource::MediaIsPlaying() {
197   DCHECK(render_task_runner_->BelongsToCurrentThread());
198   media_has_played_ = true;
199   UpdateDeferStrategy(false);
200 }
201
202 void BufferedDataSource::MediaIsPaused() {
203   DCHECK(render_task_runner_->BelongsToCurrentThread());
204   UpdateDeferStrategy(true);
205 }
206
207 /////////////////////////////////////////////////////////////////////////////
208 // DataSource implementation.
209 void BufferedDataSource::Stop() {
210   {
211     base::AutoLock auto_lock(lock_);
212     StopInternal_Locked();
213   }
214
215   render_task_runner_->PostTask(
216       FROM_HERE,
217       base::Bind(&BufferedDataSource::StopLoader, weak_factory_.GetWeakPtr()));
218 }
219
220 void BufferedDataSource::SetBitrate(int bitrate) {
221   render_task_runner_->PostTask(FROM_HERE,
222                          base::Bind(&BufferedDataSource::SetBitrateTask,
223                                     weak_factory_.GetWeakPtr(),
224                                     bitrate));
225 }
226
227 void BufferedDataSource::Read(
228     int64 position, int size, uint8* data,
229     const DataSource::ReadCB& read_cb) {
230   DVLOG(1) << "Read: " << position << " offset, " << size << " bytes";
231   DCHECK(!read_cb.is_null());
232
233   {
234     base::AutoLock auto_lock(lock_);
235     DCHECK(!read_op_);
236
237     if (stop_signal_received_) {
238       read_cb.Run(kReadError);
239       return;
240     }
241
242     read_op_.reset(new ReadOperation(position, size, data, read_cb));
243   }
244
245   render_task_runner_->PostTask(
246       FROM_HERE,
247       base::Bind(&BufferedDataSource::ReadTask, weak_factory_.GetWeakPtr()));
248 }
249
250 bool BufferedDataSource::GetSize(int64* size_out) {
251   if (total_bytes_ != kPositionNotSpecified) {
252     *size_out = total_bytes_;
253     return true;
254   }
255   *size_out = 0;
256   return false;
257 }
258
259 bool BufferedDataSource::IsStreaming() {
260   return streaming_;
261 }
262
263 /////////////////////////////////////////////////////////////////////////////
264 // Render thread tasks.
265 void BufferedDataSource::ReadTask() {
266   DCHECK(render_task_runner_->BelongsToCurrentThread());
267   ReadInternal();
268 }
269
270 void BufferedDataSource::StopInternal_Locked() {
271   lock_.AssertAcquired();
272   if (stop_signal_received_)
273     return;
274
275   stop_signal_received_ = true;
276
277   // Initialize() isn't part of the DataSource interface so don't call it in
278   // response to Stop().
279   init_cb_.Reset();
280
281   if (read_op_)
282     ReadOperation::Run(read_op_.Pass(), kReadError);
283 }
284
285 void BufferedDataSource::StopLoader() {
286   DCHECK(render_task_runner_->BelongsToCurrentThread());
287
288   if (loader_)
289     loader_->Stop();
290 }
291
292 void BufferedDataSource::SetBitrateTask(int bitrate) {
293   DCHECK(render_task_runner_->BelongsToCurrentThread());
294   DCHECK(loader_.get());
295
296   bitrate_ = bitrate;
297   loader_->SetBitrate(bitrate);
298 }
299
300 // This method is the place where actual read happens, |loader_| must be valid
301 // prior to make this method call.
302 void BufferedDataSource::ReadInternal() {
303   DCHECK(render_task_runner_->BelongsToCurrentThread());
304   int64 position = 0;
305   int size = 0;
306   {
307     base::AutoLock auto_lock(lock_);
308     if (stop_signal_received_)
309       return;
310
311     position = read_op_->position();
312     size = read_op_->size();
313   }
314
315   // First we prepare the intermediate read buffer for BufferedResourceLoader
316   // to write to.
317   if (static_cast<int>(intermediate_read_buffer_.size()) < size)
318     intermediate_read_buffer_.resize(size);
319
320   // Perform the actual read with BufferedResourceLoader.
321   DCHECK(!intermediate_read_buffer_.empty());
322   loader_->Read(position,
323                 size,
324                 &intermediate_read_buffer_[0],
325                 base::Bind(&BufferedDataSource::ReadCallback,
326                            weak_factory_.GetWeakPtr()));
327 }
328
329
330 /////////////////////////////////////////////////////////////////////////////
331 // BufferedResourceLoader callback methods.
332 void BufferedDataSource::StartCallback(
333     BufferedResourceLoader::Status status) {
334   DCHECK(render_task_runner_->BelongsToCurrentThread());
335   DCHECK(loader_.get());
336
337   bool init_cb_is_null = false;
338   {
339     base::AutoLock auto_lock(lock_);
340     init_cb_is_null = init_cb_.is_null();
341   }
342   if (init_cb_is_null) {
343     loader_->Stop();
344     return;
345   }
346
347   // All responses must be successful. Resources that are assumed to be fully
348   // buffered must have a known content length.
349   bool success = status == BufferedResourceLoader::kOk &&
350                  (!assume_fully_buffered() ||
351                   loader_->instance_size() != kPositionNotSpecified);
352
353   if (success) {
354     total_bytes_ = loader_->instance_size();
355     streaming_ =
356         !assume_fully_buffered() &&
357         (total_bytes_ == kPositionNotSpecified || !loader_->range_supported());
358
359     media_log_->SetDoubleProperty("total_bytes",
360                                   static_cast<double>(total_bytes_));
361     media_log_->SetBooleanProperty("streaming", streaming_);
362   } else {
363     loader_->Stop();
364   }
365
366   // TODO(scherkus): we shouldn't have to lock to signal host(), see
367   // http://crbug.com/113712 for details.
368   base::AutoLock auto_lock(lock_);
369   if (stop_signal_received_)
370     return;
371
372   if (success) {
373     if (total_bytes_ != kPositionNotSpecified) {
374       host_->SetTotalBytes(total_bytes_);
375       if (assume_fully_buffered())
376         host_->AddBufferedByteRange(0, total_bytes_);
377     }
378
379     media_log_->SetBooleanProperty("single_origin", loader_->HasSingleOrigin());
380     media_log_->SetBooleanProperty("passed_cors_access_check",
381                                    loader_->DidPassCORSAccessCheck());
382     media_log_->SetBooleanProperty("range_header_supported",
383                                    loader_->range_supported());
384   }
385
386   base::ResetAndReturn(&init_cb_).Run(success);
387 }
388
389 void BufferedDataSource::PartialReadStartCallback(
390     BufferedResourceLoader::Status status) {
391   DCHECK(render_task_runner_->BelongsToCurrentThread());
392   DCHECK(loader_.get());
393
394   if (status == BufferedResourceLoader::kOk) {
395     // Once the request has started successfully, we can proceed with
396     // reading from it.
397     ReadInternal();
398     return;
399   }
400
401   // Stop the resource loader since we have received an error.
402   loader_->Stop();
403
404   // TODO(scherkus): we shouldn't have to lock to signal host(), see
405   // http://crbug.com/113712 for details.
406   base::AutoLock auto_lock(lock_);
407   if (stop_signal_received_)
408     return;
409   ReadOperation::Run(read_op_.Pass(), kReadError);
410 }
411
412 void BufferedDataSource::ReadCallback(
413     BufferedResourceLoader::Status status,
414     int bytes_read) {
415   DCHECK(render_task_runner_->BelongsToCurrentThread());
416
417   // TODO(scherkus): we shouldn't have to lock to signal host(), see
418   // http://crbug.com/113712 for details.
419   base::AutoLock auto_lock(lock_);
420   if (stop_signal_received_)
421     return;
422
423   if (status != BufferedResourceLoader::kOk) {
424     // Stop the resource load if it failed.
425     loader_->Stop();
426
427     if (status == BufferedResourceLoader::kCacheMiss &&
428         read_op_->retries() < kNumCacheMissRetries) {
429       read_op_->IncrementRetries();
430
431       // Recreate a loader starting from where we last left off until the
432       // end of the resource.
433       loader_.reset(CreateResourceLoader(
434           read_op_->position(), kPositionNotSpecified));
435
436       base::WeakPtr<BufferedDataSource> weak_this = weak_factory_.GetWeakPtr();
437       loader_->Start(
438           base::Bind(&BufferedDataSource::PartialReadStartCallback, weak_this),
439           base::Bind(&BufferedDataSource::LoadingStateChangedCallback,
440                      weak_this),
441           base::Bind(&BufferedDataSource::ProgressCallback, weak_this),
442           frame_);
443       return;
444     }
445
446     ReadOperation::Run(read_op_.Pass(), kReadError);
447     return;
448   }
449
450   if (bytes_read > 0) {
451     DCHECK(!intermediate_read_buffer_.empty());
452     memcpy(read_op_->data(), &intermediate_read_buffer_[0], bytes_read);
453   } else if (bytes_read == 0 && total_bytes_ == kPositionNotSpecified) {
454     // We've reached the end of the file and we didn't know the total size
455     // before. Update the total size so Read()s past the end of the file will
456     // fail like they would if we had known the file size at the beginning.
457     total_bytes_ = loader_->instance_size();
458
459     if (total_bytes_ != kPositionNotSpecified) {
460       host_->SetTotalBytes(total_bytes_);
461       host_->AddBufferedByteRange(loader_->first_byte_position(),
462                                   total_bytes_);
463     }
464   }
465   ReadOperation::Run(read_op_.Pass(), bytes_read);
466 }
467
468 void BufferedDataSource::LoadingStateChangedCallback(
469     BufferedResourceLoader::LoadingState state) {
470   DCHECK(render_task_runner_->BelongsToCurrentThread());
471
472   if (assume_fully_buffered())
473     return;
474
475   bool is_downloading_data;
476   switch (state) {
477     case BufferedResourceLoader::kLoading:
478       is_downloading_data = true;
479       break;
480     case BufferedResourceLoader::kLoadingDeferred:
481     case BufferedResourceLoader::kLoadingFinished:
482       is_downloading_data = false;
483       break;
484
485     // TODO(scherkus): we don't signal network activity changes when loads
486     // fail to preserve existing behaviour when deferring is toggled, however
487     // we should consider changing DownloadingCB to also propagate loading
488     // state. For example there isn't any signal today to notify the client that
489     // loading has failed (we only get errors on subsequent reads).
490     case BufferedResourceLoader::kLoadingFailed:
491       return;
492   }
493
494   downloading_cb_.Run(is_downloading_data);
495 }
496
497 void BufferedDataSource::ProgressCallback(int64 position) {
498   DCHECK(render_task_runner_->BelongsToCurrentThread());
499
500   if (assume_fully_buffered())
501     return;
502
503   // TODO(scherkus): we shouldn't have to lock to signal host(), see
504   // http://crbug.com/113712 for details.
505   base::AutoLock auto_lock(lock_);
506   if (stop_signal_received_)
507     return;
508
509   host_->AddBufferedByteRange(loader_->first_byte_position(), position);
510 }
511
512 void BufferedDataSource::UpdateDeferStrategy(bool paused) {
513   // No need to aggressively buffer when we are assuming the resource is fully
514   // buffered.
515   if (assume_fully_buffered()) {
516     loader_->UpdateDeferStrategy(BufferedResourceLoader::kCapacityDefer);
517     return;
518   }
519
520   // If the playback has started (at which point the preload value is ignored)
521   // and we're paused, then try to load as much as possible (the loader will
522   // fall back to kCapacityDefer if it knows the current response won't be
523   // useful from the cache in the future).
524   if (media_has_played_ && paused && loader_->range_supported()) {
525     loader_->UpdateDeferStrategy(BufferedResourceLoader::kNeverDefer);
526     return;
527   }
528
529   // If media is currently playing or the page indicated preload=auto or the
530   // the server does not support the byte range request or we do not want to go
531   // too far ahead of the read head, use threshold strategy to enable/disable
532   // deferring when the buffer is full/depleted.
533   loader_->UpdateDeferStrategy(BufferedResourceLoader::kCapacityDefer);
534 }
535
536 }  // namespace media