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

Side by Side Diff: media/base/pipeline_impl.cc

Issue 8399023: Fire canplaythrough event at the proper time for audio/video (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Move canplaythrough logic into pipeline; fix other stuff Created 9 years, 1 month 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
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 // 4 //
5 // TODO(scherkus): clean up PipelineImpl... too many crazy function names, 5 // TODO(scherkus): clean up PipelineImpl... too many crazy function names,
6 // potential deadlocks, etc... 6 // potential deadlocks, etc...
7 7
8 #include "media/base/pipeline_impl.h" 8 #include "media/base/pipeline_impl.h"
9 9
10 #include <algorithm> 10 #include <algorithm>
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after
177 base::AutoLock auto_lock(lock_); 177 base::AutoLock auto_lock(lock_);
178 return has_video_; 178 return has_video_;
179 } 179 }
180 180
181 float PipelineImpl::GetPlaybackRate() const { 181 float PipelineImpl::GetPlaybackRate() const {
182 base::AutoLock auto_lock(lock_); 182 base::AutoLock auto_lock(lock_);
183 return playback_rate_; 183 return playback_rate_;
184 } 184 }
185 185
186 void PipelineImpl::SetPlaybackRate(float playback_rate) { 186 void PipelineImpl::SetPlaybackRate(float playback_rate) {
187 if (playback_rate < 0.0f) { 187 if (playback_rate < 0.0f)
188 return; 188 return;
189 }
190 189
191 base::AutoLock auto_lock(lock_); 190 base::AutoLock auto_lock(lock_);
192 playback_rate_ = playback_rate; 191 playback_rate_ = playback_rate;
193 if (running_ && !tearing_down_) { 192 if (running_ && !tearing_down_) {
194 message_loop_->PostTask(FROM_HERE, base::Bind( 193 message_loop_->PostTask(FROM_HERE, base::Bind(
195 &PipelineImpl::PlaybackRateChangedTask, this, playback_rate)); 194 &PipelineImpl::PlaybackRateChangedTask, this, playback_rate));
196 } 195 }
197 } 196 }
198 197
199 float PipelineImpl::GetVolume() const { 198 float PipelineImpl::GetVolume() const {
200 base::AutoLock auto_lock(lock_); 199 base::AutoLock auto_lock(lock_);
201 return volume_; 200 return volume_;
202 } 201 }
203 202
204 void PipelineImpl::SetVolume(float volume) { 203 void PipelineImpl::SetVolume(float volume) {
205 if (volume < 0.0f || volume > 1.0f) { 204 if (volume < 0.0f || volume > 1.0f)
206 return; 205 return;
207 }
208 206
209 base::AutoLock auto_lock(lock_); 207 base::AutoLock auto_lock(lock_);
210 volume_ = volume; 208 volume_ = volume;
211 if (running_ && !tearing_down_) { 209 if (running_ && !tearing_down_) {
212 message_loop_->PostTask(FROM_HERE, base::Bind( 210 message_loop_->PostTask(FROM_HERE, base::Bind(
213 &PipelineImpl::VolumeChangedTask, this, volume)); 211 &PipelineImpl::VolumeChangedTask, this, volume));
214 } 212 }
215 } 213 }
216 214
217 Preload PipelineImpl::GetPreload() const { 215 Preload PipelineImpl::GetPreload() const {
(...skipping 12 matching lines...) Expand all
230 228
231 base::TimeDelta PipelineImpl::GetCurrentTime() const { 229 base::TimeDelta PipelineImpl::GetCurrentTime() const {
232 // TODO(scherkus): perhaps replace checking state_ == kEnded with a bool that 230 // TODO(scherkus): perhaps replace checking state_ == kEnded with a bool that
233 // is set/get under the lock, because this is breaching the contract that 231 // is set/get under the lock, because this is breaching the contract that
234 // |state_| is only accessed on |message_loop_|. 232 // |state_| is only accessed on |message_loop_|.
235 base::AutoLock auto_lock(lock_); 233 base::AutoLock auto_lock(lock_);
236 return GetCurrentTime_Locked(); 234 return GetCurrentTime_Locked();
237 } 235 }
238 236
239 base::TimeDelta PipelineImpl::GetCurrentTime_Locked() const { 237 base::TimeDelta PipelineImpl::GetCurrentTime_Locked() const {
238 lock_.AssertAcquired();
240 base::TimeDelta elapsed = clock_->Elapsed(); 239 base::TimeDelta elapsed = clock_->Elapsed();
241 if (state_ == kEnded || elapsed > duration_) { 240 if (state_ == kEnded || elapsed > duration_) {
242 return duration_; 241 return duration_;
243 } 242 }
244 return elapsed; 243 return elapsed;
245 } 244 }
246 245
247 base::TimeDelta PipelineImpl::GetBufferedTime() { 246 base::TimeDelta PipelineImpl::GetBufferedTime() {
248 base::AutoLock auto_lock(lock_); 247 base::AutoLock auto_lock(lock_);
248 return GetBufferedTime_Locked();
249 }
249 250
251 base::TimeDelta PipelineImpl::GetBufferedTime_Locked() {
252 lock_.AssertAcquired();
250 // If media is fully loaded, then return duration. 253 // If media is fully loaded, then return duration.
251 if (loaded_ || total_bytes_ == buffered_bytes_) { 254 if (loaded_ || total_bytes_ == buffered_bytes_) {
252 max_buffered_time_ = duration_; 255 max_buffered_time_ = duration_;
253 return duration_; 256 return duration_;
254 } 257 }
255 258
256 base::TimeDelta current_time = GetCurrentTime_Locked(); 259 base::TimeDelta current_time = GetCurrentTime_Locked();
257 260
258 // If buffered time was set, we report that value directly. 261 // If buffered time was set, we report that value directly.
259 if (buffered_time_.ToInternalValue() > 0) { 262 if (buffered_time_.ToInternalValue() > 0) {
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
362 volume_ = 1.0f; 365 volume_ = 1.0f;
363 preload_ = AUTO; 366 preload_ = AUTO;
364 playback_rate_ = 0.0f; 367 playback_rate_ = 0.0f;
365 pending_playback_rate_ = 0.0f; 368 pending_playback_rate_ = 0.0f;
366 status_ = PIPELINE_OK; 369 status_ = PIPELINE_OK;
367 has_audio_ = false; 370 has_audio_ = false;
368 has_video_ = false; 371 has_video_ = false;
369 waiting_for_clock_update_ = false; 372 waiting_for_clock_update_ = false;
370 audio_disabled_ = false; 373 audio_disabled_ = false;
371 clock_->SetTime(kZero); 374 clock_->SetTime(kZero);
375 starting_bytes_loaded_ = 0;
376 starting_time_ = base::Time();
377 has_notified_can_play_through_ = false;
378 is_downloading_data_ = false;
379 last_approximate_download_rate_ = -1;
372 } 380 }
373 381
374 void PipelineImpl::SetState(State next_state) { 382 void PipelineImpl::SetState(State next_state) {
375 state_ = next_state; 383 state_ = next_state;
376 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); 384 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state));
377 } 385 }
378 386
379 bool PipelineImpl::IsPipelineOk() { 387 bool PipelineImpl::IsPipelineOk() {
380 base::AutoLock auto_lock(lock_); 388 base::AutoLock auto_lock(lock_);
381 return status_ == PIPELINE_OK; 389 return status_ == PIPELINE_OK;
(...skipping 25 matching lines...) Expand all
407 } 415 }
408 416
409 void PipelineImpl::FinishInitialization() { 417 void PipelineImpl::FinishInitialization() {
410 DCHECK_EQ(MessageLoop::current(), message_loop_); 418 DCHECK_EQ(MessageLoop::current(), message_loop_);
411 // Execute the seek callback, if present. Note that this might be the 419 // Execute the seek callback, if present. Note that this might be the
412 // initial callback passed into Start(). 420 // initial callback passed into Start().
413 if (!seek_callback_.is_null()) { 421 if (!seek_callback_.is_null()) {
414 seek_callback_.Run(status_); 422 seek_callback_.Run(status_);
415 seek_callback_.Reset(); 423 seek_callback_.Reset();
416 } 424 }
425 is_downloading_data_ = true;
426 starting_time_ = base::Time::Now();
427 NotifyCanPlayThroughIfNeeded();
417 } 428 }
418 429
419 // static 430 // static
420 bool PipelineImpl::TransientState(State state) { 431 bool PipelineImpl::TransientState(State state) {
421 return state == kPausing || 432 return state == kPausing ||
422 state == kFlushing || 433 state == kFlushing ||
423 state == kSeeking || 434 state == kSeeking ||
424 state == kStarting || 435 state == kStarting ||
425 state == kStopping; 436 state == kStopping;
426 } 437 }
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
507 media_log_->AddEvent( 518 media_log_->AddEvent(
508 media_log_->CreateIntegerEvent( 519 media_log_->CreateIntegerEvent(
509 MediaLogEvent::TOTAL_BYTES_SET, "total_bytes", total_bytes)); 520 MediaLogEvent::TOTAL_BYTES_SET, "total_bytes", total_bytes));
510 521
511 base::AutoLock auto_lock(lock_); 522 base::AutoLock auto_lock(lock_);
512 total_bytes_ = total_bytes; 523 total_bytes_ = total_bytes;
513 } 524 }
514 525
515 void PipelineImpl::SetBufferedBytes(int64 buffered_bytes) { 526 void PipelineImpl::SetBufferedBytes(int64 buffered_bytes) {
516 DCHECK(IsRunning()); 527 DCHECK(IsRunning());
517 base::AutoLock auto_lock(lock_); 528 {
518 529 base::AutoLock auto_lock(lock_);
519 // See comments in SetCurrentReadPosition() about capping. 530 // See comments in SetCurrentReadPosition() about capping.
520 if (buffered_bytes < current_bytes_) 531 if (buffered_bytes < current_bytes_)
521 current_bytes_ = buffered_bytes; 532 current_bytes_ = buffered_bytes;
522 buffered_bytes_ = buffered_bytes; 533 buffered_bytes_ = buffered_bytes;
534 }
535 NotifyCanPlayThroughIfNeeded();
523 } 536 }
524 537
525 void PipelineImpl::SetNaturalVideoSize(const gfx::Size& size) { 538 void PipelineImpl::SetNaturalVideoSize(const gfx::Size& size) {
526 DCHECK(IsRunning()); 539 DCHECK(IsRunning());
527 media_log_->AddEvent(media_log_->CreateVideoSizeSetEvent( 540 media_log_->AddEvent(media_log_->CreateVideoSizeSetEvent(
528 size.width(), size.height())); 541 size.width(), size.height()));
529 542
530 base::AutoLock auto_lock(lock_); 543 base::AutoLock auto_lock(lock_);
531 natural_size_ = size; 544 natural_size_ = size;
532 } 545 }
533 546
534 void PipelineImpl::SetStreaming(bool streaming) { 547 void PipelineImpl::SetStreaming(bool streaming) {
535 DCHECK(IsRunning()); 548 DCHECK(IsRunning());
536 media_log_->AddEvent( 549 media_log_->AddEvent(
537 media_log_->CreateBooleanEvent( 550 media_log_->CreateBooleanEvent(
538 MediaLogEvent::STREAMING_SET, "streaming", streaming)); 551 MediaLogEvent::STREAMING_SET, "streaming", streaming));
539 552
540 base::AutoLock auto_lock(lock_); 553 base::AutoLock auto_lock(lock_);
541 streaming_ = streaming; 554 streaming_ = streaming;
542 } 555 }
543 556
544 void PipelineImpl::NotifyEnded() { 557 void PipelineImpl::NotifyEnded() {
545 DCHECK(IsRunning()); 558 DCHECK(IsRunning());
546 message_loop_->PostTask(FROM_HERE, 559 message_loop_->PostTask(FROM_HERE,
547 base::Bind(&PipelineImpl::NotifyEndedTask, this)); 560 base::Bind(&PipelineImpl::NotifyEndedTask, this));
548 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED)); 561 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED));
562 NotifyCanPlayThroughIfNeeded();
549 } 563 }
550 564
551 void PipelineImpl::SetLoaded(bool loaded) { 565 void PipelineImpl::SetLoaded(bool loaded) {
552 DCHECK(IsRunning()); 566 DCHECK(IsRunning());
553 media_log_->AddEvent( 567 media_log_->AddEvent(
554 media_log_->CreateBooleanEvent( 568 media_log_->CreateBooleanEvent(
555 MediaLogEvent::LOADED_SET, "loaded", loaded)); 569 MediaLogEvent::LOADED_SET, "loaded", loaded));
556 570
557 base::AutoLock auto_lock(lock_); 571 base::AutoLock auto_lock(lock_);
558 loaded_ = loaded; 572 loaded_ = loaded;
559 } 573 }
560 574
561 void PipelineImpl::SetNetworkActivity(bool is_downloading_data) { 575 void PipelineImpl::SetNetworkActivity(bool is_downloading_data) {
562 DCHECK(IsRunning()); 576 DCHECK(IsRunning());
577
578 base::AutoLock auto_lock(lock_);
acolwell GONE FROM CHROMIUM 2011/10/28 18:24:13 Why are we doing these operations here instead of
vrk (LEFT CHROMIUM) 2011/11/01 21:57:34 Hmm, no real reason; I guess I was doing this here
579 if (is_downloading_data_ == is_downloading_data)
580 return;
581
582 is_downloading_data_ = is_downloading_data;
583 NetworkEvent type = DOWNLOAD_PAUSED;
584 if (is_downloading_data) {
585 type = DOWNLOAD_CONTINUED;
586 // Reset CanPlayThrough-related state when downloading continues.
587 starting_bytes_loaded_ = buffered_bytes_;
588 starting_time_ = base::Time::Now();
589 }
563 message_loop_->PostTask(FROM_HERE, 590 message_loop_->PostTask(FROM_HERE,
564 base::Bind( 591 base::Bind(
565 &PipelineImpl::NotifyNetworkEventTask, this, is_downloading_data)); 592 &PipelineImpl::NotifyNetworkEventTask, this, type));
566 media_log_->AddEvent( 593 media_log_->AddEvent(
567 media_log_->CreateBooleanEvent( 594 media_log_->CreateBooleanEvent(
568 MediaLogEvent::NETWORK_ACTIVITY_SET, 595 MediaLogEvent::NETWORK_ACTIVITY_SET,
569 "is_downloading_data", is_downloading_data)); 596 "is_downloading_data", is_downloading_data));
570 } 597 }
571 598
572 void PipelineImpl::DisableAudioRenderer() { 599 void PipelineImpl::DisableAudioRenderer() {
573 DCHECK(IsRunning()); 600 DCHECK(IsRunning());
574 601
575 // Disable renderer on the message loop. 602 // Disable renderer on the message loop.
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after
661 // If we have received the stop or error signal, return immediately. 688 // If we have received the stop or error signal, return immediately.
662 if (IsPipelineStopPending() || IsPipelineStopped() || !IsPipelineOk()) 689 if (IsPipelineStopPending() || IsPipelineStopped() || !IsPipelineOk())
663 return; 690 return;
664 691
665 DCHECK(state_ == kInitDemuxer || 692 DCHECK(state_ == kInitDemuxer ||
666 state_ == kInitAudioDecoder || 693 state_ == kInitAudioDecoder ||
667 state_ == kInitAudioRenderer || 694 state_ == kInitAudioRenderer ||
668 state_ == kInitVideoDecoder || 695 state_ == kInitVideoDecoder ||
669 state_ == kInitVideoRenderer); 696 state_ == kInitVideoRenderer);
670 697
671
672 // Demuxer created, create audio decoder. 698 // Demuxer created, create audio decoder.
673 if (state_ == kInitDemuxer) { 699 if (state_ == kInitDemuxer) {
674 SetState(kInitAudioDecoder); 700 SetState(kInitAudioDecoder);
675 // If this method returns false, then there's no audio stream. 701 // If this method returns false, then there's no audio stream.
676 if (InitializeAudioDecoder(demuxer_)) 702 if (InitializeAudioDecoder(demuxer_))
677 return; 703 return;
678 } 704 }
679 705
680 // Assuming audio decoder was created, create audio renderer. 706 // Assuming audio decoder was created, create audio renderer.
681 if (state_ == kInitAudioDecoder) { 707 if (state_ == kInitAudioDecoder) {
(...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after
919 return; 945 return;
920 } 946 }
921 947
922 // Transition to ended, executing the callback if present. 948 // Transition to ended, executing the callback if present.
923 SetState(kEnded); 949 SetState(kEnded);
924 if (!ended_callback_.is_null()) { 950 if (!ended_callback_.is_null()) {
925 ended_callback_.Run(status_); 951 ended_callback_.Run(status_);
926 } 952 }
927 } 953 }
928 954
929 void PipelineImpl::NotifyNetworkEventTask(bool is_downloading_data) { 955 void PipelineImpl::NotifyNetworkEventTask(NetworkEvent type) {
930 DCHECK_EQ(MessageLoop::current(), message_loop_); 956 DCHECK_EQ(MessageLoop::current(), message_loop_);
931 if (!network_callback_.is_null()) 957 if (!network_callback_.is_null())
932 network_callback_.Run(is_downloading_data); 958 network_callback_.Run(type);
933 } 959 }
934 960
935 void PipelineImpl::DisableAudioRendererTask() { 961 void PipelineImpl::DisableAudioRendererTask() {
936 DCHECK_EQ(MessageLoop::current(), message_loop_); 962 DCHECK_EQ(MessageLoop::current(), message_loop_);
937 963
938 base::AutoLock auto_lock(lock_); 964 base::AutoLock auto_lock(lock_);
939 has_audio_ = false; 965 has_audio_ = false;
940 audio_disabled_ = true; 966 audio_disabled_ = true;
941 967
942 // Notify all filters of disabled audio renderer. If the filter isn't 968 // Notify all filters of disabled audio renderer. If the filter isn't
(...skipping 425 matching lines...) Expand 10 before | Expand all | Expand 10 after
1368 base::Bind(&PipelineImpl::OnFilterStateTransitionWithStatus, this); 1394 base::Bind(&PipelineImpl::OnFilterStateTransitionWithStatus, this);
1369 1395
1370 if (status == PIPELINE_OK && pipeline_filter_) { 1396 if (status == PIPELINE_OK && pipeline_filter_) {
1371 pipeline_filter_->Seek(seek_timestamp, done_cb); 1397 pipeline_filter_->Seek(seek_timestamp, done_cb);
1372 return; 1398 return;
1373 } 1399 }
1374 1400
1375 done_cb.Run(status); 1401 done_cb.Run(status);
1376 } 1402 }
1377 1403
1404 void PipelineImpl::NotifyCanPlayThroughIfNeeded() {
1405 if (!IsRunning() || !IsInitialized())
1406 return;
1407
1408 base::AutoLock auto_lock(lock_);
1409 if (!ShouldNotifyCanPlayThrough_Locked())
1410 return;
1411
1412 has_notified_can_play_through_ = true;
1413 message_loop_->PostTask(FROM_HERE,
acolwell GONE FROM CHROMIUM 2011/10/28 18:24:13 Do we need this PostTask? Aren't we always on the
vrk (LEFT CHROMIUM) 2011/11/01 21:57:34 I believe we are not on this thread for 2 out of 3
1414 base::Bind(
1415 &PipelineImpl::NotifyNetworkEventTask, this, CAN_PLAY_THROUGH));
1416 }
1417
1418 int PipelineImpl::ApproximateDownloadRate_Locked() {
1419 lock_.AssertAcquired();
1420
1421 // Playback hasn't started yet.
1422 if (starting_time_.is_null())
1423 return -1;
1424
1425 float seconds_elapsed = (base::Time::Now() - starting_time_).InSecondsF();
1426 DCHECK(seconds_elapsed >= 0);
1427 // Update approximation if pipeline is downloading data and has been
1428 // downloading data long enough to get an accurate estimate.
1429 // XXX: How many seconds should we wait to get an accurate reading?
1430 if (is_downloading_data_ && seconds_elapsed > 1) {
acolwell GONE FROM CHROMIUM 2011/10/28 18:24:13 Make the time elapsed threshold a constant and let
vrk (LEFT CHROMIUM) 2011/11/01 21:57:34 Done.
1431 int bytes_downloaded = buffered_bytes_ - starting_bytes_loaded_;
1432 DCHECK(bytes_downloaded > 0);
acolwell GONE FROM CHROMIUM 2011/10/28 18:24:13 Should this really be a DCHECK? Shouldn't we just
vrk (LEFT CHROMIUM) 2011/11/01 21:57:34 Oops, the ">" was meant to be a ">=". Am fine with
1433 last_approximate_download_rate_ = bytes_downloaded / seconds_elapsed;
1434 }
1435 return last_approximate_download_rate_;
1436 }
1437
1438 bool PipelineImpl::ShouldNotifyCanPlayThrough_Locked() {
1439 lock_.AssertAcquired();
1440 if (has_notified_can_play_through_ || total_bytes_ == 0)
acolwell GONE FROM CHROMIUM 2011/10/28 18:24:13 Might want to add a comment explaining what total_
vrk (LEFT CHROMIUM) 2011/11/01 21:57:34 Done.
1441 return false;
1442 if (loaded_ || buffered_bytes_ == total_bytes_)
1443 return true;
1444
1445 int download_rate = ApproximateDownloadRate_Locked();
1446 // If download rate is unknown, cannot approximate when the media can play
1447 // through.
1448 if (download_rate == -1)
1449 return false;
1450
1451 // If we are downloading at or faster than the media's bitrate, then we can
1452 // play through to the end of the media without stopping to buffer.
1453 int bitrate = demuxer_->GetBitrate();
1454 if (download_rate > bitrate * 8)
acolwell GONE FROM CHROMIUM 2011/10/28 18:24:13 Shouldn't this be (8 * download_rate > bitrate) or
vrk (LEFT CHROMIUM) 2011/11/01 21:57:34 D'OH! Yes, absolutely. Done.
1455 return true;
1456
1457 // If the rate of downloading the media is slower than the rate at which the
1458 // media is being played back, see if there's enough data buffered to let the
1459 // media play through until the end without buffering.
1460 base::TimeDelta current_time = GetCurrentTime_Locked();
1461 base::TimeDelta buffered_time = GetBufferedTime_Locked();
1462 DCHECK(buffered_time >= current_time);
1463 float seconds_buffered = (buffered_time - current_time).InSecondsF();
acolwell GONE FROM CHROMIUM 2011/10/28 18:24:13 std::max(..., 0) to protect the computation in rel
vrk (LEFT CHROMIUM) 2011/11/01 21:57:34 Done.
1464 int bytes_downloaded_after_buffer_is_consumed =
acolwell GONE FROM CHROMIUM 2011/10/28 18:24:13 s/after/while/ ?
vrk (LEFT CHROMIUM) 2011/11/01 21:57:34 Done.
1465 static_cast<int>(seconds_buffered * download_rate);
1466 int bytes_left_to_download = total_bytes_ - buffered_bytes_;
1467
1468 return bytes_downloaded_after_buffer_is_consumed >= bytes_left_to_download;
1469 }
1470
1378 } // namespace media 1471 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698