Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .token_helpers/gen_and_set.bash
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
# Generate a LiveKit access token via `lk` and set LIVEKIT_TOKEN (and LIVEKIT_URL)
# for your current shell session.
#
# source examples/tokens/gen_and_set.bash --id PARTICIPANT_ID --room ROOM_NAME [--view-token]
# eval "$(bash examples/tokens/gen_and_set.bash --id ID --room ROOM [--view-token])"
# source .token_helpers/gen_and_set.bash --id PARTICIPANT_ID --room ROOM_NAME [--view-token]
# eval "$(bash .token_helpers/gen_and_set.bash --id ID --room ROOM [--view-token])"
#
# Optional env: LIVEKIT_API_KEY, LIVEKIT_API_SECRET, LIVEKIT_VALID_FOR.

Expand Down
4 changes: 2 additions & 2 deletions .token_helpers/set_data_track_test_tokens.bash
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
# Generate two LiveKit access tokens via `lk` and set the environment variables
# required by src/tests/integration/test_data_track.cpp.
#
# source examples/tokens/set_data_track_test_tokens.bash
# eval "$(bash examples/tokens/set_data_track_test_tokens.bash)"
# source .token_helpers/set_data_track_test_tokens.bash
# eval "$(bash .token_helpers/set_data_track_test_tokens.bash)"
#
# Exports:
# LK_TOKEN_TEST_A
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,10 @@ lk token create \
--grant '{"canPublish":true,"canSubscribe":true,"canPublishData":true}'
```

# Deprecation
- livekit_bridge (bridge/ folder) is deprecated. Avoid using it. Migrate to the base SDK. This will be removed on 06/01/2026.
- setOn*FrameCallback with TrackSource is deprecated. Use track name instead. This will be removed on 06/01/2026.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this chunk here to keep track of deprecated and breaking changes. If we decide to not introduce breaking changes right now, we can just commit the README changes and officially remove with the bridge on 06/01.


<!--BEGIN_REPO_NAV-->
<br/><table>
<thead><tr><th colspan="2">LiveKit Ecosystem</th></tr></thead>
Expand Down
20 changes: 10 additions & 10 deletions bridge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ mic->pushFrame(pcm_data, samples_per_channel);
cam->pushFrame(rgba_data, timestamp_us);

// 4. Receive frames from a remote participant
bridge.setOnAudioFrameCallback("remote-peer", livekit::TrackSource::SOURCE_MICROPHONE,
bridge.setOnAudioFrameCallback("remote-peer", "mic",
[](const livekit::AudioFrame& frame) {
// Called on a background reader thread
});

bridge.setOnVideoFrameCallback("remote-peer", livekit::TrackSource::SOURCE_CAMERA,
bridge.setOnVideoFrameCallback("remote-peer", "cam",
[](const livekit::VideoFrame& frame, int64_t timestamp_us) {
// Called on a background reader thread
});
Expand Down Expand Up @@ -117,9 +117,9 @@ Reader threads are managed by `Room` internally. They are created when a matchin

### Callback Registration Timing

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.
Callbacks are keyed by `(participant_identity, track_name)`. 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.

> **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.
> **Note:** Only one callback may be set per `(participant_identity, track_name)` pair. Calling `setOnAudioFrameCallback` or `setOnVideoFrameCallback` again with the same identity and track name will silently replace the previous callback. If you need to fan-out a single stream to multiple consumers, do so inside your callback.

This means the typical pattern is:

Expand All @@ -128,7 +128,7 @@ This means the typical pattern is:
livekit::RoomOptions options;
options.auto_subscribe = true;
bridge.connect(url, token, options);
bridge.setOnAudioFrameCallback("robot-1", livekit::TrackSource::SOURCE_MICROPHONE, my_callback);
bridge.setOnAudioFrameCallback("robot-1", "mic", my_callback);
// When robot-1 joins and publishes a mic track, my_callback starts firing.
```

