Memory Leak in LiveKitWebRTC when streaming via BufferCapturer
Hi @pblazej and @hiroshihorie ,
this is a follow-up to my previous report:
Previous issue: Memory Leaking - High memory after Room.disconnect #827
I can now provide a minimal, runnable LiveKit + ARKit reproduction project that isolates the leak.
This project contains only the essential components from the LiveKit example plus a minimal custom AR → BufferCapturer bridge.
This issue focuses exclusively on internal leaks.
Summary
The minimal project supports two Room lifecycle modes:
-
Global shared Room instance (matching the original LiveKit Example)
→ Reproduces one leak(LiveKitWebRTC) -
Per-connection mode (fresh
Room()for each join)
→ After a leave + rejoin, Instruments reports more than 24 leaks
In the per-connection mode, the previous Room instance becomes unreachable from the app side (replaced by a new placeholder Room), but internal LiveKit objects remain alive and are reported as leaks.
Minimal Reproduction Project
@pblazej and @hiroshihorie, I invited you to the private GitHub project.
The project is a very small SwiftUI app based on these components from the official LiveKit examples:
RoomSwitchViewRoomContextViewParticipantViewConnectViewLKButton,LKTextFieldSecureStoreParticipant+HelperExampleRoomMessageConnectionHistoryBundlehelper
Added on top:
- A minimal ARKit → BufferCapturer bridge (
ARVideoCapturer) publishing AR frames as aLocalVideoTrack.
How to Run the Minimal Setup
-
Clone the provided sample project.
-
Configure your LiveKit server URL and token in
RoomContext(file:RoomContext.swift):url = "wss://<your-livekit-host>.livekit.cloud" token = "<your-jwt-token>" -
Open
LiveKit_ARKit_MinimalSetupApp.swift. -
Choose one of the two lifecycle configurations:
A. Global shared instance (original example behavior)
RoomContextView(lifecycleMode: .globalSharedInstance)
→ Produces one reproducible leak after leaving the room.
B. New Room per join (perConnectionNewInstance)
RoomContextView(lifecycleMode: .perConnectionNewInstance)
→ Produces more than 24 leaks after rejoining a session.
In this mode:
connect()creates a freshRoom().- After
disconnect(), the previous Room is replaced by a new placeholder Room
(alternatively it could be set tonil, but that's not required for the repro).
- Run the application
- Join a session
- Activate the camera (top right corner)
- Leave the session
- Repeat joining a session
However, the previous Room instance is no longer referenced anywhere in the app.
Despite that, Instruments shows multiple LiveKit internal structures as leaked
(DataChannel / Async / Delegate / StateSync objects, etc.).