Skip to content

Commit 3a21a9d

Browse files
committed
Implement handling for audioOutputOn
1 parent 9b20956 commit 3a21a9d

File tree

5 files changed

+99
-9
lines changed

5 files changed

+99
-9
lines changed

DemoApp/Sources/ViewModifiers/MoreControls/DemoMoreControlsViewModifier.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ struct DemoMoreControlsViewModifier: ViewModifier {
5252
)
5353
}
5454

55+
DemoMoreControlListButtonView(
56+
action: { viewModel.toggleAudioOutput() },
57+
label: viewModel.callSettings.audioOutputOn ? "Disable audio output" : "Enable audio output"
58+
) {
59+
Image(
60+
systemName: viewModel.callSettings.audioOutputOn
61+
? "speaker.fill"
62+
: "speaker.slash"
63+
)
64+
}
65+
5566
DemoTranscriptionAndClosedCaptionsButtonView(viewModel: viewModel)
5667

5768
DemoMoreThermalStateButtonView()

Sources/StreamVideo/Utils/AudioSession/AudioSessionConfiguration.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import AVFoundation
66

77
/// Represents the audio session configuration.
88
public struct AudioSessionConfiguration: ReflectiveStringConvertible, Equatable, Sendable {
9+
var isActive: Bool
910
/// The audio session category.
1011
var category: AVAudioSession.Category
1112
/// The audio session mode.
@@ -17,7 +18,8 @@ public struct AudioSessionConfiguration: ReflectiveStringConvertible, Equatable,
1718

1819
/// Compares two `AudioSessionConfiguration` instances for equality.
1920
public static func == (lhs: Self, rhs: Self) -> Bool {
20-
lhs.category == rhs.category &&
21+
lhs.isActive == rhs.isActive &&
22+
lhs.category == rhs.category &&
2123
lhs.mode == rhs.mode &&
2224
lhs.options.rawValue == rhs.options.rawValue &&
2325
lhs.overrideOutputAudioPort?.rawValue ==

Sources/StreamVideo/Utils/AudioSession/CallAudioSession.swift

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,15 +129,17 @@ final class CallAudioSession: @unchecked Sendable, Encodable {
129129
}
130130

131131
do {
132-
try await audioStore.dispatchAsync(
133-
.audioSession(
134-
.setCategory(
135-
configuration.category,
136-
mode: configuration.mode,
137-
options: configuration.options
132+
if configuration.isActive {
133+
try await audioStore.dispatchAsync(
134+
.audioSession(
135+
.setCategory(
136+
configuration.category,
137+
mode: configuration.mode,
138+
options: configuration.options
139+
)
138140
)
139141
)
140-
)
142+
}
141143
} catch {
142144
log.error(
143145
"Unable to apply configuration category:\(configuration.category) mode:\(configuration.mode) options:\(configuration.options).",
@@ -146,7 +148,7 @@ final class CallAudioSession: @unchecked Sendable, Encodable {
146148
)
147149
}
148150

149-
if let overrideOutputAudioPort = configuration.overrideOutputAudioPort {
151+
if configuration.isActive, let overrideOutputAudioPort = configuration.overrideOutputAudioPort {
150152
do {
151153
try await audioStore.dispatchAsync(
152154
.audioSession(
@@ -161,10 +163,81 @@ final class CallAudioSession: @unchecked Sendable, Encodable {
161163
)
162164
}
163165
}
166+
167+
await handleAudioOutputUpdateIfRequired(configuration)
164168

165169
statsAdapter?.trace(.init(audioSession: self))
166170
}
167171

172+
private func handleAudioOutputUpdateIfRequired(
173+
_ configuration: AudioSessionConfiguration
174+
) async {
175+
guard
176+
configuration.isActive != audioStore.state.isActive
177+
else {
178+
return
179+
}
180+
do {
181+
/// In the case where audioOutputOn has changed the order of actions matters
182+
/// When activating we need:
183+
/// 1. activate AVAudioSession
184+
/// 2. set isAudioEnabled = true
185+
/// 3. set RTCAudioSession.isActive = true
186+
///
187+
/// When deactivating we need:
188+
/// 1. set RTCAudioSession.isActive = false
189+
/// 2. set isAudioEnabled = false
190+
/// 3. deactivate AVAudioSession
191+
if configuration.isActive {
192+
193+
try AVAudioSession.sharedInstance().setActive(true)
194+
195+
try await audioStore.dispatchAsync(
196+
.audioSession(
197+
.isAudioEnabled(true)
198+
)
199+
)
200+
201+
try await audioStore.dispatchAsync(
202+
.audioSession(
203+
.isActive(true)
204+
)
205+
)
206+
207+
log.debug(
208+
"AudioSession audioOutput has been enabled.",
209+
subsystems: .audioSession
210+
)
211+
} else {
212+
213+
try await audioStore.dispatchAsync(
214+
.audioSession(
215+
.isActive(false)
216+
)
217+
)
218+
219+
try await audioStore.dispatchAsync(
220+
.audioSession(
221+
.isAudioEnabled(false)
222+
)
223+
)
224+
225+
try AVAudioSession.sharedInstance().setActive(false)
226+
227+
log.debug(
228+
"AudioSession audioOutput has been disabled.",
229+
subsystems: .audioSession
230+
)
231+
}
232+
} catch {
233+
log.error(
234+
"Failed while to applying AudioSession isActive:\(configuration.isActive) in order to match CallSettings.audioOutputOn.",
235+
subsystems: .audioSession,
236+
error: error
237+
)
238+
}
239+
}
240+
168241
/// - Important: This method runs whenever an CallAudioSession is created and ensures that
169242
/// the configuration is correctly for calling. This is quite important for CallKit as if the category and
170243
/// mode aren't set correctly it won't activate the audioSession.

Sources/StreamVideo/Utils/AudioSession/Policies/DefaultAudioSessionPolicy.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public struct DefaultAudioSessionPolicy: AudioSessionPolicy {
2525
) -> AudioSessionConfiguration {
2626
guard applicationStateAdapter.state == .foreground else {
2727
return .init(
28+
isActive: callSettings.audioOutputOn,
2829
category: .playAndRecord,
2930
mode: callSettings.videoOn ? .videoChat : .voiceChat,
3031
options: .playAndRecord(
@@ -39,6 +40,7 @@ public struct DefaultAudioSessionPolicy: AudioSessionPolicy {
3940
}
4041

4142
return .init(
43+
isActive: callSettings.audioOutputOn,
4244
category: .playAndRecord,
4345
mode: callSettings.videoOn && callSettings.speakerOn ? .videoChat : .voiceChat,
4446
options: .playAndRecord(

Sources/StreamVideo/Utils/AudioSession/Policies/OwnCapabilitiesAudioSessionPolicy.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public struct OwnCapabilitiesAudioSessionPolicy: AudioSessionPolicy {
3737
) -> AudioSessionConfiguration {
3838
guard ownCapabilities.contains(.sendAudio) else {
3939
return .init(
40+
isActive: callSettings.audioOutputOn,
4041
category: .playback,
4142
mode: .default,
4243
options: .playback,
@@ -71,6 +72,7 @@ public struct OwnCapabilitiesAudioSessionPolicy: AudioSessionPolicy {
7172
: nil
7273

7374
return .init(
75+
isActive: callSettings.audioOutputOn,
7476
category: category,
7577
mode: mode,
7678
options: categoryOptions,

0 commit comments

Comments
 (0)