Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 #include "content/renderer/media/renderer_webmediaplayer_delegate.h" | 5 #include "content/renderer/media/renderer_webmediaplayer_delegate.h" |
| 6 | 6 |
| 7 #include <stdint.h> | 7 #include <stdint.h> |
| 8 | 8 |
| 9 #include "base/auto_reset.h" | 9 #include "base/auto_reset.h" |
| 10 #include "base/metrics/histogram_macros.h" | 10 #include "base/metrics/histogram_macros.h" |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 25 content::RenderThread::Get()->RecordAction(action); | 25 content::RenderThread::Get()->RecordAction(action); |
| 26 } | 26 } |
| 27 | 27 |
| 28 } // namespace | 28 } // namespace |
| 29 | 29 |
| 30 namespace media { | 30 namespace media { |
| 31 | 31 |
| 32 RendererWebMediaPlayerDelegate::RendererWebMediaPlayerDelegate( | 32 RendererWebMediaPlayerDelegate::RendererWebMediaPlayerDelegate( |
| 33 content::RenderFrame* render_frame) | 33 content::RenderFrame* render_frame) |
| 34 : RenderFrameObserver(render_frame), | 34 : RenderFrameObserver(render_frame), |
| 35 idle_cleanup_timer_(true, true), | |
| 36 default_tick_clock_(new base::DefaultTickClock()), | 35 default_tick_clock_(new base::DefaultTickClock()), |
| 37 tick_clock_(default_tick_clock_.get()) { | 36 tick_clock_(default_tick_clock_.get()) { |
| 38 idle_cleanup_interval_ = base::TimeDelta::FromSeconds(5); | 37 idle_cleanup_interval_ = base::TimeDelta::FromSeconds(5); |
| 39 idle_timeout_ = base::TimeDelta::FromSeconds(15); | 38 idle_timeout_ = base::TimeDelta::FromSeconds(15); |
| 40 | 39 |
| 41 // To conserve resources, cleanup idle players more often on low end devices. | 40 // Idle players time out more aggressively on low end devices. |
| 42 is_low_end_device_ = base::SysInfo::IsLowEndDevice(); | 41 is_low_end_device_ = base::SysInfo::IsLowEndDevice(); |
| 43 | 42 |
| 44 #if defined(OS_ANDROID) | 43 #if defined(OS_ANDROID) |
| 45 // On Android, due to the instability of the OS level media components, we | 44 // On Android, due to the instability of the OS level media components, we |
| 46 // consider all pre-KitKat devices to be low end. | 45 // consider all pre-KitKat devices to be low end. |
| 47 is_low_end_device_ |= | 46 is_low_end_device_ |= |
| 48 base::android::BuildInfo::GetInstance()->sdk_int() <= 18; | 47 base::android::BuildInfo::GetInstance()->sdk_int() <= 18; |
| 49 #endif | 48 #endif |
| 50 } | 49 } |
| 51 | 50 |
| 52 RendererWebMediaPlayerDelegate::~RendererWebMediaPlayerDelegate() {} | 51 RendererWebMediaPlayerDelegate::~RendererWebMediaPlayerDelegate() {} |
| 53 | 52 |
| 54 int RendererWebMediaPlayerDelegate::AddObserver(Observer* observer) { | 53 bool RendererWebMediaPlayerDelegate::IsFrameHidden() { |
| 55 const int delegate_id = id_map_.Add(observer); | 54 return render_frame()->IsHidden() || is_frame_hidden_for_testing_ || |
|
whywhat
2017/01/06 17:18:51
nit: I'd check is_frame_hidden_for_testing_ separa
sandersd (OOO until July 31)
2017/01/06 23:08:35
Done.
| |
| 56 // Start players in the idle state to ensure we capture players which are | 55 is_frame_closed_; |
| 57 // consuming resources, but which have never played. | |
| 58 AddIdleDelegate(delegate_id); | |
| 59 return delegate_id; | |
| 60 } | 56 } |
| 61 | 57 |
| 62 void RendererWebMediaPlayerDelegate::RemoveObserver(int delegate_id) { | 58 bool RendererWebMediaPlayerDelegate::IsFrameClosed() { |
| 63 DCHECK(id_map_.Lookup(delegate_id)); | 59 return is_frame_closed_; |
| 64 id_map_.Remove(delegate_id); | 60 } |
| 65 RemoveIdleDelegate(delegate_id); | 61 |
| 66 playing_videos_.erase(delegate_id); | 62 bool RendererWebMediaPlayerDelegate::IsBackgroundVideoPlaybackAllowed() { |
| 63 // TODO(sandersd): Include a check for kResumeBackgroundVideo? | |
| 64 return background_video_allowed_; | |
| 65 } | |
| 66 | |
| 67 int RendererWebMediaPlayerDelegate::AddObserver(Observer* observer) { | |
| 68 return id_map_.Add(observer); | |
| 69 } | |
| 70 | |
| 71 void RendererWebMediaPlayerDelegate::RemoveObserver(int player_id) { | |
| 72 DCHECK(id_map_.Lookup(player_id)); | |
| 73 id_map_.Remove(player_id); | |
| 74 idle_player_map_.erase(player_id); | |
| 75 stale_players_.erase(player_id); | |
| 76 playing_videos_.erase(player_id); | |
| 77 | |
| 78 Send( | |
| 79 new MediaPlayerDelegateHostMsg_OnMediaDestroyed(routing_id(), player_id)); | |
| 80 | |
| 81 ScheduleUpdateTask(); | |
| 67 } | 82 } |
| 68 | 83 |
| 69 void RendererWebMediaPlayerDelegate::DidPlay( | 84 void RendererWebMediaPlayerDelegate::DidPlay( |
| 70 int delegate_id, | 85 int player_id, |
| 86 bool has_audio, | |
| 71 bool has_video, | 87 bool has_video, |
| 72 bool has_audio, | |
| 73 bool is_remote, | |
| 74 MediaContentType media_content_type) { | 88 MediaContentType media_content_type) { |
| 75 DCHECK(id_map_.Lookup(delegate_id)); | 89 DVLOG(2) << __func__ << "(" << player_id << ", " << has_audio << ", " |
| 90 << has_video << ", " << static_cast<int>(media_content_type) << ")"; | |
| 91 DCHECK(id_map_.Lookup(player_id)); | |
| 92 | |
| 76 has_played_media_ = true; | 93 has_played_media_ = true; |
| 77 if (has_video && !is_remote) | 94 if (has_video) { |
| 78 playing_videos_.insert(delegate_id); | 95 if (!playing_videos_.count(player_id)) { |
| 79 else | 96 playing_videos_.insert(player_id); |
| 80 playing_videos_.erase(delegate_id); | 97 did_play_video_ = true; |
| 81 RemoveIdleDelegate(delegate_id); | 98 } |
| 82 | 99 } else { |
| 83 // Upon receipt of a playback request, suspend everything that's not used. | 100 playing_videos_.erase(player_id); |
| 84 if (is_low_end_device_) | 101 } |
| 85 CleanupIdleDelegates(base::TimeDelta()); | |
| 86 | 102 |
| 87 Send(new MediaPlayerDelegateHostMsg_OnMediaPlaying( | 103 Send(new MediaPlayerDelegateHostMsg_OnMediaPlaying( |
| 88 routing_id(), delegate_id, has_video, has_audio, is_remote, | 104 routing_id(), player_id, has_video, has_audio, false, |
| 89 media_content_type)); | 105 media_content_type)); |
| 106 | |
| 107 ScheduleUpdateTask(); | |
| 90 } | 108 } |
| 91 | 109 |
| 92 void RendererWebMediaPlayerDelegate::DidPause(int delegate_id, | 110 void RendererWebMediaPlayerDelegate::DidPause(int player_id) { |
| 93 bool reached_end_of_stream) { | 111 DVLOG(2) << __func__ << "(" << player_id << ")"; |
| 94 DCHECK(id_map_.Lookup(delegate_id)); | 112 DCHECK(id_map_.Lookup(player_id)); |
| 95 AddIdleDelegate(delegate_id); | 113 playing_videos_.erase(player_id); |
| 96 if (reached_end_of_stream) | 114 Send(new MediaPlayerDelegateHostMsg_OnMediaPaused(routing_id(), player_id, |
| 97 playing_videos_.erase(delegate_id); | 115 false)); |
| 98 Send(new MediaPlayerDelegateHostMsg_OnMediaPaused(routing_id(), delegate_id, | |
| 99 reached_end_of_stream)); | |
| 100 } | 116 } |
| 101 | 117 |
| 102 void RendererWebMediaPlayerDelegate::PlayerGone(int delegate_id) { | 118 void RendererWebMediaPlayerDelegate::PlayerGone(int player_id) { |
| 103 DCHECK(id_map_.Lookup(delegate_id)); | 119 DVLOG(2) << __func__ << "(" << player_id << ")"; |
| 104 playing_videos_.erase(delegate_id); | 120 DCHECK(id_map_.Lookup(player_id)); |
| 105 Send(new MediaPlayerDelegateHostMsg_OnMediaDestroyed(routing_id(), | 121 playing_videos_.erase(player_id); |
| 106 delegate_id)); | 122 Send( |
| 123 new MediaPlayerDelegateHostMsg_OnMediaDestroyed(routing_id(), player_id)); | |
| 107 } | 124 } |
| 108 | 125 |
| 109 bool RendererWebMediaPlayerDelegate::IsHidden() { | 126 void RendererWebMediaPlayerDelegate::SetIdle(int player_id, bool is_idle) { |
| 110 return render_frame()->IsHidden(); | 127 DVLOG(2) << __func__ << "(" << player_id << ", " << is_idle << ")"; |
| 128 | |
| 129 if (is_idle == IsIdle(player_id)) | |
| 130 return; | |
| 131 | |
| 132 if (is_idle) { | |
| 133 idle_player_map_[player_id] = tick_clock_->NowTicks(); | |
| 134 } else { | |
| 135 idle_player_map_.erase(player_id); | |
| 136 stale_players_.erase(player_id); | |
| 137 } | |
| 138 | |
| 139 ScheduleUpdateTask(); | |
| 111 } | 140 } |
| 112 | 141 |
| 113 bool RendererWebMediaPlayerDelegate::IsPlayingBackgroundVideo() { | 142 bool RendererWebMediaPlayerDelegate::IsIdle(int player_id) { |
| 114 return is_playing_background_video_; | 143 return idle_player_map_.count(player_id) || stale_players_.count(player_id); |
| 144 } | |
| 145 | |
| 146 void RendererWebMediaPlayerDelegate::ClearStaleFlag(int player_id) { | |
| 147 DVLOG(2) << __func__ << "(" << player_id << ")"; | |
| 148 | |
| 149 if (!stale_players_.erase(player_id)) | |
| 150 return; | |
| 151 | |
| 152 // Set the idle time such that the player will be considered stale the next | |
| 153 // time idle cleanup runs. | |
| 154 idle_player_map_[player_id] = tick_clock_->NowTicks() - idle_timeout_; | |
| 155 | |
| 156 ScheduleUpdateTask(); | |
| 157 } | |
| 158 | |
| 159 bool RendererWebMediaPlayerDelegate::IsStale(int player_id) { | |
| 160 return stale_players_.count(player_id); | |
| 115 } | 161 } |
| 116 | 162 |
| 117 void RendererWebMediaPlayerDelegate::WasHidden() { | 163 void RendererWebMediaPlayerDelegate::WasHidden() { |
| 164 RecordAction(base::UserMetricsAction("Media.Hidden")); | |
| 165 | |
| 118 for (IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd(); it.Advance()) | 166 for (IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd(); it.Advance()) |
| 119 it.GetCurrentValue()->OnHidden(); | 167 it.GetCurrentValue()->OnFrameHidden(); |
| 120 | 168 |
| 121 RecordAction(base::UserMetricsAction("Media.Hidden")); | 169 ScheduleUpdateTask(); |
| 122 } | 170 } |
| 123 | 171 |
| 124 void RendererWebMediaPlayerDelegate::WasShown() { | 172 void RendererWebMediaPlayerDelegate::WasShown() { |
| 125 SetIsPlayingBackgroundVideo(false); | 173 RecordAction(base::UserMetricsAction("Media.Shown")); |
| 174 is_frame_closed_ = false; | |
| 175 background_video_allowed_ = false; | |
| 176 | |
| 126 for (IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd(); it.Advance()) | 177 for (IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd(); it.Advance()) |
| 127 it.GetCurrentValue()->OnShown(); | 178 it.GetCurrentValue()->OnFrameShown(); |
| 128 | 179 |
| 129 RecordAction(base::UserMetricsAction("Media.Shown")); | 180 ScheduleUpdateTask(); |
| 130 } | 181 } |
| 131 | 182 |
| 132 bool RendererWebMediaPlayerDelegate::OnMessageReceived( | 183 bool RendererWebMediaPlayerDelegate::OnMessageReceived( |
| 133 const IPC::Message& msg) { | 184 const IPC::Message& msg) { |
| 134 bool handled = true; | |
| 135 IPC_BEGIN_MESSAGE_MAP(RendererWebMediaPlayerDelegate, msg) | 185 IPC_BEGIN_MESSAGE_MAP(RendererWebMediaPlayerDelegate, msg) |
| 136 IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_Pause, OnMediaDelegatePause) | 186 IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_Pause, OnMediaDelegatePause) |
| 137 IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_Play, OnMediaDelegatePlay) | 187 IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_Play, OnMediaDelegatePlay) |
| 138 IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_SuspendAllMediaPlayers, | 188 IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_SuspendAllMediaPlayers, |
| 139 OnMediaDelegateSuspendAllMediaPlayers) | 189 OnMediaDelegateSuspendAllMediaPlayers) |
| 140 IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_UpdateVolumeMultiplier, | 190 IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_UpdateVolumeMultiplier, |
| 141 OnMediaDelegateVolumeMultiplierUpdate) | 191 OnMediaDelegateVolumeMultiplierUpdate) |
| 142 IPC_MESSAGE_UNHANDLED(handled = false) | 192 IPC_MESSAGE_UNHANDLED(return false) |
| 143 IPC_END_MESSAGE_MAP() | 193 IPC_END_MESSAGE_MAP() |
| 144 return handled; | 194 return true; |
| 145 } | 195 } |
| 146 | 196 |
| 147 void RendererWebMediaPlayerDelegate::SetIdleCleanupParamsForTesting( | 197 void RendererWebMediaPlayerDelegate::SetIdleCleanupParamsForTesting( |
| 148 base::TimeDelta idle_timeout, | 198 base::TimeDelta idle_timeout, |
| 149 base::TickClock* tick_clock, | 199 base::TickClock* tick_clock, |
| 150 bool is_low_end_device) { | 200 bool is_low_end_device) { |
| 151 idle_cleanup_interval_ = base::TimeDelta(); | 201 idle_cleanup_interval_ = base::TimeDelta(); |
| 152 idle_timeout_ = idle_timeout; | 202 idle_timeout_ = idle_timeout; |
| 153 tick_clock_ = tick_clock; | 203 tick_clock_ = tick_clock; |
| 154 is_low_end_device_ = is_low_end_device; | 204 is_low_end_device_ = is_low_end_device; |
| 155 } | 205 } |
| 156 | 206 |
| 157 void RendererWebMediaPlayerDelegate::OnMediaDelegatePause(int delegate_id) { | 207 bool RendererWebMediaPlayerDelegate::IsIdleCleanupTimerRunningForTesting() |
| 158 Observer* observer = id_map_.Lookup(delegate_id); | 208 const { |
| 209 return idle_cleanup_timer_.IsRunning(); | |
| 210 } | |
| 211 | |
| 212 void RendererWebMediaPlayerDelegate::SetFrameHiddenForTesting(bool is_hidden) { | |
| 213 if (is_hidden == is_frame_hidden_for_testing_) | |
| 214 return; | |
| 215 | |
| 216 if (is_hidden) { | |
| 217 is_frame_hidden_for_testing_ = true; | |
| 218 } else { | |
| 219 is_frame_hidden_for_testing_ = false; | |
| 220 background_video_allowed_ = false; | |
| 221 } | |
| 222 | |
| 223 ScheduleUpdateTask(); | |
| 224 } | |
| 225 | |
| 226 void RendererWebMediaPlayerDelegate::OnMediaDelegatePause(int player_id) { | |
| 227 RecordAction(base::UserMetricsAction("Media.Controls.RemotePause")); | |
| 228 | |
| 229 Observer* observer = id_map_.Lookup(player_id); | |
| 159 if (observer) { | 230 if (observer) { |
| 160 if (playing_videos_.find(delegate_id) != playing_videos_.end()) | 231 background_video_allowed_ = false; |
| 161 SetIsPlayingBackgroundVideo(false); | |
| 162 observer->OnPause(); | 232 observer->OnPause(); |
| 163 } | 233 } |
| 164 | |
| 165 RecordAction(base::UserMetricsAction("Media.Controls.RemotePause")); | |
| 166 } | 234 } |
| 167 | 235 |
| 168 void RendererWebMediaPlayerDelegate::OnMediaDelegatePlay(int delegate_id) { | 236 void RendererWebMediaPlayerDelegate::OnMediaDelegatePlay(int player_id) { |
| 169 Observer* observer = id_map_.Lookup(delegate_id); | 237 RecordAction(base::UserMetricsAction("Media.Controls.RemotePlay")); |
| 238 | |
| 239 Observer* observer = id_map_.Lookup(player_id); | |
| 170 if (observer) { | 240 if (observer) { |
| 171 if (playing_videos_.find(delegate_id) != playing_videos_.end()) | 241 // TODO(sandersd): Ideally we would only set the flag if the player has |
| 172 SetIsPlayingBackgroundVideo(IsHidden()); | 242 // video, but we don't reliably know if a paused player has video. |
| 243 if (IsFrameHidden() && !IsFrameClosed()) | |
| 244 background_video_allowed_ = true; | |
| 173 observer->OnPlay(); | 245 observer->OnPlay(); |
| 174 } | 246 } |
| 175 | |
| 176 RecordAction(base::UserMetricsAction("Media.Controls.RemotePlay")); | |
| 177 } | 247 } |
| 178 | 248 |
| 179 void RendererWebMediaPlayerDelegate::OnMediaDelegateSuspendAllMediaPlayers() { | 249 void RendererWebMediaPlayerDelegate::OnMediaDelegateSuspendAllMediaPlayers() { |
| 250 is_frame_closed_ = true; | |
| 251 | |
| 180 for (IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd(); it.Advance()) | 252 for (IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd(); it.Advance()) |
| 181 it.GetCurrentValue()->OnSuspendRequested(true); | 253 it.GetCurrentValue()->OnFrameClosed(); |
| 182 } | 254 } |
| 183 | 255 |
| 184 void RendererWebMediaPlayerDelegate::OnMediaDelegateVolumeMultiplierUpdate( | 256 void RendererWebMediaPlayerDelegate::OnMediaDelegateVolumeMultiplierUpdate( |
| 185 int delegate_id, | 257 int player_id, |
| 186 double multiplier) { | 258 double multiplier) { |
| 187 Observer* observer = id_map_.Lookup(delegate_id); | 259 Observer* observer = id_map_.Lookup(player_id); |
| 188 if (observer) | 260 if (observer) |
| 189 observer->OnVolumeMultiplierUpdate(multiplier); | 261 observer->OnVolumeMultiplierUpdate(multiplier); |
| 190 } | 262 } |
| 191 | 263 |
| 192 void RendererWebMediaPlayerDelegate::AddIdleDelegate(int delegate_id) { | 264 void RendererWebMediaPlayerDelegate::ScheduleUpdateTask() { |
| 193 idle_delegate_map_[delegate_id] = tick_clock_->NowTicks(); | 265 if (!pending_update_task_) { |
| 194 if (!idle_cleanup_timer_.IsRunning()) { | 266 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 267 FROM_HERE, | |
| 268 base::Bind(&RendererWebMediaPlayerDelegate::UpdateTask, AsWeakPtr())); | |
| 269 pending_update_task_ = true; | |
| 270 } | |
| 271 } | |
| 272 | |
| 273 void RendererWebMediaPlayerDelegate::UpdateTask() { | |
| 274 DVLOG(3) << __func__; | |
| 275 pending_update_task_ = false; | |
| 276 | |
| 277 // Check whether a player was played since the last UpdateTask(). We basically | |
| 278 // treat this as a parameter to UpdateTask(), except that it can be changed | |
| 279 // between posting the task and UpdateTask() executing. | |
| 280 bool did_play_video_since_last_update_task = did_play_video_; | |
| 281 did_play_video_ = false; | |
| 282 | |
| 283 // Record UMAs for background video playback. | |
| 284 RecordBackgroundVideoPlayback(); | |
| 285 | |
| 286 // Clean up idle players. | |
| 287 bool aggressive_cleanup = false; | |
| 288 | |
| 289 // When we reach the maximum number of idle players, clean them up | |
| 290 // aggressively. Values chosen after testing on a Galaxy Nexus device for | |
| 291 // http://crbug.com/612909. | |
| 292 if (idle_player_map_.size() > (is_low_end_device_ ? 2u : 8u)) | |
| 293 aggressive_cleanup = true; | |
| 294 | |
| 295 // When a player plays on a low-end device, clean up idle players | |
| 296 // aggressively. | |
| 297 if (did_play_video_since_last_update_task && is_low_end_device_) | |
| 298 aggressive_cleanup = true; | |
| 299 | |
| 300 CleanUpIdlePlayers(aggressive_cleanup ? base::TimeDelta() : idle_timeout_); | |
| 301 | |
| 302 // If there are still idle players, schedule an attempt to clean them up. | |
| 303 // This construct ensures that the next callback is always | |
| 304 // |idle_cleanup_interval_| from now. | |
| 305 idle_cleanup_timer_.Stop(); | |
| 306 if (!idle_player_map_.empty()) { | |
| 195 idle_cleanup_timer_.Start( | 307 idle_cleanup_timer_.Start( |
| 196 FROM_HERE, idle_cleanup_interval_, | 308 FROM_HERE, idle_cleanup_interval_, |
| 197 base::Bind(&RendererWebMediaPlayerDelegate::CleanupIdleDelegates, | 309 base::Bind(&RendererWebMediaPlayerDelegate::UpdateTask, |
| 198 base::Unretained(this), idle_timeout_)); | 310 base::Unretained(this))); |
| 311 } | |
| 312 } | |
| 313 | |
| 314 void RendererWebMediaPlayerDelegate::RecordBackgroundVideoPlayback() { | |
| 315 #if defined(OS_ANDROID) | |
| 316 // TODO(avayvod): This would be useful to collect on desktop too and express | |
| 317 // in actual media watch time vs. just elapsed time. | |
| 318 // See https://crbug.com/638726. | |
| 319 bool has_playing_background_video = | |
| 320 IsFrameHidden() && !IsFrameClosed() && !playing_videos_.empty(); | |
| 321 | |
| 322 if (has_playing_background_video != was_playing_background_video_) { | |
| 323 was_playing_background_video_ = has_playing_background_video; | |
| 324 | |
| 325 if (has_playing_background_video) { | |
| 326 RecordAction(base::UserMetricsAction("Media.Session.BackgroundResume")); | |
| 327 background_video_start_time_ = base::TimeTicks::Now(); | |
| 328 } else { | |
| 329 RecordAction(base::UserMetricsAction("Media.Session.BackgroundSuspend")); | |
| 330 UMA_HISTOGRAM_CUSTOM_TIMES( | |
| 331 "Media.Android.BackgroundVideoTime", | |
| 332 base::TimeTicks::Now() - background_video_start_time_, | |
| 333 base::TimeDelta::FromSeconds(7), base::TimeDelta::FromHours(10), 50); | |
| 334 } | |
| 335 } | |
| 336 #endif // OS_ANDROID | |
| 337 } | |
| 338 | |
| 339 void RendererWebMediaPlayerDelegate::CleanUpIdlePlayers( | |
| 340 base::TimeDelta timeout) { | |
| 341 const base::TimeTicks now = tick_clock_->NowTicks(); | |
| 342 | |
| 343 // Create a list of stale players before making any possibly reentrant calls | |
| 344 // to OnIdleTimeout(). | |
| 345 std::vector<int> stale_players; | |
| 346 for (const auto& it : idle_player_map_) { | |
| 347 if (now - it.second >= timeout) | |
| 348 stale_players.push_back(it.first); | |
| 199 } | 349 } |
| 200 | 350 |
| 201 // When we reach the maximum number of idle players, aggressively suspend idle | 351 // Notify stale players. |
| 202 // delegates to try and remain under the limit. Values chosen after testing on | 352 for (int player_id : stale_players) { |
| 203 // a Galaxy Nexus device for http://crbug.com/612909. | 353 Observer* player = id_map_.Lookup(player_id); |
| 204 if (idle_delegate_map_.size() > (is_low_end_device_ ? 2u : 8u)) | 354 if (player && idle_player_map_.erase(player_id)) { |
| 205 CleanupIdleDelegates(base::TimeDelta()); | 355 stale_players_.insert(player_id); |
| 206 } | 356 player->OnIdleTimeout(); |
| 207 | |
| 208 void RendererWebMediaPlayerDelegate::RemoveIdleDelegate(int delegate_id) { | |
| 209 // To avoid invalidating the iterator, just mark the delegate for deletion | |
| 210 // using a sentinel value of an empty TimeTicks. | |
| 211 if (idle_cleanup_running_) { | |
| 212 idle_delegate_map_[delegate_id] = base::TimeTicks(); | |
| 213 return; | |
| 214 } | |
| 215 | |
| 216 idle_delegate_map_.erase(delegate_id); | |
| 217 if (idle_delegate_map_.empty()) | |
| 218 idle_cleanup_timer_.Stop(); | |
| 219 } | |
| 220 | |
| 221 void RendererWebMediaPlayerDelegate::CleanupIdleDelegates( | |
| 222 base::TimeDelta timeout) { | |
| 223 // Drop reentrant cleanups which can occur during forced suspension when the | |
| 224 // number of idle delegates is too high for a given device. | |
| 225 if (idle_cleanup_running_) | |
| 226 return; | |
| 227 | |
| 228 // Iterate over the delegates and suspend the idle ones. Note: The call to | |
| 229 // OnSuspendRequested() can trigger calls into RemoveIdleDelegate(), so for | |
| 230 // iterator validity we set |idle_cleanup_running_| to true and defer | |
| 231 // deletions. | |
| 232 DCHECK(!idle_cleanup_running_); | |
| 233 base::AutoReset<bool> scoper(&idle_cleanup_running_, true); | |
| 234 const base::TimeTicks now = tick_clock_->NowTicks(); | |
| 235 for (auto& idle_delegate_entry : idle_delegate_map_) { | |
| 236 if (now - idle_delegate_entry.second > timeout) { | |
| 237 if (id_map_.Lookup(idle_delegate_entry.first) | |
| 238 ->OnSuspendRequested(false)) { | |
| 239 // If the player accepted the suspension, mark it for removal | |
| 240 // from future polls to avoid running the timer forever. | |
| 241 idle_delegate_entry.second = base::TimeTicks(); | |
| 242 } | |
| 243 } | 357 } |
| 244 } | 358 } |
| 245 | |
| 246 // Take care of any removals that happened during the above iteration. | |
| 247 for (auto it = idle_delegate_map_.begin(); it != idle_delegate_map_.end();) { | |
| 248 if (it->second.is_null()) | |
| 249 it = idle_delegate_map_.erase(it); | |
| 250 else | |
| 251 ++it; | |
| 252 } | |
| 253 | |
| 254 // Shutdown the timer if no delegates are left. | |
| 255 if (idle_delegate_map_.empty()) | |
| 256 idle_cleanup_timer_.Stop(); | |
| 257 } | |
| 258 | |
| 259 void RendererWebMediaPlayerDelegate::SetIsPlayingBackgroundVideo( | |
| 260 bool is_playing) { | |
| 261 if (is_playing_background_video_ == is_playing) return; | |
| 262 | |
| 263 // TODO(avayvod): This would be useful to collect on desktop too and express in | |
| 264 // actual media watch time vs. just elapsed time. See https://crbug.com/638726. | |
| 265 #if defined(OS_ANDROID) | |
| 266 if (is_playing_background_video_) { | |
| 267 UMA_HISTOGRAM_CUSTOM_TIMES( | |
| 268 "Media.Android.BackgroundVideoTime", | |
| 269 base::TimeTicks::Now() - background_video_playing_start_time_, | |
| 270 base::TimeDelta::FromSeconds(7), base::TimeDelta::FromHours(10), 50); | |
| 271 RecordAction(base::UserMetricsAction("Media.Session.BackgroundSuspend")); | |
| 272 } else { | |
| 273 background_video_playing_start_time_ = base::TimeTicks::Now(); | |
| 274 RecordAction(base::UserMetricsAction("Media.Session.BackgroundResume")); | |
| 275 } | |
| 276 #endif // OS_ANDROID | |
| 277 | |
| 278 is_playing_background_video_ = is_playing; | |
| 279 } | 359 } |
| 280 | 360 |
| 281 void RendererWebMediaPlayerDelegate::OnDestruct() { | 361 void RendererWebMediaPlayerDelegate::OnDestruct() { |
| 282 delete this; | 362 delete this; |
| 283 } | 363 } |
| 284 | 364 |
| 285 } // namespace media | 365 } // namespace media |
| OLD | NEW |