Commit 8da7a66a authored by Becca Hughes's avatar Becca Hughes Committed by Commit Bot

[Media Session] Mojoify audio focus observer

Move AudioFocusObserver to the media session service and
convert it to use mojo. At the moment the observers
register themselves with AudioFocusManager but in the
future this will be with the Media Sesssion service.

Also adds an empty MediaSession mojo type so we can pass
it as an arg to AudioFocusObserver.

BUG=875004

Change-Id: I4bd09796018833cb9f9714b2296642bd40be48ff
Reviewed-on: https://chromium-review.googlesource.com/1180484
Commit-Queue: Becca Hughes <beccahughes@chromium.org>
Reviewed-by: default avatarNick Carter <nick@chromium.org>
Reviewed-by: default avatarMounir Lamouri <mlamouri@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Cr-Commit-Position: refs/heads/master@{#586012}
parent 3da2da6a
...@@ -647,6 +647,8 @@ void MediaInternals::AddUpdateCallback(const UpdateCallback& callback) { ...@@ -647,6 +647,8 @@ void MediaInternals::AddUpdateCallback(const UpdateCallback& callback) {
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
can_update_ = true; can_update_ = true;
RegisterAudioFocusObserver();
} }
void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) { void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) {
...@@ -660,6 +662,9 @@ void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) { ...@@ -660,6 +662,9 @@ void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) {
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
can_update_ = !update_callbacks_.empty(); can_update_ = !update_callbacks_.empty();
if (!can_update_)
UnregisterAudioFocusObserver();
} }
bool MediaInternals::CanUpdate() { bool MediaInternals::CanUpdate() {
...@@ -700,8 +705,8 @@ void MediaInternals::SendVideoCaptureDeviceCapabilities() { ...@@ -700,8 +705,8 @@ void MediaInternals::SendVideoCaptureDeviceCapabilities() {
&video_capture_capabilities_cached_data_)); &video_capture_capabilities_cached_data_));
} }
#if !defined(OS_ANDROID)
void MediaInternals::SendAudioFocusState() { void MediaInternals::SendAudioFocusState() {
#if !defined(OS_ANDROID)
if (!CanUpdate()) if (!CanUpdate())
return; return;
...@@ -725,8 +730,8 @@ void MediaInternals::SendAudioFocusState() { ...@@ -725,8 +730,8 @@ void MediaInternals::SendAudioFocusState() {
SendUpdate( SendUpdate(
SerializeUpdate("media.onReceiveAudioFocusState", &audio_focus_data)); SerializeUpdate("media.onReceiveAudioFocusState", &audio_focus_data));
}
#endif // !defined(OS_ANDROID) #endif // !defined(OS_ANDROID)
}
void MediaInternals::UpdateVideoCaptureDeviceCapabilities( void MediaInternals::UpdateVideoCaptureDeviceCapabilities(
const std::vector<std::tuple<media::VideoCaptureDeviceDescriptor, const std::vector<std::tuple<media::VideoCaptureDeviceDescriptor,
...@@ -808,9 +813,9 @@ void MediaInternals::OnProcessTerminatedForTesting(int process_id) { ...@@ -808,9 +813,9 @@ void MediaInternals::OnProcessTerminatedForTesting(int process_id) {
uma_handler_->OnProcessTerminated(process_id); uma_handler_->OnProcessTerminated(process_id);
} }
#if !defined(OS_ANDROID) void MediaInternals::OnFocusGained(
void MediaInternals::OnFocusGained(MediaSession* media_session, media_session::mojom::MediaSessionPtr media_session,
media_session::mojom::AudioFocusType type) { media_session::mojom::AudioFocusType type) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
...@@ -818,14 +823,14 @@ void MediaInternals::OnFocusGained(MediaSession* media_session, ...@@ -818,14 +823,14 @@ void MediaInternals::OnFocusGained(MediaSession* media_session,
base::Unretained(this))); base::Unretained(this)));
} }
void MediaInternals::OnFocusLost(MediaSession* media_session) { void MediaInternals::OnFocusLost(
media_session::mojom::MediaSessionPtr media_session) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::BindOnce(&MediaInternals::SendAudioFocusState, base::BindOnce(&MediaInternals::SendAudioFocusState,
base::Unretained(this))); base::Unretained(this)));
} }
#endif // !defined(OS_ANDROID)
void MediaInternals::SendUpdate(const base::string16& update) { void MediaInternals::SendUpdate(const base::string16& update) {
// SendUpdate() may be called from any thread, but must run on the UI thread. // SendUpdate() may be called from any thread, but must run on the UI thread.
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "base/values.h" #include "base/values.h"
#include "build/build_config.h"
#include "content/browser/media/session/audio_focus_observer.h" #include "content/browser/media/session/audio_focus_observer.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_observer.h"
...@@ -45,9 +44,7 @@ namespace content { ...@@ -45,9 +44,7 @@ namespace content {
// TODO(crbug.com/812557): Remove inheritance from media::AudioLogFactory once // TODO(crbug.com/812557): Remove inheritance from media::AudioLogFactory once
// the creation of the AudioManager instance moves to the audio service. // the creation of the AudioManager instance moves to the audio service.
class CONTENT_EXPORT MediaInternals : public media::AudioLogFactory, class CONTENT_EXPORT MediaInternals : public media::AudioLogFactory,
#if !defined(OS_ANDROID)
public AudioFocusObserver, public AudioFocusObserver,
#endif
public NotificationObserver { public NotificationObserver {
public: public:
// Called with the update string. // Called with the update string.
...@@ -85,10 +82,8 @@ class CONTENT_EXPORT MediaInternals : public media::AudioLogFactory, ...@@ -85,10 +82,8 @@ class CONTENT_EXPORT MediaInternals : public media::AudioLogFactory,
// UpdateCallback. // UpdateCallback.
void SendVideoCaptureDeviceCapabilities(); void SendVideoCaptureDeviceCapabilities();
#if !defined(OS_ANDROID)
// Sends all audio focus information to each registered UpdateCallback. // Sends all audio focus information to each registered UpdateCallback.
void SendAudioFocusState(); void SendAudioFocusState();
#endif
// Called to inform of the capabilities enumerated for video devices. // Called to inform of the capabilities enumerated for video devices.
void UpdateVideoCaptureDeviceCapabilities( void UpdateVideoCaptureDeviceCapabilities(
...@@ -125,12 +120,11 @@ class CONTENT_EXPORT MediaInternals : public media::AudioLogFactory, ...@@ -125,12 +120,11 @@ class CONTENT_EXPORT MediaInternals : public media::AudioLogFactory,
MediaInternals(); MediaInternals();
#if !defined(OS_ANDROID)
// AudioFocusObserver implementation. // AudioFocusObserver implementation.
void OnFocusGained(MediaSession* media_session, void OnFocusGained(media_session::mojom::MediaSessionPtr media_session,
media_session::mojom::AudioFocusType type) override; media_session::mojom::AudioFocusType type) override;
void OnFocusLost(MediaSession* media_session) override; void OnFocusLost(
#endif media_session::mojom::MediaSessionPtr media_session) override;
// Sends |update| to each registered UpdateCallback. Safe to call from any // Sends |update| to each registered UpdateCallback. Safe to call from any
// thread, but will forward to the IO thread. // thread, but will forward to the IO thread.
......
...@@ -342,6 +342,11 @@ class MediaInternalsAudioFocusTest : public testing::Test, ...@@ -342,6 +342,11 @@ class MediaInternalsAudioFocusTest : public testing::Test,
session->RemoveAllPlayersForTest(); session->RemoveAllPlayersForTest();
} }
void WaitForCallback() {
AudioFocusManager::GetInstance()->FlushForTesting();
base::RunLoop().RunUntilIdle();
}
MediaInternals::UpdateCallback update_cb_; MediaInternals::UpdateCallback update_cb_;
private: private:
...@@ -355,7 +360,7 @@ TEST_F(MediaInternalsAudioFocusTest, AudioFocusStateIsUpdated) { ...@@ -355,7 +360,7 @@ TEST_F(MediaInternalsAudioFocusTest, AudioFocusStateIsUpdated) {
web_contents1->SetTitle(base::UTF8ToUTF16(kTestTitle1)); web_contents1->SetTitle(base::UTF8ToUTF16(kTestTitle1));
MediaSessionImpl* media_session1 = MediaSessionImpl::Get(web_contents1.get()); MediaSessionImpl* media_session1 = MediaSessionImpl::Get(web_contents1.get());
media_session1->RequestSystemAudioFocus(AudioFocusType::kGain); media_session1->RequestSystemAudioFocus(AudioFocusType::kGain);
base::RunLoop().RunUntilIdle(); WaitForCallback();
// Check JSON is what we expect. // Check JSON is what we expect.
{ {
...@@ -375,7 +380,7 @@ TEST_F(MediaInternalsAudioFocusTest, AudioFocusStateIsUpdated) { ...@@ -375,7 +380,7 @@ TEST_F(MediaInternalsAudioFocusTest, AudioFocusStateIsUpdated) {
MediaSessionImpl* media_session2 = MediaSessionImpl::Get(web_contents2.get()); MediaSessionImpl* media_session2 = MediaSessionImpl::Get(web_contents2.get());
media_session2->RequestSystemAudioFocus( media_session2->RequestSystemAudioFocus(
AudioFocusType::kGainTransientMayDuck); AudioFocusType::kGainTransientMayDuck);
base::RunLoop().RunUntilIdle(); WaitForCallback();
// Check JSON is what we expect. // Check JSON is what we expect.
{ {
...@@ -397,7 +402,7 @@ TEST_F(MediaInternalsAudioFocusTest, AudioFocusStateIsUpdated) { ...@@ -397,7 +402,7 @@ TEST_F(MediaInternalsAudioFocusTest, AudioFocusStateIsUpdated) {
// Abandon audio focus. // Abandon audio focus.
RemoveAllPlayersForTest(media_session2); RemoveAllPlayersForTest(media_session2);
base::RunLoop().RunUntilIdle(); WaitForCallback();
// Check JSON is what we expect. // Check JSON is what we expect.
{ {
...@@ -413,7 +418,7 @@ TEST_F(MediaInternalsAudioFocusTest, AudioFocusStateIsUpdated) { ...@@ -413,7 +418,7 @@ TEST_F(MediaInternalsAudioFocusTest, AudioFocusStateIsUpdated) {
// Abandon audio focus. // Abandon audio focus.
RemoveAllPlayersForTest(media_session1); RemoveAllPlayersForTest(media_session1);
base::RunLoop().RunUntilIdle(); WaitForCallback();
// Check JSON is what we expect. // Check JSON is what we expect.
{ {
......
...@@ -6,13 +6,26 @@ ...@@ -6,13 +6,26 @@
#include "content/browser/media/session/audio_focus_observer.h" #include "content/browser/media/session/audio_focus_observer.h"
#include "content/browser/media/session/media_session_impl.h" #include "content/browser/media/session/media_session_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h" #include "services/media_session/public/mojom/audio_focus.mojom.h"
namespace content { namespace content {
using AudioFocusType = media_session::mojom::AudioFocusType; using AudioFocusType = media_session::mojom::AudioFocusType;
namespace {
media_session::mojom::MediaSessionPtr GetSessionMojoPtr(
MediaSessionImpl* session) {
media_session::mojom::MediaSessionPtr media_session_ptr;
session->BindToMojoRequest(mojo::MakeRequest(&media_session_ptr));
return media_session_ptr;
}
} // namespace
// static // static
AudioFocusManager* AudioFocusManager::GetInstance() { AudioFocusManager* AudioFocusManager::GetInstance() {
return base::Singleton<AudioFocusManager>::get(); return base::Singleton<AudioFocusManager>::get();
...@@ -55,8 +68,11 @@ void AudioFocusManager::RequestAudioFocus(MediaSessionImpl* media_session, ...@@ -55,8 +68,11 @@ void AudioFocusManager::RequestAudioFocus(MediaSessionImpl* media_session,
audio_focus_stack_.back()->StopDucking(); audio_focus_stack_.back()->StopDucking();
// Notify observers that we were gained audio focus. // Notify observers that we were gained audio focus.
for (AudioFocusObserver* observer : audio_focus_observers_) observers_.ForAllPtrs(
observer->OnFocusGained(media_session, type); [media_session,
type](media_session::mojom::AudioFocusObserver* observer) {
observer->OnFocusGained(GetSessionMojoPtr(media_session), type);
});
} }
void AudioFocusManager::AbandonAudioFocus(MediaSessionImpl* media_session) { void AudioFocusManager::AbandonAudioFocus(MediaSessionImpl* media_session) {
...@@ -71,8 +87,10 @@ void AudioFocusManager::AbandonAudioFocus(MediaSessionImpl* media_session) { ...@@ -71,8 +87,10 @@ void AudioFocusManager::AbandonAudioFocus(MediaSessionImpl* media_session) {
audio_focus_stack_.pop_back(); audio_focus_stack_.pop_back();
if (audio_focus_stack_.empty()) { if (audio_focus_stack_.empty()) {
// Notify observers that we lost audio focus. // Notify observers that we lost audio focus.
for (AudioFocusObserver* observer : audio_focus_observers_) observers_.ForAllPtrs(
observer->OnFocusLost(media_session); [media_session](media_session::mojom::AudioFocusObserver* observer) {
observer->OnFocusLost(GetSessionMojoPtr(media_session));
});
return; return;
} }
...@@ -96,19 +114,35 @@ void AudioFocusManager::AbandonAudioFocus(MediaSessionImpl* media_session) { ...@@ -96,19 +114,35 @@ void AudioFocusManager::AbandonAudioFocus(MediaSessionImpl* media_session) {
audio_focus_stack_.back()->StopDucking(); audio_focus_stack_.back()->StopDucking();
// Notify observers that we lost audio focus. // Notify observers that we lost audio focus.
for (AudioFocusObserver* observer : audio_focus_observers_) observers_.ForAllPtrs(
observer->OnFocusLost(media_session); [media_session](media_session::mojom::AudioFocusObserver* observer) {
observer->OnFocusLost(GetSessionMojoPtr(media_session));
});
} }
void AudioFocusManager::AddObserver(AudioFocusObserver* observer) { mojo::InterfacePtrSetElementId AudioFocusManager::AddObserver(
audio_focus_observers_.push_back(observer); media_session::mojom::AudioFocusObserverPtr observer) {
return observers_.AddPtr(std::move(observer));
} }
void AudioFocusManager::RemoveObserver(AudioFocusObserver* observer) { void AudioFocusManager::RemoveObserver(mojo::InterfacePtrSetElementId id) {
audio_focus_observers_.remove(observer); observers_.RemovePtr(id);
} }
AudioFocusManager::AudioFocusManager() = default; void AudioFocusManager::ResetForTesting() {
audio_focus_stack_.clear();
observers_.CloseAll();
}
void AudioFocusManager::FlushForTesting() {
observers_.FlushForTesting();
}
AudioFocusManager::AudioFocusManager() {
// Make sure we start AudioFocusManager on the browser UI thread. This is to
// ensure thread consistency for mojo::InterfaceSetPtr.
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
AudioFocusManager::~AudioFocusManager() = default; AudioFocusManager::~AudioFocusManager() = default;
......
...@@ -11,16 +11,11 @@ ...@@ -11,16 +11,11 @@
#include "base/memory/singleton.h" #include "base/memory/singleton.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_observer.h"
#include "mojo/public/cpp/bindings/interface_ptr_set.h"
namespace media_session { #include "services/media_session/public/mojom/audio_focus.mojom.h"
namespace mojom {
enum class AudioFocusType;
} // namespace mojom
} // namespace media_session
namespace content { namespace content {
class AudioFocusObserver;
class MediaSessionImpl; class MediaSessionImpl;
class CONTENT_EXPORT AudioFocusManager { class CONTENT_EXPORT AudioFocusManager {
...@@ -34,16 +29,24 @@ class CONTENT_EXPORT AudioFocusManager { ...@@ -34,16 +29,24 @@ class CONTENT_EXPORT AudioFocusManager {
void AbandonAudioFocus(MediaSessionImpl* media_session); void AbandonAudioFocus(MediaSessionImpl* media_session);
// Adds/removes audio focus observers. // Adds/removes audio focus observers.
void AddObserver(AudioFocusObserver*); mojo::InterfacePtrSetElementId AddObserver(
void RemoveObserver(AudioFocusObserver*); media_session::mojom::AudioFocusObserverPtr);
void RemoveObserver(mojo::InterfacePtrSetElementId);
private: private:
friend struct base::DefaultSingletonTraits<AudioFocusManager>; friend struct base::DefaultSingletonTraits<AudioFocusManager>;
friend class AudioFocusManagerTest; friend class AudioFocusManagerTest;
// Media internals UI needs access to internal state. // Media internals UI needs access to internal state.
friend class MediaInternalsAudioFocusTest;
friend class MediaInternals; friend class MediaInternals;
// Flush for testing will flush any pending messages to the observers.
void FlushForTesting();
// Reset for testing will clear any built up internal state.
void ResetForTesting();
AudioFocusManager(); AudioFocusManager();
~AudioFocusManager(); ~AudioFocusManager();
...@@ -51,7 +54,7 @@ class CONTENT_EXPORT AudioFocusManager { ...@@ -51,7 +54,7 @@ class CONTENT_EXPORT AudioFocusManager {
// Weak reference of managed observers. Observers are expected to remove // Weak reference of managed observers. Observers are expected to remove
// themselves before being destroyed. // themselves before being destroyed.
std::list<AudioFocusObserver*> audio_focus_observers_; mojo::InterfacePtrSet<media_session::mojom::AudioFocusObserver> observers_;
// Weak reference of managed MediaSessions. A MediaSession must abandon audio // Weak reference of managed MediaSessions. A MediaSession must abandon audio
// focus before its destruction. // focus before its destruction.
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <memory> #include <memory>
#include "base/command_line.h" #include "base/command_line.h"
#include "base/optional.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "content/browser/media/session/audio_focus_observer.h" #include "content/browser/media/session/audio_focus_observer.h"
#include "content/browser/media/session/media_session_impl.h" #include "content/browser/media/session/media_session_impl.h"
...@@ -18,19 +19,32 @@ ...@@ -18,19 +19,32 @@
#include "media/base/media_content_type.h" #include "media/base/media_content_type.h"
#include "media/base/media_switches.h" #include "media/base/media_switches.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h" #include "services/media_session/public/mojom/audio_focus.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace content { namespace content {
using AudioFocusType = media_session::mojom::AudioFocusType; using media_session::mojom::AudioFocusType;
using media_session::mojom::MediaSessionPtr;
namespace { namespace {
class MockAudioFocusObserver : public AudioFocusObserver { class MockAudioFocusObserver : public AudioFocusObserver {
public: public:
MOCK_METHOD2(OnFocusGained, MockAudioFocusObserver() { RegisterAudioFocusObserver(); }
void(MediaSession* media_session, AudioFocusType type));
MOCK_METHOD1(OnFocusLost, void(MediaSession* media_session)); void OnFocusGained(MediaSessionPtr session, AudioFocusType type) override {
EXPECT_TRUE(session.is_bound());
focus_gained_call_ = type;
focus_lost_call_ = false;
}
void OnFocusLost(MediaSessionPtr session) override {
EXPECT_TRUE(session.is_bound());
focus_lost_call_ = true;
focus_gained_call_.reset();
}
base::Optional<AudioFocusType> focus_gained_call_;
bool focus_lost_call_ = false;
}; };
class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver { class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver {
...@@ -60,6 +74,10 @@ class AudioFocusManagerTest : public testing::Test { ...@@ -60,6 +74,10 @@ class AudioFocusManagerTest : public testing::Test {
rph_factory_.get()); rph_factory_.get());
browser_context_.reset(new TestBrowserContext()); browser_context_.reset(new TestBrowserContext());
pepper_observer_.reset(new MockMediaSessionPlayerObserver()); pepper_observer_.reset(new MockMediaSessionPlayerObserver());
// AudioFocusManager is a singleton so we should make sure we reset any
// state in between tests.
AudioFocusManager::GetInstance()->ResetForTesting();
} }
void TearDown() override { void TearDown() override {
...@@ -110,6 +128,10 @@ class AudioFocusManagerTest : public testing::Test { ...@@ -110,6 +128,10 @@ class AudioFocusManagerTest : public testing::Test {
session->AbandonSystemAudioFocusIfNeeded(); session->AbandonSystemAudioFocusIfNeeded();
} }
void FlushForTesting() {
AudioFocusManager::GetInstance()->FlushForTesting();
}
std::unique_ptr<WebContents> CreateWebContents() { std::unique_ptr<WebContents> CreateWebContents() {
return TestWebContents::Create(browser_context_.get(), return TestWebContents::Create(browser_context_.get(),
SiteInstance::SiteInstance::Create(browser_context_.get())); SiteInstance::SiteInstance::Create(browser_context_.get()));
...@@ -243,11 +265,14 @@ TEST_F(AudioFocusManagerTest, AbandonAudioFocus_RemovesTransientEntry) { ...@@ -243,11 +265,14 @@ TEST_F(AudioFocusManagerTest, AbandonAudioFocus_RemovesTransientEntry) {
RequestAudioFocus(media_session, AudioFocusType::kGainTransientMayDuck); RequestAudioFocus(media_session, AudioFocusType::kGainTransientMayDuck);
ASSERT_EQ(1, GetTransientMaybeDuckCount()); ASSERT_EQ(1, GetTransientMaybeDuckCount());
MockAudioFocusObserver observer; {
EXPECT_CALL(observer, OnFocusLost(media_session)); MockAudioFocusObserver observer;
AbandonAudioFocus(media_session);
FlushForTesting();
AbandonAudioFocus(media_session); EXPECT_EQ(0, GetTransientMaybeDuckCount());
ASSERT_EQ(0, GetTransientMaybeDuckCount()); EXPECT_TRUE(observer.focus_lost_call_);
}
} }
TEST_F(AudioFocusManagerTest, AbandonAudioFocus_WhileDuckingThenResume) { TEST_F(AudioFocusManagerTest, AbandonAudioFocus_WhileDuckingThenResume) {
...@@ -465,10 +490,11 @@ TEST_F(AudioFocusManagerTest, AudioFocusObserver_AbandonNoop) { ...@@ -465,10 +490,11 @@ TEST_F(AudioFocusManagerTest, AudioFocusObserver_AbandonNoop) {
{ {
MockAudioFocusObserver observer; MockAudioFocusObserver observer;
EXPECT_CALL(observer, OnFocusLost(media_session)).Times(0);
AbandonAudioFocus(media_session); AbandonAudioFocus(media_session);
FlushForTesting();
EXPECT_EQ(nullptr, GetAudioFocusedSession()); EXPECT_EQ(nullptr, GetAudioFocusedSession());
EXPECT_FALSE(observer.focus_lost_call_);
} }
} }
...@@ -476,16 +502,22 @@ TEST_F(AudioFocusManagerTest, AudioFocusObserver_RequestNoop) { ...@@ -476,16 +502,22 @@ TEST_F(AudioFocusManagerTest, AudioFocusObserver_RequestNoop) {
std::unique_ptr<WebContents> web_contents(CreateWebContents()); std::unique_ptr<WebContents> web_contents(CreateWebContents());
MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get()); MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get());
RequestAudioFocus(media_session, AudioFocusType::kGain);
EXPECT_EQ(media_session, GetAudioFocusedSession());
{ {
MockAudioFocusObserver observer; MockAudioFocusObserver observer;
EXPECT_CALL(observer, OnFocusGained(media_session, AudioFocusType::kGain)) RequestAudioFocus(media_session, AudioFocusType::kGain);
.Times(0); FlushForTesting();
EXPECT_EQ(media_session, GetAudioFocusedSession());
EXPECT_EQ(AudioFocusType::kGain, observer.focus_gained_call_.value());
}
{
MockAudioFocusObserver observer;
RequestAudioFocus(media_session, AudioFocusType::kGain); RequestAudioFocus(media_session, AudioFocusType::kGain);
FlushForTesting();
EXPECT_EQ(media_session, GetAudioFocusedSession()); EXPECT_EQ(media_session, GetAudioFocusedSession());
EXPECT_FALSE(observer.focus_gained_call_.has_value());
} }
} }
...@@ -495,19 +527,21 @@ TEST_F(AudioFocusManagerTest, AudioFocusObserver_TransientMayDuck) { ...@@ -495,19 +527,21 @@ TEST_F(AudioFocusManagerTest, AudioFocusObserver_TransientMayDuck) {
{ {
MockAudioFocusObserver observer; MockAudioFocusObserver observer;
EXPECT_CALL(observer, OnFocusGained(media_session,
AudioFocusType::kGainTransientMayDuck));
RequestAudioFocus(media_session, AudioFocusType::kGainTransientMayDuck); RequestAudioFocus(media_session, AudioFocusType::kGainTransientMayDuck);
FlushForTesting();
EXPECT_EQ(1, GetTransientMaybeDuckCount()); EXPECT_EQ(1, GetTransientMaybeDuckCount());
EXPECT_EQ(AudioFocusType::kGainTransientMayDuck,
observer.focus_gained_call_.value());
} }
{ {
MockAudioFocusObserver observer; MockAudioFocusObserver observer;
EXPECT_CALL(observer, OnFocusLost(media_session));
AbandonAudioFocus(media_session); AbandonAudioFocus(media_session);
FlushForTesting();
EXPECT_EQ(0, GetTransientMaybeDuckCount()); EXPECT_EQ(0, GetTransientMaybeDuckCount());
EXPECT_TRUE(observer.focus_lost_call_);
} }
} }
......
...@@ -6,21 +6,41 @@ ...@@ -6,21 +6,41 @@
#include "build/build_config.h" #include "build/build_config.h"
#include "content/browser/media/session/audio_focus_manager.h" #include "content/browser/media/session/audio_focus_manager.h"
#include "content/public/browser/browser_thread.h"
#include "mojo/public/cpp/bindings/interface_request.h"
namespace content { namespace content {
AudioFocusObserver::AudioFocusObserver() { AudioFocusObserver::AudioFocusObserver() : binding_(this) {}
void AudioFocusObserver::RegisterAudioFocusObserver() {
if (observer_id_.has_value())
return;
#if !defined(OS_ANDROID) #if !defined(OS_ANDROID)
// TODO(https://crbug.com/873320): Add support for Android. // TODO(https://crbug.com/873320): Add support for Android.
AudioFocusManager::GetInstance()->AddObserver(this); DCHECK_CURRENTLY_ON(BrowserThread::UI);
media_session::mojom::AudioFocusObserverPtr observer;
binding_.Bind(mojo::MakeRequest(&observer));
observer_id_ =
AudioFocusManager::GetInstance()->AddObserver(std::move(observer));
#endif #endif
} }
AudioFocusObserver::~AudioFocusObserver() { void AudioFocusObserver::UnregisterAudioFocusObserver() {
if (!observer_id_.has_value())
return;
#if !defined(OS_ANDROID) #if !defined(OS_ANDROID)
// TODO(https://crbug.com/873320): Add support for Android. // TODO(https://crbug.com/873320): Add support for Android.
AudioFocusManager::GetInstance()->RemoveObserver(this); DCHECK_CURRENTLY_ON(BrowserThread::UI);
binding_.Close();
AudioFocusManager::GetInstance()->RemoveObserver(observer_id_.value());
#endif #endif
observer_id_.reset();
} }
AudioFocusObserver::~AudioFocusObserver() = default;
} // namespace content } // namespace content
...@@ -6,33 +6,39 @@ ...@@ -6,33 +6,39 @@
#define CONTENT_BROWSER_MEDIA_SESSION_AUDIO_FOCUS_OBSERVER_H_ #define CONTENT_BROWSER_MEDIA_SESSION_AUDIO_FOCUS_OBSERVER_H_
#include "base/macros.h" #include "base/macros.h"
#include "base/optional.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "mojo/public/cpp/bindings/binding.h"
namespace media_session { #include "mojo/public/cpp/bindings/interface_ptr_set.h"
namespace mojom { #include "services/media_session/public/mojom/audio_focus.mojom.h"
enum class AudioFocusType;
} // namespace mojom
} // namespace media_session
namespace content { namespace content {
class MediaSession;
// The observer for observing audio focus events. This will not work on Android // The observer for observing audio focus events. This will not work on Android
// as it does not use the internal AudioFocusManager implementation. // as it does not use the internal AudioFocusManager implementation.
class CONTENT_EXPORT AudioFocusObserver { class CONTENT_EXPORT AudioFocusObserver
: public media_session::mojom::AudioFocusObserver {
public: public:
AudioFocusObserver(); AudioFocusObserver();
~AudioFocusObserver(); ~AudioFocusObserver() override;
// The given media session gained audio focus with the specified type.
void OnFocusGained(::media_session::mojom::MediaSessionPtr,
media_session::mojom::AudioFocusType) override {}
// The given |MediaSession| gained audio focus with the specified type. // The given media session lost audio focus.
virtual void OnFocusGained(MediaSession*, void OnFocusLost(::media_session::mojom::MediaSessionPtr) override {}
media_session::mojom::AudioFocusType) = 0;
// The given |NediaSession| lost audio focus. protected:
virtual void OnFocusLost(MediaSession*) = 0; // Called by subclasses to (un-)register the observer with AudioFocusManager.
void RegisterAudioFocusObserver();
void UnregisterAudioFocusObserver();
private: private:
base::Optional<mojo::InterfacePtrSetElementId> observer_id_;
mojo::Binding<media_session::mojom::AudioFocusObserver> binding_;
DISALLOW_COPY_AND_ASSIGN(AudioFocusObserver); DISALLOW_COPY_AND_ASSIGN(AudioFocusObserver);
}; };
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "content/browser/media/session/media_session_impl.h" #include "content/browser/media/session/media_session_impl.h"
#include <algorithm> #include <algorithm>
#include <utility>
#include "base/numerics/ranges.h" #include "base/numerics/ranges.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
...@@ -628,6 +629,11 @@ const MediaSessionImpl::DebugInfo MediaSessionImpl::GetDebugInfo() { ...@@ -628,6 +629,11 @@ const MediaSessionImpl::DebugInfo MediaSessionImpl::GetDebugInfo() {
return debug_info; return debug_info;
} }
void MediaSessionImpl::BindToMojoRequest(
mojo::InterfaceRequest<media_session::mojom::MediaSession> request) {
bindings_.AddBinding(this, std::move(request));
}
void MediaSessionImpl::AbandonSystemAudioFocusIfNeeded() { void MediaSessionImpl::AbandonSystemAudioFocusIfNeeded() {
if (audio_focus_state_ == State::INACTIVE || !normal_players_.empty() || if (audio_focus_state_ == State::INACTIVE || !normal_players_.empty() ||
!pepper_players_.empty() || !one_shot_players_.empty()) { !pepper_players_.empty() || !one_shot_players_.empty()) {
......
...@@ -23,6 +23,9 @@ ...@@ -23,6 +23,9 @@
#include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h" #include "content/public/browser/web_contents_user_data.h"
#include "content/public/common/media_metadata.h" #include "content/public/common/media_metadata.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h"
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
#include "base/android/scoped_java_ref.h" #include "base/android/scoped_java_ref.h"
...@@ -223,6 +226,10 @@ class MediaSessionImpl : public MediaSession, ...@@ -223,6 +226,10 @@ class MediaSessionImpl : public MediaSession,
}; };
const DebugInfo GetDebugInfo(); const DebugInfo GetDebugInfo();
// Creates a binding between |this| and |request|.
void BindToMojoRequest(
mojo::InterfaceRequest<media_session::mojom::MediaSession> request);
private: private:
friend class content::WebContentsUserData<MediaSessionImpl>; friend class content::WebContentsUserData<MediaSessionImpl>;
friend class ::MediaSessionImplBrowserTest; friend class ::MediaSessionImplBrowserTest;
...@@ -345,6 +352,9 @@ class MediaSessionImpl : public MediaSession, ...@@ -345,6 +352,9 @@ class MediaSessionImpl : public MediaSession,
// The currently routed service (non-owned pointer). // The currently routed service (non-owned pointer).
MediaSessionServiceImpl* routed_service_; MediaSessionServiceImpl* routed_service_;
// Bindings for Mojo pointers to |this| held by media route providers.
mojo::BindingSet<media_session::mojom::MediaSession> bindings_;
DISALLOW_COPY_AND_ASSIGN(MediaSessionImpl); DISALLOW_COPY_AND_ASSIGN(MediaSessionImpl);
}; };
......
...@@ -337,6 +337,7 @@ jumbo_source_set("browser_sources") { ...@@ -337,6 +337,7 @@ jumbo_source_set("browser_sources") {
"//ipc", "//ipc",
"//mojo/public/cpp/bindings", "//mojo/public/cpp/bindings",
"//mojo/public/cpp/system", "//mojo/public/cpp/system",
"//services/media_session/public/mojom",
"//services/network/public/mojom", "//services/network/public/mojom",
"//services/resource_coordinator/public/cpp:resource_coordinator_cpp", "//services/resource_coordinator/public/cpp:resource_coordinator_cpp",
"//services/service_manager/public/cpp", "//services/service_manager/public/cpp",
......
...@@ -7,6 +7,7 @@ include_rules = [ ...@@ -7,6 +7,7 @@ include_rules = [
"+device/usb/public/mojom", "+device/usb/public/mojom",
"+device/fido", "+device/fido",
"+services/device/public", "+services/device/public",
"+services/media_session/public",
"+services/network/public/cpp", "+services/network/public/cpp",
"+services/service_manager/sandbox", "+services/service_manager/sandbox",
"+services/resource_coordinator/public", "+services/resource_coordinator/public",
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h"
namespace blink { namespace blink {
namespace mojom { namespace mojom {
...@@ -25,7 +26,7 @@ class WebContents; ...@@ -25,7 +26,7 @@ class WebContents;
// //
// MediaSession allows clients to observe its changes via MediaSessionObserver, // MediaSession allows clients to observe its changes via MediaSessionObserver,
// and allows clients to resume/suspend/stop the managed players. // and allows clients to resume/suspend/stop the managed players.
class MediaSession { class MediaSession : public media_session::mojom::MediaSession {
public: public:
enum class SuspendType { enum class SuspendType {
// Suspended by the system because a transient sound needs to be played. // Suspended by the system because a transient sound needs to be played.
...@@ -40,7 +41,7 @@ class MediaSession { ...@@ -40,7 +41,7 @@ class MediaSession {
// none is currently available. // none is currently available.
CONTENT_EXPORT static MediaSession* Get(WebContents* contents); CONTENT_EXPORT static MediaSession* Get(WebContents* contents);
virtual ~MediaSession() = default; ~MediaSession() override = default;
// Resume the media session. // Resume the media session.
// |type| represents the origin of the request. // |type| represents the origin of the request.
......
...@@ -10,6 +10,7 @@ mojom_component("mojom") { ...@@ -10,6 +10,7 @@ mojom_component("mojom") {
sources = [ sources = [
"audio_focus.mojom", "audio_focus.mojom",
"media_session.mojom",
] ]
public_deps = [ public_deps = [
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
module media_session.mojom; module media_session.mojom;
import "services/media_session/public/mojom/media_session.mojom";
// These are the different types of audio focus that can be requested. // These are the different types of audio focus that can be requested.
enum AudioFocusType { enum AudioFocusType {
// Request permanent audio focus when you plan to play audio for the // Request permanent audio focus when you plan to play audio for the
...@@ -15,3 +17,12 @@ enum AudioFocusType { ...@@ -15,3 +17,12 @@ enum AudioFocusType {
// short time and you expect the previous holder to pause playing. // short time and you expect the previous holder to pause playing.
kGainTransientMayDuck, kGainTransientMayDuck,
}; };
// The observer for audio focus events.
interface AudioFocusObserver {
// The given |session| gained audio focus with the specified |type|.
OnFocusGained(MediaSession session, AudioFocusType type);
// The given |session| lost audio focus.
OnFocusLost(MediaSession session);
};
// Copyright 2018 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.
module media_session.mojom;
// A MediaSession manages the media session and audio focus for a given
// WebContents or ARC app.
// TODO(https://crbug.com/875004): migrate media session from content/public
// to mojo.
interface MediaSession {};
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