Skip to content

Commit 8f1e74a

Browse files
committed
Improvements
1 parent 27d71af commit 8f1e74a

14 files changed

+81
-140
lines changed

DemoApp/Sources/Views/Login/DebugMenu.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ struct DebugMenu: View {
231231
}
232232

233233
makeMenu(
234-
for: [.default, .ownCapabilities],
234+
for: [.default, .ownCapabilities, .livestream],
235235
currentValue: audioSessionPolicy,
236236
label: "AudioSession policy"
237237
) { self.audioSessionPolicy = $0 }

Sources/StreamVideo/Utils/AudioSession/AudioDeviceModule/AudioDeviceModule.swift

Lines changed: 47 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
2626
enum Event: Equatable, CustomStringConvertible {
2727
case speechActivityStarted
2828
case speechActivityEnded
29-
case didUpdateStereoPlayoutAvailable(Bool)
30-
case didUpdateStereoPlayoutEnabled(Bool)
3129
case didCreateAudioEngine(AVAudioEngine)
3230
case willEnableAudioEngine(AVAudioEngine, isPlayoutEnabled: Bool, isRecordingEnabled: Bool)
3331
case willStartAudioEngine(AVAudioEngine, isPlayoutEnabled: Bool, isRecordingEnabled: Bool)
@@ -45,12 +43,6 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
4543
case .speechActivityEnded:
4644
return ".speechActivityEnded"
4745

48-
case .didUpdateStereoPlayoutAvailable(let value):
49-
return ".didUpdateStereoPlayoutAvailable(\(value))"
50-
51-
case .didUpdateStereoPlayoutEnabled(let value):
52-
return ".didUpdateStereoPlayoutEnabled(\(value))"
53-
5446
case .didCreateAudioEngine(let engine):
5547
return ".didCreateAudioEngine(\(engine))"
5648

@@ -94,10 +86,6 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
9486
var isStereoPlayoutEnabled: Bool { isStereoPlayoutEnabledSubject.value }
9587
var isStereoPlayoutEnabledPublisher: AnyPublisher<Bool, Never> { isStereoPlayoutEnabledSubject.eraseToAnyPublisher() }
9688

97-
private let isStereoPlayoutAvailableSubject: CurrentValueSubject<Bool, Never>
98-
var isStereoPlayoutAvailable: Bool { isStereoPlayoutAvailableSubject.value }
99-
var isStereoPlayoutAvailablePublisher: AnyPublisher<Bool, Never> { isStereoPlayoutAvailableSubject.eraseToAnyPublisher() }
100-
10189
private let isVoiceProcessingBypassedSubject: CurrentValueSubject<Bool, Never>
10290
var isVoiceProcessingBypassed: Bool { isVoiceProcessingBypassedSubject.value }
10391
var isVoiceProcessingBypassedPublisher: AnyPublisher<Bool, Never> { isVoiceProcessingBypassedSubject.eraseToAnyPublisher() }
@@ -129,11 +117,10 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
129117
"isPlaying:\(isPlaying)" +
130118
", isRecording:\(isRecording)" +
131119
", isMicrophoneMuted:\(isMicrophoneMuted)" +
132-
", isStereoPlayoutEnabled:\(source.isStereoPlayoutEnabled)" +
133-
", isStereoPlayoutAvailable:\(source.isStereoPlayoutAvailable)" +
134-
", isVoiceProcessingBypassed:\(source.isVoiceProcessingBypassed)" +
135-
", isVoiceProcessingEnabled:\(source.isVoiceProcessingEnabled)" +
136-
", isVoiceProcessingAGCEnabled:\(source.isVoiceProcessingAGCEnabled)" +
120+
", isStereoPlayoutEnabled:\(isStereoPlayoutEnabled)" +
121+
", isVoiceProcessingBypassed:\(isVoiceProcessingBypassed)" +
122+
", isVoiceProcessingEnabled:\(isVoiceProcessingEnabled)" +
123+
", isVoiceProcessingAGCEnabled:\(isVoiceProcessingAGCEnabled)" +
137124
", audioLevel:\(audioLevel)" +
138125
", source:\(source)" +
139126
", engineOutput: \(engine?.outputDescription ?? "-")" +
@@ -153,7 +140,6 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
153140
self.isPlayingSubject = .init(isPlaying)
154141
self.isRecordingSubject = .init(isRecording)
155142
self.isMicrophoneMutedSubject = .init(isMicrophoneMuted)
156-
self.isStereoPlayoutAvailableSubject = .init(source.isStereoPlayoutAvailable)
157143
self.isStereoPlayoutEnabledSubject = .init(source.isStereoPlayoutEnabled)
158144
self.isVoiceProcessingBypassedSubject = .init(source.isVoiceProcessingBypassed)
159145
self.isVoiceProcessingEnabledSubject = .init(source.isVoiceProcessingEnabled)
@@ -198,13 +184,39 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
198184
.receive(on: dispatchQueue)
199185
.sink { [weak self] in self?.isVoiceProcessingAGCEnabledSubject.send($0) }
200186
.store(in: disposableBag)
201-
202-
source.manualRestoreVoiceProcessingOnMono = true
203-
(source as? RTCAudioDeviceModule)?.setRecordingAlwaysPreparedMode(true)
204187
}
205188

