Skip to main content

PlatformAudio

Struct PlatformAudio 

Source
pub struct PlatformAudio { /* private fields */ }
Expand description

Platform audio device management for microphone capture and speaker playout.

PlatformAudio provides access to the platform’s audio devices via WebRTC’s Audio Device Module (ADM). Use it to:

  • Enumerate available microphones and speakers
  • Select which devices to use
  • Create audio tracks that capture from the microphone

§Creating a PlatformAudio Instance

use livekit::PlatformAudio;

let audio = PlatformAudio::new()?;

This enables the platform ADM. If an instance already exists, the new instance shares the same underlying ADM.

§Device Enumeration

// List microphones
for device in audio.recording_devices() {
    println!("Mic {}: {}", device.index, device.name);
}

// List speakers
for device in audio.playout_devices() {
    println!("Speaker {}: {}", device.index, device.name);
}

§Device Selection

if let Some(device) = audio.recording_devices().next() {
    audio.set_recording_device(&device.id)?;
}

// Hot-swap devices during active session
let devices: Vec<_> = audio.recording_devices().collect();
if let Some(device) = devices.get(1) {
    audio.switch_recording_device(&device.id)?;
}

§Creating Audio Tracks

use livekit::prelude::*;

let audio = PlatformAudio::new()?;
let track = LocalAudioTrack::create_audio_track("microphone", audio.rtc_source());

room.local_participant()
    .publish_track(LocalTrack::Audio(track), opts)
    .await?;

§Lifecycle Management

PlatformAudio uses reference counting. Multiple instances share the same underlying ADM, and the ADM is automatically disabled when all instances are dropped.

let audio1 = PlatformAudio::new()?;  // Enables ADM
let audio2 = PlatformAudio::new()?;  // Shares ADM (ref_count = 2)
let audio3 = audio1.clone();         // Shares ADM (ref_count = 3)

drop(audio1);  // ref_count = 2, ADM still active
drop(audio2);  // ref_count = 1, ADM still active
drop(audio3);  // ref_count = 0, ADM disabled

You can also explicitly release:

audio.release();  // Equivalent to drop(audio)

§Platform-Specific Notes

  • iOS: Creates a VPIO AudioUnit (exclusive microphone access). Drop all instances to allow other audio frameworks to use the mic.
  • macOS: Uses CoreAudio for device management.
  • Windows: Uses WASAPI for device management.
  • Linux: Uses PulseAudio or ALSA.

Implementations§

Source§

impl PlatformAudio

Source

pub fn new() -> AudioResult<Self>

Creates a new PlatformAudio instance.

Platform ADM is always available and initialized at startup. If another PlatformAudio instance exists, this reuses the same handle.

§Errors

Returns AudioError::PlatformInitFailed if no audio devices are available.

§Example
use livekit::PlatformAudio;

let audio = PlatformAudio::new()?;
println!("Found {} microphones", audio.recording_devices());
Source

pub fn rtc_source(&self) -> RtcAudioSource

Returns the RtcAudioSource to use when creating audio tracks.

This returns RtcAudioSource::Device, which tells the track to capture audio from the platform’s selected recording device (microphone).

§Example
use livekit::prelude::*;

let audio = PlatformAudio::new()?;
let track = LocalAudioTrack::create_audio_track("mic", audio.rtc_source());
Source

pub fn recording_devices( &self, ) -> impl Iterator<Item = RecordingDeviceInfo> + '_

Returns an iterator over available recording (microphone) devices.

Each RecordingDeviceInfo contains the device’s unique ID, name, and index. Use the id field with set_recording_device() for type-safe device selection.

§Platform Notes

Desktop (Windows, macOS, Linux): Full device enumeration is supported. You can enumerate USB microphones, built-in mics, audio interfaces, etc. Each device has a unique ID (GUID) and descriptive name.

Android: Only a single “default” device is reported with an empty name and ID. Android does not support app-level microphone selection - the system automatically selects the best input source based on the audio mode and connected accessories. Device enumeration on Android is not meaningful for user-facing device pickers.

iOS: Similar to desktop - devices can be enumerated, though typically only the built-in microphone and any connected accessories are available.

§Example
let audio = PlatformAudio::new()?;
for device in audio.recording_devices() {
    println!("[{}] {} (ID: {})", device.index, device.name, device.id);
}

// Collect into a Vec for later use
let devices: Vec<_> = audio.recording_devices().collect();
Source

pub fn playout_devices(&self) -> impl Iterator<Item = PlayoutDeviceInfo> + '_

Returns an iterator over available playout (speaker) devices.

Each PlayoutDeviceInfo contains the device’s unique ID, name, and index. Use the id field with set_playout_device() for type-safe device selection.

§Platform Notes

