| 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 |