Skip to content

Commit 5b440d5

Browse files
migrate all examples from client-sdk-cpp. Enable local cpp sdk build with LIVEKIT_LOCAL_SDK_DIR.
1 parent a74ca36 commit 5b440d5

57 files changed

Lines changed: 5570 additions & 22 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/builds.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,14 @@ jobs:
4040
sudo apt-get install -y \
4141
cmake ninja-build pkg-config \
4242
protobuf-compiler libprotobuf-dev \
43+
libasound2-dev libpulse-dev \
44+
libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxfixes-dev \
45+
libxi-dev libxss-dev libxtst-dev libxkbcommon-dev \
46+
libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \
47+
libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev \
48+
libpipewire-0.3-dev libwayland-dev libdecor-0-dev liburing-dev \
4349
libssl-dev \
50+
libspdlog-dev \
4451
curl
4552
4653
- name: Install deps (macOS)
@@ -50,6 +57,7 @@ jobs:
5057
set -eux
5158
brew update
5259
brew install cmake ninja protobuf
60+
brew install spdlog
5361
5462
- name: Install deps (Windows)
5563
if: runner.os == 'Windows'

CMakeLists.txt

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,66 @@
1+
# Copyright 2026 LiveKit, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
115
cmake_minimum_required(VERSION 3.20)
216
project(livekit_cpp_example_collection LANGUAGES CXX)
317
set(CMAKE_CXX_STANDARD 17)
418
set(CMAKE_CXX_STANDARD_REQUIRED ON)
19+
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
520

621
# Make "include(LiveKitSDK)" search in ./cmake
722
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
823

924
set(LIVEKIT_SDK_VERSION "latest" CACHE STRING "LiveKit C++ SDK version (e.g. 0.2.0 or latest)")
25+
set(LIVEKIT_LOCAL_SDK_DIR "" CACHE PATH "Path to a local LiveKit SDK install prefix (skips download)")
1026

11-
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
12-
include(LiveKitSDK)
27+
if(LIVEKIT_LOCAL_SDK_DIR)
28+
message(STATUS "Using local LiveKit SDK: ${LIVEKIT_LOCAL_SDK_DIR}")
29+
list(PREPEND CMAKE_PREFIX_PATH "${LIVEKIT_LOCAL_SDK_DIR}")
30+
else()
31+
include(LiveKitSDK)
32+
livekit_sdk_setup(
33+
VERSION "${LIVEKIT_SDK_VERSION}"
34+
SDK_DIR "${CMAKE_BINARY_DIR}/_deps/livekit-sdk"
35+
GITHUB_TOKEN "$ENV{GITHUB_TOKEN}"
36+
)
37+
endif()
38+
find_package(LiveKit CONFIG REQUIRED)
1339

14-
livekit_sdk_setup(
15-
VERSION "${LIVEKIT_SDK_VERSION}"
16-
SDK_DIR "${CMAKE_BINARY_DIR}/_deps/livekit-sdk"
17-
GITHUB_TOKEN "$ENV{GITHUB_TOKEN}"
18-
)
40+
if(TARGET LiveKit::livekit)
41+
set(LIVEKIT_CORE_TARGET LiveKit::livekit)
42+
elseif(TARGET livekit)
43+
set(LIVEKIT_CORE_TARGET livekit)
44+
else()
45+
message(FATAL_ERROR "Could not find a LiveKit core target (expected LiveKit::livekit or livekit).")
46+
endif()
47+
48+
set(LIVEKIT_DATA_DIR "")
49+
if(DEFINED LIVEKIT_ROOT_DIR AND EXISTS "${LIVEKIT_ROOT_DIR}/data")
50+
set(LIVEKIT_DATA_DIR "${LIVEKIT_ROOT_DIR}/data")
51+
endif()
52+
53+
include(ExampleDeps)
1954

