Commit 31479d42 authored by Becca Hughes's avatar Becca Hughes Committed by Commit Bot

[Audio Focus] Add grouped audio focus

Add grouped audio focus. This means that a media session
can provide an unguessable token when requesting audio
focus. This means that the media session service will
treat all media sessions with the same group id as the
same session when it comes to audio focus.

BUG=906285

Change-Id: I184f8de58cfe3cec67db0bb5877f3b352c794f13
Reviewed-on: https://chromium-review.googlesource.com/c/1342939
Commit-Queue: Becca Hughes <beccahughes@chromium.org>
Reviewed-by: default avatarChrome Cunningham <chcunningham@chromium.org>
Reviewed-by: default avatarTommy Steimel <steimel@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#611805}
parent 38ee168d
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "content/browser/media/session/audio_focus_delegate.h" #include "content/browser/media/session/audio_focus_delegate.h"
#include "base/no_destructor.h"
#include "base/unguessable_token.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/browser_thread.h"
#include "content/public/common/service_manager_connection.h" #include "content/public/common/service_manager_connection.h"
...@@ -20,6 +22,12 @@ namespace { ...@@ -20,6 +22,12 @@ namespace {
const char kAudioFocusSourceName[] = "web"; const char kAudioFocusSourceName[] = "web";
static const base::UnguessableToken& GetBrowserGroupId() {
static const base::NoDestructor<base::UnguessableToken> token(
base::UnguessableToken::Create());
return *token;
}
// AudioFocusDelegateDefault is the default implementation of // AudioFocusDelegateDefault is the default implementation of
// AudioFocusDelegate which only handles audio focus between WebContents. // AudioFocusDelegate which only handles audio focus between WebContents.
class AudioFocusDelegateDefault : public AudioFocusDelegate { class AudioFocusDelegateDefault : public AudioFocusDelegate {
...@@ -91,9 +99,12 @@ AudioFocusDelegateDefault::RequestAudioFocus(AudioFocusType audio_focus_type) { ...@@ -91,9 +99,12 @@ AudioFocusDelegateDefault::RequestAudioFocus(AudioFocusType audio_focus_type) {
media_session::mojom::MediaSessionPtr media_session; media_session::mojom::MediaSessionPtr media_session;
media_session_->BindToMojoRequest(mojo::MakeRequest(&media_session)); media_session_->BindToMojoRequest(mojo::MakeRequest(&media_session));
audio_focus_ptr_->RequestAudioFocus( audio_focus_ptr_->RequestGroupedAudioFocus(
mojo::MakeRequest(&request_client_ptr_), std::move(media_session), mojo::MakeRequest(&request_client_ptr_), std::move(media_session),
session_info_.Clone(), audio_focus_type, session_info_.Clone(), audio_focus_type,
media_session_->audio_focus_group_id() == base::UnguessableToken::Null()
? GetBrowserGroupId()
: media_session_->audio_focus_group_id(),
base::BindOnce(&AudioFocusDelegateDefault::FinishAudioFocusRequest, base::BindOnce(&AudioFocusDelegateDefault::FinishAudioFocusRequest,
base::Unretained(this), audio_focus_type)); base::Unretained(this), audio_focus_type));
} }
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "base/command_line.h" #include "base/command_line.h"
#include "base/unguessable_token.h"
#include "content/browser/media/session/media_session_impl.h" #include "content/browser/media/session/media_session_impl.h"
#include "content/browser/media/session/mock_media_session_player_observer.h" #include "content/browser/media/session/mock_media_session_player_observer.h"
#include "content/public/common/service_manager_connection.h" #include "content/public/common/service_manager_connection.h"
...@@ -93,7 +94,9 @@ class AudioFocusDelegateDefaultBrowserTest : public ContentBrowserTest { ...@@ -93,7 +94,9 @@ class AudioFocusDelegateDefaultBrowserTest : public ContentBrowserTest {
audio_focus_ptr_.FlushForTesting(); audio_focus_ptr_.FlushForTesting();
} }
void Run(WebContents* start_contents, WebContents* interrupt_contents) { void Run(WebContents* start_contents,
WebContents* interrupt_contents,
bool use_separate_group_id) {
std::unique_ptr<MockMediaSessionPlayerObserver> std::unique_ptr<MockMediaSessionPlayerObserver>
player_observer(new MockMediaSessionPlayerObserver); player_observer(new MockMediaSessionPlayerObserver);
...@@ -104,6 +107,10 @@ class AudioFocusDelegateDefaultBrowserTest : public ContentBrowserTest { ...@@ -104,6 +107,10 @@ class AudioFocusDelegateDefaultBrowserTest : public ContentBrowserTest {
MediaSessionImpl::Get(interrupt_contents); MediaSessionImpl::Get(interrupt_contents);
EXPECT_TRUE(other_media_session); EXPECT_TRUE(other_media_session);
if (use_separate_group_id)
other_media_session->SetAudioFocusGroupId(
base::UnguessableToken::Create());
player_observer->StartNewPlayer(); player_observer->StartNewPlayer();
{ {
...@@ -139,7 +146,9 @@ class AudioFocusDelegateDefaultBrowserTest : public ContentBrowserTest { ...@@ -139,7 +146,9 @@ class AudioFocusDelegateDefaultBrowserTest : public ContentBrowserTest {
{ {
MediaSessionStateObserver state_observer(media_session); MediaSessionStateObserver state_observer(media_session);
state_observer.WaitForState( state_observer.WaitForState(
media_session::mojom::MediaSessionInfo::SessionState::kSuspended); use_separate_group_id
? media_session::mojom::MediaSessionInfo::SessionState::kSuspended
: media_session::mojom::MediaSessionInfo::SessionState::kActive);
} }
{ {
...@@ -186,20 +195,28 @@ class AudioFocusDelegateDefaultBrowserTest : public ContentBrowserTest { ...@@ -186,20 +195,28 @@ class AudioFocusDelegateDefaultBrowserTest : public ContentBrowserTest {
// Two windows from the same BrowserContext. // Two windows from the same BrowserContext.
IN_PROC_BROWSER_TEST_F(AudioFocusDelegateDefaultBrowserTest, IN_PROC_BROWSER_TEST_F(AudioFocusDelegateDefaultBrowserTest,
ActiveWebContentsPauseOthers) { ActiveWebContentsPausesOthers) {
Run(shell()->web_contents(), CreateBrowser()->web_contents()); Run(shell()->web_contents(), CreateBrowser()->web_contents(), false);
}
// Two windows with different group ids.
IN_PROC_BROWSER_TEST_F(AudioFocusDelegateDefaultBrowserTest,
ActiveWebContentsPausesOtherWithGroupId) {
Run(shell()->web_contents(), CreateBrowser()->web_contents(), true);
} }
// Regular BrowserContext is interrupted by OffTheRecord one. // Regular BrowserContext is interrupted by OffTheRecord one.
IN_PROC_BROWSER_TEST_F(AudioFocusDelegateDefaultBrowserTest, IN_PROC_BROWSER_TEST_F(AudioFocusDelegateDefaultBrowserTest,
RegularBrowserInterruptsOffTheRecord) { RegularBrowserInterruptsOffTheRecord) {
Run(shell()->web_contents(), CreateOffTheRecordBrowser()->web_contents()); Run(shell()->web_contents(), CreateOffTheRecordBrowser()->web_contents(),
false);
} }
// OffTheRecord BrowserContext is interrupted by regular one. // OffTheRecord BrowserContext is interrupted by regular one.
IN_PROC_BROWSER_TEST_F(AudioFocusDelegateDefaultBrowserTest, IN_PROC_BROWSER_TEST_F(AudioFocusDelegateDefaultBrowserTest,
OffTheRecordInterruptsRegular) { OffTheRecordInterruptsRegular) {
Run(CreateOffTheRecordBrowser()->web_contents(), shell()->web_contents()); Run(CreateOffTheRecordBrowser()->web_contents(), shell()->web_contents(),
false);
} }
} // namespace content } // namespace content
...@@ -499,6 +499,11 @@ void MediaSessionImpl::SetDuckingVolumeMultiplier(double multiplier) { ...@@ -499,6 +499,11 @@ void MediaSessionImpl::SetDuckingVolumeMultiplier(double multiplier) {
ducking_volume_multiplier_ = base::ClampToRange(multiplier, 0.0, 1.0); ducking_volume_multiplier_ = base::ClampToRange(multiplier, 0.0, 1.0);
} }
void MediaSessionImpl::SetAudioFocusGroupId(
const base::UnguessableToken& group_id) {
audio_focus_group_id_ = group_id;
}
void MediaSessionImpl::StartDucking() { void MediaSessionImpl::StartDucking() {
if (is_ducking_) if (is_ducking_)
return; return;
......
...@@ -197,6 +197,12 @@ class MediaSessionImpl : public MediaSession, ...@@ -197,6 +197,12 @@ class MediaSessionImpl : public MediaSession,
// Set the volume multiplier applied during ducking. // Set the volume multiplier applied during ducking.
CONTENT_EXPORT void SetDuckingVolumeMultiplier(double multiplier) override; CONTENT_EXPORT void SetDuckingVolumeMultiplier(double multiplier) override;
// Set the audio focus group id for this media session. Sessions in the same
// group can share audio focus. Setting this to null will use the browser
// default value.
CONTENT_EXPORT void SetAudioFocusGroupId(
const base::UnguessableToken& group_id);
// Suspend the media session. // Suspend the media session.
// |type| represents the origin of the request. // |type| represents the origin of the request.
CONTENT_EXPORT void Suspend(MediaSession::SuspendType suspend_type) override; CONTENT_EXPORT void Suspend(MediaSession::SuspendType suspend_type) override;
...@@ -232,6 +238,10 @@ class MediaSessionImpl : public MediaSession, ...@@ -232,6 +238,10 @@ class MediaSessionImpl : public MediaSession,
// Skip to the next track. // Skip to the next track.
CONTENT_EXPORT void NextTrack() override; CONTENT_EXPORT void NextTrack() override;
const base::UnguessableToken& audio_focus_group_id() const {
return audio_focus_group_id_;
}
private: private:
friend class content::WebContentsUserData<MediaSessionImpl>; friend class content::WebContentsUserData<MediaSessionImpl>;
friend class ::MediaSessionImplBrowserTest; friend class ::MediaSessionImplBrowserTest;
...@@ -355,6 +365,8 @@ class MediaSessionImpl : public MediaSession, ...@@ -355,6 +365,8 @@ class MediaSessionImpl : public MediaSession,
// StopDucking(). // StopDucking().
bool is_ducking_; bool is_ducking_;
base::UnguessableToken audio_focus_group_id_ = base::UnguessableToken::Null();
double ducking_volume_multiplier_; double ducking_volume_multiplier_;
base::CallbackList<void(State)> media_session_state_listeners_; base::CallbackList<void(State)> media_session_state_listeners_;
......
...@@ -25,9 +25,11 @@ class AudioFocusManager::StackRow : public mojom::AudioFocusRequestClient { ...@@ -25,9 +25,11 @@ class AudioFocusManager::StackRow : public mojom::AudioFocusRequestClient {
mojom::MediaSessionInfoPtr session_info, mojom::MediaSessionInfoPtr session_info,
mojom::AudioFocusType audio_focus_type, mojom::AudioFocusType audio_focus_type,
RequestId id, RequestId id,
const std::string& source_name) const std::string& source_name,
const base::UnguessableToken& group_id)
: id_(id), : id_(id),
source_name_(source_name), source_name_(source_name),
group_id_(group_id),
metrics_helper_(source_name), metrics_helper_(source_name),
session_(std::move(session)), session_(std::move(session)),
session_info_(std::move(session_info)), session_info_(std::move(session_info)),
...@@ -106,6 +108,8 @@ class AudioFocusManager::StackRow : public mojom::AudioFocusRequestClient { ...@@ -106,6 +108,8 @@ class AudioFocusManager::StackRow : public mojom::AudioFocusRequestClient {
const std::string& source_name() const { return source_name_; } const std::string& source_name() const { return source_name_; }
const base::UnguessableToken& group_id() const { return group_id_; }
mojom::AudioFocusRequestStatePtr ToAudioFocusRequestState() const { mojom::AudioFocusRequestStatePtr ToAudioFocusRequestState() const {
auto request = mojom::AudioFocusRequestState::New(); auto request = mojom::AudioFocusRequestState::New();
request->session_info = session_info_.Clone(); request->session_info = session_info_.Clone();
...@@ -135,6 +139,7 @@ class AudioFocusManager::StackRow : public mojom::AudioFocusRequestClient { ...@@ -135,6 +139,7 @@ class AudioFocusManager::StackRow : public mojom::AudioFocusRequestClient {
const RequestId id_; const RequestId id_;
const std::string source_name_; const std::string source_name_;
const base::UnguessableToken group_id_;
AudioFocusManagerMetricsHelper metrics_helper_; AudioFocusManagerMetricsHelper metrics_helper_;
bool encountered_error_ = false; bool encountered_error_ = false;
...@@ -156,11 +161,23 @@ void AudioFocusManager::RequestAudioFocus( ...@@ -156,11 +161,23 @@ void AudioFocusManager::RequestAudioFocus(
mojom::MediaSessionInfoPtr session_info, mojom::MediaSessionInfoPtr session_info,
mojom::AudioFocusType type, mojom::AudioFocusType type,
RequestAudioFocusCallback callback) { RequestAudioFocusCallback callback) {
RequestGroupedAudioFocus(
std::move(request), std::move(media_session), std::move(session_info),
type, base::UnguessableToken::Create(), std::move(callback));
}
void AudioFocusManager::RequestGroupedAudioFocus(
mojom::AudioFocusRequestClientRequest request,
mojom::MediaSessionPtr media_session,
mojom::MediaSessionInfoPtr session_info,
mojom::AudioFocusType type,
const base::UnguessableToken& group_id,
RequestGroupedAudioFocusCallback callback) {
RequestAudioFocusInternal( RequestAudioFocusInternal(
std::make_unique<StackRow>( std::make_unique<StackRow>(
this, std::move(request), std::move(media_session), this, std::move(request), std::move(media_session),
std::move(session_info), type, base::UnguessableToken::Create(), std::move(session_info), type, base::UnguessableToken::Create(),
GetBindingSourceName()), GetBindingSourceName(), group_id),
type, std::move(callback)); type, std::move(callback));
} }
...@@ -180,7 +197,20 @@ void AudioFocusManager::GetDebugInfoForRequest( ...@@ -180,7 +197,20 @@ void AudioFocusManager::GetDebugInfoForRequest(
if (row->id() != request_id) if (row->id() != request_id)
continue; continue;
row->session()->GetDebugInfo(std::move(callback)); row->session()->GetDebugInfo(base::BindOnce(
[](const base::UnguessableToken& group_id,
GetDebugInfoForRequestCallback callback,
mojom::MediaSessionDebugInfoPtr info) {
// Inject the |group_id| into the state string. This is because in
// some cases the group id is automatically generated by the media
// session service so the session is unaware of it.
if (!info->state.empty())
info->state += " ";
info->state += "GroupId=" + group_id.ToString();
std::move(callback).Run(std::move(info));
},
row->group_id(), std::move(callback)));
return; return;
} }
...@@ -264,7 +294,7 @@ void AudioFocusManager::RequestAudioFocusInternal( ...@@ -264,7 +294,7 @@ void AudioFocusManager::RequestAudioFocusInternal(
// If audio focus is enabled then we should enforce this request and make sure // If audio focus is enabled then we should enforce this request and make sure
// the new active session is not ducking. // the new active session is not ducking.
if (IsAudioFocusEnforcementEnabled()) { if (IsAudioFocusEnforcementEnabled()) {
EnforceAudioFocusRequest(type); EnforceAudioFocusRequest(type, row->group_id());
row->session()->StopDucking(); row->session()->StopDucking();
} }
...@@ -286,7 +316,9 @@ void AudioFocusManager::RequestAudioFocusInternal( ...@@ -286,7 +316,9 @@ void AudioFocusManager::RequestAudioFocusInternal(
std::move(callback).Run(); std::move(callback).Run();
} }
void AudioFocusManager::EnforceAudioFocusRequest(mojom::AudioFocusType type) { void AudioFocusManager::EnforceAudioFocusRequest(
mojom::AudioFocusType type,
const base::UnguessableToken& group_id) {
DCHECK(IsAudioFocusEnforcementEnabled()); DCHECK(IsAudioFocusEnforcementEnabled());
for (auto& old_session : audio_focus_stack_) { for (auto& old_session : audio_focus_stack_) {
...@@ -299,6 +331,11 @@ void AudioFocusManager::EnforceAudioFocusRequest(mojom::AudioFocusType type) { ...@@ -299,6 +331,11 @@ void AudioFocusManager::EnforceAudioFocusRequest(mojom::AudioFocusType type) {
switch (type) { switch (type) {
case mojom::AudioFocusType::kGain: case mojom::AudioFocusType::kGain:
case mojom::AudioFocusType::kGainTransient: case mojom::AudioFocusType::kGainTransient:
// If the session has the same group id as the new session then we
// should not suspend that session.
if (old_session->group_id() == group_id)
break;
old_session->session()->Suspend( old_session->session()->Suspend(
mojom::MediaSession::SuspendType::kSystem); mojom::MediaSession::SuspendType::kSystem);
break; break;
...@@ -338,7 +375,14 @@ void AudioFocusManager::EnforceAudioFocusAbandon(mojom::AudioFocusType type) { ...@@ -338,7 +375,14 @@ void AudioFocusManager::EnforceAudioFocusAbandon(mojom::AudioFocusType type) {
case mojom::AudioFocusType::kGainTransient: case mojom::AudioFocusType::kGainTransient:
// The abandoned session suspended all the media sessions but we should // The abandoned session suspended all the media sessions but we should
// start playing the top one again as the abandoned media was transient. // start playing the top one again as the abandoned media was transient.
top->session()->Resume(mojom::MediaSession::SuspendType::kSystem); // This will also apply to any sessions that have the same group_id as the
// new top most session.
for (auto& session : audio_focus_stack_) {
if (session->group_id() != top->group_id())
continue;
session->session()->Resume(mojom::MediaSession::SuspendType::kSystem);
}
break; break;
case mojom::AudioFocusType::kGainTransientMayDuck: case mojom::AudioFocusType::kGainTransientMayDuck:
// The abandoned session ducked all the media sessions so we should unduck // The abandoned session ducked all the media sessions so we should unduck
......
...@@ -45,6 +45,12 @@ class AudioFocusManager : public mojom::AudioFocusManager, ...@@ -45,6 +45,12 @@ class AudioFocusManager : public mojom::AudioFocusManager,
mojom::MediaSessionInfoPtr session_info, mojom::MediaSessionInfoPtr session_info,
mojom::AudioFocusType type, mojom::AudioFocusType type,
RequestAudioFocusCallback callback) override; RequestAudioFocusCallback callback) override;
void RequestGroupedAudioFocus(mojom::AudioFocusRequestClientRequest request,
mojom::MediaSessionPtr media_session,
mojom::MediaSessionInfoPtr session_info,
mojom::AudioFocusType type,
const base::UnguessableToken& group_id,
RequestAudioFocusCallback callback) override;
void GetFocusRequests(GetFocusRequestsCallback callback) override; void GetFocusRequests(GetFocusRequestsCallback callback) override;
void AddObserver(mojom::AudioFocusObserverPtr observer) override; void AddObserver(mojom::AudioFocusObserverPtr observer) override;
void SetSourceName(const std::string& name) override; void SetSourceName(const std::string& name) override;
...@@ -83,7 +89,8 @@ class AudioFocusManager : public mojom::AudioFocusManager, ...@@ -83,7 +89,8 @@ class AudioFocusManager : public mojom::AudioFocusManager,
void RequestAudioFocusInternal(std::unique_ptr<StackRow>, void RequestAudioFocusInternal(std::unique_ptr<StackRow>,
mojom::AudioFocusType, mojom::AudioFocusType,
base::OnceCallback<void()>); base::OnceCallback<void()>);
void EnforceAudioFocusRequest(mojom::AudioFocusType); void EnforceAudioFocusRequest(mojom::AudioFocusType type,
const base::UnguessableToken& group_id);
void AbandonAudioFocusInternal(RequestId); void AbandonAudioFocusInternal(RequestId);
void EnforceAudioFocusAbandon(mojom::AudioFocusType); void EnforceAudioFocusAbandon(mojom::AudioFocusType);
......
...@@ -99,6 +99,14 @@ class AudioFocusManagerTest : public testing::TestWithParam<bool> { ...@@ -99,6 +99,14 @@ class AudioFocusManagerTest : public testing::TestWithParam<bool> {
audio_focus_type); audio_focus_type);
} }
AudioFocusManager::RequestId RequestGroupedAudioFocus(
test::MockMediaSession* session,
mojom::AudioFocusType audio_focus_type,
const base::UnguessableToken& group_id) {
return session->RequestGroupedAudioFocusFromService(
audio_focus_ptr_, audio_focus_type, group_id);
}
mojom::MediaSessionDebugInfoPtr GetDebugInfo( mojom::MediaSessionDebugInfoPtr GetDebugInfo(
AudioFocusManager::RequestId request_id) { AudioFocusManager::RequestId request_id) {
mojom::MediaSessionDebugInfoPtr result; mojom::MediaSessionDebugInfoPtr result;
...@@ -1034,4 +1042,128 @@ TEST_P(AudioFocusManagerTest, ObserverActiveSessionChanged) { ...@@ -1034,4 +1042,128 @@ TEST_P(AudioFocusManagerTest, ObserverActiveSessionChanged) {
} }
} }
TEST_P(AudioFocusManagerTest, AudioFocusGrouping_AllowDucking) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
test::MockMediaSession media_session_3;
base::UnguessableToken group_id = base::UnguessableToken::Create();
RequestGroupedAudioFocus(&media_session_1, mojom::AudioFocusType::kGain,
group_id);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
RequestGroupedAudioFocus(&media_session_3, mojom::AudioFocusType::kGain,
group_id);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_3));
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest, AudioFocusGrouping_TransientResume) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
test::MockMediaSession media_session_3;
test::MockMediaSession media_session_4;
base::UnguessableToken group_id = base::UnguessableToken::Create();
RequestGroupedAudioFocus(&media_session_1, mojom::AudioFocusType::kGain,
group_id);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGain);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_2));
RequestGroupedAudioFocus(&media_session_3, mojom::AudioFocusType::kGain,
group_id);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_3));
RequestAudioFocus(&media_session_4, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_1));
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_2));
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_3));
media_session_4.AbandonAudioFocusFromClient();
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_2));
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_3));
}
TEST_P(AudioFocusManagerTest, AudioFocusGrouping_DoNotSuspendSameGroup) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
base::UnguessableToken group_id = base::UnguessableToken::Create();
RequestGroupedAudioFocus(&media_session_1, mojom::AudioFocusType::kGain,
group_id);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestGroupedAudioFocus(&media_session_2, mojom::AudioFocusType::kGain,
group_id);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_2));
}
TEST_P(AudioFocusManagerTest, AudioFocusGrouping_DuckSameGroup) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
base::UnguessableToken group_id = base::UnguessableToken::Create();
RequestGroupedAudioFocus(&media_session_1, mojom::AudioFocusType::kGain,
group_id);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestGroupedAudioFocus(
&media_session_2, mojom::AudioFocusType::kGainTransientMayDuck, group_id);
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest, AudioFocusGrouping_TransientSameGroup) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
base::UnguessableToken group_id = base::UnguessableToken::Create();
RequestGroupedAudioFocus(&media_session_1, mojom::AudioFocusType::kGain,
group_id);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestGroupedAudioFocus(&media_session_2,
mojom::AudioFocusType::kGainTransient, group_id);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_2));
}
} // namespace media_session } // namespace media_session
...@@ -174,6 +174,41 @@ base::UnguessableToken MockMediaSession::RequestAudioFocusFromService( ...@@ -174,6 +174,41 @@ base::UnguessableToken MockMediaSession::RequestAudioFocusFromService(
return GetRequestIdFromClient(); return GetRequestIdFromClient();
} }
base::UnguessableToken MockMediaSession::RequestGroupedAudioFocusFromService(
mojom::AudioFocusManagerPtr& service,
mojom::AudioFocusType audio_focus_type,
const base::UnguessableToken& group_id) {
bool result;
base::OnceClosure callback =
base::BindOnce([](bool* out_result) { *out_result = true; }, &result);
if (afr_client_.is_bound()) {
// Request audio focus through the existing request.
afr_client_->RequestAudioFocus(GetMediaSessionInfoSync(), audio_focus_type,
std::move(callback));
afr_client_.FlushForTesting();
} else {
// Build a new audio focus request.
mojom::MediaSessionPtr media_session;
bindings_.AddBinding(this, mojo::MakeRequest(&media_session));
service->RequestGroupedAudioFocus(
mojo::MakeRequest(&afr_client_), std::move(media_session),
GetMediaSessionInfoSync(), audio_focus_type, group_id,
std::move(callback));
service.FlushForTesting();
}
// If the audio focus was granted then we should set the session state to
// active.
if (result)
SetState(mojom::MediaSessionInfo::SessionState::kActive);
return GetRequestIdFromClient();
}
mojom::MediaSessionInfo::SessionState MockMediaSession::GetState() const { mojom::MediaSessionInfo::SessionState MockMediaSession::GetState() const {
return GetMediaSessionInfoSync()->state; return GetMediaSessionInfoSync()->state;
} }
......
...@@ -83,6 +83,11 @@ class COMPONENT_EXPORT(MEDIA_SESSION_TEST_SUPPORT_CPP) MockMediaSession ...@@ -83,6 +83,11 @@ class COMPONENT_EXPORT(MEDIA_SESSION_TEST_SUPPORT_CPP) MockMediaSession
mojom::AudioFocusManagerPtr&, mojom::AudioFocusManagerPtr&,
mojom::AudioFocusType); mojom::AudioFocusType);
base::UnguessableToken RequestGroupedAudioFocusFromService(
mojom::AudioFocusManagerPtr& service,
mojom::AudioFocusType audio_focus_type,
const base::UnguessableToken& group_id);
mojom::MediaSessionInfo::SessionState GetState() const; mojom::MediaSessionInfo::SessionState GetState() const;
mojom::AudioFocusRequestClient* audio_focus_request() const { mojom::AudioFocusRequestClient* audio_focus_request() const {
......
...@@ -7,7 +7,7 @@ module media_session.mojom; ...@@ -7,7 +7,7 @@ module media_session.mojom;
import "mojo/public/mojom/base/unguessable_token.mojom"; import "mojo/public/mojom/base/unguessable_token.mojom";
import "services/media_session/public/mojom/media_session.mojom"; import "services/media_session/public/mojom/media_session.mojom";
// Next MinVersion: 4 // Next MinVersion: 5
// These are the different types of audio focus that can be requested. // These are the different types of audio focus that can be requested.
[Extensible] [Extensible]
...@@ -72,7 +72,7 @@ interface AudioFocusRequestClient { ...@@ -72,7 +72,7 @@ interface AudioFocusRequestClient {
}; };
// Controls audio focus across the entire system. // Controls audio focus across the entire system.
// Next Method ID: 4 // Next Method ID: 5
interface AudioFocusManager { interface AudioFocusManager {
// Requests audio focus with |type| for the |media_session| with // Requests audio focus with |type| for the |media_session| with
// |session_info|. Media sessions should provide a |request| that will // |session_info|. Media sessions should provide a |request| that will
...@@ -83,6 +83,16 @@ interface AudioFocusManager { ...@@ -83,6 +83,16 @@ interface AudioFocusManager {
MediaSessionInfo session_info, MediaSessionInfo session_info,
AudioFocusType type) => (); AudioFocusType type) => ();
// Requests audio focus as above but with a |group_id| that is used for
// grouping sessions together. This is when a group of media sessions
// will share audio focus.
[MinVersion=4] RequestGroupedAudioFocus@4(
AudioFocusRequestClient& client,
MediaSession media_session,
MediaSessionInfo session_info,
AudioFocusType type,
mojo_base.mojom.UnguessableToken group_id) => ();
// Gets all the information about all |MediaSessions| that have requested // Gets all the information about all |MediaSessions| that have requested
// audio focus and their current requested type. // audio focus and their current requested type.
GetFocusRequests@1() => (array<AudioFocusRequestState> requests); GetFocusRequests@1() => (array<AudioFocusRequestState> requests);
......
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