206189
// MARK: - Recording
207190

191+
func terminate() {
192+
_ = source.terminate()
193+
}
194+
195+
func setStereoPlayoutPreference(_ isPreferred: Bool) {
196+
/// - Important: `.voiceProcessing` requires VP to be enabled in order to mute and
197+
/// `.restartEngine` rebuilds the whole graph. Each of them has different issues:
198+
/// - `.voiceProcessing`: as it requires VP to be enabled in order to mute/unmute that
199+
/// means that for outputs where VP is disabled (e.g. stereo) we cannot mute/unmute.
200+
/// - `.restartEngine`: rebuilds the whole graph and requires explicit calling of
201+
/// `initAndStartRecording` .
202+
(source as? RTCAudioDeviceModule)?.setMuteMode(
203+
isPreferred ? .inputMixer : .voiceProcessing
204+
)
205+
/// - Important: We can probably set this one to false when the user doesn't have
206+
/// sendAudio capability.
207+
(source as? RTCAudioDeviceModule)?.setRecordingAlwaysPreparedMode(true)
208+
source.prefersStereoPlayout = isPreferred
209+
source.setManualRestoreVoiceProcessingOnMono(isPreferred)
210+
211+
let isMuted = isMicrophoneMuted
212+
213+
_ = source.stopRecording()
214+
_ = source.initAndStartRecording()
215+
if isMuted {
216+
_ = source.setMicrophoneMuted(isMuted)
217+
}
218+
}
219+
208220
func setPlayout(_ isActive: Bool) throws {
209221
try RetriableTask.run(iterations: 3) {
210222
try throwingExecution("Unable to start playout") {
@@ -256,45 +268,15 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
256268
return
257269
}
258270

259-
if !isMuted {
260-
_ = source.initAndStartRecording()
261-
}
262-
263271
try throwingExecution("Unable to setMicrophoneMuted:\(isMuted)") {
264272
source.setMicrophoneMuted(isMuted)
265273
}
274+
266275
isMicrophoneMutedSubject.send(isMuted)
267276
}
268277

269-
func setStereoPlayoutEnabled(
270-
_ isEnabled: Bool,
271-
file: StaticString = #file,
272-
function: StaticString = #function,
273-
line: UInt = #line
274-
) throws {
275-
try throwingExecution("Failed to enable Stereo Playout.") {
276-
source.setStereoPlayoutEnabled(isEnabled)
277-
}
278-
279-
guard source.isStereoPlayoutEnabled != isEnabled else {
280-
log.debug(
281-
"Stereo playout has been \(isEnabled ? "activated" : "deactivated").",
282-
subsystems: .audioSession
283-
)
284-
return
285-
}
286-
287-
throw ClientError(
288-
"Failed to"
289-
+ " setStereoPlayoutEnabled:\(isEnabled)."
290-
+ "("
291-
+ "isVoiceProcessingEnabled:\(source.isVoiceProcessingEnabled)"
292-
+ ", isVoiceProcessingBypassed:\(source.isVoiceProcessingBypassed)"
293-
+ ", isVoiceProcessingAGCEnabled:\(source.isVoiceProcessingAGCEnabled)"
294-
+ ")",
295-
file,
296-
line
297-
)
278+
func refreshStereoPlayoutState() {
279+
source.refreshStereoPlayoutState()
298280
}
299281

300282
// MARK: - RTCAudioDeviceModuleDelegate
@@ -313,38 +295,6 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
313295
}
314296
}
315297