Desktop (Windows, macOS, Linux): Full device enumeration is supported. You can enumerate speakers, headphones, USB audio devices, HDMI outputs, etc. Each device has a unique ID (GUID) and descriptive name.

Android: Only a single “default” device is reported with an empty name and ID. Android handles audio routing (speaker, earpiece, Bluetooth, wired headset) at the system level via AudioManager, not through WebRTC device selection. To switch between speaker and earpiece on Android, use the Android AudioManager API:

  • audioManager.setSpeakerphoneOn(true/false)
  • audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION)

Device enumeration on Android is not meaningful for user-facing device pickers.

iOS: Similar to desktop - devices can be enumerated and selected, including built-in speaker, receiver, and connected Bluetooth/wired accessories.

§Example
let audio = PlatformAudio::new()?;
for device in audio.playout_devices() {
    println!("[{}] {} (ID: {})", device.index, device.name, device.id);
}

// Collect into a Vec for later use
let devices: Vec<_> = audio.playout_devices().collect();
Source

pub fn set_recording_device(&self, id: &RecordingDeviceId) -> AudioResult<()>

Selects a recording (microphone) device by ID.

This is the preferred method for device selection as IDs are stable across device hot-plug events, unlike indices which can change.

§Platform Notes

Desktop: Works as expected - select from enumerated devices.

Mobile (iOS/Android): Device selection is a no-op. Both platforms handle microphone selection at the system level. This method will succeed but has no effect.

  • iOS: VPIO AudioUnit handles input selection
  • Android: System selects best input source based on audio mode
§Arguments
§Errors

Returns AudioError::DeviceNotFound if the device ID does not exist in the current list of available recording devices.

§Example
let audio = PlatformAudio::new()?;

// Get the first microphone (desktop only - on mobile this is a no-op)
if let Some(device) = audio.recording_devices().next() {
    audio.set_recording_device(&device.id)?;
}
Source

pub fn set_playout_device(&self, id: &PlayoutDeviceId) -> AudioResult<()>

Selects a playout (speaker) device by ID.

This is the preferred method for device selection as IDs are stable across device hot-plug events, unlike indices which can change.

§Platform Notes

Desktop: Works as expected - select from enumerated devices.

Mobile (iOS/Android): Device selection is a no-op. Both platforms handle audio routing at the system level. This method will succeed but has no effect.

  • iOS: Use AVAudioSession to control routing (speaker, earpiece, Bluetooth)
  • Android: Use AudioManager.setSpeakerphoneOn() to switch outputs
§Arguments
§Errors

Returns AudioError::DeviceNotFound if the device ID does not exist in the current list of available playout devices.

§Example
let audio = PlatformAudio::new()?;

// Get the first speaker (desktop only - on mobile this is a no-op)
if let Some(device) = audio.playout_devices().next() {
    audio.set_playout_device(&device.id)?;
}
Source

pub fn switch_recording_device(&self, id: &RecordingDeviceId) -> AudioResult<()>

Switches the recording device while audio is active (hot-swap).

Unlike set_recording_device, this method handles the stop/change/restart sequence required when recording is already active.

§Arguments
§Errors
§Example
// During an active call, switch to a different microphone
let devices: Vec<_> = audio.recording_devices().collect();
audio.switch_recording_device(&devices[1].id)?;
Source

pub fn switch_playout_device(&self, id: &PlayoutDeviceId) -> AudioResult<()>

Switches the playout device while audio is active (hot-swap).

Unlike set_playout_device, this method handles the stop/change/restart sequence required when playout is already active.

§Arguments
§Errors
§Example
// During an active call, switch to a different speaker
let devices: Vec<_> = audio.playout_devices().collect();
audio.switch_playout_device(&devices[1].id)?;
Source

pub fn start_recording(&self) -> AudioResult<()>

Starts recording from the microphone.

Recording is automatically started when a track using RtcAudioSource::Device is published. Use this method to resume recording after calling stop_recording.

This method turns on the system’s recording privacy indicator (e.g., the orange dot on iOS, or the microphone icon on macOS).

§Errors

Returns AudioError::OperationFailed if recording could not be started.

§Example
let audio = PlatformAudio::new()?;
audio.start_recording()?;  // Resume recording after stop
Source

pub fn stop_recording(&self) -> AudioResult<()>

Stops recording from the microphone.

Use this method to temporarily stop recording without disposing PlatformAudio. This turns off the system’s recording privacy indicator (e.g., the orange dot on iOS, or the microphone icon on macOS).

Call start_recording to resume recording.

§Note

When recording is stopped, any published audio tracks using RtcAudioSource::Device will send silence. You should typically unpublish the track before stopping recording.

§Errors

Returns AudioError::OperationFailed if recording could not be stopped.

§Example
let audio = PlatformAudio::new()?;
// ... publish microphone track ...

