| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/pepper/plugin_instance_throttler_impl.h" | 5 #include "content/renderer/pepper/plugin_instance_throttler_impl.h" |
| 6 | 6 |
| 7 #include "base/metrics/histogram.h" | 7 #include "base/metrics/histogram.h" |
| 8 #include "base/time/time.h" | 8 #include "base/time/time.h" |
| 9 #include "content/public/common/content_constants.h" | 9 #include "content/public/common/content_constants.h" |
| 10 #include "content/public/renderer/render_thread.h" | 10 #include "content/public/renderer/render_thread.h" |
| 11 #include "content/renderer/render_frame_impl.h" | 11 #include "content/renderer/render_frame_impl.h" |
| 12 #include "third_party/WebKit/public/platform/WebRect.h" | 12 #include "third_party/WebKit/public/platform/WebRect.h" |
| 13 #include "third_party/WebKit/public/web/WebInputEvent.h" | 13 #include "third_party/WebKit/public/web/WebInputEvent.h" |
| 14 #include "third_party/WebKit/public/web/WebPluginParams.h" | 14 #include "third_party/WebKit/public/web/WebPluginParams.h" |
| 15 #include "ui/gfx/color_utils.h" | 15 #include "ui/gfx/color_utils.h" |
| 16 #include "url/gurl.h" | 16 #include "url/gurl.h" |
| 17 | 17 |
| 18 namespace content { | 18 namespace content { |
| 19 | 19 |
| 20 namespace { | 20 namespace { |
| 21 | 21 |
| 22 // Threshold for 'boring' score to accept a frame as good enough to be a | 22 // Threshold for 'boring' score to accept a frame as good enough to be a |
| 23 // representative keyframe. Units are the ratio of all pixels that are within | 23 // representative keyframe. Units are the ratio of all pixels that are within |
| 24 // the most common luma bin. The same threshold is used for history thumbnails. | 24 // the most common luma bin. The same threshold is used for history thumbnails. |
| 25 const double kAcceptableFrameMaximumBoringness = 0.94; | 25 const double kAcceptableFrameMaximumBoringness = 0.94; |
| 26 | 26 |
| 27 const int kMinimumConsecutiveInterestingFrames = 4; | 27 const int kMinimumConsecutiveInterestingFrames = 4; |
| 28 | 28 |
| 29 // When plugin audio is throttled, the plugin will sometimes stop generating |
| 30 // video frames. We use this timeout to prevent waiting forever for a good |
| 31 // poster image. Chosen arbitrarily. |
| 32 const int kAudioThrottledFrameTimeoutMilliseconds = 500; |
| 33 |
| 29 } // namespace | 34 } // namespace |
| 30 | 35 |
| 31 // static | 36 // static |
| 32 const int PluginInstanceThrottlerImpl::kMaximumFramesToExamine = 150; | 37 const int PluginInstanceThrottlerImpl::kMaximumFramesToExamine = 150; |
| 33 | 38 |
| 34 // static | 39 // static |
| 35 scoped_ptr<PluginInstanceThrottler> PluginInstanceThrottler::Create( | 40 scoped_ptr<PluginInstanceThrottler> PluginInstanceThrottler::Create( |
| 36 bool power_saver_enabled) { | 41 bool power_saver_enabled) { |
| 37 return make_scoped_ptr(new PluginInstanceThrottlerImpl(power_saver_enabled)); | 42 return make_scoped_ptr(new PluginInstanceThrottlerImpl(power_saver_enabled)); |
| 38 } | 43 } |
| 39 | 44 |
| 40 // static | 45 // static |
| 41 void PluginInstanceThrottler::RecordUnthrottleMethodMetric( | 46 void PluginInstanceThrottler::RecordUnthrottleMethodMetric( |
| 42 PluginInstanceThrottlerImpl::PowerSaverUnthrottleMethod method) { | 47 PluginInstanceThrottlerImpl::PowerSaverUnthrottleMethod method) { |
| 43 UMA_HISTOGRAM_ENUMERATION( | 48 UMA_HISTOGRAM_ENUMERATION( |
| 44 "Plugin.PowerSaver.Unthrottle", method, | 49 "Plugin.PowerSaver.Unthrottle", method, |
| 45 PluginInstanceThrottler::UNTHROTTLE_METHOD_NUM_ITEMS); | 50 PluginInstanceThrottler::UNTHROTTLE_METHOD_NUM_ITEMS); |
| 46 } | 51 } |
| 47 | 52 |
| 48 PluginInstanceThrottlerImpl::PluginInstanceThrottlerImpl( | 53 PluginInstanceThrottlerImpl::PluginInstanceThrottlerImpl( |
| 49 bool power_saver_enabled) | 54 bool power_saver_enabled) |
| 50 : state_(power_saver_enabled ? THROTTLER_STATE_AWAITING_KEYFRAME | 55 : state_(power_saver_enabled ? THROTTLER_STATE_AWAITING_KEYFRAME |
| 51 : THROTTLER_STATE_POWER_SAVER_DISABLED), | 56 : THROTTLER_STATE_POWER_SAVER_DISABLED), |
| 52 is_hidden_for_placeholder_(false), | 57 is_hidden_for_placeholder_(false), |
| 53 web_plugin_(nullptr), | 58 web_plugin_(nullptr), |
| 54 consecutive_interesting_frames_(0), | 59 consecutive_interesting_frames_(0), |
| 55 frames_examined_(0), | 60 frames_examined_(0), |
| 61 audio_throttled_(false), |
| 62 audio_throttled_frame_timeout_( |
| 63 FROM_HERE, |
| 64 base::TimeDelta::FromMilliseconds( |
| 65 kAudioThrottledFrameTimeoutMilliseconds), |
| 66 this, |
| 67 &PluginInstanceThrottlerImpl::EngageThrottle), |
| 56 weak_factory_(this) { | 68 weak_factory_(this) { |
| 57 } | 69 } |
| 58 | 70 |
| 59 PluginInstanceThrottlerImpl::~PluginInstanceThrottlerImpl() { | 71 PluginInstanceThrottlerImpl::~PluginInstanceThrottlerImpl() { |
| 60 FOR_EACH_OBSERVER(Observer, observer_list_, OnThrottlerDestroyed()); | 72 FOR_EACH_OBSERVER(Observer, observer_list_, OnThrottlerDestroyed()); |
| 61 if (state_ != THROTTLER_STATE_MARKED_ESSENTIAL) | 73 if (state_ != THROTTLER_STATE_MARKED_ESSENTIAL) |
| 62 RecordUnthrottleMethodMetric(UNTHROTTLE_METHOD_NEVER); | 74 RecordUnthrottleMethodMetric(UNTHROTTLE_METHOD_NEVER); |
| 63 } | 75 } |
| 64 | 76 |
| 65 void PluginInstanceThrottlerImpl::AddObserver(Observer* observer) { | 77 void PluginInstanceThrottlerImpl::AddObserver(Observer* observer) { |
| (...skipping 28 matching lines...) Expand all Loading... |
| 94 void PluginInstanceThrottlerImpl::SetHiddenForPlaceholder(bool hidden) { | 106 void PluginInstanceThrottlerImpl::SetHiddenForPlaceholder(bool hidden) { |
| 95 is_hidden_for_placeholder_ = hidden; | 107 is_hidden_for_placeholder_ = hidden; |
| 96 FOR_EACH_OBSERVER(Observer, observer_list_, OnHiddenForPlaceholder(hidden)); | 108 FOR_EACH_OBSERVER(Observer, observer_list_, OnHiddenForPlaceholder(hidden)); |
| 97 } | 109 } |
| 98 | 110 |
| 99 blink::WebPlugin* PluginInstanceThrottlerImpl::GetWebPlugin() const { | 111 blink::WebPlugin* PluginInstanceThrottlerImpl::GetWebPlugin() const { |
| 100 DCHECK(web_plugin_); | 112 DCHECK(web_plugin_); |
| 101 return web_plugin_; | 113 return web_plugin_; |
| 102 } | 114 } |
| 103 | 115 |
| 116 void PluginInstanceThrottlerImpl::NotifyAudioThrottled() { |
| 117 audio_throttled_ = true; |
| 118 audio_throttled_frame_timeout_.Reset(); |
| 119 } |
| 120 |
| 104 void PluginInstanceThrottlerImpl::SetWebPlugin(blink::WebPlugin* web_plugin) { | 121 void PluginInstanceThrottlerImpl::SetWebPlugin(blink::WebPlugin* web_plugin) { |
| 105 DCHECK(!web_plugin_); | 122 DCHECK(!web_plugin_); |
| 106 web_plugin_ = web_plugin; | 123 web_plugin_ = web_plugin; |
| 107 } | 124 } |
| 108 | 125 |
| 109 void PluginInstanceThrottlerImpl::Initialize( | 126 void PluginInstanceThrottlerImpl::Initialize( |
| 110 RenderFrameImpl* frame, | 127 RenderFrameImpl* frame, |
| 111 const GURL& content_origin, | 128 const GURL& content_origin, |
| 112 const std::string& plugin_module_name, | 129 const std::string& plugin_module_name, |
| 113 const blink::WebRect& bounds) { | 130 const blink::WebRect& bounds) { |
| (...skipping 27 matching lines...) Expand all Loading... |
| 141 return; | 158 return; |
| 142 | 159 |
| 143 ++frames_examined_; | 160 ++frames_examined_; |
| 144 | 161 |
| 145 double boring_score = color_utils::CalculateBoringScore(*bitmap); | 162 double boring_score = color_utils::CalculateBoringScore(*bitmap); |
| 146 if (boring_score <= kAcceptableFrameMaximumBoringness) | 163 if (boring_score <= kAcceptableFrameMaximumBoringness) |
| 147 ++consecutive_interesting_frames_; | 164 ++consecutive_interesting_frames_; |
| 148 else | 165 else |
| 149 consecutive_interesting_frames_ = 0; | 166 consecutive_interesting_frames_ = 0; |
| 150 | 167 |
| 168 // Does not make a copy, just takes a reference to the underlying pixel data. |
| 169 last_received_frame_ = *bitmap; |
| 170 |
| 171 if (audio_throttled_) |
| 172 audio_throttled_frame_timeout_.Reset(); |
| 173 |
| 151 if (frames_examined_ >= kMaximumFramesToExamine || | 174 if (frames_examined_ >= kMaximumFramesToExamine || |
| 152 consecutive_interesting_frames_ >= kMinimumConsecutiveInterestingFrames) { | 175 consecutive_interesting_frames_ >= kMinimumConsecutiveInterestingFrames) { |
| 153 FOR_EACH_OBSERVER(Observer, observer_list_, OnKeyframeExtracted(bitmap)); | |
| 154 EngageThrottle(); | 176 EngageThrottle(); |
| 155 } | 177 } |
| 156 } | 178 } |
| 157 | 179 |
| 158 bool PluginInstanceThrottlerImpl::ConsumeInputEvent( | 180 bool PluginInstanceThrottlerImpl::ConsumeInputEvent( |
| 159 const blink::WebInputEvent& event) { | 181 const blink::WebInputEvent& event) { |
| 160 // Always allow right-clicks through so users may verify it's a plugin. | 182 // Always allow right-clicks through so users may verify it's a plugin. |
| 161 // TODO(tommycli): We should instead show a custom context menu (probably | 183 // TODO(tommycli): We should instead show a custom context menu (probably |
| 162 // using PluginPlaceholder) so users aren't confused and try to click the | 184 // using PluginPlaceholder) so users aren't confused and try to click the |
| 163 // Flash-internal 'Play' menu item. This is a stopgap solution. | 185 // Flash-internal 'Play' menu item. This is a stopgap solution. |
| 164 if (event.modifiers & blink::WebInputEvent::Modifiers::RightButtonDown) | 186 if (event.modifiers & blink::WebInputEvent::Modifiers::RightButtonDown) |
| 165 return false; | 187 return false; |
| 166 | 188 |
| 167 if (state_ != THROTTLER_STATE_MARKED_ESSENTIAL && | 189 if (state_ != THROTTLER_STATE_MARKED_ESSENTIAL && |
| 168 event.type == blink::WebInputEvent::MouseUp && | 190 event.type == blink::WebInputEvent::MouseUp && |
| 169 (event.modifiers & blink::WebInputEvent::LeftButtonDown)) { | 191 (event.modifiers & blink::WebInputEvent::LeftButtonDown)) { |
| 170 bool was_throttled = IsThrottled(); | 192 bool was_throttled = IsThrottled(); |
| 171 MarkPluginEssential(UNTHROTTLE_METHOD_BY_CLICK); | 193 MarkPluginEssential(UNTHROTTLE_METHOD_BY_CLICK); |
| 172 return was_throttled; | 194 return was_throttled; |
| 173 } | 195 } |
| 174 | 196 |
| 175 return IsThrottled(); | 197 return IsThrottled(); |
| 176 } | 198 } |
| 177 | 199 |
| 178 void PluginInstanceThrottlerImpl::EngageThrottle() { | 200 void PluginInstanceThrottlerImpl::EngageThrottle() { |
| 179 if (state_ != THROTTLER_STATE_AWAITING_KEYFRAME) | 201 if (state_ != THROTTLER_STATE_AWAITING_KEYFRAME) |
| 180 return; | 202 return; |
| 181 | 203 |
| 204 if (!last_received_frame_.empty()) { |
| 205 FOR_EACH_OBSERVER(Observer, observer_list_, |
| 206 OnKeyframeExtracted(&last_received_frame_)); |
| 207 |
| 208 // Release our reference to the underlying pixel data. |
| 209 last_received_frame_.reset(); |
| 210 } |
| 211 |
| 182 state_ = THROTTLER_STATE_PLUGIN_THROTTLED; | 212 state_ = THROTTLER_STATE_PLUGIN_THROTTLED; |
| 183 FOR_EACH_OBSERVER(Observer, observer_list_, OnThrottleStateChange()); | 213 FOR_EACH_OBSERVER(Observer, observer_list_, OnThrottleStateChange()); |
| 184 } | 214 } |
| 185 | 215 |
| 186 } // namespace content | 216 } // namespace content |
| OLD | NEW |