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