Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / renderer / media / 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 "content/renderer/media/buffered_data_source.h"
6
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/message_loop/message_loop_proxy.h"
10 #include "content/public/common/url_constants.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 content {
30
31 class BufferedDataSource::ReadOperation {
32  public:
33   ReadOperation(int64 position, int size, uint8* data,
34                 const media::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   media::DataSource::ReadCB callback_;
56
57   DISALLOW_IMPLICIT_CONSTRUCTORS(ReadOperation);
58 };
59
60 BufferedDataSource::ReadOperation::ReadOperation(
61     int64 position, int size, uint8* data,
62     const media::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::MessageLoopProxy>& render_loop,
85     WebFrame* frame,
86     media::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_(new uint8[kInitialReadBufferSize]),
95       intermediate_read_buffer_size_(kInitialReadBufferSize),
96       render_loop_(render_loop),
97       stop_signal_received_(false),
98       media_has_played_(false),
99       preload_(AUTO),
100       bitrate_(0),
101       playback_rate_(0.0),
102       media_log_(media_log),
103       host_(host),
104       downloading_cb_(downloading_cb),
105       weak_factory_(this) {
106   DCHECK(host_);
107   DCHECK(!downloading_cb_.is_null());
108 }
109
110 BufferedDataSource::~BufferedDataSource() {}
111
112 // A factory method to create BufferedResourceLoader using the read parameters.
113 // This method can be overridden to inject mock BufferedResourceLoader object
114 // for testing purpose.
115 BufferedResourceLoader* BufferedDataSource::CreateResourceLoader(
116     int64 first_byte_position, int64 last_byte_position) {
117   DCHECK(render_loop_->BelongsToCurrentThread());
118
119   BufferedResourceLoader::DeferStrategy strategy = preload_ == METADATA ?
120       BufferedResourceLoader::kReadThenDefer :
121       BufferedResourceLoader::kCapacityDefer;
122
123   return new BufferedResourceLoader(url_,
124                                     cors_mode_,
125                                     first_byte_position,
126                                     last_byte_position,
127                                     strategy,
128                                     bitrate_,
129                                     playback_rate_,
130                                     media_log_.get());
131 }
132
133 void BufferedDataSource::Initialize(const InitializeCB& init_cb) {
134   DCHECK(render_loop_->BelongsToCurrentThread());
135   DCHECK(!init_cb.is_null());
136   DCHECK(!loader_.get());
137
138   init_cb_ = init_cb;
139
140   if (url_.SchemeIsHTTPOrHTTPS()) {
141     // Do an unbounded range request starting at the beginning.  If the server
142     // responds with 200 instead of 206 we'll fall back into a streaming mode.
143     loader_.reset(CreateResourceLoader(0, kPositionNotSpecified));
144   } else {
145     // For all other protocols, assume they support range request. We fetch
146     // the full range of the resource to obtain the instance size because
147     // we won't be served HTTP headers.
148     loader_.reset(CreateResourceLoader(kPositionNotSpecified,
149                                        kPositionNotSpecified));
150   }
151
152   base::WeakPtr<BufferedDataSource> weak_this = weak_factory_.GetWeakPtr();
153   loader_->Start(
154       base::Bind(&BufferedDataSource::StartCallback, weak_this),
155       base::Bind(&BufferedDataSource::LoadingStateChangedCallback, weak_this),
156       base::Bind(&BufferedDataSource::ProgressCallback, weak_this),
157       frame_);
158 }
159
160 void BufferedDataSource::SetPreload(Preload preload) {
161   DCHECK(render_loop_->BelongsToCurrentThread());
162   preload_ = preload;
163 }
164
165 bool BufferedDataSource::HasSingleOrigin() {
166   DCHECK(render_loop_->BelongsToCurrentThread());
167   DCHECK(init_cb_.is_null() && loader_.get())
168       << "Initialize() must complete before calling HasSingleOrigin()";
169   return loader_->HasSingleOrigin();
170 }
171
172 bool BufferedDataSource::DidPassCORSAccessCheck() const {
173   return loader_.get() && loader_->DidPassCORSAccessCheck();
174 }
175
176 void BufferedDataSource::Abort() {
177   DCHECK(render_loop_->BelongsToCurrentThread());
178   {
179     base::AutoLock auto_lock(lock_);
180     StopInternal_Locked();
181   }
182   StopLoader();
183   frame_ = NULL;
184 }
185
186 void BufferedDataSource::MediaPlaybackRateChanged(float playback_rate) {
187   DCHECK(render_loop_->BelongsToCurrentThread());
188   DCHECK(loader_.get());
189
190   if (playback_rate < 0.0f)
191     return;
192
193   playback_rate_ = playback_rate;
194   loader_->SetPlaybackRate(playback_rate);
195 }
196
197 void BufferedDataSource::MediaIsPlaying() {
198   DCHECK(render_loop_->BelongsToCurrentThread());
199   media_has_played_ = true;
200   UpdateDeferStrategy(false);
201 }
202
203 void BufferedDataSource::MediaIsPaused() {
204   DCHECK(render_loop_->BelongsToCurrentThread());
205   UpdateDeferStrategy(true);
206 }
207
208 /////////////////////////////////////////////////////////////////////////////
209 // media::DataSource implementation.
210 void BufferedDataSource::Stop() {
211   {
212     base::AutoLock auto_lock(lock_);
213     StopInternal_Locked();
214   }
215
216   render_loop_->PostTask(
217       FROM_HERE,
218       base::Bind(&BufferedDataSource::StopLoader, weak_factory_.GetWeakPtr()));
219 }
220
221 void BufferedDataSource::SetBitrate(int bitrate) {
222   render_loop_->PostTask(FROM_HERE,
223                          base::Bind(&BufferedDataSource::SetBitrateTask,
224                                     weak_factory_.GetWeakPtr(),
225                                     bitrate));
226 }
227
228 void BufferedDataSource::Read(
229     int64 position, int size, uint8* data,
230     const media::DataSource::ReadCB& read_cb) {
231   DVLOG(1) << "Read: " << position << " offset, " << size << " bytes";
232   DCHECK(!read_cb.is_null());
233
234   {
235     base::AutoLock auto_lock(lock_);
236     DCHECK(!read_op_);
237
238     if (stop_signal_received_) {
239       read_cb.Run(kReadError);
240       return;
241     }
242
243     read_op_.reset(new ReadOperation(position, size, data, read_cb));
244   }
245
246   render_loop_->PostTask(
247       FROM_HERE,
248       base::Bind(&BufferedDataSource::ReadTask, weak_factory_.GetWeakPtr()));
249 }
250
251 bool BufferedDataSource::GetSize(int64* size_out) {
252   if (total_bytes_ != kPositionNotSpecified) {
253     *size_out = total_bytes_;
254     return true;
255   }
256   *size_out = 0;
257   return false;
258 }
259
260 bool BufferedDataSource::IsStreaming() {
261   return streaming_;
262 }
263
264 /////////////////////////////////////////////////////////////////////////////
265 // Render thread tasks.
266 void BufferedDataSource::ReadTask() {
267   DCHECK(render_loop_->BelongsToCurrentThread());
268   ReadInternal();
269 }
270
271 void BufferedDataSource::StopInternal_Locked() {
272   lock_.AssertAcquired();
273   if (stop_signal_received_)
274     return;
275
276   stop_signal_received_ = true;
277
278   // Initialize() isn't part of the DataSource interface so don't call it in
279   // response to Stop().
280   init_cb_.Reset();
281
282   if (read_op_)
283     ReadOperation::Run(read_op_.Pass(), kReadError);
284 }
285
286 void BufferedDataSource::StopLoader() {
287   DCHECK(render_loop_->BelongsToCurrentThread());
288
289   if (loader_)
290     loader_->Stop();
291 }
292
293 void BufferedDataSource::SetBitrateTask(int bitrate) {
294   DCHECK(render_loop_->BelongsToCurrentThread());
295   DCHECK(loader_.get());
296
297   bitrate_ = bitrate;
298   loader_->SetBitrate(bitrate);
299 }
300
301 // This method is the place where actual read happens, |loader_| must be valid
302 // prior to make this method call.
303 void BufferedDataSource::ReadInternal() {
304   DCHECK(render_loop_->BelongsToCurrentThread());
305   int64 position = 0;
306   int size = 0;
307   {
308     base::AutoLock auto_lock(lock_);
309     if (stop_signal_received_)
310       return;
311
312     position = read_op_->position();
313     size = read_op_->size();
314   }
315
316   // First we prepare the intermediate read buffer for BufferedResourceLoader
317   // to write to.
318   if (size > intermediate_read_buffer_size_) {
319     intermediate_read_buffer_.reset(new uint8[size]);
320   }
321
322   // Perform the actual read with BufferedResourceLoader.
323   loader_->Read(position,
324                 size,
325                 intermediate_read_buffer_.get(),
326                 base::Bind(&BufferedDataSource::ReadCallback,
327                            weak_factory_.GetWeakPtr()));
328 }
329
330
331 /////////////////////////////////////////////////////////////////////////////
332 // BufferedResourceLoader callback methods.
333 void BufferedDataSource::StartCallback(
334     BufferedResourceLoader::Status status) {
335   DCHECK(render_loop_->BelongsToCurrentThread());
336   DCHECK(loader_.get());
337
338   bool init_cb_is_null = false;
339   {
340     base::AutoLock auto_lock(lock_);
341     init_cb_is_null = init_cb_.is_null();
342   }
343   if (init_cb_is_null) {
344     loader_->Stop();
345     return;
346   }
347
348   // All responses must be successful. Resources that are assumed to be fully
349   // buffered must have a known content length.
350   bool success = status == BufferedResourceLoader::kOk &&
351                  (!assume_fully_buffered() ||
352                   loader_->instance_size() != kPositionNotSpecified);
353
354   if (success) {
355     total_bytes_ = loader_->instance_size();
356     streaming_ =
357         !assume_fully_buffered() &&
358         (total_bytes_ == kPositionNotSpecified || !loader_->range_supported());
359
360     media_log_->SetDoubleProperty("total_bytes",
361                                   static_cast<double>(total_bytes_));
362     media_log_->SetBooleanProperty("streaming", streaming_);
363   } else {
364     loader_->Stop();
365   }
366
367   // TODO(scherkus): we shouldn't have to lock to signal host(), see
368   // http://crbug.com/113712 for details.
369   base::AutoLock auto_lock(lock_);
370   if (stop_signal_received_)
371     return;
372
373   if (success) {
374     if (total_bytes_ != kPositionNotSpecified) {
375       host_->SetTotalBytes(total_bytes_);
376       if (assume_fully_buffered())
377         host_->AddBufferedByteRange(0, total_bytes_);
378     }
379
380     media_log_->SetBooleanProperty("single_origin", loader_->HasSingleOrigin());
381     media_log_->SetBooleanProperty("passed_cors_access_check",
382                                    loader_->DidPassCORSAccessCheck());
383     media_log_->SetBooleanProperty("range_header_supported",
384                                    loader_->range_supported());
385   }
386
387   base::ResetAndReturn(&init_cb_).Run(success);
388 }
389
390 void BufferedDataSource::PartialReadStartCallback(
391     BufferedResourceLoader::Status status) {
392   DCHECK(render_loop_->BelongsToCurrentThread());
393   DCHECK(loader_.get());
394
395   if (status == BufferedResourceLoader::kOk) {
396     // Once the request has started successfully, we can proceed with
397     // reading from it.
398     ReadInternal();
399     return;
400   }
401
402   // Stop the resource loader since we have received an error.
403   loader_->Stop();
404
405   // TODO(scherkus): we shouldn't have to lock to signal host(), see
406   // http://crbug.com/113712 for details.
407   base::AutoLock auto_lock(lock_);
408   if (stop_signal_received_)
409     return;
410   ReadOperation::Run(read_op_.Pass(), kReadError);
411 }
412
413 void BufferedDataSource::ReadCallback(
414     BufferedResourceLoader::Status status,
415     int bytes_read) {
416   DCHECK(render_loop_->BelongsToCurrentThread());
417
418   // TODO(scherkus): we shouldn't have to lock to signal host(), see
419   // http://crbug.com/113712 for details.
420   base::AutoLock auto_lock(lock_);
421   if (stop_signal_received_)
422     return;
423
424   if (status != BufferedResourceLoader::kOk) {
425     // Stop the resource load if it failed.
426     loader_->Stop();
427
428     if (status == BufferedResourceLoader::kCacheMiss &&
429         read_op_->retries() < kNumCacheMissRetries) {
430       read_op_->IncrementRetries();
431
432       // Recreate a loader starting from where we last left off until the
433       // end of the resource.
434       loader_.reset(CreateResourceLoader(
435           read_op_->position(), kPositionNotSpecified));
436
437       base::WeakPtr<BufferedDataSource> weak_this = weak_factory_.GetWeakPtr();
438       loader_->Start(
439           base::Bind(&BufferedDataSource::PartialReadStartCallback, weak_this),
440           base::Bind(&BufferedDataSource::LoadingStateChangedCallback,
441                      weak_this),
442           base::Bind(&BufferedDataSource::ProgressCallback, weak_this),
443           frame_);
444       return;
445     }
446
447     ReadOperation::Run(read_op_.Pass(), kReadError);
448     return;
449   }
450
451   if (bytes_read > 0) {
452     memcpy(read_op_->data(), intermediate_read_buffer_.get(), 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_loop_->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_loop_->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 content