LiveKit Rust SDKs

repository·main·Indexed 19 days ago

https://github.com/livekit/rust-sdks

The LiveKit Rust SDK provides real-time video, audio, and data capabilities for Rust applications, enabling developers to connect to LiveKit Cloud or self-hosted servers. The repository includes the livekit and livekit-ffi crates, along with examples for agent dispatch, data tracks, text streaming, and local audio capture. It provides Docker-based build processes for x86_64 and AArch64 architectures to ensure binary compatibility with Linux distributions.

Tokens
83.7K
Snippets
251
Records
367
Agent score
61%

What's inside livekit-rust-sdks

  1. Overview of the LiveKit Rust Client SDK

    main
    The LiveKit Rust Client SDK is a standalone, cross-platform SDK designed for Rust developers. It serves as a core foundation for other platform-specific SDKs (like Unity, iOS, and Android) by encapsulating signaling protocol logic and WebRTC business logic into a clean set of abstractions. This allows for consistent behavior across different platforms and frameworks.
  2. Overview of the LiveKit Rust Client SDK

    main
    The LiveKit Rust Client SDK is the official library for integrating real-time video, audio, and data features into Rust applications. It allows developers to connect to LiveKit rooms and participate in real-time multimedia sessions.
  3. Overview of LiveKit UniFFI

    main

    LiveKit UniFFI is an experimental FFI (Foreign Function Interface) using UniFFI. It is designed to expose core business logic that can be incrementally adopted by client SDKs.

    Currently, it does not replace the existing _livekit-ffi_ interface but focuses on modularizing specific functionalities.

    Exposed Functionality:

    • Logging
    • Access token generation and verification
  4. Overview of LiveKit FFI

    main

    LiveKit FFI provides Foreign Function Interface bindings for LiveKit. It is compiled as a dynamic library, which enables high-level client SDKs written in other languages to invoke APIs from the core livekit Rust crate. This architecture is used to power the following official client SDKs:

    • Python
    • NodeJS
    • Unity
  5. Debug Rust code through Swift (UniFFI)

    main

    The published livekit-uniffi-xcframework is a release build with symbols stripped, meaning Xcode/lldb will only show Swift glue and bare memory addresses for Rust. To see full Rust stack traces, source listings, and variables, you must build a local debug xcframework that contains full DWARF debug info.

    Key Requirements:

    • Keep the rust-sdks checkout and its target/ directory available. lldb resolves DWARF via OSO references into these object files.
    • Rebuild the xcframework after any Rust changes.
    • Ensure the xcframework is built for the target platform (e.g., macOS or iOS simulator).

    Verification: You can check if a binary is debuggable by running: nm -a <binary> | grep -c ' OSO ' (A result > 0 indicates DWARF references are present; a stripped release build will return 0).

  6. Understand PlatformAudio FFI Handle Lifecycle and Reference Counting

    main

    The PlatformAudio FFI interface uses a reference-counting mechanism to manage the underlying Audio Device Module (ADM). Multiple FFI clients can create handles, but they all share the same underlying ADM instance.

    Lifecycle Rules

    • Creation: The first call to NewPlatformAudioRequest triggers the creation and initialization of the Platform ADM and enables ADM recording.
    • Sharing: Subsequent calls to NewPlatformAudioRequest increment the internal reference count and reuse the existing ADM.
    • Termination: The ADM is only terminated and disabled when the reference count reaches zero (i.e., the last handle is released via DisposeRequest).

    Reference Counting Example

    1. Client A calls NewPlatformAudioRequest() $\rightarrow$ handle_1 created (ref_count: 1).
    2. Client B calls NewPlatformAudioRequest() $\rightarrow$ handle_2 created (ref_count: 2).
    3. Client A calls DisposeRequest(handle_1) $\rightarrow$ (ref_count: 1).
    4. Client B calls DisposeRequest(handle_2) $\rightarrow$ (ref_count: 0, ADM disabled).
  7. How to use Data Tracks in LiveKit

    main

    The livekit-datatrack crate is an internal component used to power data track features within LiveKit client SDKs. It is not intended for direct use by application developers.

    To implement data track functionality in your application, you must use the public APIs provided by the official LiveKit client SDKs (such as the Rust SDK).

  8. Understand Platform Audio Mode for VoIP

    main

    Platform Audio Mode is used for dedicated VoIP applications that require direct access to the microphone and speakers, and need Acoustic Echo Cancellation (AEC). In this mode, the SDK manages the hardware via a platform-specific Audio Device Module (ADM).

    Initialization Flow

    1. PlatformAudio::new() is called.
    2. The runtime acquires the platform ADM via AdmProxy::AcquirePlatformAdm(), which increments the reference count and creates the webrtc::AudioDeviceModule if it's the first time.
    3. Recording is enabled via runtime.set_adm_recording_enabled(true).
    4. Playout is enabled via runtime.set_adm_playout_enabled(true).

    Outbound Audio (Microphone to Network)

    The Platform ADM captures microphone PCM data. This data is routed through AudioState to AudioSendStream instances where external=false (device sources). The audio is then encoded and sent over the network.

    Inbound Audio (Network to Speakers)

    Decoded audio from the network flows through AudioReceiveStream into the AudioMixer. Because playout_enabled is true, the AdmProxy's NeedMorePlayData() delegates the audio to the platform_adm_, which plays the audio directly to the hardware speakers.

    Key Characteristics

    • Platform ADM: Created and managed by the SDK.
    • AEC Support: Acoustic Echo Cancellation works because the playout signal is routed through the ADM, providing the necessary reference signal.
    • iOS Behavior: AVAudioSession is configured for VoIP mode.
  9. How ADM Reference Counting works for shared audio resources

    main

    The AdmProxy uses a reference counting pattern to manage the lifecycle of the Platform ADM. This allows multiple clients (e.g., different FFI handles or multiple users in a single application) to share the same hardware audio resources without redundant initialization or premature termination.

    • Single User: PlatformAudio::new() increments the ref count to 1 and creates the ADM. drop(audio) decrements it to 0 and terminates the ADM.
    • Multiple Users: If audio1 is active and audio2 = PlatformAudio::new() is called, the ref count becomes 2. The ADM is reused. The ADM is only terminated when the last active handle is dropped.
    • FFI Clients (Unity/Python): Multiple handles (e.g., from different Unity clients) increment the platform_adm_ref_count_. A separate process (like a Python agent) using a NativeAudioSource does not interact with the platform_adm_ and thus operates in Synthetic Mode, even if Unity clients have activated Platform Mode.
    Reference Counting Examples
    
      SCENARIO 1: Single User
      ════════════════════════
    
        Time ──────────────────────────────────────────────────────────▶
    
        ┌──────────────────────┐                    ┌──────────────────────┐
        │ PlatformAudio::new() │                    │ drop(audio)          │
        │ ref_count: 0 → 1     │                    │ ref_count: 1 → 0     │
        │ CREATE Platform ADM  │                    │ TERMINATE Platform   │
        └──────────┬───────────┘                    └──────────┬───────────┘
                   │                                           │
                   ▼                                           ▼
        ═══════════╪═════════════════════════════════════════╪═══════════
        Synthetic  │         Platform Mode Active              │  Synthetic
                   │                                           │
    
      SCENARIO 2: Multiple Users (Shared ADM)
      ═════════════════════════════════════════
    
        Time ──────────────────────────────────────────────────────────▶
    
        ┌───────────┐     ┌───────────┐     ┌───────────┐     ┌───────────┐
        │ audio1 =  │     │ audio2 =  │     │ drop(audio1│     │ drop(audio2│
        │ new()     │     │ new()     │     │           │     │           │
        │ ref: 0→1  │     │ ref: 1→2  │     │ ref: 2→1  │     │ ref: 1→0  │
        │ CREATE    │     │ (reuse)   │     │ (still    │     │ TERMINATE │
        │ ADM       │     │           │ │  active)  │     │ ADM       │
        └─────┬─────┘     └─────┬─────┘     └─────┬─────┘     └─────┬─────┘
              │                 │                 │                 │
              ▼                 ▼                 ▼                 ▼
        ════╪═════════════════╪═════════════════╪═════════════════╪══════
        Synth │    Platform Mode Active           │                 │ Synth
              │                                   │                 │ audio2 still
              │                                   │                 │ works!
    
      SCENARIO 3: FFI Clients (Unity/Python)
      ═════════════════════════════════════════
    
        ┌─────────────────┐       ┌─────────────────┐       ┌─────────────────┐
        │ Unity Client A  │       │ Unity Client B  │       │ Python Agent    │
        │                 │       │                 │                 │
        │ NewPlatformAudio│       │ NewPlatformAudio│       │ Uses Native     │
        │ Request         │       │ Request         │       │ AudioSource     │
        │ handle_1         │       │ handle_2         │       │ (no PlatformAdm)│
        └────────┬────────┘       └────────┬────────┘       └─────────────────┘
                 │                         │                         
                 ▼                         ▼                         
        ┌─────────────────────────────────────────────────────────────────┐
        │                        AdmProxy                                  │
        │                                                                  │
        │   platform_adm_ref_count_ = 2  (from Unity clients)              │
        │                                                                  │
        │   Both Unity clients share the same Platform ADM.                │
        │   Python agent uses synthetic mode (NativeAudioSource).          │
        │                                                                  │
        │   When both Unity clients call DisposeRequest:                   │
        │     handle_1 dispose → ref_count = 1                             │
        │     handle_2 dispose → ref_count = 0 → TERMINATE                 │
        └─────────────────────────────────────────────────────────────────┘
  10. Authenticate the LiveKit Server API

    main

    The server API supports two authentication modes depending on your use case:

    1. API key & secret (Backend use): Recommended for server-side applications.

      • Use LiveKitApi::new(host) to automatically read credentials from the LIVEKIT_API_KEY and LIVEKIT_API_SECRET environment variables.
      • Use LiveKitApi::with_api_key(host, key, secret) to provide credentials explicitly.
      • A short-lived token is signed for each request automatically.
    2. Access token (Client-side use): Use this when you must not expose the API secret.

      • Use LiveKitApi::with_token(host, token) to send a pre-signed access token verbatim. Ensure the token's grants cover the intended API calls.
  11. Configure Audio Recording and Playout Gates

    main

    The AdmProxy uses gates to control how the platform ADM behaves when it is active. These gates determine if the hardware is actually accessed.

    GateDefaultBehavior when true
    recording_enabled_falseInitializes and starts microphone capture via the platform ADM.
    playout_enabled_falseRoutes remote audio through platform speakers and enables AEC.

    Note on Default Behavior: When playout_enabled_ is false, the SDK enters Synthetic Playout Mode. In this mode, the WebRTC pipeline stays alive by running a periodic task (every 10ms) that pulls audio data via NeedMorePlayData, which is then delivered to your application via FFI callbacks (e.g., NativeAudioStream).