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

Unified Diff: media/blink/webmediaplayer_impl.cc

Issue 1641423002: Re-land extract state management from WebMediaPlayerImpl. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove remaining uses of base::Unretained(). Created 4 years, 10 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 side-by-side diff with in-line comments
Download patch
Index: media/blink/webmediaplayer_impl.cc
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 3749bf2f1878ab954dc318a122e029e88e123d58..0e66877eb27ff721318f6e10fda298c702f7a341 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,21 +144,20 @@ WebMediaPlayerImpl::WebMediaPlayerImpl(
worker_task_runner_(params.worker_task_runner()),
media_log_(params.media_log()),
pipeline_(media_task_runner_, media_log_.get()),
+ pipeline_controller_(
+ &pipeline_,
+ base::Bind(&WebMediaPlayerImpl::CreateRenderer,
+ base::Unretained(this)),
DaleCurtis 2016/02/24 01:48:24 Why the mix of unretained w/ AsWeakPtr() here?
sandersd (OOO until July 31) 2016/02/25 00:16:26 CreateRenderer() is a factory method; you can't bi
xhwang 2016/02/25 18:00:10 The CDM creation in CdmFactory::Create() is async,
+ base::Bind(&WebMediaPlayerImpl::OnPipelineSeeked, AsWeakPtr()),
+ base::Bind(&WebMediaPlayerImpl::OnPipelineSuspended, AsWeakPtr()),
+ base::Bind(&WebMediaPlayerImpl::OnPipelineError, AsWeakPtr())),
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),
pending_suspend_resume_cycle_(false),
ended_(false),
- pending_seek_(false),
- should_notify_time_changed_(false),
fullscreen_(false),
decoder_requires_restart_for_fullscreen_(false),
client_(client),
@@ -228,7 +228,7 @@ WebMediaPlayerImpl::~WebMediaPlayerImpl() {
data_source_->Abort();
if (chunk_demuxer_) {
chunk_demuxer_->Shutdown();
- chunk_demuxer_ = NULL;
+ chunk_demuxer_ = nullptr;
}
renderer_factory_.reset();
@@ -340,8 +340,8 @@ void WebMediaPlayerImpl::play() {
#endif
paused_ = false;
-
pipeline_.SetPlaybackRate(playback_rate_);
+
if (data_source_)
data_source_->MediaIsPlaying();
@@ -366,7 +366,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));
@@ -382,14 +388,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);
+}
- ended_ = false;
+void WebMediaPlayerImpl::DoSeek(base::TimeDelta time, bool time_updated) {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
- base::TimeDelta new_seek_time = base::TimeDelta::FromSecondsD(seconds);
+ ended_ = false;
#if defined(OS_ANDROID) // WMPI_CAST
if (isRemote()) {
- cast_impl_.seek(new_seek_time);
+ cast_impl_.seek(time);
return;
}
#endif
@@ -398,81 +407,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_controller_| 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_controller_.IsStable() && 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_controller_.Seek(time, time_updated);
}
void WebMediaPlayerImpl::setRate(double rate) {
@@ -621,23 +582,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();
}
@@ -877,67 +831,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;
- }
-
- // If we we're resuming into the playing state, notify the delegate.
- if (resuming_ && playback_rate_ > 0 && !paused_)
- NotifyPlaybackStarted();
-
- // 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;
- if (delegate_)
- delegate_->PlayerGone(delegate_id_);
-
+void WebMediaPlayerImpl::OnPipelineSuspended() {
#if defined(OS_ANDROID)
if (isRemote()) {
scoped_refptr<VideoFrame> frame = cast_impl_.GetCastingBanner();
@@ -947,10 +850,9 @@ void WebMediaPlayerImpl::OnPipelineSuspended(PipelineStatus status) {
}
#endif
- if (pending_resume_ || pending_suspend_resume_cycle_) {
- pending_resume_ = false;
+ if (pending_suspend_resume_cycle_) {
pending_suspend_resume_cycle_ = false;
- Resume();
+ pipeline_controller_.Resume();
return;
}
}
@@ -959,8 +861,8 @@ 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_controller_.IsStable())
return;
ended_ = true;
@@ -1020,8 +922,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_controller_.IsStable())
return;
// TODO(scherkus): Handle other buffering states when Pipeline starts using
@@ -1095,38 +998,7 @@ void WebMediaPlayerImpl::OnHidden(bool must_suspend) {
#endif
if (must_suspend || paused_ || hasVideo())
- ScheduleSuspend();
-}
-
-void WebMediaPlayerImpl::ScheduleSuspend() {
- if (!pipeline_.IsRunning())
- return;
-
- if (resuming_ || seeking_) {
- pending_suspend_ = true;
- return;
- }
-
- if (pending_resume_) {
- pending_resume_ = false;
- return;
- }
-
- Suspend();
-}
-
-void WebMediaPlayerImpl::Suspend() {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
-
- // Since Pipeline::IsRunning() may be set on the media thread there are cases
- // where two suspends might be issued concurrently.
- if (suspended_)
- return;
-
- suspended_ = true;
- suspending_ = true;
- pipeline_.Suspend(
- BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineSuspended));
+ pipeline_controller_.Suspend();
}
void WebMediaPlayerImpl::OnShown() {
@@ -1151,26 +1023,7 @@ 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();
+ pipeline_controller_.Resume();
}
void WebMediaPlayerImpl::OnPlay() {
@@ -1188,57 +1041,14 @@ void WebMediaPlayerImpl::OnVolumeMultiplierUpdate(double multiplier) {
setVolume(volume_);
}
-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. Use the pipeline time if there's no video.
- if (!data_source_ || !data_source_->IsStreaming()) {
- seek_time_ = hasVideo() ? compositor_->GetCurrentFrameTimestamp()
- : pipeline_.GetMediaTime();
- } else {
- // Resume from zero if a resource does not support range requests; this
- // avoids a painful "read-the-whole-file" seek penalty.
- seek_time_ = base::TimeDelta();
- }
- }
-
- if (chunk_demuxer_)
- chunk_demuxer_->StartWaitingForSeek(seek_time_);
-
- resuming_ = true;
- pipeline_.Resume(CreateRenderer(), seek_time_,
- BIND_TO_RENDER_LOOP1(&WebMediaPlayerImpl::OnPipelineSeeked,
- time_changed));
-}
-
void WebMediaPlayerImpl::ScheduleRestart() {
- // If we're suspended but not resuming there is no need to restart because
- // there is no renderer to kill.
- if (!suspended_ || resuming_) {
+ if (!pipeline_controller_.IsSuspended()) {
pending_suspend_resume_cycle_ = true;
- ScheduleSuspend();
+ pipeline_controller_.Suspend();
}
}
#if defined(OS_ANDROID) // WMPI_CAST
-
bool WebMediaPlayerImpl::isRemote() const {
return cast_impl_.isRemote();
}
@@ -1265,28 +1075,26 @@ void WebMediaPlayerImpl::OnRemotePlaybackEnded() {
}
void WebMediaPlayerImpl::OnDisconnectedFromRemoteDevice(double t) {
- paused_time_ = base::TimeDelta::FromSecondsD(t);
- pending_seek_ = true;
- pending_seek_time_ = paused_time_;
+ DoSeek(base::TimeDelta::FromSecondsD(t), false);
+ if (delegate_ && !delegate_->IsHidden())
+ pipeline_controller_.Resume();
- ScheduleResume();
-
- 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_controller_.IsSuspended()) {
+ pipeline_controller_.Suspend();
+ } else {
+ // TODO(sandersd): If PipelineController::Suspend() called |suspended_cb|
+ // when already suspended, we wouldn't need this case.
scoped_refptr<VideoFrame> frame = cast_impl_.GetCastingBanner();
if (frame) {
compositor_->PaintFrameUsingOldRenderingPath(frame);
}
}
- ScheduleSuspend();
}
gfx::Size WebMediaPlayerImpl::GetCanvasSize() const {
@@ -1397,11 +1205,10 @@ void WebMediaPlayerImpl::StartPipeline() {
seeking_ = true;
// TODO(sandersd): On Android, defer Start() if the tab is not visible.
- pipeline_.Start(
- demuxer_.get(), CreateRenderer(),
+ bool is_streaming = (data_source_ && data_source_->IsStreaming());
+ pipeline_controller_.Start(
+ chunk_demuxer_, demuxer_.get(), is_streaming,
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),
@@ -1509,17 +1316,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

Powered by Google App Engine
This is Rietveld 408576698