diff options
author | timav <timav@chromium.org> | 2015-03-02 19:07:41 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-03-03 03:08:19 +0000 |
commit | 6ad356d3e845ed9b67c5fb799b2ee8290f317268 (patch) | |
tree | 612d5f45936ca4cfa4d8204864a22ff2a8d781ad /content | |
parent | 1f67ef5a86e2c1bb43818a3948c3b50adb2137ae (diff) | |
download | chromium_src-6ad356d3e845ed9b67c5fb799b2ee8290f317268.zip chromium_src-6ad356d3e845ed9b67c5fb799b2ee8290f317268.tar.gz chromium_src-6ad356d3e845ed9b67c5fb799b2ee8290f317268.tar.bz2 |
Propagate audible state from player to the containing tab.
This is part 1 (chromium),
part 2 (clank) is https://chrome-internal-review.googlesource.com/#/c/195455/
This CL is only about propagation/plumbing.
Neither the reporting of audible state my players nor the consumption
(UI notification) are implemented.
This patch creates a new object AudioMonitorAndroid and
attaches it to MediaWebContentsObserver. The AudioMonitorAndroid
maintains the audible state for the tab. It receives notifications
from MediaPlayerAndroid objects and sends the resulting
notification to the WebContents.
BUG=414810
Review URL: https://codereview.chromium.org/896673003
Cr-Commit-Position: refs/heads/master@{#318823}
Diffstat (limited to 'content')
-rw-r--r-- | content/browser/android/media_players_observer.cc | 60 | ||||
-rw-r--r-- | content/browser/android/media_players_observer.h | 51 | ||||
-rw-r--r-- | content/browser/media/android/browser_media_player_manager.cc | 19 | ||||
-rw-r--r-- | content/browser/media/android/browser_media_player_manager.h | 16 | ||||
-rw-r--r-- | content/browser/media/audio_state_provider.cc | 30 | ||||
-rw-r--r-- | content/browser/media/audio_state_provider.h | 57 | ||||
-rw-r--r-- | content/browser/media/audio_stream_monitor.cc | 35 | ||||
-rw-r--r-- | content/browser/media/audio_stream_monitor.h | 29 | ||||
-rw-r--r-- | content/browser/media/audio_stream_monitor_unittest.cc | 8 | ||||
-rw-r--r-- | content/browser/media/media_web_contents_observer.cc | 24 | ||||
-rw-r--r-- | content/browser/media/media_web_contents_observer.h | 7 | ||||
-rw-r--r-- | content/browser/web_contents/web_contents_impl.cc | 16 | ||||
-rw-r--r-- | content/browser/web_contents/web_contents_impl.h | 14 | ||||
-rw-r--r-- | content/browser/web_contents/web_contents_impl_unittest.cc | 22 | ||||
-rw-r--r-- | content/content_browser.gypi | 4 | ||||
-rw-r--r-- | content/content_tests.gypi | 1 |
16 files changed, 327 insertions, 66 deletions
diff --git a/content/browser/android/media_players_observer.cc b/content/browser/android/media_players_observer.cc new file mode 100644 index 0000000..c4fd472 --- /dev/null +++ b/content/browser/android/media_players_observer.cc @@ -0,0 +1,60 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/android/media_players_observer.h" + +#include <climits> + +#include "base/logging.h" +#include "content/public/browser/web_contents.h" + +namespace content { + +MediaPlayersObserver::MediaPlayersObserver(WebContents* web_contents) + : AudioStateProvider(web_contents) { +} + +MediaPlayersObserver::~MediaPlayersObserver() {} + +bool MediaPlayersObserver::IsAudioStateAvailable() const { + return true; +} + +// This audio state provider does not have a monitor +AudioStreamMonitor* MediaPlayersObserver::audio_stream_monitor() { + return nullptr; +} + +void MediaPlayersObserver::OnAudibleStateChanged(RenderFrameHost* rfh, + int player_id, + bool is_audible) { + audio_status_map_[Key(rfh, player_id)] = is_audible; + UpdateStatusAndNotify(); +} + +void MediaPlayersObserver::RemovePlayer(RenderFrameHost* rfh, int player_id) { + size_t num_erased_entries = audio_status_map_.erase(Key(rfh, player_id)); + DCHECK_EQ(1u, num_erased_entries); + UpdateStatusAndNotify(); +} + +void MediaPlayersObserver::RenderFrameDeleted(RenderFrameHost* rfh) { + StatusMap::iterator begin = audio_status_map_.lower_bound(Key(rfh, 0)); + StatusMap::iterator end = audio_status_map_.upper_bound(Key(rfh, INT_MAX)); + audio_status_map_.erase(begin, end); + UpdateStatusAndNotify(); +} + +void MediaPlayersObserver::UpdateStatusAndNotify() { + for (const auto& player_status : audio_status_map_) { + if (player_status.second) { + Notify(true); // at least one player is making noise + return; + } + } + + Notify(false); +} + +} // namespace content diff --git a/content/browser/android/media_players_observer.h b/content/browser/android/media_players_observer.h new file mode 100644 index 0000000..b01a3bf --- /dev/null +++ b/content/browser/android/media_players_observer.h @@ -0,0 +1,51 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_ANDROID_MEDIA_PLAYERS_OBSERVER_H_ +#define CONTENT_BROWSER_ANDROID_MEDIA_PLAYERS_OBSERVER_H_ + +#include <map> + +#include "base/macros.h" +#include "content/browser/media/audio_state_provider.h" + +namespace content { + +class RenderFrameHost; + +// On Android the MediaPlayerAndroid objects report +// the audible state to us. +class MediaPlayersObserver : public AudioStateProvider { + public: + explicit MediaPlayersObserver(WebContents* web_contents); + ~MediaPlayersObserver() override; + + bool IsAudioStateAvailable() const override; + + // This audio state provider does not have a monitor, + // the method returns nullptr. + AudioStreamMonitor* audio_stream_monitor() override; + + // These methods constitute the observer pattern, should + // be called when corresponding event happens. They will notify + // WebContents whenever its audible state as a whole changes. + void OnAudibleStateChanged(RenderFrameHost* rfh, int player_id, + bool is_audible); + void RemovePlayer(RenderFrameHost* rfh, int player_id); + void RenderFrameDeleted(RenderFrameHost* rfh); + + private: + void UpdateStatusAndNotify(); + + // Audible status per player ID and frame + typedef std::pair<RenderFrameHost*, int> Key; + typedef std::map<Key, bool> StatusMap; + StatusMap audio_status_map_; + + DISALLOW_COPY_AND_ASSIGN(MediaPlayersObserver); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_ANDROID_MEDIA_PLAYERS_OBSERVER_H_ diff --git a/content/browser/media/android/browser_media_player_manager.cc b/content/browser/media/android/browser_media_player_manager.cc index 5fecd39..8600a75 100644 --- a/content/browser/media/android/browser_media_player_manager.cc +++ b/content/browser/media/android/browser_media_player_manager.cc @@ -7,6 +7,7 @@ #include "base/android/scoped_java_ref.h" #include "base/command_line.h" #include "content/browser/android/content_view_core_impl.h" +#include "content/browser/android/media_players_observer.h" #include "content/browser/media/android/browser_demuxer_android.h" #include "content/browser/media/android/media_resource_getter_impl.h" #include "content/browser/renderer_host/render_view_host_impl.h" @@ -57,10 +58,11 @@ void BrowserMediaPlayerManager::RegisterMediaUrlInterceptor( // static BrowserMediaPlayerManager* BrowserMediaPlayerManager::Create( - RenderFrameHost* rfh) { + RenderFrameHost* rfh, + MediaPlayersObserver* audio_monitor) { if (g_factory) - return g_factory(rfh); - return new BrowserMediaPlayerManager(rfh); + return g_factory(rfh, audio_monitor); + return new BrowserMediaPlayerManager(rfh, audio_monitor); } ContentViewCoreImpl* BrowserMediaPlayerManager::GetContentViewCore() const { @@ -121,8 +123,10 @@ MediaPlayerAndroid* BrowserMediaPlayerManager::CreateMediaPlayer( } BrowserMediaPlayerManager::BrowserMediaPlayerManager( - RenderFrameHost* render_frame_host) + RenderFrameHost* render_frame_host, + MediaPlayersObserver* audio_monitor) : render_frame_host_(render_frame_host), + audio_monitor_(audio_monitor), fullscreen_player_id_(kInvalidMediaPlayerId), fullscreen_player_is_released_(false), web_contents_(WebContents::FromRenderFrameHost(render_frame_host)), @@ -254,6 +258,12 @@ void BrowserMediaPlayerManager::OnVideoSizeChanged( video_view_->OnVideoSizeChanged(width, height); } +void BrowserMediaPlayerManager::OnAudibleStateChanged( + int player_id, bool is_audible) { + audio_monitor_->OnAudibleStateChanged( + render_frame_host_, player_id, is_audible); +} + media::MediaResourceGetter* BrowserMediaPlayerManager::GetMediaResourceGetter() { if (!media_resource_getter_.get()) { @@ -518,6 +528,7 @@ void BrowserMediaPlayerManager::RemovePlayer(int player_id) { if ((*it)->player_id() == player_id) { ReleaseMediaResources(player_id); players_.erase(it); + audio_monitor_->RemovePlayer(render_frame_host_, player_id); break; } } diff --git a/content/browser/media/android/browser_media_player_manager.h b/content/browser/media/android/browser_media_player_manager.h index e0a4013..bec4695 100644 --- a/content/browser/media/android/browser_media_player_manager.h +++ b/content/browser/media/android/browser_media_player_manager.h @@ -30,6 +30,7 @@ namespace content { class BrowserDemuxerAndroid; class ContentViewCoreImpl; class ExternalVideoSurfaceContainer; +class MediaPlayersObserver; class RenderFrameHost; class WebContents; @@ -42,7 +43,8 @@ class CONTENT_EXPORT BrowserMediaPlayerManager : public media::MediaPlayerManager { public: // Permits embedders to provide an extended version of the class. - typedef BrowserMediaPlayerManager* (*Factory)(RenderFrameHost*); + typedef BrowserMediaPlayerManager* (*Factory)(RenderFrameHost*, + MediaPlayersObserver*); static void RegisterFactory(Factory factory); // Permits embedders to handle custom urls. @@ -50,7 +52,9 @@ class CONTENT_EXPORT BrowserMediaPlayerManager media::MediaUrlInterceptor* media_url_interceptor); // Returns a new instance using the registered factory if available. - static BrowserMediaPlayerManager* Create(RenderFrameHost* rfh); + static BrowserMediaPlayerManager* Create( + RenderFrameHost* rfh, + MediaPlayersObserver* audio_monitor); ContentViewCoreImpl* GetContentViewCore() const; @@ -83,6 +87,9 @@ class CONTENT_EXPORT BrowserMediaPlayerManager const base::TimeDelta& current_time) override; void OnError(int player_id, int error) override; void OnVideoSizeChanged(int player_id, int width, int height) override; + void OnAudibleStateChanged( + int player_id, bool is_audible_now) override; + media::MediaResourceGetter* GetMediaResourceGetter() override; media::MediaUrlInterceptor* GetMediaUrlInterceptor() override; media::MediaPlayerAndroid* GetFullscreenPlayer() override; @@ -118,7 +125,8 @@ class CONTENT_EXPORT BrowserMediaPlayerManager protected: // Clients must use Create() or subclass constructor. - explicit BrowserMediaPlayerManager(RenderFrameHost* render_frame_host); + BrowserMediaPlayerManager(RenderFrameHost* render_frame_host, + MediaPlayersObserver* audio_monitor); WebContents* web_contents() const { return web_contents_; } @@ -168,6 +176,8 @@ class CONTENT_EXPORT BrowserMediaPlayerManager RenderFrameHost* const render_frame_host_; + MediaPlayersObserver* audio_monitor_; + // An array of managed players. ScopedVector<media::MediaPlayerAndroid> players_; diff --git a/content/browser/media/audio_state_provider.cc b/content/browser/media/audio_state_provider.cc new file mode 100644 index 0000000..e09356a --- /dev/null +++ b/content/browser/media/audio_state_provider.cc @@ -0,0 +1,30 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/media/audio_state_provider.h" + +#include "base/logging.h" +#include "content/browser/media/audio_stream_monitor.h" +#include "content/public/browser/web_contents.h" + +namespace content { + +AudioStateProvider::AudioStateProvider(WebContents* contents) + : web_contents_(contents), + was_recently_audible_(false) { + DCHECK(web_contents_); +} + +bool AudioStateProvider::WasRecentlyAudible() const { + return was_recently_audible_; +} + +void AudioStateProvider::Notify(bool new_state) { + if (was_recently_audible_ != new_state) { + was_recently_audible_ = new_state; + web_contents_->NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB); + } +} + +} // namespace content diff --git a/content/browser/media/audio_state_provider.h b/content/browser/media/audio_state_provider.h new file mode 100644 index 0000000..5bd1b71 --- /dev/null +++ b/content/browser/media/audio_state_provider.h @@ -0,0 +1,57 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_MEDIA_AUDIO_STATE_PROVIDER_H_ +#define CONTENT_BROWSER_MEDIA_AUDIO_STATE_PROVIDER_H_ + +#include "content/common/content_export.h" + +namespace content { +class WebContents; +class AudioStreamMonitor; + +// This class is associated with a WebContents, and maintains the audible +// state regarding all the players in it. +// The audible state is true if at least one player is playing a sound. +// Whenever the audible state of the WebContents as a whole changes, this +// class sends a notification to it. +// +// Each WebContentsImpl owns an AudioStateProvider +class CONTENT_EXPORT AudioStateProvider { + public: + explicit AudioStateProvider(WebContents* web_contents); + virtual ~AudioStateProvider() {} + + // Indicates whether this service is available on the system. + virtual bool IsAudioStateAvailable() const = 0; + + // If this provider uses monitoring (i.e. measure the signal), + // return its monitor. + virtual AudioStreamMonitor* audio_stream_monitor() = 0; + + // Returns true if the WebContents is playing or has recently been + // playing the sound. + virtual bool WasRecentlyAudible() const; + + void set_was_recently_audible_for_testing(bool value) { + was_recently_audible_ = value; + } + + protected: + // Notify WebContents that the audio state has changed. + void Notify(bool new_state); + + // The WebContents instance instance to receive indicator toggle + // notifications. This pointer should be valid for the lifetime of + // AudioStreamMonitor. + WebContents* const web_contents_; + + // The audio state that is being maintained + bool was_recently_audible_; + +}; + +} // namespace content + +#endif // CONTENT_BROWSER_MEDIA_AUDIO_STATE_PROVIDER_H_ diff --git a/content/browser/media/audio_stream_monitor.cc b/content/browser/media/audio_stream_monitor.cc index 1d2f76f7..4c0bc0a 100644 --- a/content/browser/media/audio_stream_monitor.cc +++ b/content/browser/media/audio_stream_monitor.cc @@ -21,23 +21,36 @@ AudioStreamMonitor* AudioStreamMonitorFromRenderFrame(int render_process_id, WebContentsImpl* const web_contents = static_cast<WebContentsImpl*>(WebContents::FromRenderFrameHost( RenderFrameHost::FromID(render_process_id, render_frame_id))); - return web_contents ? web_contents->audio_stream_monitor() : NULL; + + if (!web_contents) + return nullptr; + + AudioStateProvider* audio_provider = web_contents->audio_state_provider(); + return audio_provider ? audio_provider->audio_stream_monitor() : nullptr; } } // namespace AudioStreamMonitor::AudioStreamMonitor(WebContents* contents) - : web_contents_(contents), - clock_(&default_tick_clock_), - was_recently_audible_(false) { - DCHECK(web_contents_); + : AudioStateProvider(contents), + clock_(&default_tick_clock_) +{ } AudioStreamMonitor::~AudioStreamMonitor() {} +bool AudioStreamMonitor::IsAudioStateAvailable() const { + return media::AudioOutputController::will_monitor_audio_levels(); +} + +// This provider is the monitor. +AudioStreamMonitor* AudioStreamMonitor::audio_stream_monitor() { + return this; +} + bool AudioStreamMonitor::WasRecentlyAudible() const { DCHECK(thread_checker_.CalledOnValidThread()); - return was_recently_audible_; + return AudioStateProvider::WasRecentlyAudible(); } // static @@ -46,7 +59,7 @@ void AudioStreamMonitor::StartMonitoringStream( int render_frame_id, int stream_id, const ReadPowerAndClipCallback& read_power_callback) { - if (!monitoring_available()) + if (!media::AudioOutputController::will_monitor_audio_levels()) return; BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, @@ -61,7 +74,7 @@ void AudioStreamMonitor::StartMonitoringStream( void AudioStreamMonitor::StopMonitoringStream(int render_process_id, int render_frame_id, int stream_id) { - if (!monitoring_available()) + if (!media::AudioOutputController::will_monitor_audio_levels()) return; BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, @@ -138,16 +151,12 @@ void AudioStreamMonitor::Poll() { } void AudioStreamMonitor::MaybeToggle() { - const bool indicator_was_on = was_recently_audible_; const base::TimeTicks off_time = last_blurt_time_ + base::TimeDelta::FromMilliseconds(kHoldOnMilliseconds); const base::TimeTicks now = clock_->NowTicks(); const bool should_indicator_be_on = now < off_time; - if (should_indicator_be_on != indicator_was_on) { - was_recently_audible_ = should_indicator_be_on; - web_contents_->NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB); - } + Notify(should_indicator_be_on); if (!should_indicator_be_on) { off_timer_.Stop(); diff --git a/content/browser/media/audio_stream_monitor.h b/content/browser/media/audio_stream_monitor.h index d1a32d5..b3cbf16 100644 --- a/content/browser/media/audio_stream_monitor.h +++ b/content/browser/media/audio_stream_monitor.h @@ -14,6 +14,7 @@ #include "base/time/time.h" #include "base/timer/timer.h" #include "build/build_config.h" +#include "content/browser/media/audio_state_provider.h" #include "content/common/content_export.h" #include "media/audio/audio_output_controller.h" @@ -22,7 +23,6 @@ class TickClock; } namespace content { -class WebContents; // Repeatedly polls audio streams for their power levels, and "debounces" the // information into a simple, binary "was recently audible" result for the audio @@ -32,23 +32,23 @@ class WebContents; // to turn on/off repeatedly and annoy the user. AudioStreamMonitor sends UI // update notifications only when needed, but may be queried at any time. // -// Each WebContentsImpl owns an AudioStreamMonitor. -class CONTENT_EXPORT AudioStreamMonitor { +class CONTENT_EXPORT AudioStreamMonitor : public AudioStateProvider { public: explicit AudioStreamMonitor(WebContents* contents); - ~AudioStreamMonitor(); + ~AudioStreamMonitor() override; // Indicates if audio stream monitoring is available. It's only available if // AudioOutputController can and will monitor output power levels. - static bool monitoring_available() { - return media::AudioOutputController::will_monitor_audio_levels(); - } + bool IsAudioStateAvailable() const override; + + // This provider is a monitor, the method returns |this|. + AudioStreamMonitor* audio_stream_monitor() override; // Returns true if audio has recently been audible from the tab. This is // usually called whenever the tab data model is refreshed; but there are // other use cases as well (e.g., the OOM killer uses this to de-prioritize // the killing of tabs making sounds). - bool WasRecentlyAudible() const; + bool WasRecentlyAudible() const override; // Starts or stops audio level monitoring respectively for the stream owned by // the specified renderer. Safe to call from any thread. @@ -66,10 +66,6 @@ class CONTENT_EXPORT AudioStreamMonitor { int render_frame_id, int stream_id); - void set_was_recently_audible_for_testing(bool value) { - was_recently_audible_ = value; - } - private: friend class AudioStreamMonitorTest; @@ -112,11 +108,6 @@ class CONTENT_EXPORT AudioStreamMonitor { // on, |off_timer_| is started to re-invoke this method in the future. void MaybeToggle(); - // The WebContents instance instance to receive indicator toggle - // notifications. This pointer should be valid for the lifetime of - // AudioStreamMonitor. - WebContents* const web_contents_; - // Note: |clock_| is always |&default_tick_clock_|, except during unit // testing. base::DefaultTickClock default_tick_clock_; @@ -134,10 +125,6 @@ class CONTENT_EXPORT AudioStreamMonitor { // Records the last time at which sound was audible from any stream. base::TimeTicks last_blurt_time_; - // Set to true if the last call to MaybeToggle() determined the indicator - // should be turned on. - bool was_recently_audible_; - // Calls Poll() at regular intervals while |poll_callbacks_| is non-empty. base::RepeatingTimer<AudioStreamMonitor> poll_timer_; diff --git a/content/browser/media/audio_stream_monitor_unittest.cc b/content/browser/media/audio_stream_monitor_unittest.cc index 0d20621..0607b75 100644 --- a/content/browser/media/audio_stream_monitor_unittest.cc +++ b/content/browser/media/audio_stream_monitor_unittest.cc @@ -52,7 +52,13 @@ class AudioStreamMonitorTest : public RenderViewHostTestHarness { WebContentsImpl* web_contents = reinterpret_cast<WebContentsImpl*>( RenderViewHostTestHarness::web_contents()); web_contents->SetDelegate(&mock_web_contents_delegate_); - monitor_ = web_contents->audio_stream_monitor(); + + AudioStateProvider* provider = web_contents->audio_state_provider(); + ASSERT_TRUE(provider); + + monitor_ = provider->audio_stream_monitor(); + ASSERT_TRUE(monitor_); + const_cast<base::TickClock*&>(monitor_->clock_) = &clock_; } diff --git a/content/browser/media/media_web_contents_observer.cc b/content/browser/media/media_web_contents_observer.cc index cc90b55..f87cfb1 100644 --- a/content/browser/media/media_web_contents_observer.cc +++ b/content/browser/media/media_web_contents_observer.cc @@ -8,11 +8,13 @@ #include "base/stl_util.h" #include "content/browser/media/cdm/browser_cdm_manager.h" #include "content/browser/renderer_host/render_process_host_impl.h" +#include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" #include "ipc/ipc_message_macros.h" #if defined(OS_ANDROID) +#include "content/browser/android/media_players_observer.h" #include "content/browser/media/android/browser_media_player_manager.h" #include "content/common/media/media_player_messages_android.h" #include "media/base/android/media_player_android.h" @@ -22,7 +24,8 @@ namespace content { MediaWebContentsObserver::MediaWebContentsObserver( WebContents* web_contents) - : WebContentsObserver(web_contents) { + : WebContentsObserver(web_contents) +{ } MediaWebContentsObserver::~MediaWebContentsObserver() { @@ -35,6 +38,10 @@ void MediaWebContentsObserver::RenderFrameDeleted( // Always destroy the media players before CDMs because we do not support // detaching CDMs from media players yet. See http://crbug.com/330324 media_player_managers_.erase(key); + + MediaPlayersObserver* audio_observer = GetMediaPlayersObserver(); + if (audio_observer) + audio_observer->RenderFrameDeleted(render_frame_host); #endif // TODO(xhwang): Currently MediaWebContentsObserver, BrowserMediaPlayerManager // and BrowserCdmManager all run on browser UI thread. So this call is okay. @@ -161,11 +168,24 @@ BrowserMediaPlayerManager* MediaWebContentsObserver::GetMediaPlayerManager( if (!media_player_managers_.contains(key)) { media_player_managers_.set( key, - make_scoped_ptr(BrowserMediaPlayerManager::Create(render_frame_host))); + make_scoped_ptr(BrowserMediaPlayerManager::Create( + render_frame_host, GetMediaPlayersObserver()))); } return media_player_managers_.get(key); } +MediaPlayersObserver* +MediaWebContentsObserver::GetMediaPlayersObserver() const { + AudioStateProvider* provider = + static_cast<WebContentsImpl*>(web_contents())->audio_state_provider(); + + MediaPlayersObserver* audio_observer = + static_cast<MediaPlayersObserver*>(provider); + + DCHECK(audio_observer); + return audio_observer; +} + #if defined(VIDEO_HOLE) void MediaWebContentsObserver::OnFrameInfoUpdated() { for (MediaPlayerManagerMap::iterator iter = media_player_managers_.begin(); diff --git a/content/browser/media/media_web_contents_observer.h b/content/browser/media/media_web_contents_observer.h index 3b451a8..2eb08c1 100644 --- a/content/browser/media/media_web_contents_observer.h +++ b/content/browser/media/media_web_contents_observer.h @@ -7,11 +7,16 @@ #include "base/compiler_specific.h" #include "base/containers/scoped_ptr_hash_map.h" +#include "base/memory/scoped_ptr.h" #include "content/common/content_export.h" #include "content/public/browser/web_contents_observer.h" namespace content { +#if defined(OS_ANDROID) +class MediaPlayersObserver; +#endif // defined(OS_ANDROID) + class BrowserCdmManager; class BrowserMediaPlayerManager; @@ -45,6 +50,8 @@ class CONTENT_EXPORT MediaWebContentsObserver : public WebContentsObserver { void OnSetCdm(RenderFrameHost* render_frame_host, int player_id, int cdm_id); + MediaPlayersObserver* GetMediaPlayersObserver() const; + #if defined(VIDEO_HOLE) void OnFrameInfoUpdated(); #endif // defined(VIDEO_HOLE) diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 7d37660..5d51ed1 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -108,6 +108,7 @@ #if defined(OS_ANDROID) #include "content/browser/android/content_video_view.h" #include "content/browser/android/date_time_chooser_android.h" +#include "content/browser/android/media_players_observer.h" #include "content/browser/media/android/browser_media_player_manager.h" #include "content/browser/web_contents/web_contents_android.h" #endif @@ -358,7 +359,6 @@ WebContentsImpl::WebContentsImpl(BrowserContext* browser_context, geolocation_service_context_(new GeolocationServiceContext()), accessibility_mode_( BrowserAccessibilityStateImpl::GetInstance()->accessibility_mode()), - audio_stream_monitor_(this), virtual_keyboard_requested_(false), loading_weak_factory_(this) { frame_tree_.SetFrameRemoveListener( @@ -367,6 +367,12 @@ WebContentsImpl::WebContentsImpl(BrowserContext* browser_context, #if defined(ENABLE_BROWSER_CDMS) media_web_contents_observer_.reset(new MediaWebContentsObserver(this)); #endif + +#if defined(OS_ANDROID) + audio_state_provider_.reset(new MediaPlayersObserver(this)); +#else + audio_state_provider_.reset(new AudioStreamMonitor(this)); +#endif } WebContentsImpl::~WebContentsImpl() { @@ -1041,7 +1047,7 @@ void WebContentsImpl::NotifyNavigationStateChanged( // Create and release the audio power save blocker depending on whether the // tab is actively producing audio or not. if ((changed_flags & INVALIDATE_TYPE_TAB) && - AudioStreamMonitor::monitoring_available()) { + audio_state_provider_->IsAudioStateAvailable()) { if (WasRecentlyAudible()) { if (!audio_power_save_blocker_) CreateAudioPowerSaveBlocker(); @@ -2523,7 +2529,7 @@ void WebContentsImpl::InsertCSS(const std::string& css) { } bool WebContentsImpl::WasRecentlyAudible() { - return audio_stream_monitor_.WasRecentlyAudible(); + return audio_state_provider_->WasRecentlyAudible(); } void WebContentsImpl::GetManifest(const GetManifestCallback& callback) { @@ -3210,7 +3216,7 @@ void WebContentsImpl::MaybeReleasePowerSaveBlockers() { // monitoring, release the audio power save blocker here instead of during // NotifyNavigationStateChanged(). if (active_audio_players_.empty() && - !AudioStreamMonitor::monitoring_available()) { + !audio_state_provider_->IsAudioStateAvailable()) { audio_power_save_blocker_.reset(); } @@ -3233,7 +3239,7 @@ void WebContentsImpl::OnMediaPlayingNotification(int64 player_cookie, // If we don't have audio stream monitoring, allocate the audio power save // blocker here instead of during NotifyNavigationStateChanged(). if (!audio_power_save_blocker_ && - !AudioStreamMonitor::monitoring_available()) { + !audio_state_provider_->IsAudioStateAvailable()) { CreateAudioPowerSaveBlocker(); } } diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h index cc4aa3a..2e0e966 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h @@ -22,7 +22,7 @@ #include "content/browser/frame_host/navigator_delegate.h" #include "content/browser/frame_host/render_frame_host_delegate.h" #include "content/browser/frame_host/render_frame_host_manager.h" -#include "content/browser/media/audio_stream_monitor.h" +#include "content/browser/media/audio_state_provider.h" #include "content/browser/renderer_host/render_view_host_delegate.h" #include "content/browser/renderer_host/render_widget_host_delegate.h" #include "content/common/accessibility_mode_enums.h" @@ -669,8 +669,8 @@ class CONTENT_EXPORT WebContentsImpl // Forces overscroll to be disabled (used by touch emulation). void SetForceDisableOverscrollContent(bool force_disable); - AudioStreamMonitor* audio_stream_monitor() { - return &audio_stream_monitor_; + AudioStateProvider* audio_state_provider() { + return audio_state_provider_.get(); } bool has_audio_power_save_blocker_for_testing() const { @@ -1025,6 +1025,11 @@ class CONTENT_EXPORT WebContentsImpl scoped_ptr<PowerSaveBlocker> audio_power_save_blocker_; scoped_ptr<PowerSaveBlocker> video_power_save_blocker_; + // Tells whether this WebContents is actively producing sound. + // Order is important: the |frame_tree_| destruction uses + // |audio_state_provider_|. + scoped_ptr<AudioStateProvider> audio_state_provider_; + // Manages the frame tree of the page and process swaps in each node. FrameTree frame_tree_; @@ -1239,9 +1244,6 @@ class CONTENT_EXPORT WebContentsImpl // is created, and broadcast to all frames when it changes. AccessibilityMode accessibility_mode_; - // Monitors power levels for audio streams associated with this WebContents. - AudioStreamMonitor audio_stream_monitor_; - // Created on-demand to mute all audio output from this WebContents. scoped_ptr<WebContentsAudioMuter> audio_muter_; diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc index da3c82e..187f3c4 100644 --- a/content/browser/web_contents/web_contents_impl_unittest.cc +++ b/content/browser/web_contents/web_contents_impl_unittest.cc @@ -9,7 +9,7 @@ #include "content/browser/frame_host/interstitial_page_impl.h" #include "content/browser/frame_host/navigation_entry_impl.h" #include "content/browser/frame_host/render_frame_host_impl.h" -#include "content/browser/media/audio_stream_monitor.h" +#include "content/browser/media/audio_state_provider.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/site_instance_impl.h" #include "content/browser/webui/web_ui_controller_factory_registry.h" @@ -2891,20 +2891,20 @@ TEST_F(WebContentsImplTest, MediaPowerSaveBlocking) { EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing()); TestRenderFrameHost* rfh = contents()->GetMainFrame(); - AudioStreamMonitor* monitor = contents()->audio_stream_monitor(); + AudioStateProvider* audio_state = contents()->audio_state_provider(); // The audio power save blocker should not be based on having a media player // when audio stream monitoring is available. - if (AudioStreamMonitor::monitoring_available()) { + if (audio_state->IsAudioStateAvailable()) { // Send a fake audio stream monitor notification. The audio power save // blocker should be created. - monitor->set_was_recently_audible_for_testing(true); + audio_state->set_was_recently_audible_for_testing(true); contents()->NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB); EXPECT_TRUE(contents()->has_audio_power_save_blocker_for_testing()); // Send another fake notification, this time when WasRecentlyAudible() will // be false. The power save blocker should be released. - monitor->set_was_recently_audible_for_testing(false); + audio_state->set_was_recently_audible_for_testing(false); contents()->NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB); EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing()); } @@ -2916,7 +2916,7 @@ TEST_F(WebContentsImplTest, MediaPowerSaveBlocking) { 0, kPlayerAudioVideoId, true, true, false)); EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing()); EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(), - !AudioStreamMonitor::monitoring_available()); + !audio_state->IsAudioStateAvailable()); // Upon hiding the video power save blocker should be released. contents()->WasHidden(); @@ -2929,7 +2929,7 @@ TEST_F(WebContentsImplTest, MediaPowerSaveBlocking) { 0, kPlayerVideoOnlyId, true, false, false)); EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing()); EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(), - !AudioStreamMonitor::monitoring_available()); + !audio_state->IsAudioStateAvailable()); // Showing the WebContents should result in the creation of the blocker. contents()->WasShown(); @@ -2941,7 +2941,7 @@ TEST_F(WebContentsImplTest, MediaPowerSaveBlocking) { 0, kPlayerAudioOnlyId, false, true, false)); EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing()); EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(), - !AudioStreamMonitor::monitoring_available()); + !audio_state->IsAudioStateAvailable()); // Start a remote player. There should be no change in the power save // blockers. @@ -2949,7 +2949,7 @@ TEST_F(WebContentsImplTest, MediaPowerSaveBlocking) { 0, kPlayerRemoteId, true, true, true)); EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing()); EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(), - !AudioStreamMonitor::monitoring_available()); + !audio_state->IsAudioStateAvailable()); // Destroy the original audio video player. Both power save blockers should // remain. @@ -2957,7 +2957,7 @@ TEST_F(WebContentsImplTest, MediaPowerSaveBlocking) { FrameHostMsg_MediaPausedNotification(0, kPlayerAudioVideoId)); EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing()); EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(), - !AudioStreamMonitor::monitoring_available()); + !audio_state->IsAudioStateAvailable()); // Destroy the audio only player. The video power save blocker should remain. rfh->OnMessageReceived( @@ -2984,7 +2984,7 @@ TEST_F(WebContentsImplTest, MediaPowerSaveBlocking) { 0, kPlayerAudioVideoId, true, true, false)); EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing()); EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(), - !AudioStreamMonitor::monitoring_available()); + !audio_state->IsAudioStateAvailable()); // Crash the renderer. contents()->GetMainFrame()->OnMessageReceived( diff --git a/content/content_browser.gypi b/content/content_browser.gypi index e0afa12..7715d1b 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -355,6 +355,8 @@ 'browser/android/interstitial_page_delegate_android.h', 'browser/android/load_url_params.cc', 'browser/android/load_url_params.h', + 'browser/android/media_players_observer.cc', + 'browser/android/media_players_observer.h', 'browser/android/overscroll_controller_android.cc', 'browser/android/overscroll_controller_android.h', 'browser/android/overscroll_glow.cc', @@ -928,6 +930,8 @@ 'browser/media/android/media_drm_credential_manager.h', 'browser/media/android/media_resource_getter_impl.cc', 'browser/media/android/media_resource_getter_impl.h', + 'browser/media/audio_state_provider.cc', + 'browser/media/audio_state_provider.h', 'browser/media/audio_stream_monitor.cc', 'browser/media/audio_stream_monitor.h', 'browser/media/capture/audio_mirroring_manager.cc', diff --git a/content/content_tests.gypi b/content/content_tests.gypi index 75fd66b..9d5a2dc 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -1120,6 +1120,7 @@ 'sources!': [ 'browser/geolocation/network_location_provider_unittest.cc', 'browser/geolocation/wifi_data_provider_common_unittest.cc', + 'browser/media/audio_stream_monitor_unittest.cc', 'browser/webui/url_data_manager_backend_unittest.cc', ], 'dependencies': [ |