Index: media/blink/webmediaplayer_impl.cc |
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc |
index 578f7ede2799893f4ea63ba1b0b3006fefdff25e..7b761c23a6bc64b5baec5b6d55ae6df7757ab883 100644 |
--- a/media/blink/webmediaplayer_impl.cc |
+++ b/media/blink/webmediaplayer_impl.cc |
@@ -181,9 +181,6 @@ WebMediaPlayerImpl::WebMediaPlayerImpl( |
linked_ptr<UrlIndex> url_index, |
const WebMediaPlayerParams& params) |
: frame_(frame), |
- delegate_state_(DelegateState::GONE), |
- is_idle_(false), |
- must_suspend_(false), |
network_state_(WebMediaPlayer::NetworkStateEmpty), |
ready_state_(WebMediaPlayer::ReadyStateHaveNothing), |
highest_ready_state_(WebMediaPlayer::ReadyStateHaveNothing), |
@@ -240,6 +237,7 @@ WebMediaPlayerImpl::WebMediaPlayerImpl( |
use_fallback_path_(false), |
is_encrypted_(false), |
underflow_count_(0), |
+ preroll_attempt_pending_(false), |
observer_(params.media_observer()) { |
DCHECK(!adjust_allocated_memory_cb_.is_null()); |
DCHECK(renderer_factory_); |
@@ -253,8 +251,10 @@ WebMediaPlayerImpl::WebMediaPlayerImpl( |
disable_fullscreen_video_overlays_ = |
!base::FeatureList::IsEnabled(media::kOverlayFullscreenVideo); |
- if (delegate_) |
+ if (delegate_) { |
delegate_id_ = delegate_->AddObserver(this); |
+ delegate_->SetIdle(delegate_id_, true); |
+ } |
media_log_->AddEvent( |
media_log_->CreateEvent(MediaLogEvent::WEBMEDIAPLAYER_CREATED)); |
@@ -421,8 +421,10 @@ void WebMediaPlayerImpl::play() { |
return; |
} |
#endif |
+ // TODO(sandersd): Do we want to reset the idle timer here? |
+ if (delegate_) |
+ delegate_->SetIdle(delegate_id_, false); |
paused_ = false; |
- is_idle_ = false; |
pipeline_.SetPlaybackRate(playback_rate_); |
background_pause_timer_.Stop(); |
@@ -517,11 +519,11 @@ void WebMediaPlayerImpl::DoSeek(base::TimeDelta time, bool time_updated) { |
if (watch_time_reporter_) |
watch_time_reporter_->OnSeeking(); |
- // TODO(sandersd): Ideally we would not clear the idle state if |
- // |pipeline_controller_| can elide the seek. |
- is_idle_ = false; |
+ // TODO(sandersd): Move |seeking_| to PipelineController. |
+ // TODO(sandersd): Do we want to reset the idle timer here? |
+ if (delegate_) |
+ delegate_->SetIdle(delegate_id_, false); |
ended_ = false; |
- |
seeking_ = true; |
seek_time_ = time; |
if (paused_) |
@@ -788,6 +790,25 @@ blink::WebTimeRanges WebMediaPlayerImpl::seekable() const { |
return blink::WebTimeRanges(&seekable_range, 1); |
} |
+bool WebMediaPlayerImpl::IsPrerollAttemptNeeded() { |
+ // TODO(sandersd): Replace with |highest_ready_state_since_seek_| if we need |
+ // to ensure that preroll always gets a chance to complete. |
+ if (highest_ready_state_ >= ReadyState::ReadyStateHaveFutureData) |
+ return false; |
+ |
+ if (preroll_attempt_pending_) |
+ return true; |
+ |
+ // Freshly initialized; there has never been any loading progress. (Otherwise |
+ // |preroll_attempt_pending_| would be true when the start time is null.) |
+ if (preroll_attempt_start_time_.is_null()) |
+ return false; |
+ |
+ base::TimeDelta preroll_attempt_duration = |
+ tick_clock_->NowTicks() - preroll_attempt_start_time_; |
+ return preroll_attempt_duration < kLoadingToIdleTimeout; |
watk
2016/12/10 00:01:31
maybe this should be kPrerollAttemptTimeout now?
sandersd (OOO until July 31)
2017/01/05 23:12:20
Done.
|
+} |
+ |
bool WebMediaPlayerImpl::didLoadingProgress() { |
DCHECK(main_task_runner_->BelongsToCurrentThread()); |
@@ -796,19 +817,22 @@ bool WebMediaPlayerImpl::didLoadingProgress() { |
const bool data_progress = buffered_data_source_host_.DidLoadingProgress(); |
const bool did_loading_progress = pipeline_progress || data_progress; |
- // If we've idle suspended before reaching kHaveFutureData and loading has |
- // progressed we need to spin up the renderer and figure out if we have enough |
- // data yet; |client_| may be waiting on this signal to trigger playback. The |
- // idle timeout is long enough that this is a low-cost operation. |
- if (highest_ready_state_ < ReadyState::ReadyStateHaveFutureData && |
- pipeline_controller_.IsSuspended() && did_loading_progress && is_idle_) { |
- is_idle_ = false; |
+ if (did_loading_progress && |
+ highest_ready_state_ < ReadyState::ReadyStateHaveFutureData) { |
+ // Reset the preroll attempt clock. |
+ preroll_attempt_pending_ = true; |
+ preroll_attempt_start_time_ = base::TimeTicks(); |
+ |
+ // Clear any 'stale' flag and give the pipeline a chance to resume. If we |
+ // are already resumed, this will cause |preroll_attempt_start_time_| to be |
+ // set. |
+ // TODO(sandersd): Should this be on the same stack? It might be surprsing |
watk
2016/12/10 00:01:31
surprising
sandersd (OOO until July 31)
2017/01/05 23:12:20
Done.
|
+ // that didLoadingProgress() can synchronously change state. |
+ if (delegate_) |
+ delegate_->ClearStaleFlag(delegate_id_); |
UpdatePlayState(); |
} |
- if (did_loading_progress) |
- last_time_loading_progressed_ = tick_clock_->NowTicks(); |
- |
return did_loading_progress; |
} |
@@ -1201,8 +1225,10 @@ void WebMediaPlayerImpl::OnBufferingStateChange(BufferingState state) { |
data_source_->OnBufferingHaveEnough(false); |
// Blink expects a timeChanged() in response to a seek(). |
- if (should_notify_time_changed_) |
+ if (should_notify_time_changed_) { |
+ should_notify_time_changed_ = false; |
client_->timeChanged(); |
+ } |
// Once we have enough, start reporting the total memory usage. We'll also |
// report once playback starts. |
@@ -1318,7 +1344,7 @@ void WebMediaPlayerImpl::OnVideoOpacityChange(bool opaque) { |
video_weblayer_->layer()->SetContentsOpaque(opaque_); |
} |
-void WebMediaPlayerImpl::OnHidden() { |
+void WebMediaPlayerImpl::OnFrameHidden() { |
DCHECK(main_task_runner_->BelongsToCurrentThread()); |
if (IsBackgroundVideoTrackOptimizationEnabled()) |
@@ -1334,8 +1360,15 @@ void WebMediaPlayerImpl::OnHidden() { |
ScheduleIdlePauseTimer(); |
} |
-void WebMediaPlayerImpl::OnShown() { |
+void WebMediaPlayerImpl::OnFrameClosed() { |
+ DCHECK(main_task_runner_->BelongsToCurrentThread()); |
+ UpdatePlayState(); |
+} |
+ |
+void WebMediaPlayerImpl::OnFrameShown() { |
DCHECK(main_task_runner_->BelongsToCurrentThread()); |
+ background_pause_timer_.Stop(); |
+ |
if (watch_time_reporter_) |
watch_time_reporter_->OnShown(); |
@@ -1345,43 +1378,21 @@ void WebMediaPlayerImpl::OnShown() { |
selectedVideoTrackChanged(&trackId); |
} |
- must_suspend_ = false; |
- background_pause_timer_.Stop(); |
- |
UpdatePlayState(); |
} |
-bool WebMediaPlayerImpl::OnSuspendRequested(bool must_suspend) { |
+bool WebMediaPlayerImpl::OnIdleTimeout() { |
DCHECK(main_task_runner_->BelongsToCurrentThread()); |
+ if (IsPrerollAttemptNeeded()) |
+ return false; |
- if (must_suspend) { |
- must_suspend_ = true; |
- UpdatePlayState(); |
- return true; |
- } |
- |
- // If we're beyond HaveFutureData, we can safely suspend at any time. |
- if (highest_ready_state_ >= WebMediaPlayer::ReadyStateHaveFutureData) { |
- is_idle_ = true; |
- UpdatePlayState(); |
- return true; |
- } |
- |
- // Before HaveFutureData blink will not call play(), so we must be careful to |
- // only suspend if we'll eventually receive an event that will trigger a |
- // resume. If the last time loading progressed was a while ago, and we still |
- // haven't reached HaveFutureData, we assume that we're waiting on more data |
- // to continue pre-rolling. When that data is loaded the pipeline will be |
- // resumed by didLoadingProgress(). |
- if (last_time_loading_progressed_.is_null() || |
- (tick_clock_->NowTicks() - last_time_loading_progressed_) > |
- kLoadingToIdleTimeout) { |
- is_idle_ = true; |
- UpdatePlayState(); |
- return true; |
- } |
+ // Post the update as a task so that: |
+ // 1) IsStale() returns true in UpdatePlayState(). |
+ // 2) We don't have to worry if we call SetIdle(false). |
+ main_task_runner_->PostTask( |
+ FROM_HERE, base::Bind(&WebMediaPlayerImpl::UpdatePlayState, AsWeakPtr())); |
- return false; |
+ return true; |
} |
void WebMediaPlayerImpl::OnPlay() { |
@@ -1724,43 +1735,33 @@ void WebMediaPlayerImpl::UpdatePlayState() { |
bool is_backgrounded = IsBackgroundedSuspendEnabled() && IsHidden(); |
PlayState state = UpdatePlayState_ComputePlayState( |
is_remote, is_streaming, is_suspended, is_backgrounded); |
- SetDelegateState(state.delegate_state); |
+ SetDelegateState(state.delegate_state, state.is_idle); |
SetMemoryReportingState(state.is_memory_reporting_enabled); |
SetSuspendState(state.is_suspended || pending_suspend_resume_cycle_); |
} |
-void WebMediaPlayerImpl::SetDelegateState(DelegateState new_state) { |
+void WebMediaPlayerImpl::SetDelegateState(DelegateState new_state, |
+ bool is_idle) { |
if (!delegate_) |
return; |
- if (delegate_state_ == new_state) { |
- if (delegate_state_ != DelegateState::PLAYING || |
- autoplay_muted_ == client_->isAutoplayingMuted()) { |
- return; |
- } |
- } |
- |
- delegate_state_ = new_state; |
- |
- switch (delegate_state_) { |
+ switch (new_state) { |
case DelegateState::GONE: |
delegate_->PlayerGone(delegate_id_); |
break; |
case DelegateState::PLAYING: { |
- autoplay_muted_ = client_->isAutoplayingMuted(); |
- bool has_audio = autoplay_muted_ ? false : hasAudio(); |
+ bool has_audio = hasAudio() && !client_->isAutoplayingMuted(); |
delegate_->DidPlay( |
- delegate_id_, hasVideo(), has_audio, false, |
+ delegate_id_, has_audio, hasVideo(), |
media::DurationToMediaContentType(pipeline_.GetMediaDuration())); |
break; |
} |
case DelegateState::PAUSED: |
- delegate_->DidPause(delegate_id_, false); |
- break; |
- case DelegateState::ENDED: |
- delegate_->DidPause(delegate_id_, true); |
+ delegate_->DidPause(delegate_id_); |
break; |
} |
+ |
+ delegate_->SetIdle(delegate_id_, is_idle); |
} |
void WebMediaPlayerImpl::SetMemoryReportingState( |
@@ -1806,12 +1807,20 @@ void WebMediaPlayerImpl::SetSuspendState(bool is_suspended) { |
can_suspend_state_ = CanSuspendState::YES; |
#endif |
- if (can_suspend_state_ == CanSuspendState::NO) |
- return; |
- |
- if (is_suspended) { |
+ if (is_suspended && can_suspend_state_ != CanSuspendState::NO) { |
+ // If we were not resumed for long enough to satisfy the preroll attempt, |
+ // reset the clock. |
+ if (!preroll_attempt_pending_ && IsPrerollAttemptNeeded()) { |
+ preroll_attempt_pending_ = true; |
+ preroll_attempt_start_time_ = base::TimeTicks(); |
+ } |
pipeline_controller_.Suspend(); |
} else { |
+ // When resuming, start the preroll attempt clock. |
+ if (preroll_attempt_pending_) { |
+ preroll_attempt_pending_ = false; |
+ preroll_attempt_start_time_ = tick_clock_->NowTicks(); |
+ } |
pipeline_controller_.Resume(); |
} |
} |
@@ -1823,6 +1832,9 @@ WebMediaPlayerImpl::UpdatePlayState_ComputePlayState(bool is_remote, |
bool is_backgrounded) { |
PlayState result; |
+ bool must_suspend = delegate_->IsFrameClosed(); |
+ bool is_stale = delegate_->IsStale(delegate_id_); |
+ |
// This includes both data source (before pipeline startup) and pipeline |
// errors. |
bool has_error = IsNetworkStateError(network_state_); |
@@ -1841,11 +1853,11 @@ WebMediaPlayerImpl::UpdatePlayState_ComputePlayState(bool is_remote, |
// controls earlier and it's still playing. |
bool is_backgrounded_video = is_backgrounded && have_metadata && hasVideo(); |
bool can_play_backgrounded = is_backgrounded_video && !is_remote && |
- hasAudio() && IsResumeBackgroundVideosEnabled(); |
- bool is_background_playing = |
- delegate_ && delegate_->IsPlayingBackgroundVideo(); |
- bool background_suspended = !is_streaming && is_backgrounded_video && |
- !(can_play_backgrounded && is_background_playing); |
+ hasAudio() && |
+ IsResumeBackgroundVideosEnabled() && delegate_ && |
+ delegate_->IsBackgroundVideoPlaybackAllowed(); |
+ bool background_suspended = |
+ !is_streaming && is_backgrounded_video && !can_play_backgrounded; |
bool background_pause_suspended = |
!is_streaming && is_backgrounded && paused_ && have_future_data; |
@@ -1856,16 +1868,16 @@ WebMediaPlayerImpl::UpdatePlayState_ComputePlayState(bool is_remote, |
// TODO(sandersd): Make the delegate suspend idle players immediately when |
// hidden. |
bool idle_suspended = |
- !is_streaming && is_idle_ && paused_ && !seeking_ && !overlay_enabled_; |
+ !is_streaming && is_stale && paused_ && !seeking_ && !overlay_enabled_; |
// If we're already suspended, see if we can wait for user interaction. Prior |
- // to HaveFutureData, we require |is_idle_| to remain suspended. |is_idle_| |
+ // to HaveFutureData, we require |is_stale| to remain suspended. |is_stale| |
// will be cleared when we receive data which may take us to HaveFutureData. |
bool can_stay_suspended = |
- (is_idle_ || have_future_data) && is_suspended && paused_ && !seeking_; |
+ (is_stale || have_future_data) && is_suspended && paused_ && !seeking_; |
// Combined suspend state. |
- result.is_suspended = is_remote || must_suspend_ || idle_suspended || |
+ result.is_suspended = is_remote || must_suspend || idle_suspended || |
background_suspended || background_pause_suspended || |
can_stay_suspended; |
@@ -1889,30 +1901,34 @@ WebMediaPlayerImpl::UpdatePlayState_ComputePlayState(bool is_remote, |
// TODO(sandersd): If Blink told us the paused state sooner, we could create |
// the media session sooner. |
bool can_play = !has_error && !is_remote && have_future_data; |
- bool has_session_playing = |
- can_play && !must_suspend_ && !background_suspended; |
+ bool has_session_playing = can_play && !must_suspend && !background_suspended; |
// |has_session_suspended| means the player is suspended from the media |
// element point of view but paused and can be resumed from the delegate point |
// of view. Therefore it behaves like |paused_| for the delegate. |
- bool has_session_suspended = can_play && !must_suspend_ && |
+ bool has_session_suspended = can_play && !must_suspend && |
background_suspended && can_play_backgrounded; |
bool has_session = has_session_playing || has_session_suspended; |
if (!has_session) { |
result.delegate_state = DelegateState::GONE; |
+ result.is_idle = delegate_ && delegate_->IsIdle(delegate_id_); |
} else if (paused_ || has_session_suspended) { |
+ // TODO(sandersd): Is it possible to have a suspended session, be ended, |
+ // and not be paused? If so we should be in a PLAYING state. |
result.delegate_state = |
- ended_ ? DelegateState::ENDED : DelegateState::PAUSED; |
+ ended_ ? DelegateState::GONE : DelegateState::PAUSED; |
+ result.is_idle = !seeking_; |
} else { |
result.delegate_state = DelegateState::PLAYING; |
+ result.is_idle = false; |
} |
// It's not critical if some cases where memory usage can change are missed, |
// since media memory changes are usually gradual. |
result.is_memory_reporting_enabled = |
- can_play && !result.is_suspended && !paused_; |
+ can_play && !result.is_suspended && (!paused_ || seeking_); |
return result; |
} |
@@ -1980,7 +1996,7 @@ void WebMediaPlayerImpl::CreateWatchTimeReporter() { |
pipeline_metadata_.natural_size, |
base::Bind(&GetCurrentTimeInternal, this))); |
watch_time_reporter_->OnVolumeChange(volume_); |
- if (IsHidden()) |
+ if (delegate_ && delegate_->IsFrameHidden()) |
watk
2016/12/10 00:01:31
Seems like this should still call IsHidden?
sandersd (OOO until July 31)
2017/01/05 23:12:20
I'm not sure. I think that means that we could rep
|
watch_time_reporter_->OnHidden(); |
else |
watch_time_reporter_->OnShown(); |
@@ -1989,7 +2005,7 @@ void WebMediaPlayerImpl::CreateWatchTimeReporter() { |
bool WebMediaPlayerImpl::IsHidden() const { |
DCHECK(main_task_runner_->BelongsToCurrentThread()); |
- return delegate_ && delegate_->IsHidden(); |
+ return delegate_ && delegate_->IsFrameHidden() && !delegate_->IsFrameClosed(); |
} |
} // namespace media |