// Mute: stop recording to turn off privacy indicator
room.local_participant().unpublish_track(track, false).await?;
audio.stop_recording()?;

// Unmute: start recording and republish
audio.start_recording()?;
room.local_participant().publish_track(new_track, opts).await?;
Source

pub fn is_recording_initialized(&self) -> bool

Returns whether recording is currently initialized.

Recording is initialized when start_recording is called or when a track using RtcAudioSource::Device is published.

Source

pub fn ref_count(&self) -> usize

Returns the number of active references to the platform ADM.

This includes all PlatformAudio instances sharing the same ADM.

§Example
let audio1 = PlatformAudio::new()?;
assert_eq!(audio1.ref_count(), 1);

let audio2 = audio1.clone();
assert_eq!(audio1.ref_count(), 2);
Source

pub fn release(self)

Explicitly releases this instance’s reference to the platform ADM.

This is equivalent to drop(self). If this is the last reference, the platform ADM is disabled and hardware resources are released.

§Example
let audio = PlatformAudio::new()?;
// ... use audio ...
audio.release();  // ADM disabled if this was the last reference
Source

pub fn is_hardware_aec_available(&self) -> bool

Checks if hardware echo cancellation is available on this device.

§Platform Behavior
  • iOS: Returns true (VPIO provides hardware AEC)
  • Android: Returns true on devices with hardware AEC support
  • Desktop: Returns false (hardware AEC not available)
§Example
let audio = PlatformAudio::new()?;
if audio.is_hardware_aec_available() {
    println!("Hardware AEC is available");
}
Source

pub fn is_hardware_agc_available(&self) -> bool

Checks if hardware automatic gain control is available on this device.

§Platform Behavior
  • iOS: Returns true (VPIO provides hardware AGC)
  • Android: Returns true on devices with hardware AGC support
  • Desktop: Returns false (hardware AGC not available)
Source

pub fn is_hardware_ns_available(&self) -> bool

Checks if hardware noise suppression is available on this device.

§Platform Behavior
  • iOS: Returns true (VPIO provides hardware NS)
  • Android: Returns true on devices with hardware NS support
  • Desktop: Returns false (hardware NS not available)
Source

pub fn active_aec_type(&self) -> AudioProcessingType

Gets the type of echo cancellation currently active.

§Returns
§Example
let audio = PlatformAudio::new()?;
match audio.active_aec_type() {
    AudioProcessingType::Hardware => println!("Using hardware AEC"),
    AudioProcessingType::Software => println!("Using software AEC"),
    AudioProcessingType::None => println!("AEC disabled"),
}
Source

pub fn active_agc_type(&self) -> AudioProcessingType

Gets the type of automatic gain control currently active.

Source

pub fn active_ns_type(&self) -> AudioProcessingType

Gets the type of noise suppression currently active.

Source

pub fn configure_audio_processing( &self, options: AudioProcessingOptions, ) -> AudioResult<()>

Configures audio processing with the given options.

This method configures echo cancellation, noise suppression, and automatic gain control based on the provided options.

§Platform Behavior
  • iOS: prefer_hardware_processing is ignored (always uses VPIO)
  • Android: When prefer_hardware_processing is false, hardware effects are disabled and WebRTC’s software APM is used instead
  • Desktop: prefer_hardware_processing is ignored (hardware not available)
§Example
use livekit::{PlatformAudio, AudioProcessingOptions};

let audio = PlatformAudio::new()?;

// Use defaults (software processing recommended)
audio.configure_audio_processing(AudioProcessingOptions::default())?;

// Disable echo cancellation
audio.configure_audio_processing(AudioProcessingOptions {
    echo_cancellation: false,
    ..Default::default()
})?;
Source

pub fn set_echo_cancellation( &self, enable: bool, prefer_hardware: bool, ) -> AudioResult<()>

Enables or disables echo cancellation.

This is a convenience method equivalent to calling configure_audio_processing with only the echo_cancellation field changed.

§Arguments
  • enable - true to enable AEC, false to disable
  • prefer_hardware - true to prefer hardware AEC on supported devices
Source

pub fn set_auto_gain_control( &self, enable: bool, prefer_hardware: bool, ) -> AudioResult<()>

Enables or disables automatic gain control.

§Arguments
  • enable - true to enable AGC, false to disable
  • prefer_hardware - true to prefer hardware AGC on supported devices
Source

pub fn set_noise_suppression( &self, enable: bool, prefer_hardware: bool, ) -> AudioResult<()>

Enables or disables noise suppression.

§Arguments
  • enable - true to enable NS, false to disable
  • prefer_hardware - true to prefer hardware NS on supported devices

Trait Implementations§

Source§

impl Clone for PlatformAudio

Source§

fn clone(&self) -> PlatformAudio

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PlatformAudio

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more