316-
func audioDeviceModule(
317-
_ audioDeviceModule: RTCAudioDeviceModule,
318-
isStereoPlayoutAvailable: Bool
319-
) {
320-
guard isStereoPlayoutAvailable != self.isStereoPlayoutAvailable else {
321-
return
322-
}
323-
324-
isStereoPlayoutAvailableSubject.send(isStereoPlayoutAvailable)
325-
subject.send(.didUpdateStereoPlayoutAvailable(isStereoPlayoutAvailable))
326-
log.debug(
327-
"AudioDeviceModule updated isStereoPlayoutAvailable:\(isStereoPlayoutAvailable).",
328-
subsystems: .audioSession
329-
)
330-
}
331-
332-
func audioDeviceModule(
333-
_ audioDeviceModule: RTCAudioDeviceModule,
334-
isStereoPlayoutEnabled: Bool
335-
) {
336-
guard isStereoPlayoutEnabled != self.isStereoPlayoutEnabled else {
337-
return
338-
}
339-
340-
isStereoPlayoutEnabledSubject.send(isStereoPlayoutEnabled && isStereoPlayoutAvailable)
341-
subject.send(.didUpdateStereoPlayoutEnabled(isStereoPlayoutEnabled && isStereoPlayoutAvailable))
342-
log.debug(
343-
"AudioDeviceModule updated isStereoPlayoutEnabled:\(isStereoPlayoutEnabled && isStereoPlayoutAvailable).",
344-
subsystems: .audioSession
345-
)
346-
}
347-
348298
func audioDeviceModule(
349299
_ audioDeviceModule: RTCAudioDeviceModule,
350300
didCreateEngine engine: AVAudioEngine
@@ -387,6 +337,7 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
387337
)
388338
isPlayingSubject.send(isPlayoutEnabled)
389339
isRecordingSubject.send(isRecordingEnabled)
340+
390341
return Constant.successResult
391342
}
392343

@@ -403,7 +354,6 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
403354
isRecordingEnabled: isRecordingEnabled
404355
)
405356
)
406-
audioLevelsAdapter.uninstall(on: 0)
407357
isPlayingSubject.send(isPlayoutEnabled)
408358
isRecordingSubject.send(isRecordingEnabled)
409359
return Constant.successResult
@@ -422,7 +372,6 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
422372
isRecordingEnabled: isRecordingEnabled
423373
)
424374
)
425-
audioLevelsAdapter.uninstall(on: 0)
426375
isPlayingSubject.send(isPlayoutEnabled)
427376
isRecordingSubject.send(isRecordingEnabled)
428377
return Constant.successResult
@@ -488,11 +437,20 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
488437
// No-op
489438
}
490439

