Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(326)

Side by Side Diff: webkit/renderer/media/buffered_data_source.cc

Issue 18123002: Migrate webkit/renderer/media/ to content/renderer/media/. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: delegates Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 "webkit/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 "media/base/media_log.h"
11 #include "net/base/net_errors.h"
12
13 using WebKit::WebFrame;
14
15 namespace {
16
17 // BufferedDataSource has an intermediate buffer, this value governs the initial
18 // size of that buffer. It is set to 32KB because this is a typical read size
19 // of FFmpeg.
20 const int kInitialReadBufferSize = 32768;
21
22 // Number of cache misses we allow for a single Read() before signaling an
23 // error.
24 const int kNumCacheMissRetries = 3;
25
26 } // namespace
27
28 namespace webkit_media {
29
30 class BufferedDataSource::ReadOperation {
31 public:
32 ReadOperation(int64 position, int size, uint8* data,
33 const media::DataSource::ReadCB& callback);
34 ~ReadOperation();
35
36 // Runs |callback_| with the given |result|, deleting the operation
37 // afterwards.
38 static void Run(scoped_ptr<ReadOperation> read_op, int result);
39
40 // State for the number of times this read operation has been retried.
41 int retries() { return retries_; }
42 void IncrementRetries() { ++retries_; }
43
44 int64 position() { return position_; }
45 int size() { return size_; }
46 uint8* data() { return data_; }
47
48 private:
49 int retries_;
50
51 const int64 position_;
52 const int size_;
53 uint8* data_;
54 media::DataSource::ReadCB callback_;
55
56 DISALLOW_IMPLICIT_CONSTRUCTORS(ReadOperation);
57 };
58
59 BufferedDataSource::ReadOperation::ReadOperation(
60 int64 position, int size, uint8* data,
61 const media::DataSource::ReadCB& callback)
62 : retries_(0),
63 position_(position),
64 size_(size),
65 data_(data),
66 callback_(callback) {
67 DCHECK(!callback_.is_null());
68 }
69
70 BufferedDataSource::ReadOperation::~ReadOperation() {
71 DCHECK(callback_.is_null());
72 }
73
74 // static
75 void BufferedDataSource::ReadOperation::Run(
76 scoped_ptr<ReadOperation> read_op, int result) {
77 base::ResetAndReturn(&read_op->callback_).Run(result);
78 }
79
80 BufferedDataSource::BufferedDataSource(
81 const scoped_refptr<base::MessageLoopProxy>& render_loop,
82 WebFrame* frame,
83 media::MediaLog* media_log,
84 const DownloadingCB& downloading_cb)
85 : weak_factory_(this),
86 weak_this_(weak_factory_.GetWeakPtr()),
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 downloading_cb_(downloading_cb) {
102 DCHECK(!downloading_cb_.is_null());
103 }
104
105 BufferedDataSource::~BufferedDataSource() {}
106
107 // A factory method to create BufferedResourceLoader using the read parameters.
108 // This method can be overridden to inject mock BufferedResourceLoader object
109 // for testing purpose.
110 BufferedResourceLoader* BufferedDataSource::CreateResourceLoader(
111 int64 first_byte_position, int64 last_byte_position) {
112 DCHECK(render_loop_->BelongsToCurrentThread());
113
114 BufferedResourceLoader::DeferStrategy strategy = preload_ == METADATA ?
115 BufferedResourceLoader::kReadThenDefer :
116 BufferedResourceLoader::kCapacityDefer;
117
118 return new BufferedResourceLoader(url_,
119 cors_mode_,
120 first_byte_position,
121 last_byte_position,
122 strategy,
123 bitrate_,
124 playback_rate_,
125 media_log_.get());
126 }
127
128 void BufferedDataSource::set_host(media::DataSourceHost* host) {
129 DataSource::set_host(host);
130
131 if (loader_) {
132 base::AutoLock auto_lock(lock_);
133 UpdateHostState_Locked();
134 }
135 }
136
137 void BufferedDataSource::Initialize(
138 const GURL& url,
139 BufferedResourceLoader::CORSMode cors_mode,
140 const InitializeCB& init_cb) {
141 DCHECK(render_loop_->BelongsToCurrentThread());
142 DCHECK(!init_cb.is_null());
143 DCHECK(!loader_.get());
144 url_ = url;
145 cors_mode_ = cors_mode;
146
147 init_cb_ = init_cb;
148
149 if (url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)) {
150 // Do an unbounded range request starting at the beginning. If the server
151 // responds with 200 instead of 206 we'll fall back into a streaming mode.
152 loader_.reset(CreateResourceLoader(0, kPositionNotSpecified));
153 } else {
154 // For all other protocols, assume they support range request. We fetch
155 // the full range of the resource to obtain the instance size because
156 // we won't be served HTTP headers.
157 loader_.reset(CreateResourceLoader(kPositionNotSpecified,
158 kPositionNotSpecified));
159 assume_fully_buffered_ = true;
160 }
161
162 loader_->Start(
163 base::Bind(&BufferedDataSource::StartCallback, weak_this_),
164 base::Bind(&BufferedDataSource::LoadingStateChangedCallback, weak_this_),
165 base::Bind(&BufferedDataSource::ProgressCallback, weak_this_),
166 frame_);
167 }
168
169 void BufferedDataSource::SetPreload(Preload preload) {
170 DCHECK(render_loop_->BelongsToCurrentThread());
171 preload_ = preload;
172 }
173
174 bool BufferedDataSource::HasSingleOrigin() {
175 DCHECK(render_loop_->BelongsToCurrentThread());
176 DCHECK(init_cb_.is_null() && loader_.get())
177 << "Initialize() must complete before calling HasSingleOrigin()";
178 return loader_->HasSingleOrigin();
179 }
180
181 bool BufferedDataSource::DidPassCORSAccessCheck() const {
182 return loader_.get() && loader_->DidPassCORSAccessCheck();
183 }
184
185 void BufferedDataSource::Abort() {
186 DCHECK(render_loop_->BelongsToCurrentThread());
187 {
188 base::AutoLock auto_lock(lock_);
189 StopInternal_Locked();
190 }
191 StopLoader();
192 frame_ = NULL;
193 }
194
195 /////////////////////////////////////////////////////////////////////////////
196 // media::DataSource implementation.
197 void BufferedDataSource::Stop(const base::Closure& closure) {
198 {
199 base::AutoLock auto_lock(lock_);
200 StopInternal_Locked();
201 }
202 closure.Run();
203
204 render_loop_->PostTask(FROM_HERE,
205 base::Bind(&BufferedDataSource::StopLoader, weak_this_));
206 }
207
208 void BufferedDataSource::SetPlaybackRate(float playback_rate) {
209 render_loop_->PostTask(FROM_HERE, base::Bind(
210 &BufferedDataSource::SetPlaybackRateTask, weak_this_, playback_rate));
211 }
212
213 void BufferedDataSource::SetBitrate(int bitrate) {
214 render_loop_->PostTask(FROM_HERE, base::Bind(
215 &BufferedDataSource::SetBitrateTask, weak_this_, bitrate));
216 }
217
218 void BufferedDataSource::Read(
219 int64 position, int size, uint8* data,
220 const media::DataSource::ReadCB& read_cb) {
221 DVLOG(1) << "Read: " << position << " offset, " << size << " bytes";
222 DCHECK(!read_cb.is_null());
223
224 {
225 base::AutoLock auto_lock(lock_);
226 DCHECK(!read_op_);
227
228 if (stop_signal_received_) {
229 read_cb.Run(kReadError);
230 return;
231 }
232
233 read_op_.reset(new ReadOperation(position, size, data, read_cb));
234 }
235
236 render_loop_->PostTask(FROM_HERE, base::Bind(
237 &BufferedDataSource::ReadTask, weak_this_));
238 }
239
240 bool BufferedDataSource::GetSize(int64* size_out) {
241 if (total_bytes_ != kPositionNotSpecified) {
242 *size_out = total_bytes_;
243 return true;
244 }
245 *size_out = 0;
246 return false;
247 }
248
249 bool BufferedDataSource::IsStreaming() {
250 return streaming_;
251 }
252
253 /////////////////////////////////////////////////////////////////////////////
254 // Render thread tasks.
255 void BufferedDataSource::ReadTask() {
256 DCHECK(render_loop_->BelongsToCurrentThread());
257 ReadInternal();
258 }
259
260 void BufferedDataSource::StopInternal_Locked() {
261 lock_.AssertAcquired();
262 if (stop_signal_received_)
263 return;
264
265 stop_signal_received_ = true;
266
267 // Initialize() isn't part of the DataSource interface so don't call it in
268 // response to Stop().
269 init_cb_.Reset();
270
271 if (read_op_)
272 ReadOperation::Run(read_op_.Pass(), kReadError);
273 }
274
275 void BufferedDataSource::StopLoader() {
276 DCHECK(render_loop_->BelongsToCurrentThread());
277
278 if (loader_)
279 loader_->Stop();
280 }
281
282 void BufferedDataSource::SetPlaybackRateTask(float playback_rate) {
283 DCHECK(render_loop_->BelongsToCurrentThread());
284 DCHECK(loader_.get());
285
286 if (playback_rate != 0)
287 media_has_played_ = true;
288
289 playback_rate_ = playback_rate;
290 loader_->SetPlaybackRate(playback_rate);
291
292 if (!loader_->range_supported()) {
293 // 200 responses end up not being reused to satisfy future range requests,
294 // and we don't want to get too far ahead of the read-head (and thus require
295 // a restart), so keep to the thresholds.
296 loader_->UpdateDeferStrategy(BufferedResourceLoader::kCapacityDefer);
297 } else if (media_has_played_ && playback_rate == 0) {
298 // If the playback has started (at which point the preload value is ignored)
299 // and we're paused, then try to load as much as possible (the loader will
300 // fall back to kCapacityDefer if it knows the current response won't be
301 // useful from the cache in the future).
302 loader_->UpdateDeferStrategy(BufferedResourceLoader::kNeverDefer);
303 } else {
304 // If media is currently playing or the page indicated preload=auto,
305 // use threshold strategy to enable/disable deferring when the buffer
306 // is full/depleted.
307 loader_->UpdateDeferStrategy(BufferedResourceLoader::kCapacityDefer);
308 }
309 }
310
311 void BufferedDataSource::SetBitrateTask(int bitrate) {
312 DCHECK(render_loop_->BelongsToCurrentThread());
313 DCHECK(loader_.get());
314
315 bitrate_ = bitrate;
316 loader_->SetBitrate(bitrate);
317 }
318
319 // This method is the place where actual read happens, |loader_| must be valid
320 // prior to make this method call.
321 void BufferedDataSource::ReadInternal() {
322 DCHECK(render_loop_->BelongsToCurrentThread());
323 int64 position = 0;
324 int size = 0;
325 {
326 base::AutoLock auto_lock(lock_);
327 if (stop_signal_received_)
328 return;
329
330 position = read_op_->position();
331 size = read_op_->size();
332 }
333
334 // First we prepare the intermediate read buffer for BufferedResourceLoader
335 // to write to.
336 if (size > intermediate_read_buffer_size_) {
337 intermediate_read_buffer_.reset(new uint8[size]);
338 }
339
340 // Perform the actual read with BufferedResourceLoader.
341 loader_->Read(
342 position, size, intermediate_read_buffer_.get(),
343 base::Bind(&BufferedDataSource::ReadCallback, weak_this_));
344 }
345
346
347 /////////////////////////////////////////////////////////////////////////////
348 // BufferedResourceLoader callback methods.
349 void BufferedDataSource::StartCallback(
350 BufferedResourceLoader::Status status) {
351 DCHECK(render_loop_->BelongsToCurrentThread());
352 DCHECK(loader_.get());
353
354 bool init_cb_is_null = false;
355 {
356 base::AutoLock auto_lock(lock_);
357 init_cb_is_null = init_cb_.is_null();
358 }
359 if (init_cb_is_null) {
360 loader_->Stop();
361 return;
362 }
363
364 // All responses must be successful. Resources that are assumed to be fully
365 // buffered must have a known content length.
366 bool success = status == BufferedResourceLoader::kOk &&
367 (!assume_fully_buffered_ ||
368 loader_->instance_size() != kPositionNotSpecified);
369
370 if (success) {
371 total_bytes_ = loader_->instance_size();
372 streaming_ = !assume_fully_buffered_ &&
373 (total_bytes_ == kPositionNotSpecified || !loader_->range_supported());
374 } else {
375 loader_->Stop();
376 }
377
378 // TODO(scherkus): we shouldn't have to lock to signal host(), see
379 // http://crbug.com/113712 for details.
380 base::AutoLock auto_lock(lock_);
381 if (stop_signal_received_)
382 return;
383
384 if (success)
385 UpdateHostState_Locked();
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 loader_->Start(
437 base::Bind(&BufferedDataSource::PartialReadStartCallback, weak_this_),
438 base::Bind(&BufferedDataSource::LoadingStateChangedCallback,
439 weak_this_),
440 base::Bind(&BufferedDataSource::ProgressCallback, weak_this_),
441 frame_);
442 return;
443 }
444
445 ReadOperation::Run(read_op_.Pass(), kReadError);
446 return;
447 }
448
449 if (bytes_read > 0) {
450 memcpy(read_op_->data(), intermediate_read_buffer_.get(), bytes_read);
451 } else if (bytes_read == 0 && total_bytes_ == kPositionNotSpecified) {
452 // We've reached the end of the file and we didn't know the total size
453 // before. Update the total size so Read()s past the end of the file will
454 // fail like they would if we had known the file size at the beginning.
455 total_bytes_ = loader_->instance_size();
456
457 if (host() && total_bytes_ != kPositionNotSpecified) {
458 host()->SetTotalBytes(total_bytes_);
459 host()->AddBufferedByteRange(loader_->first_byte_position(),
460 total_bytes_);
461 }
462 }
463 ReadOperation::Run(read_op_.Pass(), bytes_read);
464 }
465
466 void BufferedDataSource::LoadingStateChangedCallback(
467 BufferedResourceLoader::LoadingState state) {
468 DCHECK(render_loop_->BelongsToCurrentThread());
469
470 if (assume_fully_buffered_)
471 return;
472
473 bool is_downloading_data;
474 switch (state) {
475 case BufferedResourceLoader::kLoading:
476 is_downloading_data = true;
477 break;
478 case BufferedResourceLoader::kLoadingDeferred:
479 case BufferedResourceLoader::kLoadingFinished:
480 is_downloading_data = false;
481 break;
482
483 // TODO(scherkus): we don't signal network activity changes when loads
484 // fail to preserve existing behaviour when deferring is toggled, however
485 // we should consider changing DownloadingCB to also propagate loading
486 // state. For example there isn't any signal today to notify the client that
487 // loading has failed (we only get errors on subsequent reads).
488 case BufferedResourceLoader::kLoadingFailed:
489 return;
490 }
491
492 downloading_cb_.Run(is_downloading_data);
493 }
494
495 void BufferedDataSource::ProgressCallback(int64 position) {
496 DCHECK(render_loop_->BelongsToCurrentThread());
497
498 if (assume_fully_buffered_)
499 return;
500
501 // TODO(scherkus): we shouldn't have to lock to signal host(), see
502 // http://crbug.com/113712 for details.
503 base::AutoLock auto_lock(lock_);
504 if (stop_signal_received_)
505 return;
506
507 ReportOrQueueBufferedBytes(loader_->first_byte_position(), position);
508 }
509
510 void BufferedDataSource::ReportOrQueueBufferedBytes(int64 start, int64 end) {
511 if (host())
512 host()->AddBufferedByteRange(start, end);
513 else
514 queued_buffered_byte_ranges_.Add(start, end);
515 }
516
517 void BufferedDataSource::UpdateHostState_Locked() {
518 lock_.AssertAcquired();
519
520 if (!host())
521 return;
522
523 for (size_t i = 0; i < queued_buffered_byte_ranges_.size(); ++i) {
524 host()->AddBufferedByteRange(queued_buffered_byte_ranges_.start(i),
525 queued_buffered_byte_ranges_.end(i));
526 }
527 queued_buffered_byte_ranges_.clear();
528
529 if (total_bytes_ == kPositionNotSpecified)
530 return;
531
532 host()->SetTotalBytes(total_bytes_);
533
534 if (assume_fully_buffered_)
535 host()->AddBufferedByteRange(0, total_bytes_);
536 }
537
538 } // namespace webkit_media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698