20-
find_package(LiveKit CONFIG REQUIRED)
2155
add_subdirectory(basic_room)
56+
add_subdirectory(simple_room)
57+
add_subdirectory(simple_rpc)
58+
add_subdirectory(simple_data_stream)
59+
add_subdirectory(logging_levels_basic_usage)
60+
add_subdirectory(logging_levels_custom_sinks)
61+
add_subdirectory(hello_livekit_sender)
62+
add_subdirectory(hello_livekit_receiver)
63+
add_subdirectory(simple_joystick_sender)
64+
add_subdirectory(simple_joystick_receiver)
65+
add_subdirectory(ping_pong_ping)
66+
add_subdirectory(ping_pong_pong)

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ rm -rf build
3535
cmake -S . -B build -DLIVEKIT_SDK_VERSION=0.3.1
3636
```
3737

38+
Build against a local SDK:
39+
```bash
40+
rm -rf build
41+
# install the SDK into $HOME/livekit-sdk-install (or any other directory)
42+
cmake --install <sdk-build-dir> --prefix $HOME/livekit-sdk-install
43+
44+
# build the examples against the local SDK
45+
cmake -S . -B build -DLIVEKIT_LOCAL_SDK_DIR=$HOME/livekit-sdk-install
46+
```
47+
3848

3949
### Building the examples
4050
#### macOS / Linux

basic_room/CMakeLists.txt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
# Copyright 2026 LiveKit, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
115
add_executable(basic_room
216
main.cpp
317
capture_utils.cpp
@@ -13,7 +27,6 @@ get_filename_component(_lk_lib_dir "${_lk_cmake_dir}" DIRECTORY) # .../lib
1327
target_link_directories(basic_room PRIVATE "${_lk_lib_dir}")
1428

1529

16-
1730
# Nice-to-have: copy runtime DLLs next to the exe on Windows for "run from build dir".
1831
# Only do this if your exported package provides these targets.
1932
if(WIN32)

basic_room/main.cpp

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ void printUsage(const char *prog) {
4141
<< " LIVEKIT_URL, LIVEKIT_TOKEN\n";
4242
}
4343

44-
bool parseArgs(int argc, char *argv[], std::string &url, std::string &token, bool &self_test) {
44+
bool parseArgs(int argc, char *argv[], std::string &url, std::string &token,
45+
bool &self_test) {
4546
for (int i = 1; i < argc; ++i) {
4647
const std::string a = argv[i];
4748
if (a == "-h" || a == "--help")
@@ -85,14 +86,9 @@ bool parseArgs(int argc, char *argv[], std::string &url, std::string &token, boo
8586
}
8687

8788
void print_livekit_version() {
88-
std::cout
89-
<< "LiveKit version: "
90-
<< LIVEKIT_BUILD_VERSION_FULL
91-
<< " (" << LIVEKIT_BUILD_FLAVOR
92-
<< ", commit " << LIVEKIT_BUILD_COMMIT
93-
<< ", built " << LIVEKIT_BUILD_DATE
94-
<< ")"
95-
<< std::endl;
89+
std::cout << "LiveKit version: " << LIVEKIT_BUILD_VERSION_FULL << " ("
90+
<< LIVEKIT_BUILD_FLAVOR << ", commit " << LIVEKIT_BUILD_COMMIT
91+
<< ", built " << LIVEKIT_BUILD_DATE << ")" << std::endl;
9692
}
9793

9894
} // namespace
@@ -106,7 +102,7 @@ int main(int argc, char *argv[]) {
106102
return 1;
107103
}
108104
if (self_test) {
109-
livekit::initialize(livekit::LogSink::kConsole);
105+
livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole);
110106
livekit::shutdown();
111107
std::cout << "self-test ok" << std::endl;
112108
return 0;
@@ -115,7 +111,7 @@ int main(int argc, char *argv[]) {
115111
std::signal(SIGINT, handleSignal);
116112

117113
// Init LiveKit
118-
livekit::initialize(livekit::LogSink::kConsole);
114+
livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole);
119115

120116
auto room = std::make_unique<Room>();
121117

@@ -145,7 +141,8 @@ int main(int argc, char *argv[]) {
145141

146142
std::shared_ptr<LocalTrackPublication> audioPub;
147143
try {
148-
audioPub = room->localParticipant()->publishTrack(audioTrack, audioOpts);
144+
room->localParticipant()->publishTrack(audioTrack, audioOpts);
145+
audioPub = audioTrack->publication();
149146
std::cout << "Published audio: sid=" << audioPub->sid() << "\n";
150147
} catch (const std::exception &e) {
151148
std::cerr << "Failed to publish audio: " << e.what() << "\n";
@@ -163,7 +160,8 @@ int main(int argc, char *argv[]) {
163160

164161
std::shared_ptr<LocalTrackPublication> videoPub;
165162
try {
166-
videoPub = room->localParticipant()->publishTrack(videoTrack, videoOpts);
163+
room->localParticipant()->publishTrack(videoTrack, videoOpts);
164+
videoPub = videoTrack->publication();
167165
std::cout << "Published video: sid=" << videoPub->sid() << "\n";
168166
} catch (const std::exception &e) {
169167
std::cerr << "Failed to publish video: " << e.what() << "\n";

cmake/ExampleDeps.cmake

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
include(FetchContent)
2+
3+
find_package(nlohmann_json CONFIG QUIET)
4+
if(NOT TARGET nlohmann_json::nlohmann_json)
5+
FetchContent_Declare(
6+
nlohmann_json
7+
URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz
8+
)
9+
FetchContent_MakeAvailable(nlohmann_json)
10+
endif()
11+
12+
find_package(SDL3 CONFIG QUIET)
13+
set(_need_sdl3_fetch TRUE)
14+
if(TARGET SDL3::SDL3)
15+
get_target_property(_sdl3_include_dirs SDL3::SDL3 INTERFACE_INCLUDE_DIRECTORIES)
16+
if(_sdl3_include_dirs)
17+
set(_need_sdl3_fetch FALSE)
18+
endif()
19+
endif()
20+
21+
if(_need_sdl3_fetch)
22+
FetchContent_Declare(
23+
SDL3
24+
URL https://github.com/libsdl-org/SDL/releases/download/release-3.2.26/SDL3-3.2.26.tar.gz
25+
)
26+
FetchContent_MakeAvailable(SDL3)
27+
endif()
28+
29+
unset(_need_sdl3_fetch)
30+
unset(_sdl3_include_dirs)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright 2026 LiveKit, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
add_executable(HelloLivekitReceiver
16+
main.cpp
17+
)
18+
19+
target_include_directories(HelloLivekitReceiver PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
20+
target_link_libraries(HelloLivekitReceiver PRIVATE ${LIVEKIT_CORE_TARGET})

hello_livekit_receiver/main.cpp

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright 2026 LiveKit
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/// Subscribes to the sender's camera video and data track. Run
18+
/// HelloLivekitSender first; use the identity it prints, or the sender's known
19+
/// participant name.
20+
///
21+
/// Usage:
22+
/// HelloLivekitReceiver <ws-url> <receiver-token> <sender-identity>
23+
///
24+
/// Or via environment variables:
25+
/// LIVEKIT_URL, LIVEKIT_RECEIVER_TOKEN, LIVEKIT_SENDER_IDENTITY
26+
27+
#include "livekit/livekit.h"
28+
29+
#include <atomic>
30+
#include <chrono>
31+
#include <csignal>
32+
#include <cstdlib>
33+
#include <iostream>
34+
#include <thread>
35+
36+
using namespace livekit;
37+
38+
constexpr const char *kDataTrackName = "app-data";
39+
constexpr const char *kVideoTrackName = "camera0";
40+
41+
std::atomic<bool> g_running{true};
42+
43+
void handleSignal(int) { g_running.store(false); }
44+
45+
std::string getenvOrEmpty(const char *name) {
46+
const char *v = std::getenv(name);
47+
return v ? std::string(v) : std::string{};
48+
}
49+
50+
int main(int argc, char *argv[]) {
51+
std::string url = getenvOrEmpty("LIVEKIT_URL");
52+
std::string receiver_token = getenvOrEmpty("LIVEKIT_RECEIVER_TOKEN");
53+
std::string sender_identity = getenvOrEmpty("LIVEKIT_SENDER_IDENTITY");
54+
55+
if (argc >= 4) {
56+
url = argv[1];
57+
receiver_token = argv[2];
58+
sender_identity = argv[3];
59+
}
60+
61+
if (url.empty() || receiver_token.empty() || sender_identity.empty()) {
62+
std::cerr << "[error] Usage: HelloLivekitReceiver <ws-url> <receiver-token> <sender-identity>\n or set LIVEKIT_URL, LIVEKIT_RECEIVER_TOKEN, LIVEKIT_SENDER_IDENTITY\n";
63+
return 1;
64+
}
65+
66+
std::signal(SIGINT, handleSignal);
67+
#ifdef SIGTERM
68+
std::signal(SIGTERM, handleSignal);
69+
#endif
70+
71+
livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole);
72+
73+
auto room = std::make_unique<Room>();
74+
RoomOptions options;
75+
options.auto_subscribe = true;
76+
options.dynacast = false;
77+
78+
if (!room->Connect(url, receiver_token, options)) {
79+
std::cerr << "[error] [receiver] Failed to connect\n";
80+
livekit::shutdown();
81+
return 1;
82+
}
83+
84+
LocalParticipant *lp = room->localParticipant();
85+
assert(lp);
86+
87+
std::cout << "[info] [receiver] Connected as identity='" << lp->identity() << "' room='" << room->room_info().name << "'; subscribing to sender identity='" << sender_identity << "'\n";
88+
89+
int video_frame_count = 0;
90+
room->setOnVideoFrameCallback(
91+
sender_identity, kVideoTrackName,
92+
[&video_frame_count](const VideoFrame &frame, std::int64_t timestamp_us) {
93+
const auto ts_ms =
94+
std::chrono::duration<double, std::milli>(timestamp_us).count();
95+
const int n = video_frame_count++;
96+
if (n % 10 == 0) {
97+
std::cout << "[info] [receiver] Video frame #" << n << " " << frame.width() << "x" << frame.height() << " ts_ms=" << ts_ms << "\n";
98+
}
99+
});
100+
101+
int data_frame_count = 0;
102+
room->addOnDataFrameCallback(
103+
sender_identity, kDataTrackName,
104+
[&data_frame_count](const std::vector<std::uint8_t> &payload,
105+
std::optional<std::uint64_t> user_ts) {
106+
const int n = data_frame_count++;
107+
if (n % 10 == 0) {
108+
std::cout << "[info] [receiver] Data frame #" << n << "\n";
109+
}
110+
});
111+
112+
std::cout << "[info] [receiver] Listening for video track '" << kVideoTrackName << "' + data track '" << kDataTrackName << "'; Ctrl-C to exit\n";
113+
114+
while (g_running.load()) {
115+
std::this_thread::sleep_for(std::chrono::milliseconds(50));
116+
}
117+
118+
std::cout << "[info] [receiver] Shutting down\n";
119+
room.reset();
120+
121+
livekit::shutdown();
122+
return 0;
123+
}

0 commit comments

Comments
 (0)