Skip to content

fix(platform-audio): synthesize index GUIDs on Linux and support index-based selection - #1529

Merged
xianshijing-lk merged 1 commit into
livekit:mainfrom
NabiBukhsh-AI:fix/platform-audio-empty-guid
Oct 17, 2026
Merged

xianshijing-lk merged 1 commit into
livekit:mainfrom
NabiBukhsh-AI:fix/platform-audio-empty-guid

Conversation

@NabiBukhsh-AI

Copy link
Copy Markdown
Contributor

Fixes #1528.

Problem

On Linux with PulseAudio/ALSA, WebRTC's audio device module does not populate device GUIDs (device_guid is left empty ""). As a result, PlatformAudio::recording_devices() and playout_devices() return devices with empty IDs (RecordingDeviceId("")).
When selecting or switching devices via set_recording_device / switch_recording_device (or playout equivalents), set_*_device_by_guid matches "" == "" on index 0, which always selects the default device 0 and makes it impossible to select secondary microphones or speakers on Linux.

Solution

  1. webrtc-sys (AudioDeviceController):
    • In recording_device_guid and playout_device_guid, when the underlying ADM does not provide a hardware GUID (guid[0] == '\0'), synthesize an index-based identifier ("index:" + std::to_string(index)) so devices can be uniquely distinguished and selected.
    • In set_recording_device_by_guid and set_playout_device_by_guid:
      • Reject empty GUID strings (return false).
      • Directly resolve "index:<N>" identifiers to device indices with bounds checking.
      • Match non-empty device_guids only (preventing false-positive matches when device_guid is empty).
      • Support fallback matching by device name.
      • Keep index 0 fallback restricted to mobile platforms (WEBRTC_IOS / WEBRTC_ANDROID) where device selection is handled by the system.
  2. livekit (PlatformAudio):
    • Add RecordingDeviceId::from_index and PlayoutDeviceId::from_index.
    • When constructing RecordingDeviceInfo and PlayoutDeviceInfo, fall back to from_index if guid.is_empty().
    • Support "index:<N>" in set_recording_device, set_playout_device, switch_recording_device, and switch_playout_device.
    • Add index-based selection methods: set_recording_device_by_index, set_playout_device_by_index, switch_recording_device_by_index, and switch_playout_device_by_index.
  3. Tests & Changeset:
    • Added unit tests in livekit/src/platform_audio/mod.rs for from_index and equality/hashing.
    • Added integration tests in livekit/tests/platform_audio_test.rs for index-based selection and out-of-bounds error handling.
    • Added assertion in webrtc-sys bridge test.
    • Added Knope changeset for livekit, webrtc-sys, libwebrtc, livekit-capture, and livekit-ffi.

@NabiBukhsh-AI
NabiBukhsh-AI requested a review from ladvoc as a code owner October 16, 2026 19:40

@devin-ai-integration devin-ai-integration Bot 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.

Devin Review found 2 potential issues.

3 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)

Devin Review

Comment on lines +711 to +713
let success = if let Some(idx_str) = guid.strip_prefix("index:") {
if let Ok(idx) = idx_str.parse::<u16>() {
self.handle.runtime.set_recording_device(idx)

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.

🟡 Index IDs fail on GUID-backed devices

On GUID-backed desktops, set_recording_device rejects from_index(0) before selecting the device. recording_devices exposes hardware GUIDs, so its ID check cannot match the index ID. The same failure affects recording switches and playout selection.

Learn more

The new public from_index constructors create IDs intended to select a device by index. On platforms where enumeration returns hardware GUIDs, ensure_device_exists compares these constructed IDs with GUID-backed IDs and rejects them before the index branch executes. This affects both set methods and recording switches. Playout switches lack that check and therefore behave differently.

Example: A Windows microphone at index 0 has GUID mic-123. RecordingDeviceId::from_index(0) creates index:0; set_recording_device returns DeviceNotFound despite the device existing, while set_recording_device_by_index(0) succeeds.

Recommended fix: Validate parsed index IDs against the current device count instead of comparing them to enumerated GUIDs, and apply the same approach to both set and switch paths for recording and playout.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +87 to +89
if (target_guid.empty()) {
return false;
}

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.

🟡 Existing Android device IDs stop working

When an Android client reuses its previously enumerated empty ID, set_playout_device_by_guid rejects it. The former default-device fallback accepted that ID, so persisted selections now fail despite an available device. Recording selection has the same regression.

Learn more

Android previously reported an empty device ID. The selection methods previously searched empty GUIDs and then fell back to device 0 when none matched. Callers, including the FFI selection handlers, can supply such previously enumerated IDs. The new unconditional empty-string rejection prevents mobile fallback, so upgrading breaks existing saved selections.

Example: An Android app saved its speaker ID as "" before upgrading. After upgrading, it passes that ID to set_playout_device; the call returns DeviceNotFound although the default speaker is available.

Recommended fix: Retain the existing empty-ID default-device behavior for mobile platforms only; reject empty IDs on desktop. Apply the same fix to recording and playout.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

@xianshijing-lk

Copy link
Copy Markdown
Contributor

Thanks for fixing it.

@xianshijing-lk
xianshijing-lk merged commit 4bfe759 into livekit:main Oct 17, 2026
33 checks passed
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.

PlatformAudio: device GUIDs are empty on Linux (PulseAudio), so set_*_device / switch_*_device can't select a device

2 participants