Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qavaudiosessionmanager.mm
Go to the documentation of this file.
1
// Copyright (C) 2026 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4
#
include
"qavaudiosessionmanager_p.h"
5
6
#
include
<
QtMultimedia
/
private
/
qmultimedia_ranges_p
.
h
>
7
8
#
include
<
QtCore
/
qapplicationstatic
.
h
>
9
#
include
<
QtCore
/
qcoreapplication
.
h
>
10
#
include
<
QtCore
/
qloggingcategory
.
h
>
11
#
include
<
QtCore
/
qthread
.
h
>
12
13
#
import
<
AVFoundation
/
AVAudioSession
.
h
>
14
15
QT_BEGIN_NAMESPACE
16
17
Q_STATIC_LOGGING_CATEGORY(qLcAudioSession,
"qt.multimedia.darwin.audiosession"
);
18
19
namespace
QtMultimediaPrivate
{
20
21
using
TokenId
=
QAVAudioSessionManager
::
TokenId
;
22
23
namespace
{
24
25
struct
InterruptionInfo
26
{
27
bool
began;
28
bool
shouldResume;
29
};
30
31
InterruptionInfo parseInterruption(NSNotification *notification)
32
{
33
NSDictionary *userInfo = notification.userInfo;
34
NSNumber *typeValue = userInfo[AVAudioSessionInterruptionTypeKey];
35
const
auto
type =
static_cast
<AVAudioSessionInterruptionType>(typeValue.unsignedIntegerValue);
36
37
if
(type == AVAudioSessionInterruptionTypeBegan) {
38
qCDebug(qLcAudioSession) <<
"interruption notification: began"
;
39
return
{
true
,
false
};
40
}
41
42
// AVAudioSessionInterruptionOptionKey may be absent on "ended"; absence means "do not
43
// resume", only AVAudioSessionInterruptionOptionShouldResume means resume.
44
NSNumber *optionValue = userInfo[AVAudioSessionInterruptionOptionKey];
45
const
bool
shouldResume = optionValue != nil
46
&& (optionValue.unsignedIntegerValue & AVAudioSessionInterruptionOptionShouldResume);
47
qCDebug(qLcAudioSession) <<
"interruption notification: ended, shouldResume:"
<< shouldResume;
48
return
{
false
, shouldResume };
49
}
50
51
// Merges the requirements of all currently live ActivationTokens into a single category/options
52
// pair. The manager never chooses a category of its own beyond this merge: if any live client
53
// needs recording, the session must support it; options requested by any client (e.g. "mix with
54
// others") are unioned in.
55
AVAudioSessionCategory
56
mergedCategory(
const
std::vector<std::pair<TokenId, SessionRequirement>> &requirements)
57
{
58
const
bool
needsRecord = ranges::any_of(requirements, [](
const
auto
&entry) {
59
return
entry.second.needsRecord;
60
});
61
return
needsRecord ? AVAudioSessionCategoryPlayAndRecord : AVAudioSessionCategoryPlayback;
62
}
63
64
AVAudioSessionCategoryOptions
65
mergedOptions(
const
std::vector<std::pair<TokenId, SessionRequirement>> &requirements)
66
{
67
AVAudioSessionCategoryOptions options = 0;
68
for
(
const
auto
&entry : requirements)
69
options |= entry.second.options;
70
return
options;
71
}
72
73
}
// namespace
74
75
Q_APPLICATION_STATIC(
QAVAudioSessionManager
,
s_avAudioSessionManager
)
76
77
QAVAudioSessionManager
*
QAVAudioSessionManager
::
instance
()
78
{
79
return
s_avAudioSessionManager
;
80
}
81
82
QAVAudioSessionManager
::
QAVAudioSessionManager
()
83
{
84
if
(
qApp
&& !
QThread
::
isMainThread
())
85
moveToThread
(
qApp
->
thread
());
86
87
AVAudioSession
*
session
= [
AVAudioSession
sharedInstance
];
88
const
std
::
shared_ptr
<
bool
>
destroyed
=
m_destroyed
;
89
90
m_observers
= {
91
QMacNotificationObserver
(
session
,
AVAudioSessionInterruptionNotification
,
92
[
this
,
destroyed
](
NSNotification
*
notification
) {
93
const
InterruptionInfo
info
=
parseInterruption
(
notification
);
94
QMetaObject
::
invokeMethod
(
this
, [
this
,
destroyed
,
info
] {
95
if
(*
destroyed
)
96
return
;
97
if
(
info
.
began
)
98
Q_EMIT
interruptionBegan
();
99
else
100
Q_EMIT
interruptionEnded
(
info
.
shouldResume
);
101
});
102
}),
103
QMacNotificationObserver
(
session
,
AVAudioSessionMediaServicesWereLostNotification
,
104
[
this
,
destroyed
] {
105
qCWarning
(
qLcAudioSession
) <<
"media services were lost notification"
;
106
QMetaObject
::
invokeMethod
(
this
, [
this
,
destroyed
] {
107
if
(*
destroyed
)
108
return
;
109
Q_EMIT
mediaServicesWereLost
();
110
});
111
}),
112
QMacNotificationObserver
(
session
,
AVAudioSessionMediaServicesWereResetNotification
,
113
[
this
,
destroyed
] {
114
qCWarning
(
qLcAudioSession
) <<
"media services were reset notification"
;
115
QMetaObject
::
invokeMethod
(
this
, [
this
,
destroyed
] {
116
if
(*
destroyed
)
117
return
;
118
Q_EMIT
mediaServicesWereReset
();
119
});
120
}),
121
};
122
}
123
124
QAVAudioSessionManager
::~
QAVAudioSessionManager
()
125
{
126
m_observers
= {};
127
*
m_destroyed
=
true
;
128
}
129
130
// Must be called with m_activationMutex held.
131
bool
QAVAudioSessionManager
::
applyRequirementsLocked
()
132
{
133
AVAudioSession
*
session
= [
AVAudioSession
sharedInstance
];
134
const
AVAudioSessionCategory
category
=
mergedCategory
(
m_requirements
);
135
const
AVAudioSessionCategoryOptions
options
=
mergedOptions
(
m_requirements
);
136
137
if
(![
session
.
category
isEqualToString
:
category
] ||
session
.
categoryOptions
!=
options
) {
138
NSError
*
error
=
nil
;
139
if
(![
session
setCategory
:
category
withOptions
:
options
error
:&
error
]) {
140
qCWarning
(
qLcAudioSession
) <<
"failed to set audio session category:"
141
<<
QString
::
fromNSString
(
error
.
localizedDescription
);
142
return
false
;
143
}
144
}
145
146
NSError
*
error
=
nil
;
147
const
BOOL
success
= [
session
setActive
:
YES
error
:&
error
];
148
if
(!
success
)
149
qCWarning
(
qLcAudioSession
) <<
"failed to activate audio session:"
150
<<
QString
::
fromNSString
(
error
.
localizedDescription
);
151
else
152
qCDebug
(
qLcAudioSession
) <<
"audio session activated"
;
153
154
return
success
;
155
}
156
157
QAVAudioSessionManager
::
ActivationToken
158
QAVAudioSessionManager
::
activate
(
SessionRequirement
requirement
)
159
{
160
const
std
::
lock_guard
guard
(
m_activationMutex
);
161
const
TokenId
tokenId
{ ++
m_tokenIdAllocator
};
162
m_requirements
.
emplace_back
(
tokenId
,
requirement
);
163
applyRequirementsLocked
();
164
return
ActivationToken
(
this
,
m_destroyed
,
tokenId
);
165
}
166
167
void
QAVAudioSessionManager
::
releaseActivation
(
TokenId
tokenId
)
168
{
169
const
std
::
lock_guard
guard
(
m_activationMutex
);
170
const
auto
it
=
ranges
::
find_if
(
m_requirements
, [&](
const
auto
&
entry
) {
171
return
entry
.
first
==
tokenId
;
172
});
173
if
(
it
!=
m_requirements
.
end
())
174
m_requirements
.
erase
(
it
);
175
176
if
(
m_requirements
.
empty
()) {
177
NSError
*
error
=
nil
;
178
if
(![[
AVAudioSession
sharedInstance
]
setActive
:
NO
error
:&
error
])
179
qCWarning
(
qLcAudioSession
) <<
"failed to deactivate audio session:"
180
<<
QString
::
fromNSString
(
error
.
localizedDescription
);
181
else
182
qCDebug
(
qLcAudioSession
) <<
"audio session deactivated"
;
183
return
;
184
}
185
186
applyRequirementsLocked
();
187
}
188
189
bool
QAVAudioSessionManager
::
reassertActivation
()
190
{
191
const
std
::
lock_guard
guard
(
m_activationMutex
);
192
if
(
m_requirements
.
empty
()) {
193
NSError
*
error
=
nil
;
194
const
BOOL
success
= [[
AVAudioSession
sharedInstance
]
setActive
:
YES
error
:&
error
];
195
if
(!
success
)
196
qCWarning
(
qLcAudioSession
) <<
"failed to activate audio session:"
197
<<
QString
::
fromNSString
(
error
.
localizedDescription
);
198
return
success
;
199
}
200
201
return
applyRequirementsLocked
();
202
}
203
204
QAVAudioSessionManager
::
ActivationToken
::
ActivationToken
(
QAVAudioSessionManager
*
manager
,
205
std
::
shared_ptr
<
bool
>
destroyed
,
206
TokenId
tokenId
)
207
:
m_manager
(
manager
),
m_destroyed
(
std
::
move
(
destroyed
)),
m_tokenId
(
tokenId
)
208
{
209
}
210
211
QAVAudioSessionManager
::
ActivationToken
::
ActivationToken
(
ActivationToken
&&
other
)
noexcept
212
:
m_manager
(
std
::
exchange
(
other
.
m_manager
,
nullptr
)),
213
m_destroyed
(
std
::
move
(
other
.
m_destroyed
)),
214
m_tokenId
(
other
.
m_tokenId
)
215
{
216
}
217
218
QAVAudioSessionManager
::
ActivationToken
&
219
QAVAudioSessionManager
::
ActivationToken
::
operator
=(
ActivationToken
&&
other
)
noexcept
220
{
221
if
(
this
!= &
other
) {
222
release
();
223
m_manager
=
std
::
exchange
(
other
.
m_manager
,
nullptr
);
224
m_destroyed
=
std
::
move
(
other
.
m_destroyed
);
225
m_tokenId
=
other
.
m_tokenId
;
226
}
227
return
*
this
;
228
}
229
230
QAVAudioSessionManager
::
ActivationToken
::~
ActivationToken
()
231
{
232
release
();
233
}
234
235
void
QAVAudioSessionManager
::
ActivationToken
::
release
()
236
{
237
if
(
m_manager
&&
m_destroyed
&& !*
m_destroyed
)
238
m_manager
->
releaseActivation
(
m_tokenId
);
239
m_manager
=
nullptr
;
240
}
241
242
}
// namespace QtMultimediaPrivate
243
244
QT_END_NAMESPACE
245
246
#
include
"moc_qavaudiosessionmanager_p.cpp"
QT_BEGIN_NAMESPACE
Combined button and popup list for selecting options.
Definition
qsequentialanimationgroup.cpp:47
QtMultimediaPrivate::impl
Definition
qaudiosystem_p.h:257
qtmultimedia
src
multimedia
coreaudio
qavaudiosessionmanager.mm
Generated on
for Qt by
1.18.0