1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
content / browser / media / session / audio_focus_delegate_default.cc [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/memory/raw_ptr.h"
#include "content/browser/media/session/audio_focus_delegate.h"
#include "base/functional/bind.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
#include "content/browser/media/session/media_session_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/media_session_service.h"
#include "media/base/media_switches.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/media_session/public/cpp/features.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h"
namespace content {
using media_session::mojom::AudioFocusType;
namespace {
const char kAudioFocusSourceName[] = "web";
base::UnguessableToken GetAudioFocusGroupId(MediaSessionImpl* session) {
// Allow the media session to override the group id.
if (session->audio_focus_group_id() != base::UnguessableToken::Null())
return session->audio_focus_group_id();
// Use a shared audio focus group id for the whole browser. This will means
// that tabs will share audio focus if the enforcement mode is set to
// kSingleGroup.
static const base::UnguessableToken token(base::UnguessableToken::Create());
return token;
}
// AudioFocusDelegateDefault is the default implementation of
// AudioFocusDelegate which only handles audio focus between WebContents.
class AudioFocusDelegateDefault : public AudioFocusDelegate {
public:
explicit AudioFocusDelegateDefault(MediaSessionImpl* media_session);
~AudioFocusDelegateDefault() override;
// AudioFocusDelegate implementation.
AudioFocusResult RequestAudioFocus(AudioFocusType audio_focus_type) override;
void AbandonAudioFocus() override;
std::optional<media_session::mojom::AudioFocusType> GetCurrentFocusType()
const override;
void MediaSessionInfoChanged(
const media_session::mojom::MediaSessionInfoPtr&) override;
const base::UnguessableToken& request_id() const override {
return request_id_;
}
void ReleaseRequestId() override;
private:
// Finishes an async audio focus request.
void FinishAudioFocusRequest(AudioFocusType type, bool success);
// Ensures that |audio_focus_| is connected.
void EnsureServiceConnection();
// Holds the latest MediaSessionInfo for |media_session_|.
media_session::mojom::MediaSessionInfoPtr session_info_;
// Holds a remote to the Media Session service.
mojo::Remote<media_session::mojom::AudioFocusManager> audio_focus_;
// If the media session has acquired audio focus then this will contain a
// pointer to that requests AudioFocusRequestClient.
mojo::Remote<media_session::mojom::AudioFocusRequestClient>
request_client_remote_;
// Weak pointer because |this| is owned by |media_session_|.
raw_ptr<MediaSessionImpl> media_session_;
// The last requested AudioFocusType by the associated |media_session_|.
std::optional<AudioFocusType> audio_focus_type_;
// ID to uniquely identify the audio focus delegate.
base::UnguessableToken const request_id_ = base::UnguessableToken::Create();
};
} // anonymous namespace
AudioFocusDelegateDefault::AudioFocusDelegateDefault(
MediaSessionImpl* media_session)
: media_session_(media_session) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
AudioFocusDelegateDefault::~AudioFocusDelegateDefault() = default;
AudioFocusDelegate::AudioFocusResult
AudioFocusDelegateDefault::RequestAudioFocus(AudioFocusType audio_focus_type) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!base::FeatureList::IsEnabled(
media_session::features::kMediaSessionService)) {
audio_focus_type_ = audio_focus_type;
return AudioFocusDelegate::AudioFocusResult::kSuccess;
}
if (request_client_remote_.is_bound()) {
// We have an existing request so we should request an updated focus type.
request_client_remote_->RequestAudioFocus(
session_info_.Clone(), audio_focus_type,
base::BindOnce(&AudioFocusDelegateDefault::FinishAudioFocusRequest,
base::Unretained(this), audio_focus_type,
true /* success */));
} else {
EnsureServiceConnection();
// Create a mojo interface pointer to our media session.
mojo::PendingRemote<media_session::mojom::MediaSession> media_session =
media_session_->AddRemote();
audio_focus_->RequestGroupedAudioFocus(
request_id_, request_client_remote_.BindNewPipeAndPassReceiver(),
std::move(media_session), session_info_.Clone(), audio_focus_type,
GetAudioFocusGroupId(media_session_),
base::BindOnce(&AudioFocusDelegateDefault::FinishAudioFocusRequest,
base::Unretained(this), audio_focus_type));
}
// Return delayed as we make the async call to request audio focus.
return AudioFocusDelegate::AudioFocusResult::kDelayed;
}
void AudioFocusDelegateDefault::AbandonAudioFocus() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
audio_focus_type_.reset();
if (!request_client_remote_.is_bound())
return;
request_client_remote_->AbandonAudioFocus();
request_client_remote_.reset();
audio_focus_.reset();
}
std::optional<media_session::mojom::AudioFocusType>
AudioFocusDelegateDefault::GetCurrentFocusType() const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return audio_focus_type_;
}
void AudioFocusDelegateDefault::MediaSessionInfoChanged(
const media_session::mojom::MediaSessionInfoPtr& session_info) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (request_client_remote_.is_bound())
request_client_remote_->MediaSessionInfoChanged(session_info.Clone());
session_info_ = session_info.Clone();
}
void AudioFocusDelegateDefault::ReleaseRequestId() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!base::FeatureList::IsEnabled(
media_session::features::kMediaSessionService)) {
return;
}
EnsureServiceConnection();
audio_focus_->RequestIdReleased(request_id_);
}
void AudioFocusDelegateDefault::FinishAudioFocusRequest(AudioFocusType type,
bool success) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(request_client_remote_.is_bound());
audio_focus_type_ = type;
media_session_->FinishSystemAudioFocusRequest(type, success);
}
void AudioFocusDelegateDefault::EnsureServiceConnection() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!base::FeatureList::IsEnabled(
media_session::features::kMediaSessionService)) {
return;
}
if (audio_focus_.is_bound() && audio_focus_.is_connected())
return;
audio_focus_.reset();
// Connect to the Media Session service and bind |audio_focus_| to it.
GetMediaSessionService().BindAudioFocusManager(
audio_focus_.BindNewPipeAndPassReceiver());
// We associate all media sessions with the browser context so we can filter
// by browser context in the UI.
audio_focus_->SetSource(media_session_->GetSourceId(), kAudioFocusSourceName);
}
// static
std::unique_ptr<AudioFocusDelegate> AudioFocusDelegate::Create(
MediaSessionImpl* media_session) {
return std::unique_ptr<AudioFocusDelegate>(
new AudioFocusDelegateDefault(media_session));
}
} // namespace content