Skip to content

Restore cached iOS audio session on PlatformAudio dispose#350

Open
MaxHeimbrock wants to merge 1 commit into
mainfrom
max/ios-restore-audio-session
Open

Restore cached iOS audio session on PlatformAudio dispose#350
MaxHeimbrock wants to merge 1 commit into
mainfrom
max/ios-restore-audio-session

Conversation

@MaxHeimbrock

Copy link
Copy Markdown
Contributor

Problem

On iOS, constructing PlatformAudio calls LiveKit_ConfigureAudioSessionForVoIP(), switching the shared AVAudioSession to PlayAndRecord + VoiceChat. On dispose, nothing restored it:

  • PlatformAudio.Dispose() never called the restore hook — LiveKit_RestoreDefaultAudioSession() was dead code (declared + implemented, zero callers).
  • Even if called, it hardcoded AVAudioSessionCategoryAmbient and 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.Reset does not fix this — it only reinitializes Unity's internal (FMOD) engine, not the OS-level session.

Change

  • LiveKitAudioSession.mm: snapshot category/mode/categoryOptions once, lazily, at the top of LiveKit_ConfigureAudioSessionForVoIP() — i.e. right before LiveKit first mutates the session, so it captures whatever Unity configured from its iOS Player Settings. Rewrite LiveKit_RestoreDefaultAudioSession() to re-apply that snapshot and setActive:YES (falls back to Ambient only if nothing was cached).
  • PlatformAudio.cs: track live instances with an Interlocked counter (iOS-guarded); increment after successful construction, decrement in Dispose(), and fire restore only when the count hits 0 — matching the native ADM ref-count teardown.

Explicit-Dispose() only (no finalizer): avoids touching AVAudioSession off the main thread. Kept minimal — the existing automatic-mode / VoiceChat config is unchanged.

Testing

  • C# Runtime assembly compiles cleanly with UNITY_IOS defined / UNITY_EDITOR off (0 errors) — the guarded code is actually type-checked.
  • .mm passes clang -fsyntax-only against the iOS SDK under both ARC and MRC (no errors).
  • Needs on-device verification (not doable in CI): connect a room with PlatformAudio, dispose it, then play a Unity AudioSource and confirm routing/volume return to the pre-call behavior. The new NSLogs report the cached + restored category/mode.

Follow-ups (out of scope)

  • A more advanced manual-mode prototype (RTCAudioSession useManualAudio, VideoChat, VPIO gating to keep Unity audio alive during calls) exists as a build artifact under Samples~/Meet/Builds/ — not adopted here.
  • .mm.meta also targets visionOS/tvOS, but the C# P/Invoke is UNITY_IOS-only (pre-existing).

🤖 Generated with Claude Code

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 xianshijing-lk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curiously, should we continue caching the latest session rather than simply return here ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, initialize it to 0 as default value ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants