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

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: compile fixes 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, int size, uint8* data,
58 const DataSource::ReadCB& callback);
59 ~ReadOperation();
60
61 // Runs |callback_| with the given |result|, deleting the operation
62 // afterwards.
63 static void Run(scoped_ptr<ReadOperation> read_op, int result);
64
65 int64 position() { return position_; }
66 int size() { return size_; }
67 uint8* data() { return data_; }
68
69 private:
70 const int64 position_;
71 const int size_;
72 uint8* data_;
73 DataSource::ReadCB callback_;
74
75 DISALLOW_IMPLICIT_CONSTRUCTORS(ReadOperation);
76 };
77
78 MultibufferDataSource::ReadOperation::ReadOperation(
79 int64 position, int size, uint8* data,
80 const DataSource::ReadCB& callback)
81 : position_(position),
82 size_(size),
83 data_(data),
84 callback_(callback) {
85 DCHECK(!callback_.is_null());
86 }
87
88 MultibufferDataSource::ReadOperation::~ReadOperation() {
89 DCHECK(callback_.is_null());
90 }
91
92 // static
93 void MultibufferDataSource::ReadOperation::Run(
94 scoped_ptr<ReadOperation> read_op, int result) {
95 base::ResetAndReturn(&read_op->callback_).Run(result);
96 }
97
98 MultibufferDataSource::MultibufferDataSource(
99 const GURL& url,
100 UrlData::CORSMode cors_mode,
101 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
102 ResourceMultiBuffer* multibuffer,
103 WebFrame* frame,
104 MediaLog* media_log,
105 BufferedDataSourceHost* host,
106 const DownloadingCB& downloading_cb)
107 : cors_mode_(cors_mode),
108 total_bytes_(kPositionNotSpecified),
109 streaming_(false),
110 loading_(false),
111 render_task_runner_(task_runner),
112 multibuffer_(multibuffer),
113 frame_(frame),
114 stop_signal_received_(false),
115 media_has_played_(false),
116 single_origin_(true),
117 cancel_on_defer_(false),
118 preload_(AUTO),
119 bitrate_(0),
120 playback_rate_(0.0),
121 media_log_(media_log),
122 host_(host),
123 downloading_cb_(downloading_cb),
124 weak_factory_(this) {
125 weak_ptr_ = weak_factory_.GetWeakPtr();
126 DCHECK(host_);
127 DCHECK(!downloading_cb_.is_null());
128 DCHECK(render_task_runner_->BelongsToCurrentThread());
129 url_data_ = multibuffer_->url_index()->GetByUrl(url, cors_mode_);
130 url_data_->Use();
131 DCHECK(url_data_);
132 }
133
134 MultibufferDataSource::~MultibufferDataSource() {
135 DCHECK(render_task_runner_->BelongsToCurrentThread());
136 }
137
138 bool MultibufferDataSource::media_has_played() const {
139 return media_has_played_;
140 }
141
142 bool MultibufferDataSource::assume_fully_buffered() {
143 return !url_data_->url().SchemeIsHTTPOrHTTPS();
144 }
145
146 // A factory method to create BufferedResourceLoader using the read parameters.
liberato (no reviews please) 2015/10/16 21:50:36 comment is out of date. also, it doesn't create a
hubbe 2015/10/16 23:47:05 Removed the comment.
147 void MultibufferDataSource::CreateResourceLoader(
148 int64 first_byte_position, int64 last_byte_position) {
149 DCHECK(render_task_runner_->BelongsToCurrentThread());
150
151 base::WeakPtr<MultibufferDataSource> weak_this = weak_factory_.GetWeakPtr();
152 loader_.reset(new MultiBufferReader(
153 multibuffer_,
154 destination_url_data_ ? destination_url_data_ : url_data_,
155 first_byte_position,
156 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(!loader_.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 (loader_->Available()) {
174 render_task_runner_->PostTask(
175 FROM_HERE,
176 base::Bind(&MultibufferDataSource::StartCallback, weak_this));
177 } else {
178 loader_->Wait(1, base::Bind(
179 &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() && loader_.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 (!loader_)
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(loader_.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(FROM_HERE,
261 base::Bind(&MultibufferDataSource::SetBitrateTask,
262 weak_factory_.GetWeakPtr(),
263 bitrate));
264 }
265
266 void MultibufferDataSource::OnBufferingHaveEnough() {
267 DCHECK(render_task_runner_->BelongsToCurrentThread());
268 if (loader_ && preload_ == METADATA && !media_has_played_ && !IsStreaming()) {
269 cancel_on_defer_ = true;
270 if (!loading_)
271 loader_.reset(nullptr);
272 }
273 }
274
275 void MultibufferDataSource::Read(
276 int64 position, int size, 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 (!loader_) {
328 CreateResourceLoader(read_op_->position(), kPositionNotSpecified);
329 }
liberato (no reviews please) 2015/10/16 21:50:36 else?
hubbe 2015/10/16 23:47:05 Sure, why not?
330
331 loader_->Seek(read_op_->position());
332
333 int64_t available = loader_->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 = static_cast<int>(
341 std::min<int64_t>(available, read_op_->size()));
342 bytes_read = loader_->TryRead(read_op_->data(), bytes_read);
343 ReadOperation::Run(read_op_.Pass(), bytes_read);
344 } else {
345 loader_->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 loader_.reset(nullptr);
369 UpdateLoadingState();
370 }
371
372 void MultibufferDataSource::SetBitrateTask(int bitrate) {
373 DCHECK(render_task_runner_->BelongsToCurrentThread());
374 DCHECK(loader_.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(loader_);
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 loader_.reset();
393 return;
394 }
395
396 destination_url_data_ = loader_->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 =
401 loader_->Available() > 0 &&
402 destination_url_data_ &&
403 (!assume_fully_buffered() ||
404 destination_url_data_->length() != kPositionNotSpecified);
405
406 if (success) {
407 total_bytes_ = destination_url_data_->length();
408 streaming_ =
409 !assume_fully_buffered() &&
410 (total_bytes_ == kPositionNotSpecified ||
411 !destination_url_data_->range_supported());
412
413 media_log_->SetDoubleProperty("total_bytes",
414 static_cast<double>(total_bytes_));
415 media_log_->SetBooleanProperty("streaming", streaming_);
416 } else {
417 loader_.reset(nullptr);
418 }
419
420 // TODO(scherkus): we shouldn't have to lock to signal host(), see
421 // http://crbug.com/113712 for details.
422 base::AutoLock auto_lock(lock_);
423 if (stop_signal_received_)
424 return;
425
426 if (success) {
427 if (total_bytes_ != kPositionNotSpecified) {
428 host_->SetTotalBytes(total_bytes_);
429 if (assume_fully_buffered())
430 host_->AddBufferedByteRange(0, total_bytes_);
431 }
432
433 // Progress callback might be called after the start callback,
434 // make sure that we update has_single_origin_ now.
liberato (no reviews please) 2015/10/16 21:50:36 single_origin_
hubbe 2015/10/16 23:47:05 Done.
435 UpdateSingleOrigin();
436
437 media_log_->SetBooleanProperty("single_origin", single_origin_);
438 media_log_->SetBooleanProperty("passed_cors_access_check",
439 DidPassCORSAccessCheck());
440 media_log_->SetBooleanProperty("range_header_supported",
441 destination_url_data_->range_supported());
442 }
443
444 UpdateLoadingState();
445 render_task_runner_->PostTask(
446 FROM_HERE, base::Bind(base::ResetAndReturn(&init_cb_), success));
447 }
448
449 void MultibufferDataSource::UpdateSingleOrigin() {
450 DCHECK(render_task_runner_->BelongsToCurrentThread());
451 if (loader_ && destination_url_data_) {
452 scoped_refptr<UrlData> new_url_data = loader_->GetUrlData();
453 if (new_url_data && new_url_data != destination_url_data_) {
454 // A redirect has happened.
455 // Check if origin has changed.
456 if (destination_url_data_->url().GetOrigin() !=
457 new_url_data->url().GetOrigin()) {
458 single_origin_ = false;
459 }
460 }
461 }
462 }
463
464 void MultibufferDataSource::ProgressCallback(int64 begin, int64 end) {
465 DCHECK(render_task_runner_->BelongsToCurrentThread());
466
467 UpdateSingleOrigin();
468 if (assume_fully_buffered())
469 return;
470
471 if (end > begin) {
472 // TODO(scherkus): we shouldn't have to lock to signal host(), see
473 // http://crbug.com/113712 for details.
474 base::AutoLock auto_lock(lock_);
475 if (stop_signal_received_)
476 return;
477
478 host_->AddBufferedByteRange(begin, end);
479 }
480
481 UpdateLoadingState();
482 }
483
484 void MultibufferDataSource::UpdateLoadingState() {
485 // Update loading state.
486 if ((!!loader_ && loader_->IsLoading()) != loading_) {
liberato (no reviews please) 2015/10/16 21:50:36 extra points to use s/!=/^ :)
hubbe 2015/10/16 23:47:05 Acknowledged.
487 loading_ = !loading_;
488
489 if (!loading_ && cancel_on_defer_) {
490 loader_.reset(nullptr);
491 }
492
493 // Callback could kill us, be sure to call it last.
494 downloading_cb_.Run(loading_);
495 }
496 }
497
498 void MultibufferDataSource::UpdateBufferSizes() {
499 if (!loader_)
500 return;
501
502 if (!assume_fully_buffered()) {
503 // If the playback has started and we're paused, then try to load as much as
504 // possible, assuming that the file is cacheable. (If not, why bother?)
505 if (media_has_played_ && paused_ &&
506 destination_url_data_ &&
507 destination_url_data_->range_supported() &&
508 destination_url_data_->cacheable()) {
509 loader_->SetPreload(1LL << 40, 1LL << 40); // 1 Tb
510 return;
511 }
512 }
513
514 // Use a default bit rate if unknown and clamp to prevent overflow.
515 int64 bitrate = clamp<int64>(bitrate_, 0, kMaxBitrate);
516 if (bitrate == 0)
517 bitrate = kDefaultBitrate;
518
519 // Only scale the buffer window for playback rates greater than 1.0 in
520 // magnitude and clamp to prevent overflow.
521 bool backward_playback = false;
522 double playback_rate = playback_rate_;
523 if (playback_rate < 0.0) {
524 backward_playback = true;
525 playback_rate *= -1.0;
526 }
527
528 playback_rate = std::max(playback_rate, 1.0);
529 playback_rate = std::min(playback_rate, kMaxPlaybackRate);
530
531 int64 bytes_per_second = (bitrate / 8.0) * playback_rate;
532
533 int64 preload = clamp(kTargetSecondsBufferedAhead * bytes_per_second,
534 kMinBufferPreload,
535 kMaxBufferPreload);
536 int64 back_buffer = clamp(kTargetSecondsBufferedBehind * bytes_per_second,
537 kMinBufferPreload,
538 kMaxBufferPreload);
539 if (backward_playback)
540 std::swap(preload, back_buffer);
541
542 int64 pin_forwards = kMaxBufferSize - back_buffer;
543 DCHECK_LE(preload_ + kPreloadHighExtra, pin_forwards);
544 loader_->SetMaxBuffer(back_buffer, pin_forwards);
545
546 if (preload_ == METADATA) {
547 loader_->SetPreload(0, 0);
548 } else {
549 loader_->SetPreload(preload + kPreloadHighExtra, preload);
550 }
551 }
552
553 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698