Skip to content

Commit 32c593f

Browse files
livekit_bridge deprecation
move core convenience functionality of the bridge to the core sdk: subscription thread manager and helpers for track publication.
1 parent a0c739b commit 32c593f

28 files changed

+1453
-747
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CMakeCache.txt
55
Makefile
66
cmake_install.cmake
77
out
8+
build.log
89
build/
910
build-debug/
1011
build-release/

CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ add_library(livekit SHARED
335335
src/room.cpp
336336
src/room_proto_converter.cpp
337337
src/room_proto_converter.h
338+
src/subscription_thread_dispatcher.cpp
338339
src/local_participant.cpp
339340
src/remote_participant.cpp
340341
src/stats.cpp
@@ -723,4 +724,4 @@ add_custom_target(clean_all
723724
COMMAND ${CMAKE_COMMAND} -E chdir "${CMAKE_SOURCE_DIR}" ${CMAKE_COMMAND} -E rm -rf "${CMAKE_BINARY_DIR}"
724725
COMMENT "Full clean: CMake outputs + Rust target + generated protos + delete build/"
725726
VERBATIM
726-
)
727+
)

bridge/CMakeLists.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ add_library(livekit_bridge SHARED
99
src/livekit_bridge.cpp
1010
src/bridge_audio_track.cpp
1111
src/bridge_video_track.cpp
12-
src/bridge_room_delegate.cpp
13-
src/bridge_room_delegate.h
1412
src/rpc_constants.cpp
1513
src/rpc_controller.cpp
1614
src/rpc_controller.h

bridge/README.md

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# LiveKit Bridge
22

3+
# **WARNING: This library is deprecated, use the base sdk found in src/**
4+
35
A simplified, high-level C++ wrapper around the [LiveKit C++ SDK](../README.md). The bridge abstracts away room lifecycle management, track creation, publishing, and subscription boilerplate so that external codebases can interface with LiveKit in just a few lines. It is intended that this library will be used to bridge the LiveKit C++ SDK into other SDKs such as, but not limited to, Foxglove, ROS, and Rerun.
46

57
It is intended that this library closely matches the style of the core LiveKit C++ SDK.
@@ -98,44 +100,41 @@ Your Application
98100
99101
**`BridgeAudioTrack` / `BridgeVideoTrack`** -- RAII handles for published local tracks. Created via `createAudioTrack()` / `createVideoTrack()`. When the `shared_ptr` is dropped, the track is automatically unpublished and all underlying SDK resources are freed. Call `pushFrame()` to send audio/video data to remote participants.
100102
101-
**`BridgeRoomDelegate`** -- Internal (not part of the public API; lives in `src/`). Listens for `onTrackSubscribed` / `onTrackUnsubscribed` events from the LiveKit SDK and wires up reader threads automatically.
102-
103103
### What is a Reader?
104104
105105
A **reader** is a background thread that receives decoded media frames from a remote participant.
106106
107-
When a remote participant publishes an audio or video track and the bridge subscribes to it (auto-subscribe is enabled by default), the bridge creates an `AudioStream` or `VideoStream` from that track and spins up a dedicated thread. This thread loops on `stream->read()`, which blocks until a new frame arrives. Each received frame is forwarded to the user's registered callback.
107+
When a remote participant publishes an audio or video track and the room subscribes to it (auto-subscribe is enabled by default), `Room` creates an `AudioStream` or `VideoStream` from that track and spins up a dedicated thread. This thread loops on `stream->read()`, which blocks until a new frame arrives. Each received frame is forwarded to the user's registered callback.
108108
109109
In short:
110110
111111
- **Sending** (you -> remote): `BridgeAudioTrack::pushFrame()` / `BridgeVideoTrack::pushFrame()`
112112
- **Receiving** (remote -> you): reader threads invoke your registered callbacks
113113
114-
Reader threads are managed entirely by the bridge. They are created when a matching remote track is subscribed, and torn down (stream closed, thread joined) when the track is unsubscribed, the callback is unregistered, or `disconnect()` is called.
114+
Reader threads are managed by `Room` internally. They are created when a matching remote track is subscribed, and torn down (stream closed, thread joined) when the track is unsubscribed, the callback is unregistered, or the `Room` is destroyed.
115115
116116
### Callback Registration Timing
117117
118-
Callbacks are keyed by `(participant_identity, track_source)`. You can register them **before** the remote participant has joined the room. The bridge stores the callback and automatically wires it up when the matching track is subscribed.
118+
Callbacks are keyed by `(participant_identity, track_source)`. You can register them **after connecting** but before the remote participant has joined the room. `Room` stores the callback and automatically wires it up when the matching track is subscribed.
119119
120120
> **Note:** Only one callback may be set per `(participant_identity, track_source)` pair. Calling `setOnAudioFrameCallback` or `setOnVideoFrameCallback` again with the same identity and source will silently replace the previous callback. If you need to fan-out a single stream to multiple consumers, do so inside your callback.
121121
122122
This means the typical pattern is:
123123
124124
```cpp
125-
// Register first, connect second -- or register after connect but before
126-
// the remote participant joins.
127-
bridge.setOnAudioFrameCallback("robot-1", livekit::TrackSource::SOURCE_MICROPHONE, my_callback);
125+
// Connect first, then register callbacks before the remote participant joins.
128126
livekit::RoomOptions options;
129127
options.auto_subscribe = true;
130128
bridge.connect(url, token, options);
129+
bridge.setOnAudioFrameCallback("robot-1", livekit::TrackSource::SOURCE_MICROPHONE, my_callback);
131130
// When robot-1 joins and publishes a mic track, my_callback starts firing.
132131
```
133132

134133
### Thread Safety
135134

136135
- `LiveKitBridge` uses a mutex to protect the callback map and active reader state.
137136
- Frame callbacks fire on background reader threads. If your callback accesses shared application state, you are responsible for synchronization.
138-
- `disconnect()` closes all streams and joins all reader threads before returning -- it is safe to destroy the bridge immediately after.
137+
- `disconnect()` destroys the `Room`, which closes all streams and joins all reader threads before returning -- it is safe to destroy the bridge immediately after.
139138

140139
## API Reference
141140

@@ -226,9 +225,8 @@ The human will print periodic summaries like:
226225
## Testing
227226

228227
The bridge includes a unit test suite built with [Google Test](https://github.com/google/googletest). Tests cover
229-
1. `CallbackKey` hashing/equality,
230-
2. `BridgeAudioTrack`/`BridgeVideoTrack` state management, and
231-
3. `LiveKitBridge` pre-connection behaviour (callback registration, error handling).
228+
1. `BridgeAudioTrack`/`BridgeVideoTrack` state management, and
229+
2. `LiveKitBridge` pre-connection behaviour (callback registration, error handling).
232230

233231
### Building and running tests
234232

bridge/include/livekit_bridge/bridge_audio_track.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ class BridgeAudioTrack {
140140

141141
std::shared_ptr<livekit::AudioSource> source_;
142142
std::shared_ptr<livekit::LocalAudioTrack> track_;
143+
/* DEPRECATED. use track_->publication() instead */
143144
std::shared_ptr<livekit::LocalTrackPublication> publication_;
144145
livekit::LocalParticipant *participant_ = nullptr; // not owned
145146
};

bridge/include/livekit_bridge/bridge_video_track.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ class BridgeVideoTrack {
138138

139139
std::shared_ptr<livekit::VideoSource> source_;
140140
std::shared_ptr<livekit::LocalVideoTrack> track_;
141+
/* DEPRECATED. use track_->publication() instead */
141142
std::shared_ptr<livekit::LocalTrackPublication> publication_;
142143
livekit::LocalParticipant *participant_ = nullptr; // not owned
143144
};

bridge/include/livekit_bridge/livekit_bridge.h

Lines changed: 19 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -29,51 +29,45 @@
2929
#include "livekit/rpc_error.h"
3030

3131
#include <cstdint>
32-
#include <functional>
3332
#include <memory>
3433
#include <mutex>
3534
#include <string>
36-
#include <thread>
37-
#include <unordered_map>
3835
#include <vector>
3936

4037
namespace livekit {
4138
class Room;
4239
class AudioFrame;
4340
class VideoFrame;
44-
class AudioStream;
45-
class VideoStream;
46-
class Track;
4741
enum class TrackSource;
4842
} // namespace livekit
4943

5044
namespace livekit_bridge {
5145

52-
class BridgeRoomDelegate;
5346
class RpcController;
5447

5548
namespace test {
56-
class CallbackKeyTest;
5749
class LiveKitBridgeTest;
5850
} // namespace test
5951

6052
/// Callback type for incoming audio frames.
61-
/// Called on a background reader thread.
62-
using AudioFrameCallback = std::function<void(const livekit::AudioFrame &)>;
53+
/// Called on a background reader thread owned by Room.
54+
using AudioFrameCallback = livekit::AudioFrameCallback;
6355

6456
/// Callback type for incoming video frames.
65-
/// Called on a background reader thread.
57+
/// Called on a background reader thread owned by Room.
6658
/// @param frame The decoded video frame (RGBA by default).
6759
/// @param timestamp_us Presentation timestamp in microseconds.
68-
using VideoFrameCallback = std::function<void(const livekit::VideoFrame &frame,
69-
std::int64_t timestamp_us)>;
60+
using VideoFrameCallback = livekit::VideoFrameCallback;
7061

7162
/**
7263
* High-level bridge to the LiveKit C++ SDK.
7364
*
7465
* Owns the full room lifecycle: initialize SDK, create Room, connect,
7566
* publish tracks, and manage incoming frame callbacks.
7667
*
68+
* Frame callback reader threads are managed by Room internally via
69+
* Room::setOnAudioFrameCallback / Room::setOnVideoFrameCallback.
70+
*
7771
* The bridge retains a shared_ptr to every track it creates. On
7872
* disconnect(), all tracks are released (unpublished) before the room
7973
* is torn down, guaranteeing safe teardown order. To unpublish a track
@@ -114,7 +108,7 @@ class LiveKitBridge {
114108
LiveKitBridge();
115109
~LiveKitBridge();
116110

117-
// Non-copyable, non-movable (owns threads, callbacks, room)
111+
// Non-copyable, non-movable (owns room, callbacks)
118112
LiveKitBridge(const LiveKitBridge &) = delete;
119113
LiveKitBridge &operator=(const LiveKitBridge &) = delete;
120114
LiveKitBridge(LiveKitBridge &&) = delete;
@@ -139,7 +133,6 @@ class LiveKitBridge {
139133
* @param url WebSocket URL of the LiveKit server.
140134
* @param token Access token for authentication.
141135
* @param options Room options.
142-
143136
* @return true if connection succeeded (or was already connected).
144137
*/
145138
bool connect(const std::string &url, const std::string &token,
@@ -148,8 +141,9 @@ class LiveKitBridge {
148141
/**
149142
* Disconnect from the room and release all resources.
150143
*
151-
* All published tracks are unpublished, all reader threads are joined,
152-
* and the SDK is shut down. Safe to call multiple times.
144+
* All published tracks are unpublished, reader threads are stopped
145+
* by Room's destructor, and the SDK is shut down. Safe to call
146+
* multiple times.
153147
*/
154148
void disconnect();
155149

@@ -209,17 +203,16 @@ class LiveKitBridge {
209203
livekit::TrackSource source);
210204

211205
// ---------------------------------------------------------------
212-
// Incoming frame callbacks
206+
// Incoming frame callbacks (delegates to Room)
213207
// ---------------------------------------------------------------
214208

215209
/**
216210
* Set the callback for audio frames from a specific remote participant
217211
* and track source.
218212
*
219-
* The callback fires on a background thread whenever a new audio frame
220-
* is received. If the remote participant has not yet connected, the
221-
* callback is stored and auto-wired when the participant's track is
222-
* subscribed.
213+
* Delegates to Room::setOnAudioFrameCallback. The callback fires on a
214+
* dedicated reader thread owned by Room whenever a new audio frame is
215+
* received.
223216
*
224217
* @note Only **one** callback may be registered per (participant, source)
225218
* pair. Calling this again with the same identity and source will
@@ -237,6 +230,8 @@ class LiveKitBridge {
237230
* Register a callback for video frames from a specific remote participant
238231
* and track source.
239232
*
233+
* Delegates to Room::setOnVideoFrameCallback.
234+
*
240235
* @note Only **one** callback may be registered per (participant, source)
241236
* pair. Calling this again with the same identity and source will
242237
* silently replace the previous callback.
@@ -253,8 +248,7 @@ class LiveKitBridge {
253248
* Clear the audio frame callback for a specific remote participant + track
254249
* source.
255250
*
256-
* If a reader thread is active for this (identity, source), it is
257-
* stopped and joined.
251+
* Delegates to Room::clearOnAudioFrameCallback.
258252
*/
259253
void clearOnAudioFrameCallback(const std::string &participant_identity,
260254
livekit::TrackSource source);
@@ -263,8 +257,7 @@ class LiveKitBridge {
263257
* Clear the video frame callback for a specific remote participant + track
264258
* source.
265259
*
266-
* If a reader thread is active for this (identity, source), it is
267-
* stopped and joined.
260+
* Delegates to Room::clearOnVideoFrameCallback.
268261
*/
269262
void clearOnVideoFrameCallback(const std::string &participant_identity,
270263
livekit::TrackSource source);
@@ -354,55 +347,8 @@ class LiveKitBridge {
354347
const std::string &track_name);
355348

356349
private:
357-
friend class BridgeRoomDelegate;
358-
friend class test::CallbackKeyTest;
359350
friend class test::LiveKitBridgeTest;
360351

361-
/// Composite key for the callback map: (participant_identity, source).
362-
/// Only one callback can exist per key -- re-registering overwrites.
363-
struct CallbackKey {
364-
std::string identity;
365-
livekit::TrackSource source;
366-
367-
bool operator==(const CallbackKey &o) const;
368-
};
369-
370-
struct CallbackKeyHash {
371-
std::size_t operator()(const CallbackKey &k) const;
372-
};
373-
374-
/// Active reader thread + stream for an incoming track.
375-
struct ActiveReader {
376-
std::shared_ptr<livekit::AudioStream> audio_stream;
377-
std::shared_ptr<livekit::VideoStream> video_stream;
378-
std::thread thread;
379-
bool is_audio = false;
380-
};
381-
382-
/// Called by BridgeRoomDelegate when a remote track is subscribed.
383-
void onTrackSubscribed(const std::string &participant_identity,
384-
livekit::TrackSource source,
385-
const std::shared_ptr<livekit::Track> &track);
386-
387-
/// Called by BridgeRoomDelegate when a remote track is unsubscribed.
388-
void onTrackUnsubscribed(const std::string &participant_identity,
389-
livekit::TrackSource source);
390-
391-
/// Extract the thread for the given callback key.
392-
/// @pre Caller must hold @c mutex_.
393-
std::thread extractReaderThread(const CallbackKey &key);
394-
395-
/// Start a reader thread for a subscribed track.
396-
/// @return The reader thread for this track.
397-
/// @pre Caller must hold @c mutex_.
398-
std::thread startAudioReader(const CallbackKey &key,
399-
const std::shared_ptr<livekit::Track> &track,
400-
AudioFrameCallback cb);
401-
/// @copydoc startAudioReader
402-
std::thread startVideoReader(const CallbackKey &key,
403-
const std::shared_ptr<livekit::Track> &track,
404-
VideoFrameCallback cb);
405-
406352
/// Execute a track action (mute/unmute) by track name.
407353
/// Used as the TrackActionFn callback for RpcController.
408354
/// Throws livekit::RpcError if the track is not found.
@@ -415,23 +361,9 @@ class LiveKitBridge {
415361
bool connecting_; // guards against concurrent connect() calls
416362
bool sdk_initialized_;
417363

418-
static constexpr int kMaxActiveReaders = 20;
419-
420364
std::unique_ptr<livekit::Room> room_;
421-
std::unique_ptr<BridgeRoomDelegate> delegate_;
422365
std::unique_ptr<RpcController> rpc_controller_;
423366

424-
/// Registered callbacks (may be registered before tracks are subscribed).
425-
std::unordered_map<CallbackKey, AudioFrameCallback, CallbackKeyHash>
426-
audio_callbacks_;
427-
/// @copydoc audio_callbacks_
428-
std::unordered_map<CallbackKey, VideoFrameCallback, CallbackKeyHash>
429-
video_callbacks_;
430-
431-
/// Active reader threads for subscribed tracks.
432-
std::unordered_map<CallbackKey, ActiveReader, CallbackKeyHash>
433-
active_readers_;
434-
435367
/// All tracks created by this bridge. The bridge retains a shared_ptr so
436368
/// it can force-release every track on disconnect() before the room is
437369
/// destroyed, preventing dangling @c participant_ pointers.

bridge/src/bridge_audio_track.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
#include "livekit/audio_source.h"
2424
#include "livekit/local_audio_track.h"
2525
#include "livekit/local_participant.h"
26-
#include "livekit/local_track_publication.h"
2726

2827
#include <stdexcept>
2928

@@ -111,9 +110,9 @@ void BridgeAudioTrack::release() {
111110
released_ = true;
112111

113112
// Unpublish the track from the room
114-
if (participant_ && publication_) {
113+
if (participant_ && track_ && track_->publication()) {
115114
try {
116-
participant_->unpublishTrack(publication_->sid());
115+
participant_->unpublishTrack(track_->publication()->sid());
117116
} catch (...) {
118117
// Best-effort cleanup; ignore errors during teardown
119118
LK_LOG_WARN("BridgeAudioTrack unpublishTrack error, continuing with "
@@ -122,7 +121,6 @@ void BridgeAudioTrack::release() {
122121
}
123122

124123
// Release SDK objects in reverse order
125-
publication_.reset();
126124
track_.reset();
127125
source_.reset();
128126
participant_ = nullptr;

0 commit comments

Comments
 (0)