Expand All @@ -149,10 +149,10 @@ bridge.setOnAudioFrameCallback("robot-1", livekit::TrackSource::SOURCE_MICROPHON
| `isConnected()` | Returns whether the bridge is currently connected. |
| `createAudioTrack(name, sample_rate, num_channels, source)` | Create and publish a local audio track with the given `TrackSource` (e.g. `SOURCE_MICROPHONE`, `SOURCE_SCREENSHARE_AUDIO`). Returns an RAII `shared_ptr<BridgeAudioTrack>`. |
| `createVideoTrack(name, width, height, source)` | Create and publish a local video track with the given `TrackSource` (e.g. `SOURCE_CAMERA`, `SOURCE_SCREENSHARE`). Returns an RAII `shared_ptr<BridgeVideoTrack>`. |
| `setOnAudioFrameCallback(identity, source, callback)` | Register a callback for audio frames from a specific remote participant + track source. |
| `setOnVideoFrameCallback(identity, source, callback)` | Register a callback for video frames from a specific remote participant + track source. |
| `clearOnAudioFrameCallback(identity, source)` | Clear the audio callback for a specific remote participant + track source. Stops and joins the reader thread if active. |
| `clearOnVideoFrameCallback(identity, source)` | Clear the video callback for a specific remote participant + track source. Stops and joins the reader thread if active. |
| `setOnAudioFrameCallback(identity, track_name, callback)` | Register a callback for audio frames from a specific remote participant + track name. |
| `setOnVideoFrameCallback(identity, track_name, callback)` | Register a callback for video frames from a specific remote participant + track name. |
| `clearOnAudioFrameCallback(identity, track_name)` | Clear the audio callback for a specific remote participant + track name. Stops and joins the reader thread if active. |
| `clearOnVideoFrameCallback(identity, track_name)` | Clear the video callback for a specific remote participant + track name. Stops and joins the reader thread if active. |
| `performRpc(destination_identity, method, payload, response_timeout?)` | Blocking RPC call to a remote participant. Returns the response payload. Throws `livekit::RpcError` on failure. |
| `registerRpcMethod(method_name, handler)` | Register a handler for incoming RPC invocations. The handler returns an optional response payload or throws `livekit::RpcError`. |
| `unregisterRpcMethod(method_name)` | Unregister a previously registered RPC handler. |
Expand Down Expand Up @@ -263,6 +263,6 @@ The bridge is designed for simplicity and currently only supports limited audio
- Simulcast tuning
- Video format selection (RGBA is the default; no format option yet)
- Custom `RoomOptions` or `TrackPublishOptions`
- **One callback per (participant, source):** Only a single callback can be registered for each `(participant_identity, track_source)` pair. Re-registering with the same key silently replaces the previous callback. To fan-out a stream to multiple consumers, dispatch from within your single callback.
- **One callback per (participant, track_name):** Only a single callback can be registered for each `(participant_identity, track_name)` pair. Re-registering with the same key silently replaces the previous callback. To fan-out a stream to multiple consumers, dispatch from within your single callback.

For advanced use cases, use the full `client-sdk-cpp` API directly, or expand the bridge to support your use case.
35 changes: 16 additions & 19 deletions bridge/include/livekit_bridge/livekit_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ namespace livekit {
class Room;
class AudioFrame;
class VideoFrame;
enum class TrackSource;
} // namespace livekit

namespace livekit_bridge {
Expand Down Expand Up @@ -88,12 +87,10 @@ using VideoFrameCallback = livekit::VideoFrameCallback;
* mic->pushFrame(pcm_data, samples_per_channel);
* cam->pushFrame(rgba_data, timestamp_us);
*
* bridge.setOnAudioFrameCallback("remote-participant",
* livekit::TrackSource::SOURCE_MICROPHONE,
* bridge.setOnAudioFrameCallback("remote-participant", "mic",
* [](const livekit::AudioFrame& f) { process(f); });
*
* bridge.setOnVideoFrameCallback("remote-participant",
* livekit::TrackSource::SOURCE_CAMERA,
* bridge.setOnVideoFrameCallback("remote-participant", "cam",
* [](const livekit::VideoFrame& f, int64_t ts) { render(f); });
*
* // Unpublish a single track mid-session:
Expand Down Expand Up @@ -207,59 +204,59 @@ class LiveKitBridge {

/**
* Set the callback for audio frames from a specific remote participant
* and track source.
* and track name.
*
* Delegates to Room::setOnAudioFrameCallback. The callback fires on a
* dedicated reader thread owned by Room whenever a new audio frame is
* received.
*
* @note Only **one** callback may be registered per (participant, source)
* pair. Calling this again with the same identity and source will
* @note Only **one** callback may be registered per (participant, track_name)
* pair. Calling this again with the same identity and track_name will
* silently replace the previous callback.
*
* @param participant_identity Identity of the remote participant.
* @param source Track source (e.g. SOURCE_MICROPHONE).
* @param track_name Name of the remote track.
* @param callback Function to invoke per audio frame.
*/
void setOnAudioFrameCallback(const std::string &participant_identity,
livekit::TrackSource source,
const std::string &track_name,
AudioFrameCallback callback);

/**
* Register a callback for video frames from a specific remote participant
* and track source.
* and track name.
*
* Delegates to Room::setOnVideoFrameCallback.
*
* @note Only **one** callback may be registered per (participant, source)
* pair. Calling this again with the same identity and source will
* @note Only **one** callback may be registered per (participant, track_name)
* pair. Calling this again with the same identity and track_name will
* silently replace the previous callback.
*
* @param participant_identity Identity of the remote participant.
* @param source Track source (e.g. SOURCE_CAMERA).
* @param track_name Name of the remote track.
* @param callback Function to invoke per video frame.
*/
void setOnVideoFrameCallback(const std::string &participant_identity,
livekit::TrackSource source,
const std::string &track_name,
VideoFrameCallback callback);

/**
* Clear the audio frame callback for a specific remote participant + track
* source.
* name.
*
* Delegates to Room::clearOnAudioFrameCallback.
*/
void clearOnAudioFrameCallback(const std::string &participant_identity,
livekit::TrackSource source);
const std::string &track_name);

/**
* Clear the video frame callback for a specific remote participant + track
* source.
* name.
*
* Delegates to Room::clearOnVideoFrameCallback.
*/
void clearOnVideoFrameCallback(const std::string &participant_identity,
livekit::TrackSource source);
const std::string &track_name);

// ---------------------------------------------------------------
// RPC (Remote Procedure Call)
Expand Down
16 changes: 8 additions & 8 deletions bridge/src/livekit_bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,47 +238,47 @@ LiveKitBridge::createVideoTrack(const std::string &name, int width, int height,
// ---------------------------------------------------------------

void LiveKitBridge::setOnAudioFrameCallback(
const std::string &participant_identity, livekit::TrackSource source,
const std::string &participant_identity, const std::string &track_name,
AudioFrameCallback callback) {
std::lock_guard<std::mutex> lock(mutex_);
if (!room_) {
std::cerr << "[warn] setOnAudioFrameCallback called before connect(); "
"ignored\n";
return;
}
room_->setOnAudioFrameCallback(participant_identity, source,
room_->setOnAudioFrameCallback(participant_identity, track_name,
std::move(callback));
}

void LiveKitBridge::setOnVideoFrameCallback(
const std::string &participant_identity, livekit::TrackSource source,
const std::string &participant_identity, const std::string &track_name,
VideoFrameCallback callback) {
std::lock_guard<std::mutex> lock(mutex_);
if (!room_) {
std::cerr << "[warn] setOnVideoFrameCallback called before connect(); "
"ignored\n";
return;
}
room_->setOnVideoFrameCallback(participant_identity, source,
room_->setOnVideoFrameCallback(participant_identity, track_name,
std::move(callback));
}

void LiveKitBridge::clearOnAudioFrameCallback(
const std::string &participant_identity, livekit::TrackSource source) {
const std::string &participant_identity, const std::string &track_name) {
std::lock_guard<std::mutex> lock(mutex_);
if (!room_) {
return;
}
room_->clearOnAudioFrameCallback(participant_identity, source);
room_->clearOnAudioFrameCallback(participant_identity, track_name);
}

void LiveKitBridge::clearOnVideoFrameCallback(
const std::string &participant_identity, livekit::TrackSource source) {
const std::string &participant_identity, const std::string &track_name) {
std::lock_guard<std::mutex> lock(mutex_);
if (!room_) {
return;
}
room_->clearOnVideoFrameCallback(participant_identity, source);
room_->clearOnVideoFrameCallback(participant_identity, track_name);
}

// ---------------------------------------------------------------
Expand Down
17 changes: 6 additions & 11 deletions bridge/tests/test_livekit_bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,10 @@ TEST_F(LiveKitBridgeTest, SetAndClearAudioCallbackBeforeConnectDoesNotCrash) {
LiveKitBridge bridge;

EXPECT_NO_THROW({
bridge.setOnAudioFrameCallback("remote-participant",
livekit::TrackSource::SOURCE_MICROPHONE,
bridge.setOnAudioFrameCallback("remote-participant", "mic",
[](const livekit::AudioFrame &) {});

bridge.clearOnAudioFrameCallback("remote-participant",
livekit::TrackSource::SOURCE_MICROPHONE);
bridge.clearOnAudioFrameCallback("remote-participant", "mic");
}) << "set/clear audio callback before connect should be safe (warns)";
}

Expand All @@ -115,22 +113,19 @@ TEST_F(LiveKitBridgeTest, SetAndClearVideoCallbackBeforeConnectDoesNotCrash) {

EXPECT_NO_THROW({
bridge.setOnVideoFrameCallback(
"remote-participant", livekit::TrackSource::SOURCE_CAMERA,
"remote-participant", "cam",
[](const livekit::VideoFrame &, std::int64_t) {});

bridge.clearOnVideoFrameCallback("remote-participant",
livekit::TrackSource::SOURCE_CAMERA);
bridge.clearOnVideoFrameCallback("remote-participant", "cam");
}) << "set/clear video callback before connect should be safe (warns)";
}

TEST_F(LiveKitBridgeTest, ClearNonExistentCallbackIsNoOp) {
LiveKitBridge bridge;

EXPECT_NO_THROW({
bridge.clearOnAudioFrameCallback("nonexistent",
livekit::TrackSource::SOURCE_MICROPHONE);
bridge.clearOnVideoFrameCallback("nonexistent",
livekit::TrackSource::SOURCE_CAMERA);
bridge.clearOnAudioFrameCallback("nonexistent", "mic");
bridge.clearOnVideoFrameCallback("nonexistent", "cam");
}) << "Clearing a callback that was never registered should be a no-op";
}

Expand Down
25 changes: 0 additions & 25 deletions include/livekit/room.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,13 +240,6 @@ class Room {
// Frame callbacks
// ---------------------------------------------------------------

/**
* @brief Sets the audio frame callback via SubscriptionThreadDispatcher.
*/
void setOnAudioFrameCallback(const std::string &participant_identity,
TrackSource source, AudioFrameCallback callback,
const AudioStream::Options &opts = {});

/**
* @brief Sets the audio frame callback via SubscriptionThreadDispatcher.
*/
Expand All @@ -255,13 +248,6 @@ class Room {
AudioFrameCallback callback,
const AudioStream::Options &opts = {});

/**
* @brief Sets the video frame callback via SubscriptionThreadDispatcher.
*/
void setOnVideoFrameCallback(const std::string &participant_identity,
TrackSource source, VideoFrameCallback callback,
const VideoStream::Options &opts = {});

/**
* @brief Sets the video frame callback via SubscriptionThreadDispatcher.
*/
Expand All @@ -279,23 +265,12 @@ class Room {
VideoFrameEventCallback callback,
const VideoStream::Options &opts = {});

/**
* @brief Clears the audio frame callback via SubscriptionThreadDispatcher.
*/
void clearOnAudioFrameCallback(const std::string &participant_identity,
TrackSource source);
/**
* @brief Clears the audio frame callback via SubscriptionThreadDispatcher.
*/
void clearOnAudioFrameCallback(const std::string &participant_identity,
const std::string &track_name);

/**
* @brief Clears the video frame callback via SubscriptionThreadDispatcher.
*/
void clearOnVideoFrameCallback(const std::string &participant_identity,
TrackSource source);

/**
* @brief Clears the video frame callback via SubscriptionThreadDispatcher.
*/
Expand Down
Loading
Loading