Skip to content

Commit c71fd6f

Browse files
Add option to disable automatic audio session deactivation (#881)
Calling `setActive(false)` could disrupt other audio in the app. I believe it's relatively harmless to not call it. But in this PR we keep the default to automatic deactivation and add doc about it. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added `isAutomaticDeactivationEnabled` option to control whether the audio session is automatically deactivated when both playout and recording are disabled. The audio session will remain active after calls end when this option is disabled. * **Documentation** * Added documentation section explaining the new automatic audio session deactivation control, including usage examples. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Błażej Pankowski <[email protected]>
1 parent 4ec6dd9 commit c71fd6f

File tree

3 files changed

+43
-15
lines changed

3 files changed

+43
-15
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
patch type="changed" "Add option to disable automatic audio session deactivation"

Docs/audio.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,20 @@ By default, the SDK automatically configures the `AVAudioSession`. However, this
88
AudioManager.shared.audioSession.isAutomaticConfigurationEnabled = false
99
```
1010

11+
## Disabling automatic `AVAudioSession` deactivation
12+
13+
> **Note**: If you have already set `isAutomaticConfigurationEnabled = false`, you don't need to worry about this setting since the SDK won't touch the audio session at all.
14+
15+
By default, the SDK deactivates the `AVAudioSession` when both playout and recording are disabled (e.g., after disconnecting from a room). This allows other apps' audio (like Music) to resume.
16+
17+
However, if your app has its own audio features that could be disrupted by deactivating the audio session, you can disable automatic deactivation:
18+
19+
```swift
20+
AudioManager.shared.audioSession.isAutomaticDeactivationEnabled = false
21+
```
22+
23+
When set to `false`, the audio session remains active after the LiveKit call ends, preserving your app's audio state.
24+
1125
## Disabling Voice Processing
1226

1327
Apple's voice processing is enabled by default, such as echo cancellation and auto-gain control.

Sources/LiveKit/Audio/AudioSessionEngineObserver.swift

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818

1919
import AVFoundation
2020

21-
internal import LiveKitWebRTC
22-
2321
/// An ``AudioEngineObserver`` that configures the `AVAudioSession` based on the state of the audio engine.
2422
public class AudioSessionEngineObserver: AudioEngineObserver, Loggable, @unchecked Sendable {
2523
/// Controls automatic configuration of the `AVAudioSession` based on audio engine state.
@@ -35,6 +33,22 @@ public class AudioSessionEngineObserver: AudioEngineObserver, Loggable, @uncheck
3533
set { _state.mutate { $0.isAutomaticConfigurationEnabled = newValue } }
3634
}
3735

36+
/// Controls whether the audio session is deactivated when the audio engine stops.
37+
///
38+
/// - When `true`: The `AVAudioSession` is deactivated when both playout and recording are disabled
39+
/// - When `false`: The `AVAudioSession` remains active when the audio engine stops
40+
///
41+
/// > Note: This value is only used when `isAutomaticConfigurationEnabled` is `true`.
42+
///
43+
/// > Tip: Set to `false` if your app has other audio features that could be disrupted
44+
/// > by deactivating the audio session.
45+
///
46+
/// Default value: `true`
47+
public var isAutomaticDeactivationEnabled: Bool {
48+
get { _state.isAutomaticDeactivationEnabled }
49+
set { _state.mutate { $0.isAutomaticDeactivationEnabled = newValue } }
50+
}
51+
3852
/// Controls the speaker output preference for audio routing.
3953
///
4054
/// - When `true`: The speaker output is preferred over the receiver output
@@ -52,6 +66,7 @@ public class AudioSessionEngineObserver: AudioEngineObserver, Loggable, @uncheck
5266
var next: (any AudioEngineObserver)?
5367

5468
var isAutomaticConfigurationEnabled: Bool = true
69+
var isAutomaticDeactivationEnabled: Bool = true
5570
var isPlayoutEnabled: Bool = false
5671
var isRecordingEnabled: Bool = false
5772
var isSpeakerOutputPreferred: Bool = true
@@ -84,20 +99,18 @@ public class AudioSessionEngineObserver: AudioEngineObserver, Loggable, @uncheck
8499
}
85100

86101
@Sendable func configure(oldState: State, newState: State) {
87-
let session = LKRTCAudioSession.sharedInstance()
88-
89-
session.lockForConfiguration()
90-
defer {
91-
session.unlockForConfiguration()
92-
log("AudioSession activationCount: \(session.activationCount), webRTCSessionCount: \(session.webRTCSessionCount)")
93-
}
102+
let session = AVAudioSession.sharedInstance()
94103

95104
if (!newState.isPlayoutEnabled && !newState.isRecordingEnabled) && (oldState.isPlayoutEnabled || oldState.isRecordingEnabled) {
96-
do {
97-
log("AudioSession deactivating...")
98-
try session.setActive(false)
99-
} catch {
100-
log("AudioSession failed to deactivate with error: \(error)", .error)
105+
if newState.isAutomaticDeactivationEnabled {
106+
do {
107+
log("AudioSession deactivating...")
108+
try session.setActive(false, options: .notifyOthersOnDeactivation)
109+
} catch {
110+
log("AudioSession failed to deactivate with error: \(error)", .error)
111+
}
112+
} else {
113+
log("AudioSession deactivation skipped...")
101114
}
102115
} else if newState.isRecordingEnabled || newState.isPlayoutEnabled {
103116
// Configure and activate the session with the appropriate category
@@ -106,7 +119,7 @@ public class AudioSessionEngineObserver: AudioEngineObserver, Loggable, @uncheck
106119

107120
do {
108121
log("AudioSession configuring category to: \(config.category)")
109-
try session.setConfiguration(config.toRTCType())
122+
try session.setCategory(config.category, mode: config.mode, options: config.categoryOptions)
110123
} catch {
111124
log("AudioSession failed to configure with error: \(error)", .error)
112125
}

0 commit comments

Comments
 (0)