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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
50 changes: 40 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@
# 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).

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.

## Examples

| Example | Description |
Comment thread
alan-george-lk marked this conversation as resolved.
| --- | --- |
| [`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

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

Expand All @@ -23,59 +41,71 @@ You can pin a specific SDK version using the `LIVEKIT_SDK_VERSION` CMake option.
### Examples

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
```

Use a specific version:

```bash
cmake -S . -B build -DLIVEKIT_SDK_VERSION=1.3.0
```

Reconfigure to change versions:

```bash
rm -rf build
cmake -S . -B build -DLIVEKIT_SDK_VERSION=0.3.1
cmake -S . -B build -DLIVEKIT_SDK_VERSION=1.3.0
```

Build against a local SDK:

```bash
rm -rf build
# install the SDK into $HOME/livekit-sdk-install (or any other directory)
cmake --install <sdk-build-dir> --prefix $HOME/livekit-sdk-install
cmake --install <sdk-build-dir> --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_LOCAL_SDK_DIR="$HOME/livekit-sdk-install"
```


### Building the examples

#### macOS / Linux

```bash
cmake -S . -B build # add -DLIVEKIT_SDK_VERSION=0.2.0 if using a specific version
cmake -S . -B build
cmake --build build
```

#### Windows (Visual Studio generator)

```powershell
cmake -S . -B build # add -DLIVEKIT_SDK_VERSION=0.2.0 if using a specific version
cmake -S . -B build
cmake --build build --config Release
```

The Livekit Release SDK is downloaded into **build/_deps/livekit-sdk/**
The LiveKit Release SDK is downloaded into **build/_deps/livekit-sdk/**

### Running the examples

After building, example binaries are located under:

```bash
build/<example-name>/
```

For example:

```bash
./build/basic_room/basic_room --url <ws-url> --token <token>
```

### Supported platforms

Prebuilt SDKs are downloaded automatically for:

* Windows: x64
* macOS: x64, arm64 (Apple Silicon)
* Linux: x64
Expand Down
45 changes: 45 additions & 0 deletions token_source/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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 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 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)
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)
125 changes: 125 additions & 0 deletions token_source/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# 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
Comment thread
alan-george-lk marked this conversation as resolved.
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).

## Types

| 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, ...).

## Configuring the Examples

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. |

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 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 <path-to>/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 with metadata
# export LIVEKIT_AGENT_NAME=my-agent
# export LIVEKIT_AGENT_METADATA='{"greeting": "hello from cpp"}'
```

### Executing

The built binaries live under `build/token_source/`:

```bash
# Literal: bring your own URL + token
export LIVEKIT_URL=ws://localhost:7880
export LIVEKIT_TOKEN=<participant-jwt>
./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=<your-sandbox-id>
export LIVEKIT_AGENT_NAME=<your-agent-name> # optional
export LIVEKIT_AGENT_METADATA=<your-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=<participant-jwt>
./build/token_source/token_source_custom
```
83 changes: 83 additions & 0 deletions token_source/token_source_caching.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* 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 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
// (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 <livekit/livekit.h>
#include <livekit/token_source.h>

#include <iostream>
#include <string>
#include <utility>

#include "token_source_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::create takes
// ownership of the inner source via unique_ptr.
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";
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(credentials.value().server_url, credentials.value().participant_token, 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;
}
Loading
Loading