@@ -27,7 +27,9 @@ public class AudioManager: Loggable {
27
27
public typealias ConfigureAudioSessionFunc = ( _ newState: State ,
28
28
_ oldState: State ) -> Void
29
29
30
- public var customConfigureFunc : ConfigureAudioSessionFunc ?
30
+ /// Use this to provide a custom func to configure the audio session instead of ``defaultConfigureAudioSessionFunc(newState:oldState:)``.
31
+ /// This method should not block and is expected to return immediately.
32
+ public var customConfigureAudioSessionFunc : ConfigureAudioSessionFunc ?
31
33
32
34
public enum TrackState {
33
35
case none
@@ -59,7 +61,6 @@ public class AudioManager: Loggable {
59
61
// MARK: - Private
60
62
61
63
private var _state = StateSync ( State ( ) )
62
- private let configureQueue = DispatchQueue ( label: " LiveKitSDK.AudioManager.configure " , qos: . default)
63
64
64
65
#if os(iOS)
65
66
private let notificationQueue = OperationQueue ( )
@@ -93,9 +94,7 @@ public class AudioManager: Loggable {
93
94
// trigger events when state mutates
94
95
_state. onMutate = { [ weak self] newState, oldState in
95
96
guard let self = self else { return }
96
- self . configureQueue. async {
97
- self . configureAudioSession ( newState: newState, oldState: oldState)
98
- }
97
+ self . configureAudioSession ( newState: newState, oldState: oldState)
99
98
}
100
99
}
101
100
@@ -124,106 +123,96 @@ public class AudioManager: Loggable {
124
123
}
125
124
}
126
125
127
- private func configureAudioSession( newState: State ,
128
- oldState : State ) {
126
+ private func configureAudioSession( newState: State , oldState : State ) {
127
+
129
128
log ( " \( oldState) -> \( newState) " )
130
129
131
130
#if os(iOS)
132
131
if let _deprecatedFunc = LiveKit . onShouldConfigureAudioSession {
133
132
_deprecatedFunc ( newState. trackState, oldState. trackState)
134
- } else if let customConfigureFunc = customConfigureFunc {
135
- customConfigureFunc ( newState, oldState)
133
+ } else if let customConfigureAudioSessionFunc = customConfigureAudioSessionFunc {
134
+ customConfigureAudioSessionFunc ( newState, oldState)
136
135
} else {
137
- defaultShouldConfigureAudioSessionFunc ( newState: newState,
138
- oldState: oldState)
136
+ defaultConfigureAudioSessionFunc ( newState: newState, oldState: oldState)
139
137
}
140
138
#endif
141
139
}
142
140
143
141
#if os(iOS)
142
+ /// The default implementation when audio session configuration is requested by the SDK.
144
143
/// Configure the `RTCAudioSession` of `WebRTC` framework.
145
144
///
146
145
/// > Note: It is recommended to use `RTCAudioSessionConfiguration.webRTC()` to obtain an instance of `RTCAudioSessionConfiguration` instead of instantiating directly.
147
146
///
148
- /// View ``defaultShouldConfigureAudioSessionFunc(newState:oldState:)`` for usage of this method.
149
- ///
150
147
/// - Parameters:
151
148
/// - configuration: A configured RTCAudioSessionConfiguration
152
149
/// - setActive: passing true/false will call `AVAudioSession.setActive` internally
153
- public func configureAudioSession( _ configuration: RTCAudioSessionConfiguration ,
154
- setActive: Bool ? = nil ,
155
- preferSpeakerOutput: Bool = true ) {
156
-
157
- let session : RTCAudioSession = DispatchQueue . webRTC. sync {
158
- let result = RTCAudioSession . sharedInstance ( )
159
- result. lockForConfiguration ( )
160
- return result
161
- }
150
+ public func defaultConfigureAudioSessionFunc( newState: State , oldState: State ) {
162
151
163
- defer { DispatchQueue . webRTC. sync { session . unlockForConfiguration ( ) } }
152
+ DispatchQueue . webRTC. async { [ weak self ] in
164
153
165
- do {
166
- logger. log ( " configuring audio session with category: \( configuration. category) , mode: \( configuration. mode) , setActive: \( String ( describing: setActive) ) " , type: AudioManager . self)
154
+ guard let self = self else { return }
167
155
168
- if let setActive = setActive {
169
- try DispatchQueue . webRTC. sync { try session. setConfiguration ( configuration, active: setActive) }
170
- } else {
171
- try DispatchQueue . webRTC. sync { try session. setConfiguration ( configuration) }
172
- }
156
+ // prepare config
157
+ let configuration = RTCAudioSessionConfiguration . webRTC ( )
158
+ var categoryOptions : AVAudioSession . CategoryOptions = [ ]
173
159
174
- } catch let error {
175
- logger. log ( " Failed to configureAudioSession with error: \( error) " , . error, type: AudioManager . self)
176
- }
160
+ switch newState. trackState {
161
+ case . remoteOnly:
162
+ configuration. category = AVAudioSession . Category. playback. rawValue
163
+ configuration. mode = AVAudioSession . Mode. spokenAudio. rawValue
164
+ case . localOnly, . localAndRemote:
165
+ configuration. category = AVAudioSession . Category. playAndRecord. rawValue
166
+ configuration. mode = AVAudioSession . Mode. videoChat. rawValue
177
167
178
- do {
179
- logger. log ( " preferSpeakerOutput: \( preferSpeakerOutput) " , type: AudioManager . self)
180
- try DispatchQueue . webRTC. sync { try session. overrideOutputAudioPort ( preferSpeakerOutput ? . speaker : . none) }
181
- } catch let error {
182
- logger. log ( " Failed to overrideOutputAudioPort with error: \( error) " , . error, type: AudioManager . self)
183
- }
184
- }
168
+ categoryOptions = [ . allowBluetooth, . allowBluetoothA2DP]
185
169
186
- /// The default implementation when audio session configuration is requested by the SDK.
187
- public func defaultShouldConfigureAudioSessionFunc ( newState : State ,
188
- oldState : State ) {
170
+ if newState . preferSpeakerOutput {
171
+ categoryOptions . insert ( . defaultToSpeaker )
172
+ }
189
173
190
- let config = DispatchQueue . webRTC. sync { RTCAudioSessionConfiguration . webRTC ( ) }
174
+ default :
175
+ configuration. category = AVAudioSession . Category. soloAmbient. rawValue
176
+ configuration. mode = AVAudioSession . Mode. default. rawValue
177
+ }
191
178
192
- var categoryOptions : AVAudioSession . CategoryOptions = [ ]
179
+ configuration . categoryOptions = categoryOptions
193
180
194
- switch newState. trackState {
195
- case . remoteOnly:
196
- config. category = AVAudioSession . Category. playback. rawValue
197
- config. mode = AVAudioSession . Mode. spokenAudio. rawValue
198
- case . localOnly, . localAndRemote:
199
- config. category = AVAudioSession . Category. playAndRecord. rawValue
200
- config. mode = AVAudioSession . Mode. videoChat. rawValue
181
+ var setActive : Bool ?
182
+ if newState. trackState != . none, oldState. trackState == . none {
183
+ // activate audio session when there is any local/remote audio track
184
+ setActive = true
185
+ } else if newState. trackState == . none, oldState. trackState != . none {
186
+ // deactivate audio session when there are no more local/remote audio tracks
187
+ setActive = false
188
+ }
201
189
202
- categoryOptions = [ . allowBluetooth, . allowBluetoothA2DP]
190
+ // configure session
191
+ let session = RTCAudioSession . sharedInstance ( )
192
+ session. lockForConfiguration ( )
193
+ // always unlock
194
+ defer { session. unlockForConfiguration ( ) }
203
195
204
- if newState. preferSpeakerOutput {
205
- categoryOptions. insert ( . defaultToSpeaker)
206
- }
196
+ do {
197
+ self . log ( " configuring audio session with category: \( configuration. category) , mode: \( configuration. mode) , setActive: \( String ( describing: setActive) ) " )
207
198
208
- default :
209
- config. category = AVAudioSession . Category. soloAmbient. rawValue
210
- config. mode = AVAudioSession . Mode. default. rawValue
211
- }
199
+ if let setActive = setActive {
200
+ try session. setConfiguration ( configuration, active: setActive)
201
+ } else {
202
+ try session. setConfiguration ( configuration)
203
+ }
212
204
213
- config. categoryOptions = categoryOptions
205
+ } catch let error {
206
+ self . log ( " Failed to configureAudioSession with error: \( error) " , . error)
207
+ }
214
208
215
- var setActive : Bool ?
216
- if newState. trackState != . none, oldState. trackState == . none {
217
- // activate audio session when there is any local/remote audio track
218
- setActive = true
219
- } else if newState. trackState == . none, oldState. trackState != . none {
220
- // deactivate audio session when there are no more local/remote audio tracks
221
- setActive = false
209
+ do {
210
+ self . log ( " preferSpeakerOutput: \( newState. preferSpeakerOutput) " )
211
+ try session. overrideOutputAudioPort ( newState. preferSpeakerOutput ? . speaker : . none)
212
+ } catch let error {
213
+ self . log ( " Failed to overrideOutputAudioPort with error: \( error) " , . error)
214
+ }
222
215
}
223
-
224
- configureAudioSession ( config,
225
- setActive: setActive,
226
- preferSpeakerOutput: newState. preferSpeakerOutput)
227
216
}
228
217
#endif
229
218
}
0 commit comments