Index: media/blink/webmediaplayer_impl.cc |
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc |
index e56b4bb17a0bc88f2c75e690abca676b8fcddeb9..cbc4dbb5b78523a038ae9289164aed71095a2738 100644 |
--- a/media/blink/webmediaplayer_impl.cc |
+++ b/media/blink/webmediaplayer_impl.cc |
@@ -11,6 +11,7 @@ |
#include <utility> |
#include "base/bind.h" |
+#include "base/bind_helpers.h" |
#include "base/callback.h" |
#include "base/callback_helpers.h" |
#include "base/command_line.h" |
@@ -143,20 +144,21 @@ WebMediaPlayerImpl::WebMediaPlayerImpl( |
worker_task_runner_(params.worker_task_runner()), |
media_log_(params.media_log()), |
pipeline_(media_task_runner_, media_log_.get()), |
+ pipeline_state_(&pipeline_, |
+ base::Bind(&WebMediaPlayerImpl::CreateRenderer, |
+ base::Unretained(this)), |
+ base::Bind(&WebMediaPlayerImpl::OnPipelineSeeked, |
+ base::Unretained(this)), |
+ base::Bind(&WebMediaPlayerImpl::OnPipelineSuspended, |
+ base::Unretained(this)), |
+ base::Bind(&WebMediaPlayerImpl::OnPipelineError, |
+ base::Unretained(this))), |
load_type_(LoadTypeURL), |
opaque_(false), |
playback_rate_(0.0), |
paused_(true), |
seeking_(false), |
- pending_suspend_(false), |
- pending_time_change_(false), |
- pending_resume_(false), |
- suspending_(false), |
- suspended_(false), |
- resuming_(false), |
ended_(false), |
- pending_seek_(false), |
- should_notify_time_changed_(false), |
client_(client), |
encrypted_client_(encrypted_client), |
delegate_(delegate), |
@@ -227,7 +229,7 @@ WebMediaPlayerImpl::~WebMediaPlayerImpl() { |
data_source_->Abort(); |
if (chunk_demuxer_) { |
chunk_demuxer_->Shutdown(); |
- chunk_demuxer_ = NULL; |
+ chunk_demuxer_ = nullptr; |
} |
renderer_factory_.reset(); |
@@ -325,8 +327,8 @@ void WebMediaPlayerImpl::play() { |
#endif |
paused_ = false; |
- |
pipeline_.SetPlaybackRate(playback_rate_); |
+ |
if (data_source_) |
data_source_->MediaIsPlaying(); |
@@ -351,7 +353,13 @@ void WebMediaPlayerImpl::pause() { |
#endif |
pipeline_.SetPlaybackRate(0.0); |
- UpdatePausedTime(); |
+ |
+ // pause() may be called after playback has ended and the HTMLMediaElement |
+ // requires that currentTime() == duration() after ending. We want to ensure |
+ // |paused_time_| matches currentTime() in this case or a future seek() may |
+ // incorrectly discard what it thinks is a seek to the existing time. |
+ paused_time_ = |
+ ended_ ? pipeline_.GetMediaDuration() : pipeline_.GetMediaTime(); |
media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::PAUSE)); |
@@ -367,14 +375,17 @@ bool WebMediaPlayerImpl::supportsSave() const { |
void WebMediaPlayerImpl::seek(double seconds) { |
DVLOG(1) << __FUNCTION__ << "(" << seconds << "s)"; |
DCHECK(main_task_runner_->BelongsToCurrentThread()); |
+ DoSeek(base::TimeDelta::FromSecondsD(seconds), true); |
+} |
+ |
+void WebMediaPlayerImpl::DoSeek(base::TimeDelta time, bool time_updated) { |
+ DCHECK(main_task_runner_->BelongsToCurrentThread()); |
ended_ = false; |
- base::TimeDelta new_seek_time = base::TimeDelta::FromSecondsD(seconds); |
- |
#if defined(OS_ANDROID) // WMPI_CAST |
if (isRemote()) { |
- cast_impl_.seek(new_seek_time); |
+ cast_impl_.seek(time); |
return; |
} |
#endif |
@@ -383,81 +394,33 @@ void WebMediaPlayerImpl::seek(double seconds) { |
if (ready_state_ > WebMediaPlayer::ReadyStateHaveMetadata) |
SetReadyState(WebMediaPlayer::ReadyStateHaveMetadata); |
- if (seeking_ || suspended_) { |
- // Once resuming, it's too late to change the resume time and so the |
- // implementation is a little different. |
- bool is_suspended = suspended_ && !resuming_; |
- |
- // If we are currently seeking or resuming to |new_seek_time|, skip the |
- // seek (except for MSE, which always seeks). |
- if (!is_suspended && new_seek_time == seek_time_) { |
- if (chunk_demuxer_) { |
- // Don't suppress any redundant in-progress MSE seek. There could have |
- // been changes to the underlying buffers after seeking the demuxer and |
- // before receiving OnPipelineSeeked() for the currently in-progress |
- // seek. |
- MEDIA_LOG(DEBUG, media_log_) |
- << "Detected MediaSource seek to same time as in-progress seek to " |
- << seek_time_ << "."; |
- } else { |
- // Suppress all redundant seeks if unrestricted by media source demuxer |
- // API. |
- pending_seek_ = false; |
- pending_seek_time_ = base::TimeDelta(); |
- return; |
- } |
- } |
- |
- // If |chunk_demuxer_| is already seeking, cancel that seek and schedule the |
- // new one. |
- if (!is_suspended && chunk_demuxer_) |
- chunk_demuxer_->CancelPendingSeek(new_seek_time); |
- |
- // Schedule a seek once the current suspend or seek finishes. |
- pending_seek_ = true; |
- pending_seek_time_ = new_seek_time; |
- |
- // In the case of seeking while suspended, the seek is considered to have |
- // started immediately (but won't complete until the pipeline is resumed). |
- if (is_suspended) { |
- seeking_ = true; |
- seek_time_ = new_seek_time; |
- } |
- |
- return; |
- } |
- |
- media_log_->AddEvent(media_log_->CreateSeekEvent(seconds)); |
- |
- // Update our paused time. |
- // For non-MSE playbacks, in paused state ignore the seek operations to |
- // current time if the loading is completed and generate |
- // OnPipelineBufferingStateChanged event to eventually fire seeking and seeked |
- // events. We don't short-circuit MSE seeks in this logic because the |
- // underlying buffers around the seek time might have changed (or even been |
- // removed) since previous seek/preroll/pause action, and the pipeline might |
- // need to flush so the new buffers are decoded and rendered instead of the |
- // old ones. |
- if (paused_) { |
- if (paused_time_ != new_seek_time || chunk_demuxer_) { |
- paused_time_ = new_seek_time; |
- } else if (old_state == ReadyStateHaveEnoughData) { |
+ // When paused, we know exactly what the current time is and can elide seeks |
+ // to it. However, there are two cases that are not elided: |
+ // 1) When the pipeline state is not stable. |
+ // In this case we just let |pipeline_state_| decide what to do, as it |
+ // has complete information. |
+ // 2) For MSE. |
+ // Because the buffers may have changed between seeks, MSE seeks are |
+ // never elided. |
+ if (paused_ && pipeline_state_.IsPlaying() && paused_time_ == time && |
+ !chunk_demuxer_) { |
+ // If the ready state was high enough before, we can indicate that the seek |
+ // completed just by restoring it. Otherwise we will just wait for the real |
+ // ready state change to eventually happen. |
+ if (old_state == ReadyStateHaveEnoughData) { |
main_task_runner_->PostTask( |
FROM_HERE, |
base::Bind(&WebMediaPlayerImpl::OnPipelineBufferingStateChanged, |
AsWeakPtr(), BUFFERING_HAVE_ENOUGH)); |
- return; |
} |
+ return; |
} |
seeking_ = true; |
- seek_time_ = new_seek_time; |
- |
- if (chunk_demuxer_) |
- chunk_demuxer_->StartWaitingForSeek(seek_time_); |
- |
- pipeline_.Seek(seek_time_, BIND_TO_RENDER_LOOP1( |
- &WebMediaPlayerImpl::OnPipelineSeeked, true)); |
+ seek_time_ = time; |
+ if (paused_) |
+ paused_time_ = time; |
+ pipeline_state_.Seek(time, time_updated); |
} |
void WebMediaPlayerImpl::setRate(double rate) { |
@@ -615,23 +578,16 @@ double WebMediaPlayerImpl::currentTime() const { |
if (ended_) |
return duration(); |
- // We know the current seek time better than pipeline: pipeline may processing |
- // an earlier seek before a pending seek has been started, or it might not yet |
- // have the current seek time returnable via GetMediaTime(). |
- if (seeking()) { |
- return pending_seek_ ? pending_seek_time_.InSecondsF() |
- : seek_time_.InSecondsF(); |
- } |
+ if (seeking()) |
+ return seek_time_.InSecondsF(); |
#if defined(OS_ANDROID) // WMPI_CAST |
- if (isRemote()) { |
+ if (isRemote()) |
return cast_impl_.currentTime(); |
- } |
#endif |
- if (paused_) { |
+ if (paused_) |
return paused_time_.InSecondsF(); |
- } |
return pipeline_.GetMediaTime().InSecondsF(); |
} |
@@ -905,61 +861,16 @@ void WebMediaPlayerImpl::OnCdmAttached(bool success) { |
set_cdm_result_.reset(); |
} |
-void WebMediaPlayerImpl::OnPipelineSeeked(bool time_changed, |
- PipelineStatus status) { |
- DVLOG(1) << __FUNCTION__ << "(" << time_changed << ", " << status << ")"; |
- DCHECK(main_task_runner_->BelongsToCurrentThread()); |
- |
- if (status != PIPELINE_OK) { |
- OnPipelineError(status); |
- return; |
- } |
- |
- // Whether or not the seek was caused by a resume, we're not suspended now. |
- resuming_ = false; |
- suspended_ = false; |
- |
- // If there is a pending suspend, the seek does not complete until after the |
- // next resume. |
- if (pending_suspend_) { |
- pending_suspend_ = false; |
- pending_time_change_ = time_changed; |
- Suspend(); |
- return; |
- } |
- |
- // Clear seek state. Note that if the seek was caused by a resume, then |
- // |seek_time_| is always set but |seeking_| is only set if there was a |
- // pending seek at the time. |
+void WebMediaPlayerImpl::OnPipelineSeeked(bool time_updated) { |
seeking_ = false; |
seek_time_ = base::TimeDelta(); |
- |
- if (pending_seek_) { |
- double pending_seek_seconds = pending_seek_time_.InSecondsF(); |
- pending_seek_ = false; |
- pending_seek_time_ = base::TimeDelta(); |
- seek(pending_seek_seconds); |
- return; |
- } |
- |
- // Update our paused time. |
if (paused_) |
- UpdatePausedTime(); |
- |
- should_notify_time_changed_ = time_changed; |
+ paused_time_ = pipeline_.GetMediaTime(); |
+ if (time_updated) |
+ should_notify_time_changed_ = true; |
} |
-void WebMediaPlayerImpl::OnPipelineSuspended(PipelineStatus status) { |
- DVLOG(1) << __FUNCTION__ << "(" << status << ")"; |
- DCHECK(main_task_runner_->BelongsToCurrentThread()); |
- |
- if (status != PIPELINE_OK) { |
- OnPipelineError(status); |
- return; |
- } |
- |
- suspending_ = false; |
- |
+void WebMediaPlayerImpl::OnPipelineSuspended() { |
#if defined(OS_ANDROID) |
if (isRemote()) { |
scoped_refptr<VideoFrame> frame = cast_impl_.GetCastingBanner(); |
@@ -968,20 +879,14 @@ void WebMediaPlayerImpl::OnPipelineSuspended(PipelineStatus status) { |
} |
} |
#endif |
- |
- if (pending_resume_) { |
- pending_resume_ = false; |
- Resume(); |
- return; |
- } |
} |
void WebMediaPlayerImpl::OnPipelineEnded() { |
DVLOG(1) << __FUNCTION__; |
DCHECK(main_task_runner_->BelongsToCurrentThread()); |
- // Ignore state changes until we've completed all outstanding seeks. |
- if (seeking_ || pending_seek_) |
+ // Ignore state changes until we've completed all outstanding operations. |
+ if (!pipeline_state_.IsPlaying()) |
return; |
ended_ = true; |
@@ -1041,8 +946,9 @@ void WebMediaPlayerImpl::OnPipelineBufferingStateChanged( |
BufferingState buffering_state) { |
DVLOG(1) << __FUNCTION__ << "(" << buffering_state << ")"; |
- // Ignore buffering state changes until we've completed all outstanding seeks. |
- if (seeking_ || pending_seek_) |
+ // Ignore buffering state changes until we've completed all outstanding |
+ // operations. |
+ if (!pipeline_state_.IsPlaying()) |
return; |
// TODO(scherkus): Handle other buffering states when Pipeline starts using |
@@ -1115,33 +1021,8 @@ void WebMediaPlayerImpl::OnHidden() { |
return; |
#endif |
- ScheduleSuspend(); |
-} |
- |
-void WebMediaPlayerImpl::ScheduleSuspend() { |
- if (!pipeline_.IsRunning() || !hasVideo()) |
- return; |
- |
- if (resuming_ || seeking_) { |
- pending_suspend_ = true; |
- return; |
- } |
- |
- if (pending_resume_) { |
- pending_resume_ = false; |
- return; |
- } |
- |
- Suspend(); |
-} |
- |
-void WebMediaPlayerImpl::Suspend() { |
- DCHECK(main_task_runner_->BelongsToCurrentThread()); |
- CHECK(!suspended_); |
- suspended_ = true; |
- suspending_ = true; |
- pipeline_.Suspend( |
- BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineSuspended)); |
+ if (pipeline_.IsRunning() && hasVideo()) |
+ pipeline_state_.Suspend(); |
} |
void WebMediaPlayerImpl::OnShown() { |
@@ -1166,59 +1047,8 @@ void WebMediaPlayerImpl::OnShown() { |
return; |
#endif |
- ScheduleResume(); |
-} |
- |
-void WebMediaPlayerImpl::ScheduleResume() { |
- if (!pipeline_.IsRunning()) |
- return; |
- |
- if (suspending_) { |
- pending_resume_ = true; |
- return; |
- } |
- |
- if (pending_suspend_) { |
- pending_suspend_ = false; |
- return; |
- } |
- |
- // Might already be resuming iff we came back from remote playback recently. |
- if (suspended_ && !resuming_) |
- Resume(); |
-} |
- |
-void WebMediaPlayerImpl::Resume() { |
- DCHECK(main_task_runner_->BelongsToCurrentThread()); |
- CHECK(suspended_); |
- CHECK(!resuming_); |
- |
- // If there was a time change pending when we suspended (which can happen when |
- // we suspend immediately after a seek), surface it after resuming. |
- bool time_changed = pending_time_change_; |
- pending_time_change_ = false; |
- |
- if (seeking_ || pending_seek_) { |
- if (pending_seek_) { |
- seek_time_ = pending_seek_time_; |
- pending_seek_ = false; |
- pending_seek_time_ = base::TimeDelta(); |
- } |
- time_changed = true; |
- } else { |
- // It is safe to call GetCurrentFrameTimestamp() because VFC is stopped |
- // during Suspend(). It won't be started again until after Resume() is |
- // called. |
- seek_time_ = compositor_->GetCurrentFrameTimestamp(); |
- } |
- |
- if (chunk_demuxer_) |
- chunk_demuxer_->StartWaitingForSeek(seek_time_); |
- |
- resuming_ = true; |
- pipeline_.Resume(CreateRenderer(), seek_time_, |
- BIND_TO_RENDER_LOOP1(&WebMediaPlayerImpl::OnPipelineSeeked, |
- time_changed)); |
+ if (pipeline_.IsRunning()) |
+ pipeline_state_.Resume(); |
} |
#if defined(OS_ANDROID) // WMPI_CAST |
@@ -1249,28 +1079,23 @@ void WebMediaPlayerImpl::OnRemotePlaybackEnded() { |
} |
void WebMediaPlayerImpl::OnDisconnectedFromRemoteDevice(double t) { |
- paused_time_ = base::TimeDelta::FromSecondsD(t); |
- pending_seek_ = true; |
- pending_seek_time_ = paused_time_; |
- |
- ScheduleResume(); |
+ DoSeek(base::TimeDelta::FromSecondsD(t), false); |
wolenetz
2016/01/29 21:42:30
Do we need to do anything with paused_time_/seek_t
sandersd (OOO until July 31)
2016/02/01 23:19:26
We do; it should be done correctly by DoSeek().
I
wolenetz
2016/02/26 02:30:11
Acknowledged.
|
+ if (delegate_ && !delegate_->IsHidden()) |
wolenetz
2016/01/29 21:42:30
I confess I don't understand what's going on here
sandersd (OOO until July 31)
2016/02/01 23:19:26
This just says that we want to resume playback whe
wolenetz
2016/02/26 02:30:11
Acknowledged.
|
+ pipeline_state_.Resume(); |
- if (paused_time_ == pipeline_.GetMediaDuration()) { |
- ended_ = true; |
- } |
// We already told the delegate we're paused when remoting started. |
client_->playbackStateChanged(); |
client_->disconnectedFromRemoteDevice(); |
} |
void WebMediaPlayerImpl::SuspendForRemote() { |
- if (suspended_ && !suspending_) { |
+ if (pipeline_state_.IsSuspended()) { |
scoped_refptr<VideoFrame> frame = cast_impl_.GetCastingBanner(); |
if (frame) { |
compositor_->PaintFrameUsingOldRenderingPath(frame); |
} |
} |
- ScheduleSuspend(); |
+ pipeline_state_.Suspend(); |
} |
gfx::Size WebMediaPlayerImpl::GetCanvasSize() const { |
@@ -1345,13 +1170,9 @@ void WebMediaPlayerImpl::StartPipeline() { |
// ... and we're ready to go! |
seeking_ = true; |
- |
- // TODO(sandersd): On Android, defer Start() if the tab is not visible. |
- pipeline_.Start( |
- demuxer_.get(), CreateRenderer(), |
+ pipeline_state_.Start( |
+ chunk_demuxer_, demuxer_.get(), |
BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineEnded), |
- BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineError), |
- BIND_TO_RENDER_LOOP1(&WebMediaPlayerImpl::OnPipelineSeeked, false), |
BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineMetadata), |
BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineBufferingStateChanged), |
BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChanged), |
@@ -1454,17 +1275,6 @@ WebMediaPlayerImpl::GetCurrentFrameFromCompositor() { |
return video_frame; |
} |
-void WebMediaPlayerImpl::UpdatePausedTime() { |
- DCHECK(main_task_runner_->BelongsToCurrentThread()); |
- |
- // pause() may be called after playback has ended and the HTMLMediaElement |
- // requires that currentTime() == duration() after ending. We want to ensure |
- // |paused_time_| matches currentTime() in this case or a future seek() may |
- // incorrectly discard what it thinks is a seek to the existing time. |
- paused_time_ = |
- ended_ ? pipeline_.GetMediaDuration() : pipeline_.GetMediaTime(); |
-} |
- |
void WebMediaPlayerImpl::NotifyPlaybackStarted() { |
#if defined(OS_ANDROID) // WMPI_CAST |
// We do not tell our delegates about remote playback, becuase that would |