From 17d2cc74ee2fe99c84e3f95819dad8dd02e9e6d9 Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 28 May 2026 20:58:58 -0600 Subject: [PATCH 01/12] Remove second arg init --- basic_room/main.cpp | 4 ++-- hello_livekit/receiver/main.cpp | 2 +- hello_livekit/sender/main.cpp | 2 +- ping_pong/ping/main.cpp | 2 +- ping_pong/pong/main.cpp | 2 +- simple_data_stream/main.cpp | 2 +- simple_joystick/receiver/main.cpp | 2 +- simple_joystick/sender/main.cpp | 2 +- simple_rpc/main.cpp | 2 +- user_timestamped_video/consumer/main.cpp | 2 +- user_timestamped_video/producer/main.cpp | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/basic_room/main.cpp b/basic_room/main.cpp index 59893bc..6d84959 100644 --- a/basic_room/main.cpp +++ b/basic_room/main.cpp @@ -94,7 +94,7 @@ int main(int argc, char* argv[]) { return 1; } if (self_test) { - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); + livekit::initialize(livekit::LogLevel::Info); livekit::shutdown(); std::cout << "self-test ok" << std::endl; return 0; @@ -103,7 +103,7 @@ int main(int argc, char* argv[]) { std::signal(SIGINT, handleSignal); // Init LiveKit - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); + livekit::initialize(livekit::LogLevel::Info); auto room = std::make_unique(); diff --git a/hello_livekit/receiver/main.cpp b/hello_livekit/receiver/main.cpp index 7e877a5..22f1d42 100644 --- a/hello_livekit/receiver/main.cpp +++ b/hello_livekit/receiver/main.cpp @@ -70,7 +70,7 @@ int main(int argc, char* argv[]) { std::signal(SIGTERM, handleSignal); #endif - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); + livekit::initialize(livekit::LogLevel::Info); auto room = std::make_unique(); RoomOptions options; diff --git a/hello_livekit/sender/main.cpp b/hello_livekit/sender/main.cpp index d388f84..5d83a16 100644 --- a/hello_livekit/sender/main.cpp +++ b/hello_livekit/sender/main.cpp @@ -71,7 +71,7 @@ int main(int argc, char* argv[]) { std::signal(SIGTERM, handleSignal); #endif - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); + livekit::initialize(livekit::LogLevel::Info); auto room = std::make_unique(); RoomOptions options; diff --git a/ping_pong/ping/main.cpp b/ping_pong/ping/main.cpp index b21c125..2e92dc5 100644 --- a/ping_pong/ping/main.cpp +++ b/ping_pong/ping/main.cpp @@ -85,7 +85,7 @@ int main(int argc, char* argv[]) { std::signal(SIGTERM, handleSignal); #endif - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); + livekit::initialize(livekit::LogLevel::Info); auto room = std::make_unique(); RoomOptions options; diff --git a/ping_pong/pong/main.cpp b/ping_pong/pong/main.cpp index 3421779..7692bfc 100644 --- a/ping_pong/pong/main.cpp +++ b/ping_pong/pong/main.cpp @@ -64,7 +64,7 @@ int main(int argc, char* argv[]) { std::signal(SIGTERM, handleSignal); #endif - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); + livekit::initialize(livekit::LogLevel::Info); auto room = std::make_unique(); RoomOptions options; diff --git a/simple_data_stream/main.cpp b/simple_data_stream/main.cpp index 1edb2f7..eb474c3 100644 --- a/simple_data_stream/main.cpp +++ b/simple_data_stream/main.cpp @@ -207,7 +207,7 @@ int main(int argc, char* argv[]) { #endif // Initialize the livekit with logging to console. - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); + livekit::initialize(livekit::LogLevel::Info); auto room = std::make_unique(); RoomOptions options; options.auto_subscribe = true; diff --git a/simple_joystick/receiver/main.cpp b/simple_joystick/receiver/main.cpp index f227606..67091da 100644 --- a/simple_joystick/receiver/main.cpp +++ b/simple_joystick/receiver/main.cpp @@ -59,7 +59,7 @@ int main(int argc, char* argv[]) { std::cout << "[Receiver] Connecting to: " << url << "\n"; std::signal(SIGINT, handleSignal); - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); + livekit::initialize(livekit::LogLevel::Info); auto room = std::make_unique(); RoomOptions options; options.auto_subscribe = true; diff --git a/simple_joystick/sender/main.cpp b/simple_joystick/sender/main.cpp index 128c662..7777326 100644 --- a/simple_joystick/sender/main.cpp +++ b/simple_joystick/sender/main.cpp @@ -127,7 +127,7 @@ int main(int argc, char* argv[]) { std::cout << "[Sender] Connecting to: " << url << "\n"; std::signal(SIGINT, handleSignal); - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); + livekit::initialize(livekit::LogLevel::Info); auto room = std::make_unique(); RoomOptions options; options.auto_subscribe = true; diff --git a/simple_rpc/main.cpp b/simple_rpc/main.cpp index 2e22989..42e5c46 100644 --- a/simple_rpc/main.cpp +++ b/simple_rpc/main.cpp @@ -402,7 +402,7 @@ int main(int argc, char* argv[]) { std::signal(SIGINT, handleSignal); // Initialize the livekit with logging to console. - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); + livekit::initialize(livekit::LogLevel::Info); auto room = std::make_unique(); RoomOptions options; options.auto_subscribe = true; diff --git a/user_timestamped_video/consumer/main.cpp b/user_timestamped_video/consumer/main.cpp index 291f646..8d52403 100644 --- a/user_timestamped_video/consumer/main.cpp +++ b/user_timestamped_video/consumer/main.cpp @@ -146,7 +146,7 @@ int main(int argc, char* argv[]) { user_timestamped_video::installSignalHandlers(); - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); + livekit::initialize(livekit::LogLevel::Info); int exit_code = 0; { diff --git a/user_timestamped_video/producer/main.cpp b/user_timestamped_video/producer/main.cpp index 13830a0..c2b03fc 100644 --- a/user_timestamped_video/producer/main.cpp +++ b/user_timestamped_video/producer/main.cpp @@ -79,7 +79,7 @@ int main(int argc, char* argv[]) { user_timestamped_video::installSignalHandlers(); - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); + livekit::initialize(livekit::LogLevel::Info); int exit_code = 0; { From f7ad2e0f2d0a4ef45e51e8f44cc8197e6536e3c4 Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 24 Jun 2026 16:07:38 -0600 Subject: [PATCH 02/12] Add token_source examples Add one example per token source type (literal, endpoint, sandbox, custom, caching) that connects to a room, plus a shared common.h for the logging delegate and connect/observe session loop. The sandbox example reads its sandbox ID from the environment so no IDs are committed. These build against a local install of the SDK's token source API branch via -DLIVEKIT_LOCAL_SDK_DIR; see token_source/README.md. Co-authored-by: Cursor --- CMakeLists.txt | 1 + token_source/CMakeLists.txt | 45 +++++++++ token_source/README.md | 105 +++++++++++++++++++++ token_source/caching.cpp | 78 ++++++++++++++++ token_source/common.h | 177 ++++++++++++++++++++++++++++++++++++ token_source/custom.cpp | 94 +++++++++++++++++++ token_source/endpoint.cpp | 78 ++++++++++++++++ token_source/literal.cpp | 69 ++++++++++++++ token_source/sandbox.cpp | 82 +++++++++++++++++ 9 files changed, 729 insertions(+) create mode 100644 token_source/CMakeLists.txt create mode 100644 token_source/README.md create mode 100644 token_source/caching.cpp create mode 100644 token_source/common.h create mode 100644 token_source/custom.cpp create mode 100644 token_source/endpoint.cpp create mode 100644 token_source/literal.cpp create mode 100644 token_source/sandbox.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d382af..d0a0bc0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,7 @@ endfunction() include(ExampleDeps) add_subdirectory(basic_room) +add_subdirectory(token_source) add_subdirectory(simple_room) add_subdirectory(simple_rpc) add_subdirectory(simple_data_stream) diff --git a/token_source/CMakeLists.txt b/token_source/CMakeLists.txt new file mode 100644 index 0000000..9b048e3 --- /dev/null +++ b/token_source/CMakeLists.txt @@ -0,0 +1,45 @@ +# Copyright 2026 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# One executable per token source type. Each shares common.h for the logging +# delegate and the connect/observe session loop. + +# Resolve the SDK lib directory so -llivekit_ffi is found at link time. +get_filename_component(_lk_cmake_dir "${LiveKit_DIR}" DIRECTORY) # .../lib/cmake +get_filename_component(_lk_lib_dir "${_lk_cmake_dir}" DIRECTORY) # .../lib + +set(_token_source_examples + token_source_literal literal.cpp + token_source_endpoint endpoint.cpp + token_source_sandbox sandbox.cpp + token_source_custom custom.cpp + token_source_caching caching.cpp +) + +list(LENGTH _token_source_examples _count) +math(EXPR _last "${_count} - 1") +foreach(_i RANGE 0 ${_last} 2) + list(GET _token_source_examples ${_i} _target) + math(EXPR _src_idx "${_i} + 1") + list(GET _token_source_examples ${_src_idx} _src) + + add_executable(${_target} ${_src}) + target_include_directories(${_target} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + target_link_libraries(${_target} PRIVATE ${LIVEKIT_CORE_TARGET}) + target_link_directories(${_target} PRIVATE "${_lk_lib_dir}") + + livekit_copy_windows_runtime_dlls(${_target}) +endforeach() + +unset(_token_source_examples) diff --git a/token_source/README.md b/token_source/README.md new file mode 100644 index 0000000..e37f9b9 --- /dev/null +++ b/token_source/README.md @@ -0,0 +1,105 @@ +# Token sources + +These examples show the different ways to obtain the credentials +(**WebSocket URL** + **participant JWT**) the SDK needs to join a room. Each +example builds its own executable, constructs a single kind of +[`TokenSource`](https://github.com/livekit/client-sdk-cpp/blob/main/include/livekit/token_source.h), +connects to a room, logs participant join/leave for a few seconds, then +disconnects. + +> Token sources are for the **initial connection only**. Once connected, the +> LiveKit server refreshes the session token internally — your token source is +> not called again unless you connect again. See the SDK's +> [authentication docs](https://github.com/livekit/client-sdk-cpp/blob/main/docs/authentication.md). + +## The types + +| Example | Type | When to use | +|---|---|---| +| `literal.cpp` | `LiteralTokenSource` | You already have a URL + JWT (minted out of band, e.g. `lk token create`). The SDK consumes them as-is. | +| `endpoint.cpp` | `EndpointTokenSource` | Recommended for production. The SDK POSTs request options to your backend token endpoint, which returns the URL + a fresh JWT. API keys stay server-side. | +| `sandbox.cpp` | `SandboxTokenSource` | Local development only. Uses LiveKit Cloud's sandbox token server. Not for production. | +| `custom.cpp` | `CustomTokenSource` | You have an internal auth/token system. Plug in your own async callback that returns credentials. | +| `caching.cpp` | `CachingTokenSource` | A decorator that adds JWT-aware caching around any configurable source (endpoint/sandbox/custom) to cut down on fetch calls. | + +`LiteralTokenSource` is *fixed* (no per-call options); the others are +*configurable* and accept +[`TokenRequestOptions`](https://github.com/livekit/client-sdk-cpp/blob/main/include/livekit/token_source.h) +(room name, participant identity, agent dispatch, ...). + +## Configuration + +All inputs come from environment variables, so no secrets or sandbox IDs are +committed to source. + +| Variable | Used by | Notes | +|---|---|---| +| `LIVEKIT_URL` | literal, custom | WebSocket URL, e.g. `ws://localhost:7880`. | +| `LIVEKIT_TOKEN` | literal, custom | Participant JWT. | +| `LIVEKIT_TOKEN_ENDPOINT` | endpoint, caching | Token endpoint URL. Default `http://127.0.0.1:3000/createToken`. | +| `LIVEKIT_TOKEN_ENDPOINT_METHOD` | endpoint, caching | Optional HTTP method (default `POST`). | +| `LIVEKIT_TOKEN_ENDPOINT_HEADERS` | endpoint, caching | Optional newline-separated `Name: Value` headers. | +| `LIVEKIT_SANDBOX_ID` | sandbox | Required. Sandbox ID from LiveKit Cloud. | +| `LIVEKIT_AGENT_NAME` | sandbox | Optional agent to dispatch into the room. | +| `LIVEKIT_AGENT_METADATA` | sandbox | Optional metadata for the dispatched agent. | + +## Building + +The token source API is newer than the latest published SDK release, so build +these examples against a **local SDK build** of the +[`feature/token_source_api`](https://github.com/livekit/client-sdk-cpp/tree/feature/token_source_api) +branch rather than a downloaded release. + +```bash +# 1. Build and install the SDK from the feature branch. +git clone https://github.com/livekit/client-sdk-cpp.git +cd client-sdk-cpp +git checkout feature/token_source_api +git submodule update --init --recursive +./build.sh release --bundle --prefix "$HOME/livekit-sdk-install" + +# 2. Configure the examples against that local install. +cd /path/to/cpp-example-collection +cmake -S . -B build -DLIVEKIT_LOCAL_SDK_DIR="$HOME/livekit-sdk-install" +cmake --build build +``` + +Once the token source API ships in a release, you can drop +`-DLIVEKIT_LOCAL_SDK_DIR` and use the normal download flow (optionally pinning +`-DLIVEKIT_SDK_VERSION`). + +## Running + +The built binaries live under `build/token_source/`: + +```bash +# Literal: bring your own URL + token +export LIVEKIT_URL=ws://localhost:7880 +export LIVEKIT_TOKEN= +./build/token_source/token_source_literal + +# Endpoint / caching: point at your token endpoint +export LIVEKIT_TOKEN_ENDPOINT=http://127.0.0.1:3000/createToken +./build/token_source/token_source_endpoint +./build/token_source/token_source_caching + +# Sandbox: development-only, ID from the environment +export LIVEKIT_SANDBOX_ID= +./build/token_source/token_source_sandbox + +# Custom: callback returns credentials (this example reads the env) +export LIVEKIT_URL=ws://localhost:7880 +export LIVEKIT_TOKEN= +./build/token_source/token_source_custom +``` + +Generate a development token with the +[LiveKit CLI](https://docs.livekit.io/home/cli/cli-setup/) (a dev server started +with `livekit-server --dev` uses `devkey` / `secret`): + +```bash +export LIVEKIT_TOKEN=$(lk token create \ + --api-key devkey --api-secret secret \ + -i my-participant --join --room my-room \ + --valid-for 24h --token-only) +``` diff --git a/token_source/caching.cpp b/token_source/caching.cpp new file mode 100644 index 0000000..a1779b9 --- /dev/null +++ b/token_source/caching.cpp @@ -0,0 +1,78 @@ +/* + * Copyright 2026 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Caching token source: a decorator that adds JWT-aware caching to any +// configurable token source (endpoint, sandbox, or custom). +// +// Repeated fetches for the same request options reuse the cached token until it +// nears expiry or a force-refresh is requested, cutting down calls to your +// backend. This example wraps an EndpointTokenSource. +// +// Environment: +// LIVEKIT_TOKEN_ENDPOINT Token endpoint URL +// (default http://127.0.0.1:3000/createToken) +// LIVEKIT_TOKEN_ENDPOINT_METHOD Optional HTTP method (default POST) +// LIVEKIT_TOKEN_ENDPOINT_HEADERS Optional newline-separated "Name: Value" headers + +#include +#include + +#include +#include +#include + +#include "common.h" + +namespace { + +using namespace token_source_example; + +bool cachingTokenSourceConnect() { + std::string endpoint_url = getenvOrEmpty("LIVEKIT_TOKEN_ENDPOINT"); + if (endpoint_url.empty()) { + endpoint_url = "http://127.0.0.1:3000/createToken"; + } + + std::cout << "Caching token source wrapping endpoint: " << endpoint_url << "\n"; + + // Build the inner source, then wrap it. CachingTokenSource::wrap takes + // ownership of the inner source via unique_ptr. + auto inner = livekit::EndpointTokenSource::fromUrl(endpoint_url, endpointOptionsFromEnv()); + auto token_source = livekit::CachingTokenSource::wrap(std::move(inner)); + + livekit::TokenRequestOptions request_options; + request_options.participant_identity = "robot-a"; + + livekit::Room room; + ParticipantLogDelegate delegate; + room.setDelegate(&delegate); + if (!room.connect(*token_source, request_options, livekit::RoomOptions())) { + std::cerr << "Failed to connect to room\n"; + return false; + } + std::cout << "Connected to room: " << room.roomInfo().name << " (caching token source)\n"; + + return runConnectedSession(room); +} + +} // namespace + +int main() { + livekit::initialize(livekit::LogLevel::Info); + const bool ok = cachingTokenSourceConnect(); + livekit::shutdown(); + return ok ? 0 : 1; +} diff --git a/token_source/common.h b/token_source/common.h new file mode 100644 index 0000000..ebb4d23 --- /dev/null +++ b/token_source/common.h @@ -0,0 +1,177 @@ +/* + * Copyright 2026 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Shared helpers for the token_source examples. +// +// Each example focuses on constructing a single kind of token source; the +// logging delegate, the "connect then observe" session loop, and the small +// environment-variable helpers are factored out here so the per-type files stay +// short and highlight only the token-source-specific code. +// +// This mirrors the SDK's own token_source_tester +// (client-sdk-cpp/src/tests/token_source_tester.cpp). + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace token_source_example { + +using namespace std::chrono_literals; + +// How long to stay connected so participant join/leave events can be observed. +constexpr auto kObserveDuration = 5s; + +/// Returns the value of an environment variable, or an empty string when unset. +inline std::string getenvOrEmpty(const char* name) { + const char* value = std::getenv(name); + return (value == nullptr) ? std::string() : std::string(value); +} + +/// Reads a required environment variable. Prints an error and returns false +/// when it is unset or empty. +inline bool requireEnv(const char* name, std::string& out) { + out = getenvOrEmpty(name); + if (out.empty()) { + std::cerr << name << " not set\n"; + return false; + } + return true; +} + +/// Trims leading and trailing ASCII whitespace. +inline std::string trimWhitespace(const std::string& value) { + std::size_t begin = 0; + std::size_t end = value.size(); + while (begin < end && std::isspace(static_cast(value[begin])) != 0) { + ++begin; + } + while (end > begin && std::isspace(static_cast(value[end - 1])) != 0) { + --end; + } + return value.substr(begin, end - begin); +} + +/// Renders a participant display name, falling back to "" when empty. +inline std::string displayName(const std::string& name) { return name.empty() ? "" : name; } + +/// Standard one-line description of a participant (local or remote). +inline std::string formatParticipant(const livekit::Participant& participant) { + return "identity=" + participant.identity() + ", name=" + displayName(participant.name()); +} + +/// Minimal delegate that logs remote participant join/leave activity. +class ParticipantLogDelegate : public livekit::RoomDelegate { +public: + void onParticipantConnected(livekit::Room& /*room*/, const livekit::ParticipantConnectedEvent& event) override { + if (event.participant == nullptr) { + return; + } + std::cout << "Participant connected: " << formatParticipant(*event.participant) << "\n"; + } + + void onParticipantDisconnected(livekit::Room& /*room*/, + const livekit::ParticipantDisconnectedEvent& event) override { + if (event.participant == nullptr) { + return; + } + std::cout << "Participant disconnected: identity=" << event.participant->identity() << "\n"; + } +}; + +inline void logRemoteParticipants(const livekit::Room& room) { + const auto participants = room.remoteParticipants(); + std::cout << "Remote participants currently in room: " << participants.size() << "\n"; + for (const auto& participant_weak : participants) { + if (const auto participant = participant_weak.lock()) { + std::cout << " - " << formatParticipant(*participant) << "\n"; + } + } +} + +/// Logs local/remote participants, stays connected briefly so join/leave events +/// surface, then disconnects gracefully. Returns false on any failure. +inline bool runConnectedSession(livekit::Room& room) { + const auto local_participant = room.localParticipant().lock(); + if (!local_participant) { + std::cerr << "Failed to get local participant\n"; + return false; + } + std::cout << "Local participant info: " << formatParticipant(*local_participant) << "\n"; + + logRemoteParticipants(room); + + // Stay connected briefly so participant join/leave events are surfaced. + std::this_thread::sleep_for(kObserveDuration); + logRemoteParticipants(room); + + if (!room.disconnect()) { + std::cerr << "Failed to gracefully disconnect from room\n"; + return false; + } + + std::cout << "Disconnected from room\n"; + return true; +} + +/// Parses HTTP transport options for EndpointTokenSource from the environment. +/// +/// LIVEKIT_TOKEN_ENDPOINT_METHOD - optional HTTP method (default POST). +/// LIVEKIT_TOKEN_ENDPOINT_HEADERS - optional newline-separated "Name: Value" +/// pairs, e.g. "Authorization: Bearer ...". +inline livekit::TokenEndpointOptions endpointOptionsFromEnv() { + livekit::TokenEndpointOptions options; + + if (const std::string method = getenvOrEmpty("LIVEKIT_TOKEN_ENDPOINT_METHOD"); !method.empty()) { + options.method = method; + } + + const std::string headers_text = getenvOrEmpty("LIVEKIT_TOKEN_ENDPOINT_HEADERS"); + if (headers_text.empty()) { + return options; + } + + std::size_t start = 0; + while (start <= headers_text.size()) { + const std::size_t newline = headers_text.find('\n', start); + const std::string line = + headers_text.substr(start, newline == std::string::npos ? std::string::npos : newline - start); + const std::size_t colon = line.find(':'); + if (colon != std::string::npos) { + const std::string name = trimWhitespace(line.substr(0, colon)); + const std::string value = trimWhitespace(line.substr(colon + 1)); + if (!name.empty()) { + options.headers[name] = value; + } + } + if (newline == std::string::npos) { + break; + } + start = newline + 1; + } + + return options; +} + +} // namespace token_source_example diff --git a/token_source/custom.cpp b/token_source/custom.cpp new file mode 100644 index 0000000..4de7174 --- /dev/null +++ b/token_source/custom.cpp @@ -0,0 +1,94 @@ +/* + * Copyright 2026 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Custom token source: plug your own async token-fetching logic into the SDK. +// +// Use this when you already have an internal auth/token system and want to +// integrate it without adopting the standardized token-endpoint format. Your +// callback receives the per-call TokenRequestOptions and returns a future of +// credentials. +// +// This example's callback simulates a private backend by reading credentials +// from the environment, but the body could call any internal service. +// +// Environment: +// LIVEKIT_URL WebSocket URL of the LiveKit server +// LIVEKIT_TOKEN Participant JWT + +#include +#include +#include + +#include +#include +#include + +#include "common.h" + +namespace { + +using namespace token_source_example; + +using TokenResult = livekit::Result; + +/// Wraps an already-computed value in a ready std::future. +std::future makeReadyFuture(TokenResult result) { + std::promise promise; + promise.set_value(std::move(result)); + return promise.get_future(); +} + +bool customTokenSourceConnect() { + // The callback is invoked for each fetch with the request options. Here we + // pretend to call an internal service; in a real app this is where you would + // talk to your own auth backend. + auto token_source = livekit::CustomTokenSource::fromCallback( + [](const livekit::TokenRequestOptions& options) -> std::future { + (void)options; // A real backend would honor these (room, identity, ...). + + const std::string url = getenvOrEmpty("LIVEKIT_URL"); + const std::string token = getenvOrEmpty("LIVEKIT_TOKEN"); + if (url.empty() || token.empty()) { + return makeReadyFuture(TokenResult::failure( + livekit::TokenSourceError{"LIVEKIT_URL and LIVEKIT_TOKEN must be set for the custom example"})); + } + + livekit::TokenSourceResponse response; + response.server_url = url; + response.participant_token = token; + return makeReadyFuture(TokenResult::success(std::move(response))); + }); + + livekit::Room room; + ParticipantLogDelegate delegate; + room.setDelegate(&delegate); + if (!room.connect(*token_source, livekit::TokenRequestOptions(), livekit::RoomOptions())) { + std::cerr << "Failed to connect to room\n"; + return false; + } + std::cout << "Connected to room: " << room.roomInfo().name << " (custom token source)\n"; + + return runConnectedSession(room); +} + +} // namespace + +int main() { + livekit::initialize(livekit::LogLevel::Info); + const bool ok = customTokenSourceConnect(); + livekit::shutdown(); + return ok ? 0 : 1; +} diff --git a/token_source/endpoint.cpp b/token_source/endpoint.cpp new file mode 100644 index 0000000..aca1478 --- /dev/null +++ b/token_source/endpoint.cpp @@ -0,0 +1,78 @@ +/* + * Copyright 2026 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Endpoint token source: fetch credentials from your own backend token endpoint. +// +// This is the recommended pattern for production: API keys stay server-side and +// the SDK POSTs request options (room, identity, ...) to your endpoint, which +// returns the server URL and a freshly minted JWT. +// +// Environment: +// LIVEKIT_TOKEN_ENDPOINT Token endpoint URL +// (default http://127.0.0.1:3000/createToken) +// LIVEKIT_TOKEN_ENDPOINT_METHOD Optional HTTP method (default POST) +// LIVEKIT_TOKEN_ENDPOINT_HEADERS Optional newline-separated "Name: Value" headers, +// e.g. "Authorization: Bearer abc123" + +#include +#include + +#include +#include +#include + +#include "common.h" + +namespace { + +using namespace token_source_example; + +bool endpointTokenSourceConnect() { + std::string endpoint_url = getenvOrEmpty("LIVEKIT_TOKEN_ENDPOINT"); + if (endpoint_url.empty()) { + endpoint_url = "http://127.0.0.1:3000/createToken"; + } + + auto endpoint_options = endpointOptionsFromEnv(); + std::cout << "Endpoint token source: " << endpoint_options.method << " " << endpoint_url << " (" + << endpoint_options.headers.size() << " custom header(s))\n"; + + auto token_source = livekit::EndpointTokenSource::fromUrl(endpoint_url, std::move(endpoint_options)); + + // These options are sent to your endpoint, which embeds them into the JWT. + livekit::TokenRequestOptions request_options; + request_options.participant_identity = "robot-a"; + + livekit::Room room; + ParticipantLogDelegate delegate; + room.setDelegate(&delegate); + if (!room.connect(*token_source, request_options, livekit::RoomOptions())) { + std::cerr << "Failed to connect to room\n"; + return false; + } + std::cout << "Connected to room: " << room.roomInfo().name << " (endpoint token source)\n"; + + return runConnectedSession(room); +} + +} // namespace + +int main() { + livekit::initialize(livekit::LogLevel::Info); + const bool ok = endpointTokenSourceConnect(); + livekit::shutdown(); + return ok ? 0 : 1; +} diff --git a/token_source/literal.cpp b/token_source/literal.cpp new file mode 100644 index 0000000..5b48798 --- /dev/null +++ b/token_source/literal.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2026 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Literal token source: connect with a server URL and JWT you already have. +// +// Use this when your app mints or fetches tokens out of band (e.g. with +// `lk token create`) and just wants the SDK to consume them as-is. The room, +// identity, and grants all come from the token itself. +// +// Environment: +// LIVEKIT_URL WebSocket URL of the LiveKit server (e.g. ws://localhost:7880) +// LIVEKIT_TOKEN Participant JWT + +#include +#include + +#include +#include + +#include "common.h" + +namespace { + +using namespace token_source_example; + +bool literalTokenSourceConnect() { + std::string url; + std::string token; + if (!requireEnv("LIVEKIT_URL", url) || !requireEnv("LIVEKIT_TOKEN", token)) { + return false; + } + + // Each fetch() returns these exact credentials; nothing is requested over the + // network. Room and participant identity are encoded in the token. + auto token_source = livekit::LiteralTokenSource::fromValue(url, token); + + livekit::Room room; + ParticipantLogDelegate delegate; + room.setDelegate(&delegate); + if (!room.connect(*token_source, livekit::RoomOptions())) { + std::cerr << "Failed to connect to room\n"; + return false; + } + std::cout << "Connected to room: " << room.roomInfo().name << " (literal token source)\n"; + + return runConnectedSession(room); +} + +} // namespace + +int main() { + livekit::initialize(livekit::LogLevel::Info); + const bool ok = literalTokenSourceConnect(); + livekit::shutdown(); + return ok ? 0 : 1; +} diff --git a/token_source/sandbox.cpp b/token_source/sandbox.cpp new file mode 100644 index 0000000..8f55613 --- /dev/null +++ b/token_source/sandbox.cpp @@ -0,0 +1,82 @@ +/* + * Copyright 2026 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Sandbox token source: use LiveKit Cloud's sandbox token server for quick +// local development. Do not use in production. +// +// The sandbox ID is read from the environment (never hard-coded) so this +// example does not link a personal sandbox into source control. +// +// Environment: +// LIVEKIT_SANDBOX_ID Sandbox identifier from LiveKit Cloud (required) +// LIVEKIT_AGENT_NAME Optional registered agent to dispatch into the room +// LIVEKIT_AGENT_METADATA Optional metadata passed to the dispatched agent + +#include +#include + +#include +#include + +#include "common.h" + +namespace { + +using namespace token_source_example; + +bool sandboxTokenSourceConnect() { + std::string sandbox_id; + if (!requireEnv("LIVEKIT_SANDBOX_ID", sandbox_id)) { + return false; + } + + // POSTs to cloud-api.livekit.io/api/v2/sandbox/connection-details with + // X-Sandbox-ID set from LIVEKIT_SANDBOX_ID. + auto token_source = livekit::SandboxTokenSource::fromSandboxId(sandbox_id); + + livekit::TokenRequestOptions request_options; + request_options.participant_identity = "robot-a"; + + // Optional agent dispatch: when LIVEKIT_AGENT_NAME is set, the request embeds + // room_config.agents so the token server dispatches a named agent. + if (const std::string agent_name = getenvOrEmpty("LIVEKIT_AGENT_NAME"); !agent_name.empty()) { + request_options.agent_name = agent_name; + if (const std::string agent_metadata = getenvOrEmpty("LIVEKIT_AGENT_METADATA"); !agent_metadata.empty()) { + request_options.agent_metadata = agent_metadata; + } + std::cout << "Requesting sandbox token with agent dispatch: agent_name=" << *request_options.agent_name << "\n"; + } + + livekit::Room room; + ParticipantLogDelegate delegate; + room.setDelegate(&delegate); + if (!room.connect(*token_source, request_options, livekit::RoomOptions())) { + std::cerr << "Failed to connect to room\n"; + return false; + } + std::cout << "Connected to room: " << room.roomInfo().name << " (sandbox token source)\n"; + + return runConnectedSession(room); +} + +} // namespace + +int main() { + livekit::initialize(livekit::LogLevel::Info); + const bool ok = sandboxTokenSourceConnect(); + livekit::shutdown(); + return ok ? 0 : 1; +} From 93c960d457f702246caa956b0d833c99e7e839ba Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 25 Jun 2026 09:44:16 -0600 Subject: [PATCH 03/12] Latest header --- token_source/common.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/token_source/common.h b/token_source/common.h index ebb4d23..0a7026b 100644 --- a/token_source/common.h +++ b/token_source/common.h @@ -20,9 +20,6 @@ // logging delegate, the "connect then observe" session loop, and the small // environment-variable helpers are factored out here so the per-type files stay // short and highlight only the token-source-specific code. -// -// This mirrors the SDK's own token_source_tester -// (client-sdk-cpp/src/tests/token_source_tester.cpp). #pragma once From 6876b08fc39a9afdc5733ee696235013d258109b Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 25 Jun 2026 15:14:14 -0600 Subject: [PATCH 04/12] Prefix token_source example files; pin SDK commit Rename the per-type token source examples and shared helper to a token_source_ prefix so source filenames match their executable target names, and update the CMake source list, includes, and README references. Pin the README build instructions to client-sdk-cpp commit bbf6a41 and update the caching example comment to reference invalidate() now that force_refresh was dropped from the SDK API. Co-authored-by: Cursor --- token_source/CMakeLists.txt | 14 +++++++------- token_source/README.md | 17 ++++++++++------- .../{caching.cpp => token_source_caching.cpp} | 6 +++--- .../{common.h => token_source_common.h} | 0 .../{custom.cpp => token_source_custom.cpp} | 2 +- .../{endpoint.cpp => token_source_endpoint.cpp} | 2 +- .../{literal.cpp => token_source_literal.cpp} | 2 +- .../{sandbox.cpp => token_source_sandbox.cpp} | 2 +- 8 files changed, 24 insertions(+), 21 deletions(-) rename token_source/{caching.cpp => token_source_caching.cpp} (93%) rename token_source/{common.h => token_source_common.h} (100%) rename token_source/{custom.cpp => token_source_custom.cpp} (99%) rename token_source/{endpoint.cpp => token_source_endpoint.cpp} (98%) rename token_source/{literal.cpp => token_source_literal.cpp} (98%) rename token_source/{sandbox.cpp => token_source_sandbox.cpp} (98%) diff --git a/token_source/CMakeLists.txt b/token_source/CMakeLists.txt index 9b048e3..098426a 100644 --- a/token_source/CMakeLists.txt +++ b/token_source/CMakeLists.txt @@ -12,19 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -# One executable per token source type. Each shares common.h for the logging -# delegate and the connect/observe session loop. +# One executable per token source type. Each shares token_source_common.h for +# the logging delegate and the connect/observe session loop. # Resolve the SDK lib directory so -llivekit_ffi is found at link time. get_filename_component(_lk_cmake_dir "${LiveKit_DIR}" DIRECTORY) # .../lib/cmake get_filename_component(_lk_lib_dir "${_lk_cmake_dir}" DIRECTORY) # .../lib set(_token_source_examples - token_source_literal literal.cpp - token_source_endpoint endpoint.cpp - token_source_sandbox sandbox.cpp - token_source_custom custom.cpp - token_source_caching caching.cpp + token_source_literal token_source_literal.cpp + token_source_endpoint token_source_endpoint.cpp + token_source_sandbox token_source_sandbox.cpp + token_source_custom token_source_custom.cpp + token_source_caching token_source_caching.cpp ) list(LENGTH _token_source_examples _count) diff --git a/token_source/README.md b/token_source/README.md index e37f9b9..b257177 100644 --- a/token_source/README.md +++ b/token_source/README.md @@ -16,11 +16,11 @@ disconnects. | Example | Type | When to use | |---|---|---| -| `literal.cpp` | `LiteralTokenSource` | You already have a URL + JWT (minted out of band, e.g. `lk token create`). The SDK consumes them as-is. | -| `endpoint.cpp` | `EndpointTokenSource` | Recommended for production. The SDK POSTs request options to your backend token endpoint, which returns the URL + a fresh JWT. API keys stay server-side. | -| `sandbox.cpp` | `SandboxTokenSource` | Local development only. Uses LiveKit Cloud's sandbox token server. Not for production. | -| `custom.cpp` | `CustomTokenSource` | You have an internal auth/token system. Plug in your own async callback that returns credentials. | -| `caching.cpp` | `CachingTokenSource` | A decorator that adds JWT-aware caching around any configurable source (endpoint/sandbox/custom) to cut down on fetch calls. | +| `token_source_literal.cpp` | `LiteralTokenSource` | You already have a URL + JWT (minted out of band, e.g. `lk token create`). The SDK consumes them as-is. | +| `token_source_endpoint.cpp` | `EndpointTokenSource` | Recommended for production. The SDK POSTs request options to your backend token endpoint, which returns the URL + a fresh JWT. API keys stay server-side. | +| `token_source_sandbox.cpp` | `SandboxTokenSource` | Local development only. Uses LiveKit Cloud's sandbox token server. Not for production. | +| `token_source_custom.cpp` | `CustomTokenSource` | You have an internal auth/token system. Plug in your own async callback that returns credentials. | +| `token_source_caching.cpp` | `CachingTokenSource` | A decorator that adds JWT-aware caching around any configurable source (endpoint/sandbox/custom) to cut down on fetch calls. | `LiteralTokenSource` is *fixed* (no per-call options); the others are *configurable* and accept @@ -48,13 +48,16 @@ committed to source. The token source API is newer than the latest published SDK release, so build these examples against a **local SDK build** of the [`feature/token_source_api`](https://github.com/livekit/client-sdk-cpp/tree/feature/token_source_api) -branch rather than a downloaded release. +branch rather than a downloaded release. The commands below pin the branch to +commit [`bbf6a41`](https://github.com/livekit/client-sdk-cpp/commit/bbf6a41fae42607ee19ff44ccddac786767b34e3) +so the examples build against a known-good API; drop the `git checkout` of the +hash to track the branch tip instead. ```bash # 1. Build and install the SDK from the feature branch. git clone https://github.com/livekit/client-sdk-cpp.git cd client-sdk-cpp -git checkout feature/token_source_api +git checkout bbf6a41fae42607ee19ff44ccddac786767b34e3 git submodule update --init --recursive ./build.sh release --bundle --prefix "$HOME/livekit-sdk-install" diff --git a/token_source/caching.cpp b/token_source/token_source_caching.cpp similarity index 93% rename from token_source/caching.cpp rename to token_source/token_source_caching.cpp index a1779b9..86f7235 100644 --- a/token_source/caching.cpp +++ b/token_source/token_source_caching.cpp @@ -18,8 +18,8 @@ // configurable token source (endpoint, sandbox, or custom). // // Repeated fetches for the same request options reuse the cached token until it -// nears expiry or a force-refresh is requested, cutting down calls to your -// backend. This example wraps an EndpointTokenSource. +// nears expiry or you call invalidate() to drop the cached credentials, cutting +// down calls to your backend. This example wraps an EndpointTokenSource. // // Environment: // LIVEKIT_TOKEN_ENDPOINT Token endpoint URL @@ -34,7 +34,7 @@ #include #include -#include "common.h" +#include "token_source_common.h" namespace { diff --git a/token_source/common.h b/token_source/token_source_common.h similarity index 100% rename from token_source/common.h rename to token_source/token_source_common.h diff --git a/token_source/custom.cpp b/token_source/token_source_custom.cpp similarity index 99% rename from token_source/custom.cpp rename to token_source/token_source_custom.cpp index 4de7174..3dcdf32 100644 --- a/token_source/custom.cpp +++ b/token_source/token_source_custom.cpp @@ -36,7 +36,7 @@ #include #include -#include "common.h" +#include "token_source_common.h" namespace { diff --git a/token_source/endpoint.cpp b/token_source/token_source_endpoint.cpp similarity index 98% rename from token_source/endpoint.cpp rename to token_source/token_source_endpoint.cpp index aca1478..e6ce9ec 100644 --- a/token_source/endpoint.cpp +++ b/token_source/token_source_endpoint.cpp @@ -34,7 +34,7 @@ #include #include -#include "common.h" +#include "token_source_common.h" namespace { diff --git a/token_source/literal.cpp b/token_source/token_source_literal.cpp similarity index 98% rename from token_source/literal.cpp rename to token_source/token_source_literal.cpp index 5b48798..a612397 100644 --- a/token_source/literal.cpp +++ b/token_source/token_source_literal.cpp @@ -30,7 +30,7 @@ #include #include -#include "common.h" +#include "token_source_common.h" namespace { diff --git a/token_source/sandbox.cpp b/token_source/token_source_sandbox.cpp similarity index 98% rename from token_source/sandbox.cpp rename to token_source/token_source_sandbox.cpp index 8f55613..abe0257 100644 --- a/token_source/sandbox.cpp +++ b/token_source/token_source_sandbox.cpp @@ -31,7 +31,7 @@ #include #include -#include "common.h" +#include "token_source_common.h" namespace { From 677fb78e9ed9a3daac0a055aa07c3984349b091b Mon Sep 17 00:00:00 2001 From: Alan George Date: Tue, 30 Jun 2026 11:44:08 -0600 Subject: [PATCH 05/12] Update token source examples for create factories Co-authored-by: Cursor --- token_source/token_source_caching.cpp | 6 +++--- token_source/token_source_custom.cpp | 2 +- token_source/token_source_endpoint.cpp | 2 +- token_source/token_source_literal.cpp | 2 +- token_source/token_source_sandbox.cpp | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/token_source/token_source_caching.cpp b/token_source/token_source_caching.cpp index 86f7235..1eee385 100644 --- a/token_source/token_source_caching.cpp +++ b/token_source/token_source_caching.cpp @@ -48,10 +48,10 @@ bool cachingTokenSourceConnect() { std::cout << "Caching token source wrapping endpoint: " << endpoint_url << "\n"; - // Build the inner source, then wrap it. CachingTokenSource::wrap takes + // Build the inner source, then wrap it. CachingTokenSource::create takes // ownership of the inner source via unique_ptr. - auto inner = livekit::EndpointTokenSource::fromUrl(endpoint_url, endpointOptionsFromEnv()); - auto token_source = livekit::CachingTokenSource::wrap(std::move(inner)); + auto inner = livekit::EndpointTokenSource::create(endpoint_url, endpointOptionsFromEnv()); + auto token_source = livekit::CachingTokenSource::create(std::move(inner)); livekit::TokenRequestOptions request_options; request_options.participant_identity = "robot-a"; diff --git a/token_source/token_source_custom.cpp b/token_source/token_source_custom.cpp index 3dcdf32..1b88d34 100644 --- a/token_source/token_source_custom.cpp +++ b/token_source/token_source_custom.cpp @@ -55,7 +55,7 @@ bool customTokenSourceConnect() { // The callback is invoked for each fetch with the request options. Here we // pretend to call an internal service; in a real app this is where you would // talk to your own auth backend. - auto token_source = livekit::CustomTokenSource::fromCallback( + auto token_source = livekit::CustomTokenSource::create( [](const livekit::TokenRequestOptions& options) -> std::future { (void)options; // A real backend would honor these (room, identity, ...). diff --git a/token_source/token_source_endpoint.cpp b/token_source/token_source_endpoint.cpp index e6ce9ec..4df124e 100644 --- a/token_source/token_source_endpoint.cpp +++ b/token_source/token_source_endpoint.cpp @@ -50,7 +50,7 @@ bool endpointTokenSourceConnect() { std::cout << "Endpoint token source: " << endpoint_options.method << " " << endpoint_url << " (" << endpoint_options.headers.size() << " custom header(s))\n"; - auto token_source = livekit::EndpointTokenSource::fromUrl(endpoint_url, std::move(endpoint_options)); + auto token_source = livekit::EndpointTokenSource::create(endpoint_url, std::move(endpoint_options)); // These options are sent to your endpoint, which embeds them into the JWT. livekit::TokenRequestOptions request_options; diff --git a/token_source/token_source_literal.cpp b/token_source/token_source_literal.cpp index a612397..ba60850 100644 --- a/token_source/token_source_literal.cpp +++ b/token_source/token_source_literal.cpp @@ -45,7 +45,7 @@ bool literalTokenSourceConnect() { // Each fetch() returns these exact credentials; nothing is requested over the // network. Room and participant identity are encoded in the token. - auto token_source = livekit::LiteralTokenSource::fromValue(url, token); + auto token_source = livekit::LiteralTokenSource::create(url, token); livekit::Room room; ParticipantLogDelegate delegate; diff --git a/token_source/token_source_sandbox.cpp b/token_source/token_source_sandbox.cpp index abe0257..378c940 100644 --- a/token_source/token_source_sandbox.cpp +++ b/token_source/token_source_sandbox.cpp @@ -45,7 +45,7 @@ bool sandboxTokenSourceConnect() { // POSTs to cloud-api.livekit.io/api/v2/sandbox/connection-details with // X-Sandbox-ID set from LIVEKIT_SANDBOX_ID. - auto token_source = livekit::SandboxTokenSource::fromSandboxId(sandbox_id); + auto token_source = livekit::SandboxTokenSource::create(sandbox_id); livekit::TokenRequestOptions request_options; request_options.participant_identity = "robot-a"; From 3424acc1023b955da4e42b2d9db36d6494187879 Mon Sep 17 00:00:00 2001 From: Alan George Date: Tue, 30 Jun 2026 11:50:09 -0600 Subject: [PATCH 06/12] Fetch token credentials before connecting Co-authored-by: Cursor --- token_source/token_source_caching.cpp | 7 ++++++- token_source/token_source_custom.cpp | 7 ++++++- token_source/token_source_endpoint.cpp | 7 ++++++- token_source/token_source_literal.cpp | 7 ++++++- token_source/token_source_sandbox.cpp | 7 ++++++- 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/token_source/token_source_caching.cpp b/token_source/token_source_caching.cpp index 1eee385..30ceb3b 100644 --- a/token_source/token_source_caching.cpp +++ b/token_source/token_source_caching.cpp @@ -55,11 +55,16 @@ bool cachingTokenSourceConnect() { livekit::TokenRequestOptions request_options; request_options.participant_identity = "robot-a"; + const auto credentials = token_source->fetch(request_options).get(); + if (!credentials) { + std::cerr << "Failed to fetch credentials: " << credentials.error().message << "\n"; + return false; + } livekit::Room room; ParticipantLogDelegate delegate; room.setDelegate(&delegate); - if (!room.connect(*token_source, request_options, livekit::RoomOptions())) { + if (!room.connect(credentials.value().server_url, credentials.value().participant_token, livekit::RoomOptions())) { std::cerr << "Failed to connect to room\n"; return false; } diff --git a/token_source/token_source_custom.cpp b/token_source/token_source_custom.cpp index 1b88d34..558b802 100644 --- a/token_source/token_source_custom.cpp +++ b/token_source/token_source_custom.cpp @@ -71,11 +71,16 @@ bool customTokenSourceConnect() { response.participant_token = token; return makeReadyFuture(TokenResult::success(std::move(response))); }); + const auto credentials = token_source->fetch(livekit::TokenRequestOptions()).get(); + if (!credentials) { + std::cerr << "Failed to fetch credentials: " << credentials.error().message << "\n"; + return false; + } livekit::Room room; ParticipantLogDelegate delegate; room.setDelegate(&delegate); - if (!room.connect(*token_source, livekit::TokenRequestOptions(), livekit::RoomOptions())) { + if (!room.connect(credentials.value().server_url, credentials.value().participant_token, livekit::RoomOptions())) { std::cerr << "Failed to connect to room\n"; return false; } diff --git a/token_source/token_source_endpoint.cpp b/token_source/token_source_endpoint.cpp index 4df124e..dc9ce7b 100644 --- a/token_source/token_source_endpoint.cpp +++ b/token_source/token_source_endpoint.cpp @@ -55,11 +55,16 @@ bool endpointTokenSourceConnect() { // These options are sent to your endpoint, which embeds them into the JWT. livekit::TokenRequestOptions request_options; request_options.participant_identity = "robot-a"; + const auto credentials = token_source->fetch(request_options).get(); + if (!credentials) { + std::cerr << "Failed to fetch credentials: " << credentials.error().message << "\n"; + return false; + } livekit::Room room; ParticipantLogDelegate delegate; room.setDelegate(&delegate); - if (!room.connect(*token_source, request_options, livekit::RoomOptions())) { + if (!room.connect(credentials.value().server_url, credentials.value().participant_token, livekit::RoomOptions())) { std::cerr << "Failed to connect to room\n"; return false; } diff --git a/token_source/token_source_literal.cpp b/token_source/token_source_literal.cpp index ba60850..13bcb46 100644 --- a/token_source/token_source_literal.cpp +++ b/token_source/token_source_literal.cpp @@ -46,11 +46,16 @@ bool literalTokenSourceConnect() { // Each fetch() returns these exact credentials; nothing is requested over the // network. Room and participant identity are encoded in the token. auto token_source = livekit::LiteralTokenSource::create(url, token); + const auto credentials = token_source->fetch().get(); + if (!credentials) { + std::cerr << "Failed to fetch credentials: " << credentials.error().message << "\n"; + return false; + } livekit::Room room; ParticipantLogDelegate delegate; room.setDelegate(&delegate); - if (!room.connect(*token_source, livekit::RoomOptions())) { + if (!room.connect(credentials.value().server_url, credentials.value().participant_token, livekit::RoomOptions())) { std::cerr << "Failed to connect to room\n"; return false; } diff --git a/token_source/token_source_sandbox.cpp b/token_source/token_source_sandbox.cpp index 378c940..4fb0445 100644 --- a/token_source/token_source_sandbox.cpp +++ b/token_source/token_source_sandbox.cpp @@ -59,11 +59,16 @@ bool sandboxTokenSourceConnect() { } std::cout << "Requesting sandbox token with agent dispatch: agent_name=" << *request_options.agent_name << "\n"; } + const auto credentials = token_source->fetch(request_options).get(); + if (!credentials) { + std::cerr << "Failed to fetch credentials: " << credentials.error().message << "\n"; + return false; + } livekit::Room room; ParticipantLogDelegate delegate; room.setDelegate(&delegate); - if (!room.connect(*token_source, request_options, livekit::RoomOptions())) { + if (!room.connect(credentials.value().server_url, credentials.value().participant_token, livekit::RoomOptions())) { std::cerr << "Failed to connect to room\n"; return false; } From a045d9b35120d4c8b0af6e466d43ecdeaa47533e Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 1 Jul 2026 12:55:47 -0600 Subject: [PATCH 07/12] Doc updates --- CMakeLists.txt | 2 +- README.md | 98 +++++++++++++++++++++----------- token_source/README.md | 54 +++++------------- user_timestamped_video/README.md | 2 +- 4 files changed, 80 insertions(+), 76 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fc9e124..fe800fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) # Make "include(LiveKitSDK)" search in ./cmake list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") -set(LIVEKIT_SDK_VERSION "latest" CACHE STRING "LiveKit C++ SDK version (e.g. 0.2.0 or latest)") +set(LIVEKIT_SDK_VERSION "latest" CACHE STRING "LiveKit C++ SDK version (e.g. 1.3.0 or latest)") set(LIVEKIT_LOCAL_SDK_DIR "" CACHE PATH "Path to a local LiveKit SDK install prefix (skips download)") if(LIVEKIT_LOCAL_SDK_DIR) diff --git a/README.md b/README.md index 88107bd..41151c7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # cpp-example-collection + This repository contains a collection of small, self-contained examples for the [LiveKit C++ SDK](https://github.com/livekit/client-sdk-cpp). @@ -6,73 +7,101 @@ The goal of these examples is to demonstrate common usage patterns of the LiveKit C++ SDK (connecting to a room, publishing tracks, RPC, data streams, etc.) without requiring users to build the SDK from source. - ## How the SDK is provided These examples **automatically download a prebuilt LiveKit C++ SDK release** from GitHub at CMake configure time. -This is handled by the cmake helper: [LiveKitSDK.cmake ](https://github.com/livekit-examples/cpp-example-collection/cmake/LiveKitSDK.cmake) +This is handled by the CMake helper: +[`LiveKitSDK.cmake`](https://github.com/livekit-examples/cpp-example-collection/blob/main/cmake/LiveKitSDK.cmake). -## Selecting a LiveKit SDK version +By default, CMake resolves `LIVEKIT_SDK_VERSION=latest` to the newest GitHub +release and downloads the matching archive for your platform. The extracted SDK +lands under **build/_deps/livekit-sdk/**. -By default, the examples download the **latest released** LiveKit C++ SDK. +## Building the examples -You can pin a specific SDK version using the `LIVEKIT_SDK_VERSION` CMake option. +Run these from the repository root. All examples — including +[`token_source/`](token_source/) — build together in one pass. -### Examples +### macOS / Linux -Use the latest release: ```bash cmake -S . -B build -# Or use a specific version (recommended for reproducibility): -cmake -S . -B build -DLIVEKIT_SDK_VERSION=0.2.0 +cmake --build build ``` -Reconfigure to change versions: -```bash -rm -rf build -cmake -S . -B build -DLIVEKIT_SDK_VERSION=0.3.1 +### Windows (Visual Studio generator) + +```powershell +cmake -S . -B build +cmake --build build --config Release ``` -Build against a local SDK: +The token source examples require LiveKit C++ SDK **v1.3.0** or newer. If +configure succeeds but those targets fail to compile, pin the SDK version +explicitly: + ```bash rm -rf build -# install the SDK into $HOME/livekit-sdk-install (or any other directory) -cmake --install --prefix $HOME/livekit-sdk-install - -# build the examples against the local SDK -cmake -S . -B build -DLIVEKIT_LOCAL_SDK_DIR=$HOME/livekit-sdk-install +cmake -S . -B build -DLIVEKIT_SDK_VERSION=1.3.0 +cmake --build build ``` +### Selecting an SDK version + +Pin a specific release with `-DLIVEKIT_SDK_VERSION`: -### Building the examples -#### macOS / Linux ```bash -cmake -S . -B build # add -DLIVEKIT_SDK_VERSION=0.2.0 if using a specific version -cmake --build build +cmake -S . -B build -DLIVEKIT_SDK_VERSION=1.3.0 ``` -#### Windows (Visual Studio generator) -```powershell -cmake -S . -B build # add -DLIVEKIT_SDK_VERSION=0.2.0 if using a specific version -cmake --build build --config Release +When changing versions, remove the build directory first so CMake re-downloads +the SDK: + +```bash +rm -rf build +cmake -S . -B build -DLIVEKIT_SDK_VERSION=1.3.0 ``` -The Livekit Release SDK is downloaded into **build/_deps/livekit-sdk/** +### Build against a local SDK -### Running the examples +Use this when validating an unreleased `client-sdk-cpp` commit, or when a +release tag exists but its prebuilt archives are not yet published: -After building, example binaries are located under: ```bash -build// +git clone --recurse-submodules https://github.com/livekit/client-sdk-cpp.git +cd client-sdk-cpp +./build.sh release --bundle --prefix "$HOME/livekit-sdk-install" + +cd /path/to/cpp-example-collection +rm -rf build +cmake -S . -B build -DLIVEKIT_LOCAL_SDK_DIR="$HOME/livekit-sdk-install" +cmake --build build ``` +### Troubleshooting configure + +- **Start from a clean build directory** after a failed configure or download: + `rm -rf build` +- **GitHub API rate limits** when resolving `latest`: pin + `-DLIVEKIT_SDK_VERSION=1.3.0`, or export `GITHUB_TOKEN` and re-run configure. +- **404 on SDK download**: the release may not have platform archives yet. Pin + an older release that does, or use `-DLIVEKIT_LOCAL_SDK_DIR` as above. + +## Running the examples + +After building, example binaries are located under `build//`. + For example: + ```bash ./build/basic_room/basic_room --url --token ``` +See [`token_source/README.md`](token_source/README.md) for the token-source +examples. + ### PlatformAudio The `platform_audio` examples show microphone capture and speaker playout using @@ -86,8 +115,9 @@ WebRTC's platform Audio Device Module: ### Supported platforms Prebuilt SDKs are downloaded automatically for: -* Windows: x64 -* macOS: x64, arm64 (Apple Silicon) -* Linux: x64 + +- Windows: x64 +- macOS: x64, arm64 (Apple Silicon) +- Linux: x64 If no matching SDK is available for your platform, CMake configuration will fail with a clear error. diff --git a/token_source/README.md b/token_source/README.md index b257177..e32bcda 100644 --- a/token_source/README.md +++ b/token_source/README.md @@ -1,4 +1,4 @@ -# Token sources +# Token Source Examples These examples show the different ways to obtain the credentials (**WebSocket URL** + **participant JWT**) the SDK needs to join a room. Each @@ -12,28 +12,27 @@ disconnects. > not called again unless you connect again. See the SDK's > [authentication docs](https://github.com/livekit/client-sdk-cpp/blob/main/docs/authentication.md). -## The types +## Types -| Example | Type | When to use | -|---|---|---| -| `token_source_literal.cpp` | `LiteralTokenSource` | You already have a URL + JWT (minted out of band, e.g. `lk token create`). The SDK consumes them as-is. | -| `token_source_endpoint.cpp` | `EndpointTokenSource` | Recommended for production. The SDK POSTs request options to your backend token endpoint, which returns the URL + a fresh JWT. API keys stay server-side. | -| `token_source_sandbox.cpp` | `SandboxTokenSource` | Local development only. Uses LiveKit Cloud's sandbox token server. Not for production. | -| `token_source_custom.cpp` | `CustomTokenSource` | You have an internal auth/token system. Plug in your own async callback that returns credentials. | -| `token_source_caching.cpp` | `CachingTokenSource` | A decorator that adds JWT-aware caching around any configurable source (endpoint/sandbox/custom) to cut down on fetch calls. | +| Type | Example | When to use | +| --- | --- | --- | +| `LiteralTokenSource` | `token_source_literal.cpp` | You already have a URL + JWT (minted out of band, e.g. `lk token create`). The SDK consumes them as-is. | +| `EndpointTokenSource` | `token_source_endpoint.cpp` | Recommended for production. The SDK POSTs request options to your backend token endpoint, which returns the URL + a fresh JWT. API keys stay server-side. | +| `SandboxTokenSource` | `token_source_sandbox.cpp` | Local development only. Uses LiveKit Cloud's sandbox token server. Not for production. | +| `CustomTokenSource` | `token_source_custom.cpp` | You have an internal auth/token system. Plug in your own async callback that returns credentials. | +| `CachingTokenSource` | `token_source_caching.cpp` | A decorator that adds JWT-aware caching around any configurable source (endpoint/sandbox/custom) to cut down on fetch calls. | `LiteralTokenSource` is *fixed* (no per-call options); the others are *configurable* and accept [`TokenRequestOptions`](https://github.com/livekit/client-sdk-cpp/blob/main/include/livekit/token_source.h) (room name, participant identity, agent dispatch, ...). -## Configuration +## Configuring the Examples -All inputs come from environment variables, so no secrets or sandbox IDs are -committed to source. +All inputs come from environment variables, so no secrets or sandbox IDs are committed to source. | Variable | Used by | Notes | -|---|---|---| +| --- | --- | --- | | `LIVEKIT_URL` | literal, custom | WebSocket URL, e.g. `ws://localhost:7880`. | | `LIVEKIT_TOKEN` | literal, custom | Participant JWT. | | `LIVEKIT_TOKEN_ENDPOINT` | endpoint, caching | Token endpoint URL. Default `http://127.0.0.1:3000/createToken`. | @@ -43,33 +42,8 @@ committed to source. | `LIVEKIT_AGENT_NAME` | sandbox | Optional agent to dispatch into the room. | | `LIVEKIT_AGENT_METADATA` | sandbox | Optional metadata for the dispatched agent. | -## Building - -The token source API is newer than the latest published SDK release, so build -these examples against a **local SDK build** of the -[`feature/token_source_api`](https://github.com/livekit/client-sdk-cpp/tree/feature/token_source_api) -branch rather than a downloaded release. The commands below pin the branch to -commit [`bbf6a41`](https://github.com/livekit/client-sdk-cpp/commit/bbf6a41fae42607ee19ff44ccddac786767b34e3) -so the examples build against a known-good API; drop the `git checkout` of the -hash to track the branch tip instead. - -```bash -# 1. Build and install the SDK from the feature branch. -git clone https://github.com/livekit/client-sdk-cpp.git -cd client-sdk-cpp -git checkout bbf6a41fae42607ee19ff44ccddac786767b34e3 -git submodule update --init --recursive -./build.sh release --bundle --prefix "$HOME/livekit-sdk-install" - -# 2. Configure the examples against that local install. -cd /path/to/cpp-example-collection -cmake -S . -B build -DLIVEKIT_LOCAL_SDK_DIR="$HOME/livekit-sdk-install" -cmake --build build -``` - -Once the token source API ships in a release, you can drop -`-DLIVEKIT_LOCAL_SDK_DIR` and use the normal download flow (optionally pinning -`-DLIVEKIT_SDK_VERSION`). +These examples require LiveKit C++ SDK **v1.3.0** or newer. Build them with the +rest of the repo — see the root [README](../README.md#building-the-examples). ## Running diff --git a/user_timestamped_video/README.md b/user_timestamped_video/README.md index ea9fd27..b8d65ce 100644 --- a/user_timestamped_video/README.md +++ b/user_timestamped_video/README.md @@ -23,7 +23,7 @@ Requirements: `VideoFrameMetadata` and `setOnVideoFrameEventCallback`, which are not available in older SDK releases. - To pin the SDK version when configuring the examples, pass - `-DLIVEKIT_SDK_VERSION=0.3.4` to CMake. + `-DLIVEKIT_SDK_VERSION=1.3.0` to CMake. Flags: From 6ea57b0d64a918d36502337dbfaf3a9c288f1700 Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 1 Jul 2026 14:04:54 -0600 Subject: [PATCH 08/12] More README updates --- token_source/README.md | 62 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/token_source/README.md b/token_source/README.md index e32bcda..975d07a 100644 --- a/token_source/README.md +++ b/token_source/README.md @@ -45,7 +45,52 @@ All inputs come from environment variables, so no secrets or sandbox IDs are com These examples require LiveKit C++ SDK **v1.3.0** or newer. Build them with the rest of the repo — see the root [README](../README.md#building-the-examples). -## Running +## Running the Examples + +### Prerequisites + +#### LiveKit Server + +Start a development server via: + +```bash +livekit-server --dev +``` + +#### Token Sources + +For literal and custom, generate a development token with the +[LiveKit CLI](https://docs.livekit.io/home/cli/cli-setup/) (a dev server started +with `livekit-server --dev` uses `devkey` / `secret`): + +```bash +export LIVEKIT_TOKEN=$(lk token create \ + --api-key devkey --api-secret secret \ + -i my-participant --join --room my-room \ + --valid-for 24h --token-only) +``` + +For the endpoint and caching examples, run a local token server such as +[token-server-node](https://github.com/livekit-examples/token-server-node). This can be run via: + +```bash +cd /token-server-node +LIVEKIT_URL=ws://localhost:7880 LIVEKIT_API_KEY=devkey LIVEKIT_API_SECRET=secret PORT=3000 pnpm start +``` + +For sandbox, create a **Token Server** sandbox in [LiveKit Cloud](https://cloud.livekit.io) +(Sandbox → create from the token-server template), then copy the sandbox ID +(`token-server-xxxxxx`). See the +[sandbox token server docs](https://docs.livekit.io/frontends/build/authentication/sandbox-token-server/) +for setup details. Do not use this in production. + +```bash +export LIVEKIT_SANDBOX_ID=token-server-xxxxxx +# optional: dispatch a registered agent into the room +# export LIVEKIT_AGENT_NAME=my-agent +``` + +### Executing The built binaries live under `build/token_source/`: @@ -54,12 +99,16 @@ The built binaries live under `build/token_source/`: export LIVEKIT_URL=ws://localhost:7880 export LIVEKIT_TOKEN= ./build/token_source/token_source_literal +``` +```bash # Endpoint / caching: point at your token endpoint export LIVEKIT_TOKEN_ENDPOINT=http://127.0.0.1:3000/createToken ./build/token_source/token_source_endpoint ./build/token_source/token_source_caching +``` +```bash # Sandbox: development-only, ID from the environment export LIVEKIT_SANDBOX_ID= ./build/token_source/token_source_sandbox @@ -69,14 +118,3 @@ export LIVEKIT_URL=ws://localhost:7880 export LIVEKIT_TOKEN= ./build/token_source/token_source_custom ``` - -Generate a development token with the -[LiveKit CLI](https://docs.livekit.io/home/cli/cli-setup/) (a dev server started -with `livekit-server --dev` uses `devkey` / `secret`): - -```bash -export LIVEKIT_TOKEN=$(lk token create \ - --api-key devkey --api-secret secret \ - -i my-participant --join --room my-room \ - --valid-for 24h --token-only) -``` From d84d1bd84f0f21ba9089611ca938a703757d5a1b Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 1 Jul 2026 14:33:22 -0600 Subject: [PATCH 09/12] Additional readme --- token_source/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/token_source/README.md b/token_source/README.md index 975d07a..3db7bbd 100644 --- a/token_source/README.md +++ b/token_source/README.md @@ -86,8 +86,9 @@ for setup details. Do not use this in production. ```bash export LIVEKIT_SANDBOX_ID=token-server-xxxxxx -# optional: dispatch a registered agent into the room +# optional: dispatch a registered agent into the room with metadata # export LIVEKIT_AGENT_NAME=my-agent +# export LIVEKIT_AGENT_METADATA='{"greeting": "hello from cpp"}' ``` ### Executing @@ -111,8 +112,12 @@ export LIVEKIT_TOKEN_ENDPOINT=http://127.0.0.1:3000/createToken ```bash # Sandbox: development-only, ID from the environment export LIVEKIT_SANDBOX_ID= +export LIVEKIT_AGENT_NAME= # optional +export LIVEKIT_AGENT_METADATA= # optional ./build/token_source/token_source_sandbox +``` +```bash # Custom: callback returns credentials (this example reads the env) export LIVEKIT_URL=ws://localhost:7880 export LIVEKIT_TOKEN= From 1a4f31f92b9fbc509d3288ccf188d5c9e67c704b Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 1 Jul 2026 20:46:40 -0600 Subject: [PATCH 10/12] Scale back README updates --- README.md | 94 ++++++++++---------------------- user_timestamped_video/README.md | 2 +- 2 files changed, 31 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 41151c7..4b1252c 100644 --- a/README.md +++ b/README.md @@ -12,112 +12,78 @@ etc.) without requiring users to build the SDK from source. These examples **automatically download a prebuilt LiveKit C++ SDK release** from GitHub at CMake configure time. -This is handled by the CMake helper: -[`LiveKitSDK.cmake`](https://github.com/livekit-examples/cpp-example-collection/blob/main/cmake/LiveKitSDK.cmake). +This is handled by the CMake helper: [`LiveKitSDK.cmake`](https://github.com/livekit-examples/cpp-example-collection/blob/main/cmake/LiveKitSDK.cmake). -By default, CMake resolves `LIVEKIT_SDK_VERSION=latest` to the newest GitHub -release and downloads the matching archive for your platform. The extracted SDK -lands under **build/_deps/livekit-sdk/**. +## Selecting a LiveKit SDK version -## Building the examples +By default, the examples download the **latest released** LiveKit C++ SDK. -Run these from the repository root. All examples — including -[`token_source/`](token_source/) — build together in one pass. +You can pin a specific SDK version using the `LIVEKIT_SDK_VERSION` CMake option. -### macOS / Linux +### Examples -```bash -cmake -S . -B build -cmake --build build -``` +Use the latest release: -### Windows (Visual Studio generator) - -```powershell +```bash cmake -S . -B build -cmake --build build --config Release ``` -The token source examples require LiveKit C++ SDK **v1.3.0** or newer. If -configure succeeds but those targets fail to compile, pin the SDK version -explicitly: +Use a specific version: ```bash -rm -rf build cmake -S . -B build -DLIVEKIT_SDK_VERSION=1.3.0 -cmake --build build ``` -### Selecting an SDK version - -Pin a specific release with `-DLIVEKIT_SDK_VERSION`: +Reconfigure to change versions: ```bash +rm -rf build cmake -S . -B build -DLIVEKIT_SDK_VERSION=1.3.0 ``` -When changing versions, remove the build directory first so CMake re-downloads -the SDK: +Build against a local SDK: ```bash rm -rf build -cmake -S . -B build -DLIVEKIT_SDK_VERSION=1.3.0 +# install the SDK into $HOME/livekit-sdk-install (or any other directory) +cmake --install --prefix "$HOME/livekit-sdk-install" + +# build the examples against the local SDK +cmake -S . -B build -DLIVEKIT_LOCAL_SDK_DIR="$HOME/livekit-sdk-install" ``` -### Build against a local SDK +### Building the examples -Use this when validating an unreleased `client-sdk-cpp` commit, or when a -release tag exists but its prebuilt archives are not yet published: +#### macOS / Linux ```bash -git clone --recurse-submodules https://github.com/livekit/client-sdk-cpp.git -cd client-sdk-cpp -./build.sh release --bundle --prefix "$HOME/livekit-sdk-install" - -cd /path/to/cpp-example-collection -rm -rf build -cmake -S . -B build -DLIVEKIT_LOCAL_SDK_DIR="$HOME/livekit-sdk-install" +cmake -S . -B build cmake --build build ``` -### Troubleshooting configure - -- **Start from a clean build directory** after a failed configure or download: - `rm -rf build` -- **GitHub API rate limits** when resolving `latest`: pin - `-DLIVEKIT_SDK_VERSION=1.3.0`, or export `GITHUB_TOKEN` and re-run configure. -- **404 on SDK download**: the release may not have platform archives yet. Pin - an older release that does, or use `-DLIVEKIT_LOCAL_SDK_DIR` as above. - -## Running the examples +#### Windows (Visual Studio generator) -After building, example binaries are located under `build//`. - -For example: - -```bash -./build/basic_room/basic_room --url --token +```powershell +cmake -S . -B build +cmake --build build --config Release ``` -See [`token_source/README.md`](token_source/README.md) for the token-source -examples. +The LiveKit Release SDK is downloaded into **build/_deps/livekit-sdk/** -### PlatformAudio +### Running the examples -The `platform_audio` examples show microphone capture and speaker playout using -WebRTC's platform Audio Device Module: +After building, example binaries are located under: ```bash -./build/platform_audio/player/PlatformAudioPlayer -./build/platform_audio/sender/PlatformAudioSender +build// ``` ### Supported platforms Prebuilt SDKs are downloaded automatically for: -- Windows: x64 -- macOS: x64, arm64 (Apple Silicon) -- Linux: x64 +* Windows: x64 +* macOS: x64, arm64 (Apple Silicon) +* Linux: x64 If no matching SDK is available for your platform, CMake configuration will fail with a clear error. diff --git a/user_timestamped_video/README.md b/user_timestamped_video/README.md index b8d65ce..ea9fd27 100644 --- a/user_timestamped_video/README.md +++ b/user_timestamped_video/README.md @@ -23,7 +23,7 @@ Requirements: `VideoFrameMetadata` and `setOnVideoFrameEventCallback`, which are not available in older SDK releases. - To pin the SDK version when configuring the examples, pass - `-DLIVEKIT_SDK_VERSION=1.3.0` to CMake. + `-DLIVEKIT_SDK_VERSION=0.3.4` to CMake. Flags: From 9076bc9a5d5ac72e4070f930a9323d55e9a400dc Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 2 Jul 2026 09:45:15 -0600 Subject: [PATCH 11/12] Update README --- README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 42ffe04..245cc1a 100644 --- a/README.md +++ b/README.md @@ -7,23 +7,23 @@ The goal of these examples is to demonstrate common usage patterns of the LiveKit C++ SDK (connecting to a room, publishing tracks, RPC, data streams, etc.) without requiring users to build the SDK from source. -## Example Directories - -| Example sub-folder | Folder path | Description | -| --- | --- | --- | -| `basic_room` | `basic_room/` | Connects to a room and publishes synthetic audio and video tracks. | -| `token_source` | `token_source/` | Shows ways to supply connection credentials through LiveKit token sources. | -| `simple_room` | `simple_room/` | Connects to a room, publishes local media, and subscribes to remote media. | -| `simple_rpc` | `simple_rpc/` | Demonstrates LiveKit RPC calls between participants. | -| `simple_data_stream` | `simple_data_stream/` | Sends and receives text and byte streams over LiveKit data streams. | -| `logging_levels/basic_usage` | `logging_levels/basic_usage/` | Demonstrates SDK log-level filtering and log callbacks. | -| `logging_levels/custom_sinks` | `logging_levels/custom_sinks/` | Shows custom SDK log sinks such as file, JSON, and ROS-style output. | -| `hello_livekit/sender` | `hello_livekit/sender/` | Publishes synthetic video and data for a paired receiver example. | -| `hello_livekit/receiver` | `hello_livekit/receiver/` | Subscribes to the sender example's video and data tracks. | -| `platform_audio` | `platform_audio/` | Demonstrates microphone capture and speaker playout with platform audio devices. | -| `ping_pong/ping` | `ping_pong/ping/` | Sends ping messages over a data track and records response latency. | -| `ping_pong/pong` | `ping_pong/pong/` | Listens for ping messages and publishes matching pong responses. | -| `frame_metadata` | `frame_metadata/` | Publishes and consumes video frames with frame metadata such as IDs, timestamps, and user data. | +## Examples + +| Example | Description | +| --- | --- | +| [`basic_room`](basic_room/) | Connects to a room and publishes synthetic audio and video tracks. | +| [`frame_metadata`](frame_metadata/) | Publishes and consumes video frames with frame metadata such as IDs, timestamps, and user data. | +| [`hello_livekit/receiver`](hello_livekit/receiver/) | Subscribes to the sender example's video and data tracks. | +| [`hello_livekit/sender`](hello_livekit/sender/) | Publishes synthetic video and data for a paired receiver example. | +| [`logging_levels/basic_usage`](logging_levels/basic_usage/) | Demonstrates SDK log-level filtering and log callbacks. | +| [`logging_levels/custom_sinks`](logging_levels/custom_sinks/) | Shows custom SDK log sinks such as file, JSON, and ROS-style output. | +| [`ping_pong/ping`](ping_pong/ping/) | Sends ping messages over a data track and records response latency. | +| [`ping_pong/pong`](ping_pong/pong/) | Listens for ping messages and publishes matching pong responses. | +| [`platform_audio`](platform_audio/) | Demonstrates microphone capture and speaker playout with platform audio devices. | +| [`simple_data_stream`](simple_data_stream/) | Sends and receives text and byte streams over LiveKit data streams. | +| [`simple_room`](simple_room/) | Connects to a room, publishes local media, and subscribes to remote media. | +| [`simple_rpc`](simple_rpc/) | Demonstrates LiveKit RPC calls between participants. | +| [`token_source`](token_source/) | Shows ways to supply connection credentials through LiveKit token sources. | ## How the SDK is provided From f5c5175c87dcf43170e82f33a09aadcff2ee1d51 Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 2 Jul 2026 13:36:54 -0600 Subject: [PATCH 12/12] PR feedback --- token_source/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/token_source/README.md b/token_source/README.md index 3db7bbd..932868a 100644 --- a/token_source/README.md +++ b/token_source/README.md @@ -18,7 +18,7 @@ disconnects. | --- | --- | --- | | `LiteralTokenSource` | `token_source_literal.cpp` | You already have a URL + JWT (minted out of band, e.g. `lk token create`). The SDK consumes them as-is. | | `EndpointTokenSource` | `token_source_endpoint.cpp` | Recommended for production. The SDK POSTs request options to your backend token endpoint, which returns the URL + a fresh JWT. API keys stay server-side. | -| `SandboxTokenSource` | `token_source_sandbox.cpp` | Local development only. Uses LiveKit Cloud's sandbox token server. Not for production. | +| `SandboxTokenSource` | `token_source_sandbox.cpp` | Local development only. Uses LiveKit Cloud's sandbox token server. **Not for production.** | | `CustomTokenSource` | `token_source_custom.cpp` | You have an internal auth/token system. Plug in your own async callback that returns credentials. | | `CachingTokenSource` | `token_source_caching.cpp` | A decorator that adds JWT-aware caching around any configurable source (endpoint/sandbox/custom) to cut down on fetch calls. |