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" |
11 #include "base/metrics/user_metrics_action.h" | 11 #include "base/metrics/user_metrics_action.h" |
| 12 #include "base/sys_info.h" |
12 #include "content/common/media/media_player_delegate_messages.h" | 13 #include "content/common/media/media_player_delegate_messages.h" |
13 #include "content/public/renderer/render_frame.h" | 14 #include "content/public/renderer/render_frame.h" |
14 #include "content/public/renderer/render_thread.h" | 15 #include "content/public/renderer/render_thread.h" |
15 #include "third_party/WebKit/public/platform/WebMediaPlayer.h" | 16 #include "third_party/WebKit/public/platform/WebMediaPlayer.h" |
16 | 17 |
| 18 #if defined(OS_ANDROID) |
| 19 #include "base/android/build_info.h" |
| 20 #endif |
| 21 |
17 namespace { | 22 namespace { |
18 | 23 |
19 void RecordAction(const base::UserMetricsAction& action) { | 24 void RecordAction(const base::UserMetricsAction& action) { |
20 content::RenderThread::Get()->RecordAction(action); | 25 content::RenderThread::Get()->RecordAction(action); |
21 } | 26 } |
22 | 27 |
23 } // namespace | 28 } // namespace |
24 | 29 |
25 namespace media { | 30 namespace media { |
26 | 31 |
27 RendererWebMediaPlayerDelegate::RendererWebMediaPlayerDelegate( | 32 RendererWebMediaPlayerDelegate::RendererWebMediaPlayerDelegate( |
28 content::RenderFrame* render_frame) | 33 content::RenderFrame* render_frame) |
29 : RenderFrameObserver(render_frame), | 34 : RenderFrameObserver(render_frame), |
| 35 idle_cleanup_timer_(true, true), |
30 default_tick_clock_(new base::DefaultTickClock()), | 36 default_tick_clock_(new base::DefaultTickClock()), |
31 tick_clock_(default_tick_clock_.get()) { | 37 tick_clock_(default_tick_clock_.get()) { |
32 idle_cleanup_interval_ = base::TimeDelta::FromSeconds(5); | 38 idle_cleanup_interval_ = base::TimeDelta::FromSeconds(5); |
33 idle_timeout_ = base::TimeDelta::FromSeconds(15); | 39 idle_timeout_ = base::TimeDelta::FromSeconds(15); |
| 40 |
| 41 // To conserve resources, cleanup idle players more often on low end devices. |
| 42 is_low_end_device_ = base::SysInfo::IsLowEndDevice(); |
| 43 |
| 44 #if defined(OS_ANDROID) |
| 45 // On Android, due to the instability of the OS level media components, we |
| 46 // consider all pre-KitKat devices to be low end. |
| 47 is_low_end_device_ |= |
| 48 base::android::BuildInfo::GetInstance()->sdk_int() <= 18; |
| 49 #endif |
34 } | 50 } |
35 | 51 |
36 RendererWebMediaPlayerDelegate::~RendererWebMediaPlayerDelegate() {} | 52 RendererWebMediaPlayerDelegate::~RendererWebMediaPlayerDelegate() {} |
37 | 53 |
38 int RendererWebMediaPlayerDelegate::AddObserver(Observer* observer) { | 54 int RendererWebMediaPlayerDelegate::AddObserver(Observer* observer) { |
39 const int delegate_id = id_map_.Add(observer); | 55 const int delegate_id = id_map_.Add(observer); |
40 // Start players in the idle state to ensure we capture players which are | 56 // Start players in the idle state to ensure we capture players which are |
41 // consuming resources, but which have never played. | 57 // consuming resources, but which have never played. |
42 AddIdleDelegate(delegate_id); | 58 AddIdleDelegate(delegate_id); |
43 return delegate_id; | 59 return delegate_id; |
(...skipping 12 matching lines...) Expand all Loading... |
56 bool has_audio, | 72 bool has_audio, |
57 bool is_remote, | 73 bool is_remote, |
58 MediaContentType media_content_type) { | 74 MediaContentType media_content_type) { |
59 DCHECK(id_map_.Lookup(delegate_id)); | 75 DCHECK(id_map_.Lookup(delegate_id)); |
60 has_played_media_ = true; | 76 has_played_media_ = true; |
61 if (has_video && !is_remote) | 77 if (has_video && !is_remote) |
62 playing_videos_.insert(delegate_id); | 78 playing_videos_.insert(delegate_id); |
63 else | 79 else |
64 playing_videos_.erase(delegate_id); | 80 playing_videos_.erase(delegate_id); |
65 RemoveIdleDelegate(delegate_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 |
66 Send(new MediaPlayerDelegateHostMsg_OnMediaPlaying( | 87 Send(new MediaPlayerDelegateHostMsg_OnMediaPlaying( |
67 routing_id(), delegate_id, has_video, has_audio, is_remote, | 88 routing_id(), delegate_id, has_video, has_audio, is_remote, |
68 media_content_type)); | 89 media_content_type)); |
69 } | 90 } |
70 | 91 |
71 void RendererWebMediaPlayerDelegate::DidPause(int delegate_id, | 92 void RendererWebMediaPlayerDelegate::DidPause(int delegate_id, |
72 bool reached_end_of_stream) { | 93 bool reached_end_of_stream) { |
73 DCHECK(id_map_.Lookup(delegate_id)); | 94 DCHECK(id_map_.Lookup(delegate_id)); |
74 AddIdleDelegate(delegate_id); | 95 AddIdleDelegate(delegate_id); |
75 if (reached_end_of_stream) | 96 if (reached_end_of_stream) |
76 playing_videos_.erase(delegate_id); | 97 playing_videos_.erase(delegate_id); |
77 Send(new MediaPlayerDelegateHostMsg_OnMediaPaused(routing_id(), delegate_id, | 98 Send(new MediaPlayerDelegateHostMsg_OnMediaPaused(routing_id(), delegate_id, |
78 reached_end_of_stream)); | 99 reached_end_of_stream)); |
79 } | 100 } |
80 | 101 |
81 void RendererWebMediaPlayerDelegate::PlayerGone(int delegate_id) { | 102 void RendererWebMediaPlayerDelegate::PlayerGone(int delegate_id) { |
82 DCHECK(id_map_.Lookup(delegate_id)); | 103 DCHECK(id_map_.Lookup(delegate_id)); |
83 RemoveIdleDelegate(delegate_id); | |
84 playing_videos_.erase(delegate_id); | 104 playing_videos_.erase(delegate_id); |
85 Send(new MediaPlayerDelegateHostMsg_OnMediaDestroyed(routing_id(), | 105 Send(new MediaPlayerDelegateHostMsg_OnMediaDestroyed(routing_id(), |
86 delegate_id)); | 106 delegate_id)); |
87 } | 107 } |
88 | 108 |
89 bool RendererWebMediaPlayerDelegate::IsHidden() { | 109 bool RendererWebMediaPlayerDelegate::IsHidden() { |
90 return render_frame()->IsHidden(); | 110 return render_frame()->IsHidden(); |
91 } | 111 } |
92 | 112 |
93 bool RendererWebMediaPlayerDelegate::IsPlayingBackgroundVideo() { | 113 bool RendererWebMediaPlayerDelegate::IsPlayingBackgroundVideo() { |
(...skipping 25 matching lines...) Expand all Loading... |
119 OnMediaDelegateSuspendAllMediaPlayers) | 139 OnMediaDelegateSuspendAllMediaPlayers) |
120 IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_UpdateVolumeMultiplier, | 140 IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_UpdateVolumeMultiplier, |
121 OnMediaDelegateVolumeMultiplierUpdate) | 141 OnMediaDelegateVolumeMultiplierUpdate) |
122 IPC_MESSAGE_UNHANDLED(handled = false) | 142 IPC_MESSAGE_UNHANDLED(handled = false) |
123 IPC_END_MESSAGE_MAP() | 143 IPC_END_MESSAGE_MAP() |
124 return handled; | 144 return handled; |
125 } | 145 } |
126 | 146 |
127 void RendererWebMediaPlayerDelegate::SetIdleCleanupParamsForTesting( | 147 void RendererWebMediaPlayerDelegate::SetIdleCleanupParamsForTesting( |
128 base::TimeDelta idle_timeout, | 148 base::TimeDelta idle_timeout, |
129 base::TickClock* tick_clock) { | 149 base::TickClock* tick_clock, |
| 150 bool is_low_end_device) { |
130 idle_cleanup_interval_ = base::TimeDelta(); | 151 idle_cleanup_interval_ = base::TimeDelta(); |
131 idle_timeout_ = idle_timeout; | 152 idle_timeout_ = idle_timeout; |
132 tick_clock_ = tick_clock; | 153 tick_clock_ = tick_clock; |
| 154 is_low_end_device_ = is_low_end_device; |
133 } | 155 } |
134 | 156 |
135 void RendererWebMediaPlayerDelegate::OnMediaDelegatePause(int delegate_id) { | 157 void RendererWebMediaPlayerDelegate::OnMediaDelegatePause(int delegate_id) { |
136 Observer* observer = id_map_.Lookup(delegate_id); | 158 Observer* observer = id_map_.Lookup(delegate_id); |
137 if (observer) { | 159 if (observer) { |
138 if (playing_videos_.find(delegate_id) != playing_videos_.end()) | 160 if (playing_videos_.find(delegate_id) != playing_videos_.end()) |
139 SetIsPlayingBackgroundVideo(false); | 161 SetIsPlayingBackgroundVideo(false); |
140 observer->OnPause(); | 162 observer->OnPause(); |
141 } | 163 } |
142 | 164 |
(...skipping 21 matching lines...) Expand all Loading... |
164 double multiplier) { | 186 double multiplier) { |
165 Observer* observer = id_map_.Lookup(delegate_id); | 187 Observer* observer = id_map_.Lookup(delegate_id); |
166 if (observer) | 188 if (observer) |
167 observer->OnVolumeMultiplierUpdate(multiplier); | 189 observer->OnVolumeMultiplierUpdate(multiplier); |
168 } | 190 } |
169 | 191 |
170 void RendererWebMediaPlayerDelegate::AddIdleDelegate(int delegate_id) { | 192 void RendererWebMediaPlayerDelegate::AddIdleDelegate(int delegate_id) { |
171 idle_delegate_map_[delegate_id] = tick_clock_->NowTicks(); | 193 idle_delegate_map_[delegate_id] = tick_clock_->NowTicks(); |
172 if (!idle_cleanup_timer_.IsRunning()) { | 194 if (!idle_cleanup_timer_.IsRunning()) { |
173 idle_cleanup_timer_.Start( | 195 idle_cleanup_timer_.Start( |
174 FROM_HERE, idle_cleanup_interval_, this, | 196 FROM_HERE, idle_cleanup_interval_, |
175 &RendererWebMediaPlayerDelegate::CleanupIdleDelegates); | 197 base::Bind(&RendererWebMediaPlayerDelegate::CleanupIdleDelegates, |
| 198 base::Unretained(this), idle_timeout_)); |
176 } | 199 } |
| 200 |
| 201 // When we reach the maximum number of idle players, aggressively suspend idle |
| 202 // delegates to try and remain under the limit. Values chosen after testing on |
| 203 // a Galaxy Nexus device for http://crbug.com/612909. |
| 204 if (idle_delegate_map_.size() > (is_low_end_device_ ? 2u : 8u)) |
| 205 CleanupIdleDelegates(base::TimeDelta()); |
177 } | 206 } |
178 | 207 |
179 void RendererWebMediaPlayerDelegate::RemoveIdleDelegate(int delegate_id) { | 208 void RendererWebMediaPlayerDelegate::RemoveIdleDelegate(int delegate_id) { |
180 // To avoid invalidating the iterator, just mark the delegate for deletion | 209 // To avoid invalidating the iterator, just mark the delegate for deletion |
181 // using a sentinel value of an empty TimeTicks. | 210 // using a sentinel value of an empty TimeTicks. |
182 if (idle_cleanup_running_) { | 211 if (idle_cleanup_running_) { |
183 idle_delegate_map_[delegate_id] = base::TimeTicks(); | 212 idle_delegate_map_[delegate_id] = base::TimeTicks(); |
184 return; | 213 return; |
185 } | 214 } |
186 | 215 |
187 idle_delegate_map_.erase(delegate_id); | 216 idle_delegate_map_.erase(delegate_id); |
188 if (idle_delegate_map_.empty()) | 217 if (idle_delegate_map_.empty()) |
189 idle_cleanup_timer_.Stop(); | 218 idle_cleanup_timer_.Stop(); |
190 } | 219 } |
191 | 220 |
192 void RendererWebMediaPlayerDelegate::CleanupIdleDelegates() { | 221 void RendererWebMediaPlayerDelegate::CleanupIdleDelegates( |
| 222 base::TimeDelta timeout) { |
193 // Iterate over the delegates and suspend the idle ones. Note: The call to | 223 // Iterate over the delegates and suspend the idle ones. Note: The call to |
194 // OnHidden() can trigger calls into RemoveIdleDelegate(), so for iterator | 224 // OnHidden() can trigger calls into RemoveIdleDelegate(), so for iterator |
195 // validity we set |idle_cleanup_running_| to true and defer deletions. | 225 // validity we set |idle_cleanup_running_| to true and defer deletions. |
196 base::AutoReset<bool> scoper(&idle_cleanup_running_, true); | 226 base::AutoReset<bool> scoper(&idle_cleanup_running_, true); |
197 const base::TimeTicks now = tick_clock_->NowTicks(); | 227 const base::TimeTicks now = tick_clock_->NowTicks(); |
198 for (auto& idle_delegate_entry : idle_delegate_map_) { | 228 for (auto& idle_delegate_entry : idle_delegate_map_) { |
199 if (now - idle_delegate_entry.second > idle_timeout_) { | 229 if (now - idle_delegate_entry.second > timeout) { |
200 id_map_.Lookup(idle_delegate_entry.first)->OnSuspendRequested(false); | 230 id_map_.Lookup(idle_delegate_entry.first)->OnSuspendRequested(false); |
201 | 231 |
202 // Whether or not the player accepted the suspension, mark it for removal | 232 // Whether or not the player accepted the suspension, mark it for removal |
203 // from future polls to avoid running the timer forever. | 233 // from future polls to avoid running the timer forever. |
204 idle_delegate_entry.second = base::TimeTicks(); | 234 idle_delegate_entry.second = base::TimeTicks(); |
205 } | 235 } |
206 } | 236 } |
207 | 237 |
208 // Take care of any removals that happened during the above iteration. | 238 // Take care of any removals that happened during the above iteration. |
209 for (auto it = idle_delegate_map_.begin(); it != idle_delegate_map_.end();) { | 239 for (auto it = idle_delegate_map_.begin(); it != idle_delegate_map_.end();) { |
(...skipping 28 matching lines...) Expand all Loading... |
238 #endif // OS_ANDROID | 268 #endif // OS_ANDROID |
239 | 269 |
240 is_playing_background_video_ = is_playing; | 270 is_playing_background_video_ = is_playing; |
241 } | 271 } |
242 | 272 |
243 void RendererWebMediaPlayerDelegate::OnDestruct() { | 273 void RendererWebMediaPlayerDelegate::OnDestruct() { |
244 delete this; | 274 delete this; |
245 } | 275 } |
246 | 276 |
247 } // namespace media | 277 } // namespace media |
OLD | NEW |