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_ || |
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 playing_videos_.insert(player_id); |
79 else | 96 else |
80 playing_videos_.erase(delegate_id); | 97 playing_videos_.erase(player_id); |
81 RemoveIdleDelegate(delegate_id); | |
82 | |
83 // Upon receipt of a playback request, suspend everything that's not used. | |
84 if (is_low_end_device_) | |
85 CleanupIdleDelegates(base::TimeDelta()); | |
86 | 98 |
87 Send(new MediaPlayerDelegateHostMsg_OnMediaPlaying( | 99 Send(new MediaPlayerDelegateHostMsg_OnMediaPlaying( |
88 routing_id(), delegate_id, has_video, has_audio, is_remote, | 100 routing_id(), player_id, has_video, has_audio, false, |
89 media_content_type)); | 101 media_content_type)); |
102 | |
103 did_play_ = true; | |
104 ScheduleUpdateTask(); | |
90 } | 105 } |
91 | 106 |
92 void RendererWebMediaPlayerDelegate::DidPause(int delegate_id, | 107 void RendererWebMediaPlayerDelegate::DidPause(int player_id) { |
93 bool reached_end_of_stream) { | 108 DVLOG(2) << __func__ << "(" << player_id << ")"; |
94 DCHECK(id_map_.Lookup(delegate_id)); | 109 DCHECK(id_map_.Lookup(player_id)); |
95 AddIdleDelegate(delegate_id); | 110 playing_videos_.erase(player_id); |
96 if (reached_end_of_stream) | 111 Send(new MediaPlayerDelegateHostMsg_OnMediaPaused(routing_id(), player_id, |
97 playing_videos_.erase(delegate_id); | 112 false)); |
98 Send(new MediaPlayerDelegateHostMsg_OnMediaPaused(routing_id(), delegate_id, | |
99 reached_end_of_stream)); | |
100 } | 113 } |
101 | 114 |
102 void RendererWebMediaPlayerDelegate::PlayerGone(int delegate_id) { | 115 void RendererWebMediaPlayerDelegate::PlayerGone(int player_id) { |
103 DCHECK(id_map_.Lookup(delegate_id)); | 116 DVLOG(2) << __func__ << "(" << player_id << ")"; |
104 playing_videos_.erase(delegate_id); | 117 DCHECK(id_map_.Lookup(player_id)); |
105 Send(new MediaPlayerDelegateHostMsg_OnMediaDestroyed(routing_id(), | 118 playing_videos_.erase(player_id); |
106 delegate_id)); | 119 Send( |
120 new MediaPlayerDelegateHostMsg_OnMediaDestroyed(routing_id(), player_id)); | |
107 } | 121 } |
108 | 122 |
109 bool RendererWebMediaPlayerDelegate::IsHidden() { | 123 void RendererWebMediaPlayerDelegate::SetIdle(int player_id, bool is_idle) { |
110 return render_frame()->IsHidden(); | 124 DVLOG(2) << __func__ << "(" << player_id << ", " << is_idle << ")"; |
125 if (is_idle) { | |
126 if (idle_player_map_.count(player_id) || stale_players_.count(player_id)) | |
watk
2016/12/10 00:01:30
I just found out about base::ContainsValue yesterd
sandersd (OOO until July 31)
2017/01/05 23:12:20
Acknowledged.
| |
127 return; | |
128 idle_player_map_[player_id] = tick_clock_->NowTicks(); | |
129 } else { | |
130 idle_player_map_.erase(player_id); | |
131 stale_players_.erase(player_id); | |
132 } | |
133 ScheduleUpdateTask(); | |
111 } | 134 } |
112 | 135 |
113 bool RendererWebMediaPlayerDelegate::IsPlayingBackgroundVideo() { | 136 bool RendererWebMediaPlayerDelegate::IsIdle(int player_id) { |
114 return is_playing_background_video_; | 137 return idle_player_map_.count(player_id) || stale_players_.count(player_id); |
138 } | |
139 | |
140 void RendererWebMediaPlayerDelegate::ClearStaleFlag(int player_id) { | |
141 DVLOG(2) << __func__ << "(" << player_id << ")"; | |
142 const auto& it = stale_players_.find(player_id); | |
143 if (it == stale_players_.end()) | |
144 return; | |
145 stale_players_.erase(it); | |
watk
2016/12/10 00:01:30
erase(key_type) returns the number of elements rem
sandersd (OOO until July 31)
2017/01/05 23:12:20
Done.
| |
146 idle_player_map_[player_id] = tick_clock_->NowTicks() - idle_timeout_; | |
147 ScheduleUpdateTask(); | |
148 } | |
149 | |
150 bool RendererWebMediaPlayerDelegate::IsStale(int player_id) { | |
151 return stale_players_.count(player_id); | |
115 } | 152 } |
116 | 153 |
117 void RendererWebMediaPlayerDelegate::WasHidden() { | 154 void RendererWebMediaPlayerDelegate::WasHidden() { |
155 RecordAction(base::UserMetricsAction("Media.Hidden")); | |
156 | |
118 for (IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd(); it.Advance()) | 157 for (IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd(); it.Advance()) |
119 it.GetCurrentValue()->OnHidden(); | 158 it.GetCurrentValue()->OnFrameHidden(); |
120 | 159 |
121 RecordAction(base::UserMetricsAction("Media.Hidden")); | 160 ScheduleUpdateTask(); |
122 } | 161 } |
123 | 162 |
124 void RendererWebMediaPlayerDelegate::WasShown() { | 163 void RendererWebMediaPlayerDelegate::WasShown() { |
125 SetIsPlayingBackgroundVideo(false); | 164 RecordAction(base::UserMetricsAction("Media.Shown")); |
165 is_frame_closed_ = false; | |
166 background_video_allowed_ = false; | |
167 | |
126 for (IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd(); it.Advance()) | 168 for (IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd(); it.Advance()) |
127 it.GetCurrentValue()->OnShown(); | 169 it.GetCurrentValue()->OnFrameShown(); |
128 | 170 |
129 RecordAction(base::UserMetricsAction("Media.Shown")); | 171 ScheduleUpdateTask(); |
130 } | 172 } |
131 | 173 |
132 bool RendererWebMediaPlayerDelegate::OnMessageReceived( | 174 bool RendererWebMediaPlayerDelegate::OnMessageReceived( |
133 const IPC::Message& msg) { | 175 const IPC::Message& msg) { |
134 bool handled = true; | |
135 IPC_BEGIN_MESSAGE_MAP(RendererWebMediaPlayerDelegate, msg) | 176 IPC_BEGIN_MESSAGE_MAP(RendererWebMediaPlayerDelegate, msg) |
136 IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_Pause, OnMediaDelegatePause) | 177 IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_Pause, OnMediaDelegatePause) |
137 IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_Play, OnMediaDelegatePlay) | 178 IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_Play, OnMediaDelegatePlay) |
138 IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_SuspendAllMediaPlayers, | 179 IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_SuspendAllMediaPlayers, |
139 OnMediaDelegateSuspendAllMediaPlayers) | 180 OnMediaDelegateSuspendAllMediaPlayers) |
140 IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_UpdateVolumeMultiplier, | 181 IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_UpdateVolumeMultiplier, |
141 OnMediaDelegateVolumeMultiplierUpdate) | 182 OnMediaDelegateVolumeMultiplierUpdate) |
142 IPC_MESSAGE_UNHANDLED(handled = false) | 183 IPC_MESSAGE_UNHANDLED(return false) |
143 IPC_END_MESSAGE_MAP() | 184 IPC_END_MESSAGE_MAP() |
144 return handled; | 185 return true; |
145 } | 186 } |
146 | 187 |
147 void RendererWebMediaPlayerDelegate::SetIdleCleanupParamsForTesting( | 188 void RendererWebMediaPlayerDelegate::SetIdleCleanupParamsForTesting( |
148 base::TimeDelta idle_timeout, | 189 base::TimeDelta idle_timeout, |
149 base::TickClock* tick_clock, | 190 base::TickClock* tick_clock, |
150 bool is_low_end_device) { | 191 bool is_low_end_device) { |
151 idle_cleanup_interval_ = base::TimeDelta(); | 192 idle_cleanup_interval_ = base::TimeDelta(); |
152 idle_timeout_ = idle_timeout; | 193 idle_timeout_ = idle_timeout; |
153 tick_clock_ = tick_clock; | 194 tick_clock_ = tick_clock; |
154 is_low_end_device_ = is_low_end_device; | 195 is_low_end_device_ = is_low_end_device; |
155 } | 196 } |
156 | 197 |
157 void RendererWebMediaPlayerDelegate::OnMediaDelegatePause(int delegate_id) { | 198 bool RendererWebMediaPlayerDelegate::IsIdleCleanupTimerRunningForTesting() |
158 Observer* observer = id_map_.Lookup(delegate_id); | 199 const { |
200 return idle_cleanup_timer_.IsRunning(); | |
201 } | |
202 | |
203 void RendererWebMediaPlayerDelegate::SetFrameHiddenForTesting(bool is_hidden) { | |
204 if (is_hidden != is_frame_hidden_for_testing_) { | |
205 is_frame_hidden_for_testing_ = is_hidden; | |
206 background_video_allowed_ &= is_hidden; | |
207 ScheduleUpdateTask(); | |
208 } | |
209 } | |
210 | |
211 void RendererWebMediaPlayerDelegate::OnMediaDelegatePause(int player_id) { | |
212 RecordAction(base::UserMetricsAction("Media.Controls.RemotePause")); | |
213 | |
214 Observer* observer = id_map_.Lookup(player_id); | |
159 if (observer) { | 215 if (observer) { |
watk
2016/12/10 00:01:30
At some point we should call these observers "play
sandersd (OOO until July 31)
2017/01/05 23:12:20
Indeed we should. I'm going to skip that right now
| |
160 if (playing_videos_.find(delegate_id) != playing_videos_.end()) | 216 background_video_allowed_ = false; |
161 SetIsPlayingBackgroundVideo(false); | |
162 observer->OnPause(); | 217 observer->OnPause(); |
163 } | 218 } |
164 | |
165 RecordAction(base::UserMetricsAction("Media.Controls.RemotePause")); | |
166 } | 219 } |
167 | 220 |
168 void RendererWebMediaPlayerDelegate::OnMediaDelegatePlay(int delegate_id) { | 221 void RendererWebMediaPlayerDelegate::OnMediaDelegatePlay(int player_id) { |
169 Observer* observer = id_map_.Lookup(delegate_id); | 222 RecordAction(base::UserMetricsAction("Media.Controls.RemotePlay")); |
223 | |
224 Observer* observer = id_map_.Lookup(player_id); | |
170 if (observer) { | 225 if (observer) { |
171 if (playing_videos_.find(delegate_id) != playing_videos_.end()) | 226 // TODO(sandersd): Ideally we would only set the flag if the player has |
172 SetIsPlayingBackgroundVideo(IsHidden()); | 227 // video, but we don't reliably know if a paused player has video. |
228 if (IsFrameHidden() && !IsFrameClosed()) | |
229 background_video_allowed_ = true; | |
173 observer->OnPlay(); | 230 observer->OnPlay(); |
174 } | 231 } |
175 | |
176 RecordAction(base::UserMetricsAction("Media.Controls.RemotePlay")); | |
177 } | 232 } |
178 | 233 |
179 void RendererWebMediaPlayerDelegate::OnMediaDelegateSuspendAllMediaPlayers() { | 234 void RendererWebMediaPlayerDelegate::OnMediaDelegateSuspendAllMediaPlayers() { |
235 is_frame_closed_ = true; | |
236 | |
180 for (IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd(); it.Advance()) | 237 for (IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd(); it.Advance()) |
181 it.GetCurrentValue()->OnSuspendRequested(true); | 238 it.GetCurrentValue()->OnFrameClosed(); |
182 } | 239 } |
183 | 240 |
184 void RendererWebMediaPlayerDelegate::OnMediaDelegateVolumeMultiplierUpdate( | 241 void RendererWebMediaPlayerDelegate::OnMediaDelegateVolumeMultiplierUpdate( |
185 int delegate_id, | 242 int player_id, |
186 double multiplier) { | 243 double multiplier) { |
187 Observer* observer = id_map_.Lookup(delegate_id); | 244 Observer* observer = id_map_.Lookup(player_id); |
188 if (observer) | 245 if (observer) |
189 observer->OnVolumeMultiplierUpdate(multiplier); | 246 observer->OnVolumeMultiplierUpdate(multiplier); |
190 } | 247 } |
191 | 248 |
192 void RendererWebMediaPlayerDelegate::AddIdleDelegate(int delegate_id) { | 249 void RendererWebMediaPlayerDelegate::ScheduleUpdateTask() { |
193 idle_delegate_map_[delegate_id] = tick_clock_->NowTicks(); | 250 if (!pending_update_task_) { |
194 if (!idle_cleanup_timer_.IsRunning()) { | 251 base::ThreadTaskRunnerHandle::Get()->PostTask( |
252 FROM_HERE, | |
253 base::Bind(&RendererWebMediaPlayerDelegate::UpdateTask, AsWeakPtr())); | |
254 pending_update_task_ = true; | |
255 } | |
256 } | |
257 | |
258 void RendererWebMediaPlayerDelegate::UpdateTask() { | |
259 DVLOG(3) << __func__; | |
260 pending_update_task_ = false; | |
261 | |
262 // Check whether a player was played since the last UpdateTask(). We basically | |
263 // treat this as a parameter to UpdateTask() except that it can be changed | |
264 // between posting the task and UpdateTask() executing. | |
265 bool did_play = did_play_; | |
266 did_play_ = false; | |
267 | |
268 // Record UMAs for background video playback. | |
269 RecordBackgroundVideoPlayback(); | |
270 | |
271 // Cleanup idle players. | |
watk
2016/12/10 00:01:30
Clean up
sandersd (OOO until July 31)
2017/01/05 23:12:20
Done.
| |
272 bool aggressive_cleanup = false; | |
273 | |
274 // When we reach the maximum number of idle players, clean them up | |
275 // aggressively. Values chosen after testing on a Galaxy Nexus device for | |
276 // http://crbug.com/612909. | |
277 if (idle_player_map_.size() > (is_low_end_device_ ? 2u : 8u)) | |
278 aggressive_cleanup = true; | |
279 | |
280 // When a player plays on a low-end device, clean up idle players | |
281 // aggressively. | |
282 if (did_play && is_low_end_device_) | |
283 aggressive_cleanup = true; | |
284 | |
285 CleanupIdlePlayers(aggressive_cleanup ? base::TimeDelta() : idle_timeout_); | |
286 | |
287 // If there are still idle players, schedule an attempt to clean them up. | |
288 if (idle_player_map_.empty()) { | |
289 idle_cleanup_timer_.Stop(); | |
290 } else if (!idle_cleanup_timer_.IsRunning()) { | |
195 idle_cleanup_timer_.Start( | 291 idle_cleanup_timer_.Start( |
196 FROM_HERE, idle_cleanup_interval_, | 292 FROM_HERE, idle_cleanup_interval_, |
197 base::Bind(&RendererWebMediaPlayerDelegate::CleanupIdleDelegates, | 293 base::Bind(&RendererWebMediaPlayerDelegate::UpdateTask, |
198 base::Unretained(this), idle_timeout_)); | 294 base::Unretained(this))); |
295 } | |
296 } | |
297 | |
298 void RendererWebMediaPlayerDelegate::RecordBackgroundVideoPlayback() { | |
299 #if defined(OS_ANDROID) | |
300 // TODO(avayvod): This would be useful to collect on desktop too and express | |
301 // in actual media watch time vs. just elapsed time. | |
302 // See https://crbug.com/638726. | |
303 bool has_playing_background_video = | |
304 IsFrameHidden() && !IsFrameClosed() && !playing_videos_.empty(); | |
305 | |
306 if (has_playing_background_video != was_playing_background_video_) { | |
307 was_playing_background_video_ = has_playing_background_video; | |
308 | |
309 if (has_playing_background_video) { | |
310 RecordAction(base::UserMetricsAction("Media.Session.BackgroundResume")); | |
311 background_video_start_time_ = base::TimeTicks::Now(); | |
312 } else { | |
313 RecordAction(base::UserMetricsAction("Media.Session.BackgroundSuspend")); | |
314 UMA_HISTOGRAM_CUSTOM_TIMES( | |
315 "Media.Android.BackgroundVideoTime", | |
316 base::TimeTicks::Now() - background_video_start_time_, | |
317 base::TimeDelta::FromSeconds(7), base::TimeDelta::FromHours(10), 50); | |
318 } | |
319 } | |
320 #endif // OS_ANDROID | |
321 } | |
322 | |
323 void RendererWebMediaPlayerDelegate::CleanupIdlePlayers( | |
324 base::TimeDelta timeout) { | |
325 const base::TimeTicks now = tick_clock_->NowTicks(); | |
326 | |
327 // Create a list of stale players before making any possibly reentrant calls. | |
328 std::vector<int> stale_players; | |
329 for (const auto& it : idle_player_map_) { | |
330 if (now - it.second >= timeout) | |
331 stale_players.push_back(it.first); | |
199 } | 332 } |
200 | 333 |
201 // When we reach the maximum number of idle players, aggressively suspend idle | 334 // Notify stale players. |
202 // delegates to try and remain under the limit. Values chosen after testing on | 335 for (int player_id : stale_players) { |
203 // a Galaxy Nexus device for http://crbug.com/612909. | 336 Observer* player = id_map_.Lookup(player_id); |
204 if (idle_delegate_map_.size() > (is_low_end_device_ ? 2u : 8u)) | 337 if (player && player->OnIdleTimeout()) { |
205 CleanupIdleDelegates(base::TimeDelta()); | 338 // If we fail to erase, it means that the player called SetIdle(false) |
206 } | 339 // inside of OnIdleTimeout(), which is banned by the API documentation. |
207 | 340 // We actually handle that fine. If they *also* then called SetIdle(true), |
208 void RendererWebMediaPlayerDelegate::RemoveIdleDelegate(int delegate_id) { | 341 // we won't notice the idle time change and will still mark them as stale |
209 // To avoid invalidating the iterator, just mark the delegate for deletion | 342 // here. |
210 // using a sentinel value of an empty TimeTicks. | 343 // TODO(sandersd): Consider making this implementation the official API |
211 if (idle_cleanup_running_) { | 344 // interpretation (that is, allow calling SetIdle() from OnIdleTimeout(), |
212 idle_delegate_map_[delegate_id] = base::TimeTicks(); | 345 // with this same caveat). |
watk
2016/12/10 00:01:30
Why did you decide to not DCHECK that SetIdle isn'
sandersd (OOO until July 31)
2017/01/05 23:12:20
Basically yes. I came up with something I think is
| |
213 return; | 346 if (idle_player_map_.erase(player_id)) |
214 } | 347 stale_players_.insert(player_id); |
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 } | 348 } |
244 } | 349 } |
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 } | 350 } |
280 | 351 |
281 void RendererWebMediaPlayerDelegate::OnDestruct() { | 352 void RendererWebMediaPlayerDelegate::OnDestruct() { |
282 delete this; | 353 delete this; |
283 } | 354 } |
284 | 355 |
285 } // namespace media | 356 } // namespace media |
OLD | NEW |