Restore cached iOS audio session on PlatformAudio dispose#350
Open
MaxHeimbrock wants to merge 1 commit into
Open
Restore cached iOS audio session on PlatformAudio dispose#350MaxHeimbrock wants to merge 1 commit into
MaxHeimbrock wants to merge 1 commit into
Conversation
PlatformAudio switches the shared AVAudioSession to PlayAndRecord + VoiceChat on iOS but never restored it on dispose: the restore hook was dead code, and it only set Ambient without reactivating. After a call ended, the session stayed on the VoIP profile and deactivated, degrading normal Unity audio playback (quiet, receiver-biased routing). Snapshot the session's category/mode/options the first time LiveKit configures it (the state Unity set from its iOS Player Settings), and re-apply that snapshot + reactivate when the last PlatformAudio is disposed. Instance lifetime is tracked with an Interlocked counter so restore fires only on the final dispose, matching the native ADM ref-count teardown. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
xianshijing-lk
approved these changes
Jul 13, 2026
xianshijing-lk
left a comment
Contributor
There was a problem hiding this comment.
one question, lgtm
| /// LiveKit reconfigures the session for VoIP. Subsequent calls are no-ops so the | ||
| /// snapshot always reflects the pristine, pre-LiveKit (Unity-configured) state. | ||
| static void LiveKit_CacheSessionStateIfNeeded() { | ||
| if (s_hasCachedState) { |
Contributor
There was a problem hiding this comment.
curiously, should we continue caching the latest session rather than simply return here ?
Contributor
Author
There was a problem hiding this comment.
Sorry this is supposed to be a draft, not ready for review. We have some issues with teardown but this one does not solve it all, I am still investigating.
| // Restore the audio session Unity had before VoIP, but only once the | ||
| // last PlatformAudio is gone (the native ADM is likewise ref-counted). | ||
| if (System.Threading.Interlocked.Decrement(ref _instanceCount) == 0) | ||
| IOSAudioSessionHelper.LiveKit_RestoreDefaultAudioSession(); |
Contributor
There was a problem hiding this comment.
nit, add a {} for style
| #if UNITY_IOS && !UNITY_EDITOR | ||
| // Tracks live PlatformAudio instances so the iOS audio session is restored | ||
| // only when the last one is disposed (aligned with the native ADM ref-count). | ||
| private static int _instanceCount; |
Contributor
There was a problem hiding this comment.
nit, initialize it to 0 as default value ?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On iOS, constructing
PlatformAudiocallsLiveKit_ConfigureAudioSessionForVoIP(), switching the sharedAVAudioSessiontoPlayAndRecord+VoiceChat. On dispose, nothing restored it:PlatformAudio.Dispose()never called the restore hook —LiveKit_RestoreDefaultAudioSession()was dead code (declared + implemented, zero callers).AVAudioSessionCategoryAmbientand never reactivated.Net effect: after a call ended, the app's session stayed on the VoIP profile and deactivated, so normal Unity audio playback was degraded (quiet, receiver-biased routing).
AudioSettings.Resetdoes not fix this — it only reinitializes Unity's internal (FMOD) engine, not the OS-level session.Change
LiveKitAudioSession.mm: snapshotcategory/mode/categoryOptionsonce, lazily, at the top ofLiveKit_ConfigureAudioSessionForVoIP()— i.e. right before LiveKit first mutates the session, so it captures whatever Unity configured from its iOS Player Settings. RewriteLiveKit_RestoreDefaultAudioSession()to re-apply that snapshot andsetActive:YES(falls back toAmbientonly if nothing was cached).PlatformAudio.cs: track live instances with anInterlockedcounter (iOS-guarded); increment after successful construction, decrement inDispose(), and fire restore only when the count hits 0 — matching the native ADM ref-count teardown.Explicit-
Dispose()only (no finalizer): avoids touchingAVAudioSessionoff the main thread. Kept minimal — the existing automatic-mode /VoiceChatconfig is unchanged.Testing
UNITY_IOSdefined /UNITY_EDITORoff (0 errors) — the guarded code is actually type-checked..mmpassesclang -fsyntax-onlyagainst the iOS SDK under both ARC and MRC (no errors).PlatformAudio, dispose it, then play a UnityAudioSourceand confirm routing/volume return to the pre-call behavior. The newNSLogs report the cached + restored category/mode.Follow-ups (out of scope)
useManualAudio,VideoChat, VPIO gating to keep Unity audio alive during calls) exists as a build artifact underSamples~/Meet/Builds/— not adopted here..mm.metaalso targets visionOS/tvOS, but the C# P/Invoke isUNITY_IOS-only (pre-existing).🤖 Generated with Claude Code