Commit 43d8cc4c authored by tommycli's avatar tommycli Committed by Commit bot

Plugin Power Saver: Mute throttled plugins.

BUG=452215, 403800

Review URL: https://codereview.chromium.org/879403002

Cr-Commit-Position: refs/heads/master@{#321593}
parent 283ecc53
...@@ -84,6 +84,10 @@ class CONTENT_EXPORT PluginInstanceThrottler { ...@@ -84,6 +84,10 @@ class CONTENT_EXPORT PluginInstanceThrottler {
virtual blink::WebPlugin* GetWebPlugin() const = 0; virtual blink::WebPlugin* GetWebPlugin() const = 0;
// Throttler needs to know when the plugin audio is throttled, as this may
// prevent the plugin from generating new frames.
virtual void NotifyAudioThrottled() = 0;
protected: protected:
PluginInstanceThrottler() {} PluginInstanceThrottler() {}
......
...@@ -26,6 +26,11 @@ const double kAcceptableFrameMaximumBoringness = 0.94; ...@@ -26,6 +26,11 @@ const double kAcceptableFrameMaximumBoringness = 0.94;
const int kMinimumConsecutiveInterestingFrames = 4; const int kMinimumConsecutiveInterestingFrames = 4;
// When plugin audio is throttled, the plugin will sometimes stop generating
// video frames. We use this timeout to prevent waiting forever for a good
// poster image. Chosen arbitrarily.
const int kAudioThrottledFrameTimeoutMilliseconds = 500;
} // namespace } // namespace
// static // static
...@@ -53,6 +58,13 @@ PluginInstanceThrottlerImpl::PluginInstanceThrottlerImpl( ...@@ -53,6 +58,13 @@ PluginInstanceThrottlerImpl::PluginInstanceThrottlerImpl(
web_plugin_(nullptr), web_plugin_(nullptr),
consecutive_interesting_frames_(0), consecutive_interesting_frames_(0),
frames_examined_(0), frames_examined_(0),
audio_throttled_(false),
audio_throttled_frame_timeout_(
FROM_HERE,
base::TimeDelta::FromMilliseconds(
kAudioThrottledFrameTimeoutMilliseconds),
this,
&PluginInstanceThrottlerImpl::EngageThrottle),
weak_factory_(this) { weak_factory_(this) {
} }
...@@ -101,6 +113,11 @@ blink::WebPlugin* PluginInstanceThrottlerImpl::GetWebPlugin() const { ...@@ -101,6 +113,11 @@ blink::WebPlugin* PluginInstanceThrottlerImpl::GetWebPlugin() const {
return web_plugin_; return web_plugin_;
} }
void PluginInstanceThrottlerImpl::NotifyAudioThrottled() {
audio_throttled_ = true;
audio_throttled_frame_timeout_.Reset();
}
void PluginInstanceThrottlerImpl::SetWebPlugin(blink::WebPlugin* web_plugin) { void PluginInstanceThrottlerImpl::SetWebPlugin(blink::WebPlugin* web_plugin) {
DCHECK(!web_plugin_); DCHECK(!web_plugin_);
web_plugin_ = web_plugin; web_plugin_ = web_plugin;
...@@ -148,9 +165,14 @@ void PluginInstanceThrottlerImpl::OnImageFlush(const SkBitmap* bitmap) { ...@@ -148,9 +165,14 @@ void PluginInstanceThrottlerImpl::OnImageFlush(const SkBitmap* bitmap) {
else else
consecutive_interesting_frames_ = 0; consecutive_interesting_frames_ = 0;
// Does not make a copy, just takes a reference to the underlying pixel data.
last_received_frame_ = *bitmap;
if (audio_throttled_)
audio_throttled_frame_timeout_.Reset();
if (frames_examined_ >= kMaximumFramesToExamine || if (frames_examined_ >= kMaximumFramesToExamine ||
consecutive_interesting_frames_ >= kMinimumConsecutiveInterestingFrames) { consecutive_interesting_frames_ >= kMinimumConsecutiveInterestingFrames) {
FOR_EACH_OBSERVER(Observer, observer_list_, OnKeyframeExtracted(bitmap));
EngageThrottle(); EngageThrottle();
} }
} }
...@@ -179,6 +201,14 @@ void PluginInstanceThrottlerImpl::EngageThrottle() { ...@@ -179,6 +201,14 @@ void PluginInstanceThrottlerImpl::EngageThrottle() {
if (state_ != THROTTLER_STATE_AWAITING_KEYFRAME) if (state_ != THROTTLER_STATE_AWAITING_KEYFRAME)
return; return;
if (!last_received_frame_.empty()) {
FOR_EACH_OBSERVER(Observer, observer_list_,
OnKeyframeExtracted(&last_received_frame_));
// Release our reference to the underlying pixel data.
last_received_frame_.reset();
}
state_ = THROTTLER_STATE_PLUGIN_THROTTLED; state_ = THROTTLER_STATE_PLUGIN_THROTTLED;
FOR_EACH_OBSERVER(Observer, observer_list_, OnThrottleStateChange()); FOR_EACH_OBSERVER(Observer, observer_list_, OnThrottleStateChange());
} }
......
...@@ -8,9 +8,11 @@ ...@@ -8,9 +8,11 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/observer_list.h" #include "base/observer_list.h"
#include "base/timer/timer.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "content/public/renderer/plugin_instance_throttler.h" #include "content/public/renderer/plugin_instance_throttler.h"
#include "ppapi/shared_impl/ppb_view_shared.h" #include "ppapi/shared_impl/ppb_view_shared.h"
#include "third_party/skia/include/core/SkBitmap.h"
namespace blink { namespace blink {
class WebInputEvent; class WebInputEvent;
...@@ -36,6 +38,7 @@ class CONTENT_EXPORT PluginInstanceThrottlerImpl ...@@ -36,6 +38,7 @@ class CONTENT_EXPORT PluginInstanceThrottlerImpl
void MarkPluginEssential(PowerSaverUnthrottleMethod method) override; void MarkPluginEssential(PowerSaverUnthrottleMethod method) override;
void SetHiddenForPlaceholder(bool hidden) override; void SetHiddenForPlaceholder(bool hidden) override;
blink::WebPlugin* GetWebPlugin() const override; blink::WebPlugin* GetWebPlugin() const override;
void NotifyAudioThrottled() override;
void SetWebPlugin(blink::WebPlugin* web_plugin); void SetWebPlugin(blink::WebPlugin* web_plugin);
...@@ -80,6 +83,7 @@ class CONTENT_EXPORT PluginInstanceThrottlerImpl ...@@ -80,6 +83,7 @@ class CONTENT_EXPORT PluginInstanceThrottlerImpl
// simply suspend the plugin where it's at. Chosen arbitrarily. // simply suspend the plugin where it's at. Chosen arbitrarily.
static const int kMaximumFramesToExamine; static const int kMaximumFramesToExamine;
void AudioThrottledFrameTimeout();
void EngageThrottle(); void EngageThrottle();
ThrottlerState state_; ThrottlerState state_;
...@@ -88,12 +92,21 @@ class CONTENT_EXPORT PluginInstanceThrottlerImpl ...@@ -88,12 +92,21 @@ class CONTENT_EXPORT PluginInstanceThrottlerImpl
blink::WebPlugin* web_plugin_; blink::WebPlugin* web_plugin_;
// Holds a reference to the last received frame. This doesn't actually copy
// the pixel data, but rather increments the reference count to the pixels.
SkBitmap last_received_frame_;
// Number of consecutive interesting frames we've encountered. // Number of consecutive interesting frames we've encountered.
int consecutive_interesting_frames_; int consecutive_interesting_frames_;
// Number of frames we've examined to find a keyframe. // Number of frames we've examined to find a keyframe.
int frames_examined_; int frames_examined_;
// Video plugins with throttled audio often stop generating frames.
// This timer is so we don't wait forever for candidate poster frames.
bool audio_throttled_;
base::DelayTimer<PluginInstanceThrottlerImpl> audio_throttled_frame_timeout_;
ObserverList<Observer> observer_list_; ObserverList<Observer> observer_list_;
base::WeakPtrFactory<PluginInstanceThrottlerImpl> weak_factory_; base::WeakPtrFactory<PluginInstanceThrottlerImpl> weak_factory_;
......
...@@ -30,9 +30,24 @@ namespace content { ...@@ -30,9 +30,24 @@ namespace content {
// PPB_Audio_Impl -------------------------------------------------------------- // PPB_Audio_Impl --------------------------------------------------------------
PPB_Audio_Impl::PPB_Audio_Impl(PP_Instance instance) PPB_Audio_Impl::PPB_Audio_Impl(PP_Instance instance)
: Resource(ppapi::OBJECT_IS_IMPL, instance), audio_(NULL) {} : Resource(ppapi::OBJECT_IS_IMPL, instance),
audio_(NULL),
playback_throttled_(false) {
PepperPluginInstanceImpl* plugin_instance =
static_cast<PepperPluginInstanceImpl*>(
PepperPluginInstance::Get(pp_instance()));
if (plugin_instance && plugin_instance->throttler()) {
plugin_instance->throttler()->AddObserver(this);
}
}
PPB_Audio_Impl::~PPB_Audio_Impl() { PPB_Audio_Impl::~PPB_Audio_Impl() {
PepperPluginInstanceImpl* instance = static_cast<PepperPluginInstanceImpl*>(
PepperPluginInstance::Get(pp_instance()));
if (instance && instance->throttler()) {
instance->throttler()->RemoveObserver(this);
}
// Calling ShutDown() makes sure StreamCreated cannot be called anymore and // Calling ShutDown() makes sure StreamCreated cannot be called anymore and
// releases the audio data associated with the pointer. Note however, that // releases the audio data associated with the pointer. Note however, that
// until ShutDown returns, StreamCreated may still be called. This will be // until ShutDown returns, StreamCreated may still be called. This will be
...@@ -57,6 +72,17 @@ PP_Bool PPB_Audio_Impl::StartPlayback() { ...@@ -57,6 +72,17 @@ PP_Bool PPB_Audio_Impl::StartPlayback() {
return PP_FALSE; return PP_FALSE;
if (playing()) if (playing())
return PP_TRUE; return PP_TRUE;
// If plugin is in power saver mode, defer audio IPC communication.
PepperPluginInstanceImpl* instance = static_cast<PepperPluginInstanceImpl*>(
PepperPluginInstance::Get(pp_instance()));
if (instance && instance->throttler() &&
instance->throttler()->power_saver_enabled()) {
instance->throttler()->NotifyAudioThrottled();
playback_throttled_ = true;
return PP_TRUE;
}
SetStartPlaybackState(); SetStartPlaybackState();
return PP_FromBool(audio_->StartPlayback()); return PP_FromBool(audio_->StartPlayback());
} }
...@@ -64,11 +90,19 @@ PP_Bool PPB_Audio_Impl::StartPlayback() { ...@@ -64,11 +90,19 @@ PP_Bool PPB_Audio_Impl::StartPlayback() {
PP_Bool PPB_Audio_Impl::StopPlayback() { PP_Bool PPB_Audio_Impl::StopPlayback() {
if (!audio_) if (!audio_)
return PP_FALSE; return PP_FALSE;
if (playback_throttled_) {
// If a start playback request is still deferred, we must fulfill it first
// to shut down the audio thread correctly.
StartDeferredPlayback();
}
if (!playing()) if (!playing())
return PP_TRUE; return PP_TRUE;
if (!audio_->StopPlayback()) if (!audio_->StopPlayback())
return PP_FALSE; return PP_FALSE;
SetStopPlaybackState(); SetStopPlaybackState();
return PP_TRUE; return PP_TRUE;
} }
...@@ -85,13 +119,6 @@ int32_t PPB_Audio_Impl::Open(PP_Resource config, ...@@ -85,13 +119,6 @@ int32_t PPB_Audio_Impl::Open(PP_Resource config,
if (!instance) if (!instance)
return PP_ERROR_FAILED; return PP_ERROR_FAILED;
// Prevent any throttling since we are playing audio. This stopgap prevents
// video from appearing 'frozen' while the audio track plays.
if (instance->throttler() && instance->throttler()->power_saver_enabled()) {
instance->throttler()->MarkPluginEssential(
PluginInstanceThrottler::UNTHROTTLE_METHOD_BY_AUDIO);
}
// When the stream is created, we'll get called back on StreamCreated(). // When the stream is created, we'll get called back on StreamCreated().
DCHECK(!audio_); DCHECK(!audio_);
audio_ = PepperPlatformAudioOutput::Create( audio_ = PepperPlatformAudioOutput::Create(
...@@ -132,4 +159,22 @@ void PPB_Audio_Impl::OnSetStreamInfo( ...@@ -132,4 +159,22 @@ void PPB_Audio_Impl::OnSetStreamInfo(
enter.object()->GetSampleFrameCount()); enter.object()->GetSampleFrameCount());
} }
void PPB_Audio_Impl::OnThrottleStateChange() {
PepperPluginInstanceImpl* instance = static_cast<PepperPluginInstanceImpl*>(
PepperPluginInstance::Get(pp_instance()));
if (playback_throttled_ && instance && instance->throttler() &&
!instance->throttler()->power_saver_enabled()) {
// If we have become unthrottled, and we have a pending playback, start it.
StartDeferredPlayback();
}
}
void PPB_Audio_Impl::StartDeferredPlayback() {
DCHECK(playback_throttled_);
playback_throttled_ = false;
SetStartPlaybackState();
audio_->StartPlayback();
}
} // namespace content } // namespace content
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/shared_memory.h" #include "base/memory/shared_memory.h"
#include "base/sync_socket.h" #include "base/sync_socket.h"
#include "content/public/renderer/plugin_instance_throttler.h"
#include "content/renderer/pepper/audio_helper.h" #include "content/renderer/pepper/audio_helper.h"
#include "ppapi/c/pp_completion_callback.h" #include "ppapi/c/pp_completion_callback.h"
#include "ppapi/c/ppb_audio.h" #include "ppapi/c/ppb_audio.h"
...@@ -28,7 +29,8 @@ class PepperPlatformAudioOutput; ...@@ -28,7 +29,8 @@ class PepperPlatformAudioOutput;
// to look more like typical HostResource implementations. // to look more like typical HostResource implementations.
class PPB_Audio_Impl : public ppapi::Resource, class PPB_Audio_Impl : public ppapi::Resource,
public ppapi::PPB_Audio_Shared, public ppapi::PPB_Audio_Shared,
public AudioHelper { public AudioHelper,
public PluginInstanceThrottler::Observer {
public: public:
explicit PPB_Audio_Impl(PP_Instance instance); explicit PPB_Audio_Impl(PP_Instance instance);
...@@ -52,6 +54,12 @@ class PPB_Audio_Impl : public ppapi::Resource, ...@@ -52,6 +54,12 @@ class PPB_Audio_Impl : public ppapi::Resource,
size_t shared_memory_size_, size_t shared_memory_size_,
base::SyncSocket::Handle socket) override; base::SyncSocket::Handle socket) override;
// PluginInstanceThrottler::Observer implementation.
void OnThrottleStateChange() override;
// Starts the deferred playback and unsubscribes from the throttler.
void StartDeferredPlayback();
// AudioConfig used for creating this Audio object. We own a ref. // AudioConfig used for creating this Audio object. We own a ref.
ppapi::ScopedPPResource config_; ppapi::ScopedPPResource config_;
...@@ -59,6 +67,9 @@ class PPB_Audio_Impl : public ppapi::Resource, ...@@ -59,6 +67,9 @@ class PPB_Audio_Impl : public ppapi::Resource,
// own this pointer but are responsible for calling Shutdown on it. // own this pointer but are responsible for calling Shutdown on it.
PepperPlatformAudioOutput* audio_; PepperPlatformAudioOutput* audio_;
// Stream is playing, but throttled due to Plugin Power Saver.
bool playback_throttled_;
DISALLOW_COPY_AND_ASSIGN(PPB_Audio_Impl); DISALLOW_COPY_AND_ASSIGN(PPB_Audio_Impl);
}; };
......
...@@ -51,6 +51,7 @@ void AudioCallbackCombined::Run(void* sample_buffer, ...@@ -51,6 +51,7 @@ void AudioCallbackCombined::Run(void* sample_buffer,
PPB_Audio_Shared::PPB_Audio_Shared() PPB_Audio_Shared::PPB_Audio_Shared()
: playing_(false), : playing_(false),
shared_memory_size_(0), shared_memory_size_(0),
nacl_thread_id_(0),
nacl_thread_active_(false), nacl_thread_active_(false),
user_data_(NULL), user_data_(NULL),
client_buffer_size_bytes_(0), client_buffer_size_bytes_(0),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment