Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a995042
DataTracks integration through FFI
stephen-derosa Mar 20, 2026
257268d
compiling with new data tracks work
stephen-derosa Mar 19, 2026
3e66ac7
data tracks buffering is now handled on the rust side
stephen-derosa Mar 19, 2026
92dbca2
data tracks buffering is now handled on the rust side
stephen-derosa Mar 19, 2026
e1e6f67
replace cout/cerr with LK_LOG
stephen-derosa Mar 20, 2026
1d7579f
no longer need BridgeDataTrack()
stephen-derosa Mar 20, 2026
b393c32
create*Track -> publish*Track()
stephen-derosa Mar 20, 2026
a6ca226
dont add/remove stuff from bridge since we are targetting a branch wh…
stephen-derosa Mar 24, 2026
87e00b7
ActiveDataReader: subscriber mutex
stephen-derosa Mar 24, 2026
b237ce4
DataTrackSubscription: read() sends request for latest message
stephen-derosa Mar 24, 2026
609a624
move realsense-livekit out of this
stephen-derosa Mar 24, 2026
de3c30f
no changes to bridge
stephen-derosa Mar 24, 2026
f4f07ff
simple_status example
stephen-derosa Mar 24, 2026
a6791b7
multiple subscriptions
stephen-derosa Mar 24, 2026
f701056
hello_livekit example for a video track and data tracl
stephen-derosa Mar 25, 2026
88a9cfb
move data track callback functionality to subscription_thread_dispatcher
stephen-derosa Mar 25, 2026
e5c4362
hello_livekit: sender.cpp/receiver.cpp
stephen-derosa Mar 25, 2026
d19e539
some data_tracks tests
stephen-derosa Mar 25, 2026
dd0f986
test_data_tracks.cpp
stephen-derosa Mar 25, 2026
183eb7a
setOnAudio/VideoFrameCallback() change signature to use track name
stephen-derosa Mar 25, 2026
8fa0814
setOnAudio/VideoFrameCallback() addition signature to use track name
stephen-derosa Mar 25, 2026
0ffed0e
clean name
stephen-derosa Mar 25, 2026
2e1543b
testing for new API
stephen-derosa Mar 25, 2026
f8f7685
data_track_subscription: on pushFrame() called when rust sends new fr…
stephen-derosa Mar 25, 2026
953f20d
typo/year
stephen-derosa Mar 26, 2026
58c986a
copyright year
stephen-derosa Mar 26, 2026
fc316df
DataTrack constructor operators for compiler efficiency. remove uneed…
stephen-derosa Mar 27, 2026
608c59d
DataFrame::fromOwnedInfo to align with video/audio_stream.cpp
stephen-derosa Mar 27, 2026
c138e66
DataTrackSubscription: remove move constructor since it introduces ba…
stephen-derosa Mar 27, 2026
51d535d
result.h file for floating up specific errors provided by the FFI
stephen-derosa Mar 27, 2026
1f628b4
BUG FIX: reset the frame after usage
stephen-derosa Mar 30, 2026
a1f272e
remove simple_status. add ping_pong for one way/round trip latency
stephen-derosa Mar 30, 2026
1c001f6
lcaol_data_track: remove many overloaded tryPush() functions
stephen-derosa Mar 30, 2026
15bbecc
subscribeDataTrackAsync -> subscribeDataTrack
stephen-derosa Mar 30, 2026
8069213
Bring in VideoFrame FrameMetadata from FFI
stephen-derosa Mar 30, 2026
7f35ba0
update data tracks errors to latest proto errors
stephen-derosa Mar 31, 2026
7c91e2c
Merge branch 'sderosa/data_tracks' into sderosa/feature/user_timestamp
stephen-derosa Mar 31, 2026
a2a12d5
examples/data_sampling: producer sends user stamped video/imu, consum…
stephen-derosa Mar 31, 2026
b4fff4a
fix print
stephen-derosa Mar 31, 2026
b79940b
fix comment
stephen-derosa Mar 31, 2026
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
10 changes: 6 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ set(FFI_PROTO_FILES
${FFI_PROTO_DIR}/e2ee.proto
${FFI_PROTO_DIR}/stats.proto
${FFI_PROTO_DIR}/data_stream.proto
${FFI_PROTO_DIR}/data_track.proto
${FFI_PROTO_DIR}/rpc.proto
${FFI_PROTO_DIR}/track_publication.proto
)
Expand Down Expand Up @@ -323,15 +324,20 @@ add_library(livekit SHARED
src/audio_processing_module.cpp
src/audio_source.cpp
src/audio_stream.cpp
src/data_frame.cpp
src/data_stream.cpp
src/data_track_error.cpp
src/data_track_subscription.cpp
src/e2ee.cpp
src/ffi_handle.cpp
src/ffi_client.cpp
src/ffi_client.h
src/livekit.cpp
src/logging.cpp
src/local_audio_track.cpp
src/local_data_track.cpp
src/remote_audio_track.cpp
src/remote_data_track.cpp
src/room.cpp
src/room_proto_converter.cpp
src/room_proto_converter.h
Expand Down Expand Up @@ -683,10 +689,6 @@ install(FILES
# Build the LiveKit C++ bridge before examples (human_robot depends on it)
add_subdirectory(bridge)

# ---- Examples ----
# add_subdirectory(examples)


if(LIVEKIT_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
Expand Down
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,35 @@ CPP SDK is using clang C++ format
brew install clang-format
```


#### Memory Checks
Run valgrind on various examples or tests to check for memory leaks and other issues.
```bash
valgrind --leak-check=full ./build-debug/bin/livekit_integration_tests
valgrind --leak-check=full ./build-debug/bin/livekit_stress_tests
```

# Running locally
1. Install the livekit-server
https://docs.livekit.io/transport/self-hosting/local/

Start the livekit-server with data tracks enabled:
```bash
LIVEKIT_CONFIG="enable_data_tracks: true" livekit-server --dev
```

```bash
# generate tokens, do for all participants
lk token create \
--api-key devkey \
--api-secret secret \
-i robot \
--join \
--valid-for 99999h \
--room robo_room \
--grant '{"canPublish":true,"canSubscribe":true,"canPublishData":true}'
```

<!--BEGIN_REPO_NAV-->
<br/><table>
<thead><tr><th colspan="2">LiveKit Ecosystem</th></tr></thead>
Expand Down
131 changes: 130 additions & 1 deletion examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,18 @@ set(EXAMPLES_COMMON_DIR ${CMAKE_CURRENT_SOURCE_DIR}/common)
# All example executables (used for copying livekit_ffi DLL/shared lib)
set(EXAMPLES_ALL
SimpleRoom
DataStampingProducer
DataStampingConsumer
UserTimestampedVideoProducer
UserTimestampedVideoConsumer
SimpleRpc
SimpleJoystickSender
SimpleJoystickReceiver
SimpleDataStream
PingPongPing
PingPongPong
HelloLivekitSender
HelloLivekitReceiver
LoggingLevelsBasicUsage
LoggingLevelsCustomSinks
BridgeRobot
Expand Down Expand Up @@ -234,6 +242,56 @@ target_link_libraries(SimpleDataStream
spdlog::spdlog
)

# --- UserTimestampedVideo example ---

add_executable(DataStampingProducer
data_stamping/producer.cpp
)

target_include_directories(DataStampingProducer PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS})

target_link_libraries(DataStampingProducer
PRIVATE
livekit
spdlog::spdlog
)

add_executable(DataStampingConsumer
data_stamping/consumer.cpp
)

target_include_directories(DataStampingConsumer PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS})

target_link_libraries(DataStampingConsumer
PRIVATE
livekit
spdlog::spdlog
)

add_executable(UserTimestampedVideoProducer
user_timestamped_video/producer.cpp
)

target_include_directories(UserTimestampedVideoProducer PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS})

target_link_libraries(UserTimestampedVideoProducer
PRIVATE
livekit
spdlog::spdlog
)

add_executable(UserTimestampedVideoConsumer
user_timestamped_video/consumer.cpp
)

target_include_directories(UserTimestampedVideoConsumer PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS})

target_link_libraries(UserTimestampedVideoConsumer
PRIVATE
livekit
spdlog::spdlog
)

add_custom_command(
TARGET SimpleDataStream
POST_BUILD
Expand All @@ -242,6 +300,77 @@ add_custom_command(
$<TARGET_FILE_DIR:SimpleDataStream>/data
)

# --- ping_pong (request/response latency measurement over data tracks) ---

add_library(ping_pong_support STATIC
ping_pong/json_converters.cpp
ping_pong/json_converters.h
ping_pong/constants.h
ping_pong/messages.h
ping_pong/utils.h
)

target_include_directories(ping_pong_support PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/ping_pong
)

target_link_libraries(ping_pong_support
PRIVATE
nlohmann_json::nlohmann_json
)

add_executable(PingPongPing
ping_pong/ping.cpp
)

target_include_directories(PingPongPing PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS})

target_link_libraries(PingPongPing
PRIVATE
ping_pong_support
livekit
spdlog::spdlog
)

add_executable(PingPongPong
ping_pong/pong.cpp
)

target_include_directories(PingPongPong PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS})

target_link_libraries(PingPongPong
PRIVATE
ping_pong_support
livekit
spdlog::spdlog
)

# --- hello_livekit (minimal synthetic video + data publish / subscribe) ---

add_executable(HelloLivekitSender
hello_livekit/sender.cpp
)

target_include_directories(HelloLivekitSender PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS})

target_link_libraries(HelloLivekitSender
PRIVATE
livekit
spdlog::spdlog
)

add_executable(HelloLivekitReceiver
hello_livekit/receiver.cpp
)

target_include_directories(HelloLivekitReceiver PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS})

target_link_libraries(HelloLivekitReceiver
PRIVATE
livekit
spdlog::spdlog
)

# --- bridge_human_robot examples (robot + human; use livekit_bridge and SDL3) ---

add_executable(BridgeRobot
Expand Down Expand Up @@ -398,4 +527,4 @@ if(UNIX)
foreach(EXAMPLE ${EXAMPLES_BRIDGE})
add_dependencies(${EXAMPLE} copy_bridge_to_bin)
endforeach()
endif()
endif()
13 changes: 9 additions & 4 deletions examples/bridge_human_robot/human.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ static void renderFrame(const livekit::VideoFrame &frame) {
static std::atomic<uint64_t> g_audio_frames{0};
static std::atomic<uint64_t> g_video_frames{0};

constexpr const char *kRobotMicTrackName = "robot-mic";
constexpr const char *kRobotSimAudioTrackName = "robot-sim-audio";
constexpr const char *kRobotCamTrackName = "robot-cam";
constexpr const char *kRobotSimVideoTrackName = "robot-sim-frame";

int main(int argc, char *argv[]) {
// ----- Parse args / env -----
bool no_audio = false;
Expand Down Expand Up @@ -232,7 +237,7 @@ int main(int argc, char *argv[]) {

// ----- Set audio callbacks using Room::setOnAudioFrameCallback -----
room->setOnAudioFrameCallback(
"robot", livekit::TrackSource::SOURCE_MICROPHONE,
"robot", kRobotMicTrackName,
[playAudio, no_audio](const livekit::AudioFrame &frame) {
g_audio_frames.fetch_add(1, std::memory_order_relaxed);
if (!no_audio && g_selected_source.load(std::memory_order_relaxed) ==
Expand All @@ -242,7 +247,7 @@ int main(int argc, char *argv[]) {
});

room->setOnAudioFrameCallback(
"robot", livekit::TrackSource::SOURCE_SCREENSHARE_AUDIO,
"robot", kRobotSimAudioTrackName,
[playAudio, no_audio](const livekit::AudioFrame &frame) {
g_audio_frames.fetch_add(1, std::memory_order_relaxed);
if (!no_audio && g_selected_source.load(std::memory_order_relaxed) ==
Expand All @@ -253,7 +258,7 @@ int main(int argc, char *argv[]) {

// ----- Set video callbacks using Room::setOnVideoFrameCallback -----
room->setOnVideoFrameCallback(
"robot", livekit::TrackSource::SOURCE_CAMERA,
"robot", kRobotCamTrackName,
[](const livekit::VideoFrame &frame, std::int64_t /*timestamp_us*/) {
g_video_frames.fetch_add(1, std::memory_order_relaxed);
if (g_selected_source.load(std::memory_order_relaxed) ==
Expand All @@ -263,7 +268,7 @@ int main(int argc, char *argv[]) {
});

room->setOnVideoFrameCallback(
"robot", livekit::TrackSource::SOURCE_SCREENSHARE,
"robot", kRobotSimVideoTrackName,
[](const livekit::VideoFrame &frame, std::int64_t /*timestamp_us*/) {
g_video_frames.fetch_add(1, std::memory_order_relaxed);
if (g_selected_source.load(std::memory_order_relaxed) ==
Expand Down
2 changes: 1 addition & 1 deletion examples/common/sdl_media_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -399,4 +399,4 @@ void SDLMediaManager::render() {
if (renderer_running_.load(std::memory_order_relaxed) && sdl_renderer_) {
sdl_renderer_->render();
}
}
}
28 changes: 28 additions & 0 deletions examples/data_stamping/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# DataStamping

Minimal two-process example:

- `DataStampingProducer` publishes a synthetic camera track with
`metadata.user_timestamp_us` set on every frame.
- `DataStampingProducer` also publishes a data track named `imu` with JSON
payloads shaped like:

```json
{
"angular": {"x": 0.0, "y": 0.0, "z": 0.0},
"linear": {"x": 0.0, "y": 0.0, "z": 0.0}
}
```

The IMU values are simulated sine waves.

- `DataStampingConsumer` only registers:
- `addOnDataFrameCallback()`
- `setOnVideoFrameCallback()`

Run them in the same room with different participant identities:

```sh
LIVEKIT_URL=ws://localhost:7880 LIVEKIT_TOKEN=<producer-token> ./DataStampingProducer
LIVEKIT_URL=ws://localhost:7880 LIVEKIT_TOKEN=<consumer-token> ./DataStampingConsumer
```
Loading
Loading