Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ platformAudio.StopRecording();
platformAudio.SetSessionAudioEnabled(false);
```

While disabled, the SDK holds no call audio session: on iOS WebRTC's voice-processing unit is off and the session sits in a music-friendly idle state, and on Android 12+ the SDK requests neither `MODE_IN_COMMUNICATION` nor the output route pin, so the platform's normal routing applies. Device enumeration and `DevicesChanged` keep working on both, so a device picker can be populated before the first call. Leaving it enabled outside a call asserts the call session for as long as the instance lives, which is rarely what an app wants: the OS treats the app as being in a call, and the route stays pinned to the call policy instead of following the platform.
While disabled, the SDK holds no call audio session: on iOS WebRTC's voice-processing unit is off and the session sits in a music-friendly idle state, and on Android 12+ the SDK requests neither `MODE_IN_COMMUNICATION` nor the output route pin, so the platform's normal routing applies. On Android the session is also acquired lazily: constructing `PlatformAudio` issues no audio-mode traffic even though session audio starts enabled — the first action that needs the session while it is enabled takes it (enabling it explicitly, changing the output preference or selection, or starting capture) — so the disable-right-after-creation pattern above is completely silent at startup. Device enumeration and `DevicesChanged` keep working on both platforms, so a device picker can be populated before the first call. Leaving session audio enabled outside a call asserts the call session (on Android: from the first routing action on) for as long as the instance lives, which is rarely what an app wants: the OS treats the app as being in a call, and the route stays pinned to the call policy instead of following the platform.

Unity's own audio engine is a separate layer that the SDK does not touch, and it needs a little care from an app that plays its own audio (music, SFX) alongside calls. When an output device is added or removed, Unity reinitializes its engine, which **stops every `AudioSource`** — and it raises `AudioSettings.OnAudioConfigurationChanged` only afterwards, so by the time the app is notified there is nothing left playing to inspect. What should still be audible therefore has to be remembered from before the change and restarted in that callback. On Android the callback's `deviceWasChanged` argument is `false` even for a real device change, so it cannot be used to filter these events. The Meet sample's `PlatformAudioController` shows the whole pattern.

Expand All @@ -476,7 +476,7 @@ One consequence to design around on Android: while a call session is active on a

Per-platform behavior:

- **Android 12+ (API 31)**: the full `OutputPreference` ranking applies — the SDK routes to the highest-ranked available kind and re-routes on device changes; kinds missing from the list are never auto-selected (when nothing ranked is available, the OS default route applies). `SelectOutput` pins a device from `GetDevices().Playout` as the communication device; the pin is dropped once that device disappears. While session audio is disabled, `SelectOutput` only records the choice — it is applied when the session is next enabled, and until then `GetDevices`/`DevicesChanged` keep reporting the platform's own route. `DevicesChanged` is raised on communication-device changes; changes that fire no OS event are caught by a poll with roughly 1.5 s of latency. Requires the `MODIFY_AUDIO_SETTINGS` permission in your `AndroidManifest.xml`. Routing is asserted only while session audio is enabled: the SDK enters `MODE_IN_COMMUNICATION` and pins the route on enable, and clears the pin and restores the mode it replaced on disable, while enumeration and `DevicesChanged` stay live either way. Note: since Android 13 the OS only honors the app's communication-mode request — and with it the route pin — while the app has an active voice-communication capture, so keep the mic capture running for the whole call, even while muted with the track unpublished (see `PlatformAudioController` in the Meet sample); an active capture without an enabled session hands routing back to the platform, so pair the two at the call boundaries.
- **Android 12+ (API 31)**: the full `OutputPreference` ranking applies — the SDK routes to the highest-ranked available kind and re-routes on device changes; kinds missing from the list are never auto-selected (when nothing ranked is available, the OS default route applies). `SelectOutput` pins a device from `GetDevices().Playout` as the communication device; the pin is dropped once that device disappears. While session audio is disabled, `SelectOutput` only records the choice — it is applied when the session is next enabled, and until then `GetDevices`/`DevicesChanged` keep reporting the platform's own route. `DevicesChanged` is raised on communication-device changes; changes that fire no OS event are caught by a poll with roughly 1.5 s of latency. Requires the `MODIFY_AUDIO_SETTINGS` permission in your `AndroidManifest.xml`. Routing is asserted only while session audio is enabled: the session is first taken by the first trigger that needs it while enabled — an explicit enable, an output preference/selection change, or capture starting; never by construction alone — the SDK then holds `MODE_IN_COMMUNICATION` with the route pinned, and clears the pin and restores the mode it replaced on disable, while enumeration and `DevicesChanged` stay live either way. Note: since Android 13 the OS only honors the app's communication-mode request — and with it the route pin — while the app has an active voice-communication capture, so keep the mic capture running for the whole call, even while muted with the track unpublished (see `PlatformAudioController` in the Meet sample); an active capture without an enabled session hands routing back to the platform, so pair the two at the call boundaries.
- **Older Android**: no routing backend — `OutputPreference` is stored and round-trips but has no routing effect, and `SelectOutput` throws `NotSupportedException`. `DevicesChanged` is never raised.
- **iOS**: external devices (Bluetooth, wired) always take priority over the built-in outputs, so the Speaker/Earpiece relative order — `IsSpeakerOutputPreferred` — is the only part of the ranking with an effect. It decides where audio goes when no external device is connected, is applied through the audio session mode (never by overriding the output port), and takes effect immediately, including mid-call. `SelectOutput` throws `NotSupportedException` — the OS owns route selection on iOS; present the system route picker (`AVRoutePickerView`) instead. `GetDevices().Playout` is the audio session's current output route (iOS does not enumerate every reachable device), and `DevicesChanged` is raised when that route changes.
- **Desktop (Windows/macOS/Linux)**: output is selected per device — `SelectOutput` selects the playout device like `SetPlayoutDevice`, and the `OutputPreference` ranking has no routing effect. `DevicesChanged` is never raised (no hot-plug events yet).
Expand Down
Loading
Loading