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

Side by Side Diff: media/blink/multibuffer_data_source.cc

Issue 1399603003: Tie multibuffers to URLs (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@media_cache
Patch Set: added MEDIA_BLINK_EXPORT Created 5 years, 2 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
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 "media/blink/multibuffer_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 "media/blink/multibuffer_reader.h"
13 #include "net/base/net_errors.h"
14
15 using blink::WebFrame;
16
17 namespace {
18
19 // Minimum preload buffer.
20 const int64 kMinBufferPreload = 2 << 20; // 2 Mb
21 // Maxmimum preload buffer.
22 const int64 kMaxBufferPreload = 20 << 20; // 20 Mb
23
24 // Preload this much extra, then stop preloading until we fall below the
25 // kTargetSecondsBufferedAhead.
26 const int64 kPreloadHighExtra = 1 << 20; // 1 Mb
27
28 // Total size of the pinned region in the cache.
29 const int64 kMaxBufferSize = 25 << 20; // 25 Mb
30
31 // If bitrate is not known, use this.
32 const int64 kDefaultBitrate = 200 * 8 << 10; // 200 Kbps.
33
34 // Maximum bitrate for buffer calculations.
35 const int64 kMaxBitrate = 20 * 8 << 20; // 20 Mbps.
36
37 // Maximum playback rate for buffer calculations.
38 const double kMaxPlaybackRate = 25.0;
39
40 // Preload this many seconds of data by default.
41 const int64 kTargetSecondsBufferedAhead = 10;
42
43 // Keep this many seconds of data for going back by default.
44 const int64 kTargetSecondsBufferedBehind = 2;
45
46 } // namespace
47
48 namespace media {
49
50 template <typename T>
51 T clamp(T value, T min, T max) {
52 return std::max(std::min(value, max), min);
53 }
54
55 class MultibufferDataSource::ReadOperation {
56 public:
57 ReadOperation(int64 position,
58 int size,
59 uint8* data,
60 const DataSource::ReadCB& callback);
61 ~ReadOperation();
62
63 // Runs |callback_| with the given |result|, deleting the operation
64 // afterwards.
65 static void Run(scoped_ptr<ReadOperation> read_op, int result);
66
67 int64 position() { return position_; }
68 int size() { return size_; }
69 uint8* data() { return data_; }
70
71 private:
72 const int64 position_;
73 const int size_;
74 uint8* data_;
75 DataSource::ReadCB callback_;
76
77 DISALLOW_IMPLICIT_CONSTRUCTORS(ReadOperation);
78 };
79
80 MultibufferDataSource::ReadOperation::ReadOperation(
81 int64 position,
82 int size,
83 uint8* data,
84 const DataSource::ReadCB& callback)
85 : position_(position), size_(size), data_(data), callback_(callback) {
86 DCHECK(!callback_.is_null());
87 }
88
89 MultibufferDataSource::ReadOperation::~ReadOperation() {
90 DCHECK(callback_.is_null());
91 }
92
93 // static
94 void MultibufferDataSource::ReadOperation::Run(
95 scoped_ptr<ReadOperation> read_op,
96 int result) {
97 base::ResetAndReturn(&read_op->callback_).Run(result);
98 }
99
100 MultibufferDataSource::MultibufferDataSource(
101 const GURL& url,
102 UrlData::CORSMode cors_mode,
103 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
104 linked_ptr<ResourceMultiBuffer> multibuffer,
105 WebFrame* frame,
106 MediaLog* media_log,
107 BufferedDataSourceHost* host,
108 const DownloadingCB& downloading_cb)
109 : cors_mode_(cors_mode),
110 total_bytes_(kPositionNotSpecified),
111 streaming_(false),
112 loading_(false),
113 render_task_runner_(task_runner),
114 multibuffer_(multibuffer),
115 frame_(frame),
116 stop_signal_received_(false),
117 media_has_played_(false),
118 single_origin_(true),
119 cancel_on_defer_(false),
120 preload_(AUTO),
121 bitrate_(0),
122 playback_rate_(0.0),
123 media_log_(media_log),
124 host_(host),
125 downloading_cb_(downloading_cb),
126 weak_factory_(this) {
127 weak_ptr_ = weak_factory_.GetWeakPtr();
128 DCHECK(host_);
129 DCHECK(!downloading_cb_.is_null());
130 DCHECK(render_task_runner_->BelongsToCurrentThread());
131 url_data_ = multibuffer_->url_index()->GetByUrl(url, cors_mode_);
132 url_data_->Use();
133 DCHECK(url_data_);
134 }
135
136 MultibufferDataSource::~MultibufferDataSource() {
137 DCHECK(render_task_runner_->BelongsToCurrentThread());
138 }
139
140 bool MultibufferDataSource::media_has_played() const {
141 return media_has_played_;
142 }
143
144 bool MultibufferDataSource::assume_fully_buffered() {
145 return !url_data_->url().SchemeIsHTTPOrHTTPS();
146 }
147
148 void MultibufferDataSource::CreateResourceLoader(int64 first_byte_position,
149 int64 last_byte_position) {
150 DCHECK(render_task_runner_->BelongsToCurrentThread());
151
152 base::WeakPtr<MultibufferDataSource> weak_this = weak_factory_.GetWeakPtr();
153 reader_.reset(new MultiBufferReader(
154 multibuffer_.get(),
155 destination_url_data_ ? destination_url_data_ : url_data_,
156 first_byte_position, last_byte_position,
157 base::Bind(&MultibufferDataSource::ProgressCallback, weak_this)));
158 UpdateBufferSizes();
159 }
160
161 void MultibufferDataSource::Initialize(const InitializeCB& init_cb) {
162 DCHECK(render_task_runner_->BelongsToCurrentThread());
163 DCHECK(!init_cb.is_null());
164 DCHECK(!reader_.get());
165
166 init_cb_ = init_cb;
167
168 CreateResourceLoader(0, kPositionNotSpecified);
169
170 base::WeakPtr<MultibufferDataSource> weak_this = weak_factory_.GetWeakPtr();
171
172 // We're not allowed to call Wait() if data is already available.
173 if (reader_->Available()) {
174 render_task_runner_->PostTask(
175 FROM_HERE,
176 base::Bind(&MultibufferDataSource::StartCallback, weak_this));
177 } else {
178 reader_->Wait(1,
179 base::Bind(&MultibufferDataSource::StartCallback, weak_this));
180 }
181 UpdateLoadingState();
182 }
183
184 void MultibufferDataSource::SetPreload(Preload preload) {
185 DCHECK(render_task_runner_->BelongsToCurrentThread());
186 preload_ = preload;
187 UpdateBufferSizes();
188 }
189
190 bool MultibufferDataSource::HasSingleOrigin() {
191 DCHECK(render_task_runner_->BelongsToCurrentThread());
192 DCHECK(init_cb_.is_null() && reader_.get())
193 << "Initialize() must complete before calling HasSingleOrigin()";
194 return single_origin_;
195 }
196
197 bool MultibufferDataSource::DidPassCORSAccessCheck() const {
198 if (cors_mode_ == UrlData::kUnspecified)
199 return false;
200 // If init_cb is set, we initialization is not finished yet.
201 if (!init_cb_.is_null())
202 return false;
203 // Loader will be false if there was a failure.
204 if (!reader_)
205 return false;
206 return true;
207 }
208
209 void MultibufferDataSource::Abort() {
210 DCHECK(render_task_runner_->BelongsToCurrentThread());
211 {
212 base::AutoLock auto_lock(lock_);
213 StopInternal_Locked();
214 }
215 StopLoader();
216 frame_ = NULL;
217 }
218
219 void MultibufferDataSource::MediaPlaybackRateChanged(double playback_rate) {
220 DCHECK(render_task_runner_->BelongsToCurrentThread());
221 DCHECK(reader_.get());
222
223 if (playback_rate < 0.0)
224 return;
225
226 playback_rate_ = playback_rate;
227 cancel_on_defer_ = false;
228 UpdateBufferSizes();
229 }
230
231 void MultibufferDataSource::MediaIsPlaying() {
232 DCHECK(render_task_runner_->BelongsToCurrentThread());
233 media_has_played_ = true;
234 cancel_on_defer_ = false;
235 paused_ = false;
236 preload_ = AUTO;
237 UpdateBufferSizes();
238 }
239
240 void MultibufferDataSource::MediaIsPaused() {
241 DCHECK(render_task_runner_->BelongsToCurrentThread());
242 paused_ = true;
243 UpdateBufferSizes();
244 }
245
246 /////////////////////////////////////////////////////////////////////////////
247 // DataSource implementation.
248 void MultibufferDataSource::Stop() {
249 {
250 base::AutoLock auto_lock(lock_);
251 StopInternal_Locked();
252 }
253
254 render_task_runner_->PostTask(FROM_HERE,
255 base::Bind(&MultibufferDataSource::StopLoader,
256 weak_factory_.GetWeakPtr()));
257 }
258
259 void MultibufferDataSource::SetBitrate(int bitrate) {
260 render_task_runner_->PostTask(
261 FROM_HERE, base::Bind(&MultibufferDataSource::SetBitrateTask,
262 weak_factory_.GetWeakPtr(), bitrate));
263 }
264
265 void MultibufferDataSource::OnBufferingHaveEnough() {
266 DCHECK(render_task_runner_->BelongsToCurrentThread());
267 if (reader_ && preload_ == METADATA && !media_has_played_ && !IsStreaming()) {
268 cancel_on_defer_ = true;
269 if (!loading_)
270 reader_.reset(nullptr);
271 }
272 }
273
274 void MultibufferDataSource::Read(int64 position,
275 int size,
276 uint8* data,
277 const DataSource::ReadCB& read_cb) {
278 DVLOG(1) << "Read: " << position << " offset, " << size << " bytes";
279 // Reading is not allowed until after initialization.
280 DCHECK(init_cb_.is_null());
281 DCHECK(!read_cb.is_null());
282
283 {
284 base::AutoLock auto_lock(lock_);
285 DCHECK(!read_op_);
286
287 if (stop_signal_received_) {
288 read_cb.Run(kReadError);
289 return;
290 }
291
292 read_op_.reset(new ReadOperation(position, size, data, read_cb));
293 }
294
295 render_task_runner_->PostTask(
296 FROM_HERE,
297 base::Bind(&MultibufferDataSource::ReadTask, weak_factory_.GetWeakPtr()));
298 }
299
300 bool MultibufferDataSource::GetSize(int64* size_out) {
301 if (destination_url_data_) {
302 *size_out = destination_url_data_->length();
303 if (*size_out != kPositionNotSpecified) {
304 return true;
305 }
306 }
307 *size_out = 0;
308 return false;
309 }
310
311 bool MultibufferDataSource::IsStreaming() {
312 return streaming_;
313 }
314
315 /////////////////////////////////////////////////////////////////////////////
316 // This method is the place where actual read happens,
317 void MultibufferDataSource::ReadTask() {
318 DCHECK(render_task_runner_->BelongsToCurrentThread());
319
320 base::AutoLock auto_lock(lock_);
321 int bytes_read = 0;
322 if (stop_signal_received_)
323 return;
324 DCHECK(read_op_);
325 DCHECK(read_op_->size());
326
327 if (!reader_) {
328 CreateResourceLoader(read_op_->position(), kPositionNotSpecified);
329 } else {
330 reader_->Seek(read_op_->position());
331 }
332
333 int64_t available = reader_->Available();
334 if (available < 0) {
335 // A failure has occured.
336 ReadOperation::Run(read_op_.Pass(), kReadError);
337 return;
338 }
339 if (available) {
340 bytes_read =
341 static_cast<int>(std::min<int64_t>(available, read_op_->size()));
342 bytes_read = reader_->TryRead(read_op_->data(), bytes_read);
343 ReadOperation::Run(read_op_.Pass(), bytes_read);
344 } else {
345 reader_->Wait(1, base::Bind(&MultibufferDataSource::ReadTask,
346 weak_factory_.GetWeakPtr()));
347 UpdateLoadingState();
348 }
349 }
350
351 void MultibufferDataSource::StopInternal_Locked() {
352 lock_.AssertAcquired();
353 if (stop_signal_received_)
354 return;
355
356 stop_signal_received_ = true;
357
358 // Initialize() isn't part of the DataSource interface so don't call it in
359 // response to Stop().
360 init_cb_.Reset();
361
362 if (read_op_)
363 ReadOperation::Run(read_op_.Pass(), kReadError);
364 }
365
366 void MultibufferDataSource::StopLoader() {
367 DCHECK(render_task_runner_->BelongsToCurrentThread());
368 reader_.reset(nullptr);
369 UpdateLoadingState();
370 }
371
372 void MultibufferDataSource::SetBitrateTask(int bitrate) {
373 DCHECK(render_task_runner_->BelongsToCurrentThread());
374 DCHECK(reader_.get());
375
376 bitrate_ = bitrate;
377 UpdateBufferSizes();
378 }
379
380 /////////////////////////////////////////////////////////////////////////////
381 // BufferedResourceLoader callback methods.
382 void MultibufferDataSource::StartCallback() {
383 DCHECK(render_task_runner_->BelongsToCurrentThread());
384 DCHECK(reader_);
385
386 bool init_cb_is_null = false;
387 {
388 base::AutoLock auto_lock(lock_);
389 init_cb_is_null = init_cb_.is_null();
390 }
391 if (init_cb_is_null) {
392 reader_.reset();
393 return;
394 }
395
396 destination_url_data_ = reader_->GetUrlData();
397
398 // All responses must be successful. Resources that are assumed to be fully
399 // buffered must have a known content length.
400 bool success = reader_->Available() > 0 && destination_url_data_ &&
401 (!assume_fully_buffered() ||
402 destination_url_data_->length() != kPositionNotSpecified);
403
404 if (success) {
405 total_bytes_ = destination_url_data_->length();
406 streaming_ =
407 !assume_fully_buffered() && (total_bytes_ == kPositionNotSpecified ||
408 !destination_url_data_->range_supported());
409
410 media_log_->SetDoubleProperty("total_bytes",
411 static_cast<double>(total_bytes_));
412 media_log_->SetBooleanProperty("streaming", streaming_);
413 } else {
414 reader_.reset(nullptr);
415 }
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 (success) {
424 if (total_bytes_ != kPositionNotSpecified) {
425 host_->SetTotalBytes(total_bytes_);
426 if (assume_fully_buffered())
427 host_->AddBufferedByteRange(0, total_bytes_);
428 }
429
430 // Progress callback might be called after the start callback,
431 // make sure that we update single_origin_ now.
432 UpdateSingleOrigin();
433
434 media_log_->SetBooleanProperty("single_origin", single_origin_);
435 media_log_->SetBooleanProperty("passed_cors_access_check",
436 DidPassCORSAccessCheck());
437 media_log_->SetBooleanProperty("range_header_supported",
438 destination_url_data_->range_supported());
439 }
440
441 UpdateLoadingState();
442 render_task_runner_->PostTask(
443 FROM_HERE, base::Bind(base::ResetAndReturn(&init_cb_), success));
444 }
445
446 void MultibufferDataSource::UpdateSingleOrigin() {
447 DCHECK(render_task_runner_->BelongsToCurrentThread());
448 if (reader_ && destination_url_data_) {
449 scoped_refptr<UrlData> new_url_data = reader_->GetUrlData();
450 if (new_url_data && new_url_data != destination_url_data_) {
451 // A redirect has happened.
452 // Check if origin has changed.
453 if (destination_url_data_->url().GetOrigin() !=
454 new_url_data->url().GetOrigin()) {
455 single_origin_ = false;
456 }
457 }
458 }
459 }
460
461 void MultibufferDataSource::ProgressCallback(int64 begin, int64 end) {
462 DCHECK(render_task_runner_->BelongsToCurrentThread());
463
464 UpdateSingleOrigin();
465 if (assume_fully_buffered())
466 return;
467
468 if (end > begin) {
469 // TODO(scherkus): we shouldn't have to lock to signal host(), see
470 // http://crbug.com/113712 for details.
471 base::AutoLock auto_lock(lock_);
472 if (stop_signal_received_)
473 return;
474
475 host_->AddBufferedByteRange(begin, end);
476 }
477
478 UpdateLoadingState();
479 }
480
481 void MultibufferDataSource::UpdateLoadingState() {
482 // Update loading state.
483 if ((!!reader_ && reader_->IsLoading()) != loading_) {
484 loading_ = !loading_;
485
486 if (!loading_ && cancel_on_defer_) {
487 reader_.reset(nullptr);
488 }
489
490 // Callback could kill us, be sure to call it last.
491 downloading_cb_.Run(loading_);
492 }
493 }
494
495 void MultibufferDataSource::UpdateBufferSizes() {
496 if (!reader_)
497 return;
498
499 if (!assume_fully_buffered()) {
500 // If the playback has started and we're paused, then try to load as much as
501 // possible, assuming that the file is cacheable. (If not, why bother?)
502 if (media_has_played_ && paused_ && destination_url_data_ &&
503 destination_url_data_->range_supported() &&
504 destination_url_data_->cacheable()) {
505 reader_->SetPreload(1LL << 40, 1LL << 40); // 1 Tb
506 return;
507 }
508 }
509
510 // Use a default bit rate if unknown and clamp to prevent overflow.
511 int64 bitrate = clamp<int64>(bitrate_, 0, kMaxBitrate);
512 if (bitrate == 0)
513 bitrate = kDefaultBitrate;
514
515 // Only scale the buffer window for playback rates greater than 1.0 in
516 // magnitude and clamp to prevent overflow.
517 bool backward_playback = false;
518 double playback_rate = playback_rate_;
519 if (playback_rate < 0.0) {
520 backward_playback = true;
521 playback_rate *= -1.0;
522 }
523
524 playback_rate = std::max(playback_rate, 1.0);
525 playback_rate = std::min(playback_rate, kMaxPlaybackRate);
526
527 int64 bytes_per_second = (bitrate / 8.0) * playback_rate;
528
529 int64 preload = clamp(kTargetSecondsBufferedAhead * bytes_per_second,
530 kMinBufferPreload, kMaxBufferPreload);
531 int64 back_buffer = clamp(kTargetSecondsBufferedBehind * bytes_per_second,
532 kMinBufferPreload, kMaxBufferPreload);
533 if (backward_playback)
534 std::swap(preload, back_buffer);
535
536 int64 pin_forwards = kMaxBufferSize - back_buffer;
537 DCHECK_LE(preload_ + kPreloadHighExtra, pin_forwards);
538 reader_->SetMaxBuffer(back_buffer, pin_forwards);
539
540 if (preload_ == METADATA) {
541 reader_->SetPreload(0, 0);
542 } else {
543 reader_->SetPreload(preload + kPreloadHighExtra, preload);
544 }
545 }
546
547 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698