My team runs a Node.js application on Google Cloud Run that connects to multiple LiveKit rooms simultaneously, sending and receiving audio track data.
We want to isolate each room’s audio processing onto its own Node.js worker thread, but through our testing, we realized the current FFI architecture seems to prevent this.
Note: the below description of the problem and possible solution is based heavily on input from Claude Opus 4.6, as I don’t have direct experience working with Rust.
The problem:
The livekit-ffi crate exposes a single FFI_SERVER static (lazy_static! in lib.rs), and FfiServer::setup() stores exactly one callback function (mod.rs line 135). Node.js worker threads share the same process and therefore the same native addon, so they share this singleton.
When a second worker thread calls livekitInitialize, it calls setup() again, which replaces the callback — the previous thread’s ThreadsafeFunction is overwritten and it stops receiving events entirely. There’s no way to route events back to the correct worker thread because send_event() (line 167) always invokes the single stored callback.
What a solution could look like:
Rather than a single callback_fn in FfiConfig, the FfiServer could maintain a map of context IDs to callbacks. The flow would be:
-
livekitInitializecreates a new context (with its own callback) and returns a context ID -
livekitFfiRequestaccepts a context ID so that handles created through it are affiliated with that context -
send_eventlooks up the correct callback based on which context owns the handle that generated the event
The Tokio runtime, WebRTC internals, and media pipelines wouldn’t need to change — they’re already per-room. The change is scoped to event dispatch and handle ownership tracking within FfiServer.
I’m not sure if this limitation has come up before, but it seems like this would also benefit other language SDKs that use livekit-ffi and likely have similar multi-threading needs.