440+
func audioDeviceModule(
441+
_ module: RTCAudioDeviceModule,
442+
didUpdateAudioProcessingState state: RTCAudioProcessingState
443+
) {
444+
isVoiceProcessingEnabledSubject.send(state.voiceProcessingEnabled)
445+
isVoiceProcessingBypassedSubject.send(state.voiceProcessingBypassed)
446+
isVoiceProcessingAGCEnabledSubject.send(state.voiceProcessingAGCEnabled)
447+
isStereoPlayoutEnabledSubject.send(state.stereoPlayoutEnabled)
448+
}
449+
491450
private enum CodingKeys: String, CodingKey {
492451
case isPlaying
493452
case isRecording
494453
case isMicrophoneMuted
495-
case isStereoPlayoutAvailable
496454
case isStereoPlayoutEnabled
497455
case isVoiceProcessingBypassed
498456
case isVoiceProcessingEnabled
@@ -506,7 +464,6 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
506464
try container.encode(isPlaying, forKey: .isPlaying)
507465
try container.encode(isRecording, forKey: .isRecording)
508466
try container.encode(isMicrophoneMuted, forKey: .isMicrophoneMuted)
509-
try container.encode(isStereoPlayoutAvailable, forKey: .isStereoPlayoutAvailable)
510467
try container.encode(isStereoPlayoutEnabled, forKey: .isStereoPlayoutEnabled)
511468
try container.encode(isVoiceProcessingBypassed, forKey: .isVoiceProcessingBypassed)
512469
try container.encode(isVoiceProcessingEnabled, forKey: .isVoiceProcessingEnabled)

Sources/StreamVideo/Utils/AudioSession/AudioDeviceModule/AudioEngineLevelNodeAdapter.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ final class AudioEngineLevelNodeAdapter: AudioEngineNodeAdapting {
3232

3333
var subject: CurrentValueSubject<Float, Never>?
3434

35-
// private let publisher: (Float) -> Void
3635
private var inputTap: AVAudioMixerNode?
3736

3837
/// Installs a tap on the supplied audio node to monitor input levels.

Sources/StreamVideo/Utils/AudioSession/AudioDeviceModule/RTCAudioDeviceModuleControlling.swift

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,21 @@ protocol RTCAudioDeviceModuleControlling: AnyObject {
1111
var observer: RTCAudioDeviceModuleDelegate? { get set }
1212
var isMicrophoneMuted: Bool { get }
1313
var isStereoPlayoutEnabled: Bool { get }
14-
var isStereoPlayoutAvailable: Bool { get }
1514
var isVoiceProcessingBypassed: Bool { get }
1615
var isVoiceProcessingEnabled: Bool { get }
1716
var isVoiceProcessingAGCEnabled: Bool { get }
18-
var manualRestoreVoiceProcessingOnMono: Bool { get set }
17+
var isManualRestoreVoiceProcessingOnMono: Bool { get }
18+
var prefersStereoPlayout: Bool { get set }
1919

20+
func terminate() -> Int
2021
func initAndStartPlayout() -> Int
2122
func startPlayout() -> Int
2223
func stopPlayout() -> Int
2324
func initAndStartRecording() -> Int
2425
func setMicrophoneMuted(_ isMuted: Bool) -> Int
2526
func stopRecording() -> Int
26-
func setVoiceProcessingEnabled(_ isEnabled: Bool) -> Int
27-
func setVoiceProcessingBypassed(_ isBypassed: Bool) -> Int
28-
func setVoiceProcessingAGCEnabled(_ isEnabled: Bool) -> Int
29-
func setStereoPlayoutEnabled(_ isEnabled: Bool) -> Int
27+
func setManualRestoreVoiceProcessingOnMono(_ isEnabled: Bool)
28+
func refreshStereoPlayoutState()
3029

3130
/// Publisher that emits whenever the microphone mute state changes.
3231
func microphoneMutedPublisher() -> AnyPublisher<Bool, Never>

Sources/StreamVideo/Utils/AudioSession/CallAudioSession.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ final class CallAudioSession: @unchecked Sendable {
9191
self.delegate = delegate
9292
self.statsAdapter = statsAdapter
9393

94+
audioStore.dispatch(.stereo(.setPlayoutPreferred(policy is LivestreamAudioSessionPolicy)))
9495
audioStore.dispatch(.webRTCAudioSession(.setAudioEnabled(true)))
9596

9697
configureCallSettingsAndCapabilitiesObservation(

Sources/StreamVideo/Utils/AudioSession/RTCAudioStore/Namespace/Effects/RTCAudioStore+StereoPlayoutEffect.swift

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -45,25 +45,6 @@ extension RTCAudioStore {
4545
return
4646
}
4747

48-
audioDeviceModule
49-
.isStereoPlayoutAvailablePublisher
50-
.removeDuplicates()
51-
.debounce(for: .seconds(1), scheduler: processingQueue)
52-
.receive(on: processingQueue)
53-
.sink { [weak self, weak audioDeviceModule] isPlayoutAvailable in
54-
self?.dispatcher?.dispatch(
55-
.stereo(
56-
.setPlayoutAvailable(isPlayoutAvailable)
57-
)
58-
)
59-
log.throwing(subsystems: .audioSession) {
60-
try audioDeviceModule?.setStereoPlayoutEnabled(
61-
isPlayoutAvailable
62-
)
63-
}
64-
}
65-
.store(in: disposableBag)
66-
6748
audioDeviceModule
6849
.isStereoPlayoutEnabledPublisher
6950
.removeDuplicates()

Sources/StreamVideo/Utils/AudioSession/RTCAudioStore/Namespace/Middleware/RTCAudioStore+AudioDeviceModuleMiddleware.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,6 @@ extension RTCAudioStore {
2525
line: UInt
2626
) {
2727
switch action {
28-
case .setActive(let value):
29-
log.throwing(subsystems: .audioSession) {
30-
try state.audioDeviceModule?.setPlayout(value)
31-
}
32-
3328
case .setInterrupted(let value):
3429
if let audioDeviceModule = state.audioDeviceModule {
3530
log.throwing(
@@ -83,6 +78,9 @@ extension RTCAudioStore {
8378
)
8479
}
8580

81+
case .stereo(.setPlayoutPreferred(let value)):
82+
state.audioDeviceModule?.setStereoPlayoutPreference(value)
83+
8684
default:
8785
break
8886
}
@@ -151,7 +149,7 @@ extension RTCAudioStore {
151149
.throwing("Unable to disable recording.", subsystems: .audioSession) {
152150
try state.audioDeviceModule?.setRecording(false)
153151
}
154-
log.throwing("Unable to mute.", subsystems: .audioSession) { try state.audioDeviceModule?.setMuted(true) }
152+
state.audioDeviceModule?.terminate()
155153

156154
disposableBag.removeAll()
157155

@@ -160,6 +158,9 @@ extension RTCAudioStore {
160158
}
161159

162160
try audioDeviceModule.setPlayout(state.isActive)
161+
audioDeviceModule.setStereoPlayoutPreference(
162+
state.stereoConfiguration.playout.preferred
163+
)
163164

164165
audioDeviceModule
165166
.isRecordingPublisher

Sources/StreamVideo/Utils/AudioSession/RTCAudioStore/Namespace/RTCAudioStore+Action.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ extension RTCAudioStore {
1414
public enum StoreAction: Sendable, Equatable, StoreActionBoxProtocol, CustomStringConvertible {
1515

1616
enum StereoAction: Equatable, Sendable, CustomStringConvertible {
17-
case setPlayoutAvailable(Bool)
17+
case setPlayoutPreferred(Bool)
1818
case setPlayoutEnabled(Bool)
1919

2020
var description: String {
2121
switch self {
22-
case .setPlayoutAvailable(let value):
23-
return ".setPlayoutAvailable(\(value))"
22+
case .setPlayoutPreferred(let value):
23+
return ".setPlayoutPreferred(\(value))"
24+
2425
case .setPlayoutEnabled(let value):
2526
return ".setPlayoutEnabled(\(value))"
2627
}

Sources/StreamVideo/Utils/AudioSession/RTCAudioStore/Namespace/RTCAudioStore+Coordinator.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ extension RTCAudioStore {
131131
state: StoreState.StereoConfiguration
132132
) -> Bool {
133133
switch action {
134-
case let .setPlayoutAvailable(value):
135-
state.playout.available != value
134+
case let .setPlayoutPreferred(value):
135+
state.playout.preferred != value
136136

137137
case let .setPlayoutEnabled(value):
138138
state.playout.enabled != value

0 commit comments

Comments
 (0)