diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md
index 5de560a3..eb223af9 100644
--- a/DEPENDENCIES.md
+++ b/DEPENDENCIES.md
@@ -254,6 +254,6 @@ To verify you have all dependencies linked correctly:
## Need Help?
-- See `README_BUILD.md` for complete build instructions
+- See [`docs/building.md`](docs/building.md) for complete build instructions
- Check `CMakeLists.txt` lines 287-381 for the exact CMake configuration
- Open an issue at: https://github.com/livekit/client-sdk-cpp/issues
diff --git a/README.md b/README.md
index 665f0042..b1926e9e 100644
--- a/README.md
+++ b/README.md
@@ -8,624 +8,240 @@
-# C++ SDK for LiveKit
+# C++ client SDK for LiveKit
Use this SDK to add realtime video, audio and data features to your C++ app. By connecting to LiveKit Cloud or a self-hosted server, you can quickly build applications such as multi-modal AI, live streaming, or video calls with just a few lines of code.
-## Requirements
-- **CMake** ≥ 3.20
-- **Rust / Cargo** (latest stable toolchain)
-- **Git LFS** (required for examples)
- Some example data files (e.g., audio assets) are stored using Git LFS.
- You must install Git LFS before cloning or pulling the repo if you want to run the examples.
-- **livekit-cli** install livekit-cli by following the [official LiveKit docs](https://docs.livekit.io/intro/basics/cli/start/)
-- **livekit-server** install livekit-server by following the [official LiveKit docs](https://docs.livekit.io/transport/self-hosting/local/)
+[](https://github.com/livekit/client-sdk-cpp/actions/workflows/builds.yml)
+[](https://github.com/livekit/client-sdk-cpp/actions/workflows/tests.yml)
-**Platform-Specific Requirements:**
+## Docs
-### For Building the SDK:
-- **Windows:** Visual Studio 2019+, vcpkg
-- **Linux:** `sudo apt install libprotobuf-dev libssl-dev` (protobuf 3.x)
-- **macOS:** `brew install protobuf` (protobuf 3.x)
+- LiveKit docs: [docs.livekit.io](https://docs.livekit.io)
+- [SDK reference](https://docs.livekit.io/reference/client-sdk-cpp/)
+- [Repository docs](./docs/README.md)
-### For Using the Pre-built SDK:
-- **Windows:** All dependencies included (DLLs bundled) - ready to use
-- **Linux:** Requires `libprotobuf` and `libssl-dev`; deploy `liblivekit_ffi.so` with your executable
-- **macOS:** Requires `protobuf`; deploy `liblivekit_ffi.dylib` with your executable
+## Using Real-time SDK
-> **Note**: If the SDK was built with Protobuf 6.0+, you also need `libabsl-dev` (Linux) or `abseil` (macOS).
+[CMake](https://cmake.org/) (≥ 3.20) is used for building the SDK itself and for consuming it as a library. The
+[**cpp-example-collection**](https://github.com/livekit-examples/cpp-example-collection) contains a reference [LiveKitSDK.cmake](https://github.com/livekit-examples/cpp-example-collection/blob/main/cmake/LiveKitSDK.cmake)
+which downloads the latest stable release at CMake configure time. See [docs/building.md](docs/building.md) for additional documentation on integrating LiveKit into your project.
-## Clone the Repository
-
-Make sure to initialize the Rust submodule (`client-sdk-rust`):
+To build the SDK from source:
```bash
-# Option 1: Clone with submodules in one step
git clone --recurse-submodules https://github.com/livekit/client-sdk-cpp.git
-
-# Option 2: Clone first, then initialize submodules
-git clone https://github.com/livekit/client-sdk-cpp.git
cd client-sdk-cpp
-git submodule update --init --recursive
-
-# Note: If running tests, pull Git LFS to bring in test data:
-git lfs pull
+./build.sh release # or .\build.cmd release on Windows
```
-## Building
+Building requires a stable Rust toolchain and platform-specific build
+deps (`protobuf`, `abseil`, `openssl` on Linux). See [docs/building.md](docs/building.md)
+for full prerequisites table, Docker recipe, CMake presets, and troubleshooting.
-### Quick Build (Using Build Scripts)
+### Hello, LiveKit
-**Linux/macOS:**
-```bash
-./build.sh clean # Clean CMake build artifacts + local-install
-./build.sh clean-all # Deep clean (C++ + Rust + local-install + generated files)
-./build.sh debug # Build Debug version
-./build.sh release # Build Release version
-./build.sh debug-examples # Build Debug with examples
-./build.sh release-examples # Build Release with examples
-./build.sh debug-tests # Build Debug with tests
-./build.sh debug-all # Build Debug with tests + examples
-./build.sh release-tests # Build Release with tests
-./build.sh release-all # Build Release with tests + examples
-```
-**Windows**
-Using build scripts:
-```powershell
-.\build.cmd clean # Clean CMake build artifacts + local-install
-.\build.cmd clean-all # Deep clean (C++ + Rust + local-install + generated files)
-.\build.cmd debug # Build Debug version
-.\build.cmd release # Build Release version
-.\build.cmd debug-examples # Build Debug with examples
-.\build.cmd release-examples # Build Release with examples
-.\build.cmd debug-tests # Build Debug with tests
-.\build.cmd debug-all # Build Debug with tests + examples
-.\build.cmd release-tests # Build Release with tests
-.\build.cmd release-all # Build Release with tests + examples
-```
+Here is a minimal example of the `main` function for sending and receiving video and data track frames. The [sender](https://github.com/livekit-examples/cpp-example-collection/blob/main/hello_livekit/sender/main.cpp) plays the role of a robot or camera, publishing video and data track frames every 100 ms; the [receiver](https://github.com/livekit-examples/cpp-example-collection/blob/main/hello_livekit/receiver/main.cpp) stands in for the cloud service or operator UI, logging every frame it sees. In a production system the synthetic video would be a robot's perception output and the data track would carry sensor readings or operator commands, but the connection and publishing pattern is the same. Full source for both processes lives in the [cpp-example-collection](https://github.com/livekit-examples/cpp-example-collection/tree/main/hello_livekit) repo.
-The build scripts pass an explicit job count to `cmake --build --parallel`. Set
-`CMAKE_BUILD_PARALLEL_LEVEL` to override the default detected logical CPU count.
-
-### Windows build using cmake/vcpkg
-```bash
-cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE="$PWD/vcpkg/scripts/buildsystems/vcpkg.cmake" # Generate Makefiles in build folder
-# Build (Release or Debug)
-cmake --build build --config Release
-# or:
-cmake --build build --config Debug
-# Clean CMake build artifacts
-Remove-Item -Recurse -Force build
-```
-Note (Windows), This assumes vcpkg is checked out in the repo root at `.\vcpkg\`.
-You must install protobuf via vcpkg (so CMake can find ProtobufConfig.cmake and protoc), for example:
-```bash
-.\vcpkg\vcpkg install protobuf:x64-windows
-```
+The sender creates tracks and publishes data.
-### Advanced Build (Using CMake Presets)
-
-For more control and platform-specific builds, see the detailed instructions in [README_BUILD.md](README_BUILD.md).
-
-**Prerequisites (Windows only):**
-- Set `VCPKG_ROOT` environment variable pointing to your vcpkg installation
-
-```powershell
-# Windows PowerShell
-$env:VCPKG_ROOT = "C:\path\to\vcpkg"
-```
-
-**Prerequisites (Linux/macOS):**
-- Install system dependencies (see above)
-
-**Quick start:**
-```bash
-# Windows
-cmake --preset windows-release
-cmake --build --preset windows-release
-
-# Linux
-cmake --preset linux-release
-cmake --build --preset linux-release
-
-# macOS
-cmake --preset macos-release
-cmake --build --preset macos-release
-```
-
-**For complete build instructions, troubleshooting, and platform-specific notes, see [README_BUILD.md](README_BUILD.md)**
-
-### Building with Docker
-The Docker setup is split into a reusable base image and an SDK image layered on top of it.
- **NOTE:** this has only been tested on Linux
-```bash
-docker build -t livekit-cpp-sdk-base . -f docker/Dockerfile.base
-docker build --build-arg BASE_IMAGE=livekit-cpp-sdk-base -t livekit-cpp-sdk . -f docker/Dockerfile.sdk
-docker run -it --network host livekit-cpp-sdk:latest bash
-```
-
-__NOTE:__ if you are building your own Dockerfile, you will likely need to set the same `ENV` variables as in `docker/Dockerfile.base`, but to the relevant directories:
-```bash
-export CC=$HOME/gcc-14/bin/gcc
-export CXX=$HOME/gcc-14/bin/g++
-export LD_LIBRARY_PATH=$HOME/gcc-14/lib64:$LD_LIBRARY_PATH
-export PATH=$HOME/.cargo/bin:$PATH
-export PATH=$HOME/cmake-3.31/bin:$PATH
-```
-
-## Run Example
-
-### Prerequisites
-
-Ensure one of the `*-examples` build script options was run prior.
-
-### Generate Tokens
-Before running any participant, create JWT tokens with the proper identity and room name, example
-```bash
-lk token create -r test -i your_own_identity --join --valid-for 99999h --dev --room=your_own_room
-```
-
-### SimpleRoom
-
-```bash
-./build-release/cpp-example-collection-build/simple_room/SimpleRoom --url $URL --token
-```
-
-You can also provide the URL and token via environment variables:
-```bash
-export LIVEKIT_URL=ws://localhost:7880
-export LIVEKIT_TOKEN=
-./build-release/cpp-example-collection-build/simple_room/SimpleRoom
-```
-
-**End-to-End Encryption (E2EE)**
-You can enable E2E encryption for the streams via --enable_e2ee and --e2ee_key flags,
-by running the following cmds in two terminals or computers. **Note, jwt_token needs to be different identity**
-```bash
-./build-release/cpp-example-collection-build/simple_room/SimpleRoom --url $URL --token --enable_e2ee --e2ee_key="your_key"
-```
-**Note**, **all participants must use the exact same E2EE configuration and shared key.**
-If the E2EE keys do not match between participants:
-- Media cannot be decrypted
-- Video tracks will appear as a black screen
-- Audio will be silent
-- No explicit error may be shown at the UI level
-
-Press Ctrl-C to exit the example.
-
-### SimpleRpc
-The SimpleRpc example demonstrates how to:
-- Connect multiple participants to the same LiveKit room
-- Register RPC handlers (e.g., arrival, square-root, divide, long-calculation)
-- Send RPC requests from one participant to another
-- Handle success, application errors, unsupported methods, and timeouts
-- Observe round-trip times (RTT) for each RPC call
-
-#### Generate Tokens
-Before running any participant, create JWT tokens with **caller**, **greeter** and **math-genius** identities and room name.
-```bash
-lk token create -r test -i caller --join --valid-for 99999h --dev --room=your_own_room
-lk token create -r test -i greeter --join --valid-for 99999h --dev --room=your_own_room
-lk token create -r test -i math-genius --join --valid-for 99999h --dev --room=your_own_room
-```
-
-#### Start Participants
-Every participant is run as a separate terminal process, note --role needs to match the token identity.
-```bash
-./build-release/cpp-example-collection-build/simple_rpc/SimpleRpc --url $URL --token --role=math-genius
-```
-The caller will automatically:
-- Wait for the greeter and math-genius to join
-- Perform RPC calls
-- Print round-trip times
-- Annotate expected successes or expected failures
-
-### SimpleDataStream
-- The SimpleDataStream example demonstrates how to:
-- Connect multiple participants to the same LiveKit room
-- Register text stream and byte stream handlers by topic (e.g. "chat", "files")
-- Send a text stream (chat message) from one participant to another
-- Send a byte stream (file/image) from one participant to another
-- Attach custom stream metadata (e.g. sent_ms) via stream attributes
-- Measure and print one-way latency on the receiver using sender timestamps
-- Receive streamed chunks and reconstruct the full payload on the receiver
-
-#### Generate Tokens
-Before running any participant, create JWT tokens with caller and greeter identities and your room name.
-```bash
-lk token create -r test -i caller --join --valid-for 99999h --dev --room=your_own_room
-lk token create -r test -i greeter --join --valid-for 99999h --dev --room=your_own_room
-```
-
-#### Start Participants
-Start the receiver first (so it registers stream handlers before messages arrive):
-```bash
-./build-release/cpp-example-collection-build/simple_data_stream/SimpleDataStream --url $URL --token
-```
-On another terminal or computer, start the sender
-```bash
-./build-release/cpp-example-collection-build/simple_data_stream/SimpleDataStream --url $URL --token
-```
-
-**Sender** (e.g. greeter)
-- Waits for the peer, then sends a text stream ("chat") and a file stream ("files") with timestamps and metadata, logging stream IDs and send times.
-
-**Receiver** (e.g. caller)
-- Registers handlers for text and file streams, logs stream events, computes one-way latency, and saves the received file locally.
-
-
-## Logging
-
-The SDK uses [spdlog](https://github.com/gabime/spdlog) internally but does
-**not** expose it in public headers. All log output goes through a thin public
-API in ``.
-
-### Two-tier filtering
-
-| Tier | When | How | Cost |
-|------|------|-----|------|
-| **Compile-time** | CMake configure | `-DLIVEKIT_LOG_LEVEL=WARN` | Zero -- calls below the level are stripped from the binary |
-| **Runtime** | Any time after `initialize()` | `livekit::setLogLevel(LogLevel::Warn)` | Minimal -- a level check before formatting |
-
-#### Compile-time level (`LIVEKIT_LOG_LEVEL`)
-
-Set once when you configure CMake. Calls below this threshold are completely
-removed by the preprocessor -- no format-string evaluation, no function call.
-
-```bash
-# Development (default): keep everything available
-cmake -DLIVEKIT_LOG_LEVEL=TRACE ..
-
-# Release: strip TRACE / DEBUG / INFO
-cmake -DLIVEKIT_LOG_LEVEL=WARN ..
-
-# Production: only ERROR and CRITICAL survive
-cmake -DLIVEKIT_LOG_LEVEL=ERROR ..
-```
-
-Valid values: `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `CRITICAL`, `OFF`.
-
-#### Runtime level (`setLogLevel`)
-
-Among the levels that survived compilation you can still filter at runtime
-without rebuilding:
+**Initialize LiveKit and connect to the room**
```cpp
-#include
+#include "livekit/livekit.h"
-livekit::initialize(); // default level: Info
-livekit::setLogLevel(livekit::LogLevel::Debug); // show more detail
-livekit::setLogLevel(livekit::LogLevel::Warn); // suppress info chatter
-```
+// Get your url and token from env vars, args, etc.
+const std::string url = "wss://hello.livekit.cloud";
+const std::string token = "sender_token";
-### Custom log callback
+// Start the LiveKit SDK before creating rooms or tracks.
+livekit::initialize(livekit::LogLevel::Info);
-Replace the default stderr sink with your own handler. This is the integration
-point for frameworks like ROS2 (`RCLCPP_*` macros), Android logcat, or any
-structured-logging pipeline:
+// Set your room options, here we will use defaults.
+livekit::RoomOptions options;
-```cpp
-#include
-
-livekit::initialize();
-livekit::setLogLevel(livekit::LogLevel::Trace);
-
-livekit::setLogCallback(
- [](livekit::LogLevel level,
- const std::string &logger_name,
- const std::string &message) {
- // Route to your framework, e.g.:
- // RCLCPP_INFO(get_logger(), "[%s] %s", logger_name.c_str(), message.c_str());
- myLogger.log(level, logger_name, message);
- });
-
-// Pass nullptr to restore the default stderr sink:
-livekit::setLogCallback(nullptr);
+// Create the room & connect to the room using a server URL and participant token.
+auto room = std::make_unique();
+if (!room->connect(url, token, options)) {
+ std::cerr << "Failed to connect to LiveKit\n";
+ return 1;
+}
```
-See [`cpp-example-collection/logging_levels/custom_sinks.cpp`](cpp-example-collection/logging_levels/custom_sinks.cpp)
-for three copy-paste-ready patterns: **file logger**, **JSON structured lines**,
-and a **ROS2 bridge** that maps `LogLevel` to `RCLCPP_*` macros.
-
-### Available log levels
-
-| Level | Typical use |
-|-------|-------------|
-| `Trace` | Per-frame / per-packet detail (very noisy) |
-| `Debug` | Diagnostic info useful during development |
-| `Info` | Normal operational messages (connection, track events) |
-| `Warn` | Unexpected but recoverable situations |
-| `Error` | Failures that affect functionality |
-| `Critical` | Unrecoverable errors |
-| `Off` | Suppress all output |
-
----
-
-## Tracing
-
-The SDK includes built-in support for [Chromium tracing](https://www.chromium.org/developers/how-tos/trace-event-profiling-tool/), allowing you to capture detailed performance traces for debugging and optimization.
-
-### Basic Usage
+**Create the VideoSource, which provides frames to the VideoTrack, then create and publish the VideoTrack**
```cpp
-#include
+// Get the local participant to create tracks.
+auto participant = room->localParticipant().lock();
+if (!participant)
+{
+ std::cerr << "Unable to get the local participant!" << std::endl;
+ return 1;
+}
-// Start tracing to a file
-livekit::startTracing("trace.json");
-
-// ... run your application ...
-
-// Stop tracing and flush to file
-livekit::stopTracing();
+// Publish a synthetic camera track named "camera0" backed by a VideoSource.
+auto video_source = std::make_shared(640, 480);
+auto video_track = participant->publishVideoTrack("camera0", video_source, livekit::TrackSource::SOURCE_CAMERA);
+if (!video_track) {
+ std::cerr << "Failed to publish video track\n";
+ return 1;
+}
```
-### Filtering by Category
-
-You can optionally filter which categories to trace:
+**Create and publish the DataTrack**
```cpp
-// Trace only specific categories (supports wildcards)
-livekit::startTracing("trace.json", {"livekit.*", "webrtc.*"});
-```
-
-### Viewing Traces
-
-Open the generated trace file in one of these viewers:
-
-1. **Chrome**: Navigate to `chrome://tracing` and click "Load" to open the trace file
-2. **Perfetto**: Go to https://ui.perfetto.dev and drag-drop your trace file
-
----
-
-## Integration & Stress Tests
-
-The SDK includes integration and stress tests using Google Test (gtest).
-
-### Build Tests
-
-**Linux/macOS:**
-```bash
-./build.sh debug-tests # Build Debug with tests
-./build.sh release-tests # Build Release with tests
-```
-
-**Windows:**
-```powershell
-.\build.cmd debug-tests
-.\build.cmd release-tests
-```
-
-### Run Tests
-
-After building, run tests using ctest or directly:
-
-```bash
-# Run all tests via ctest
-cd build-debug
-ctest --output-on-failure
+// Publish a data track named "app-data" for app messages.
+auto data_track_result = participant->publishDataTrack("app-data");
+if (!data_track_result) {
+ std::cerr << "Failed to publish data track\n";
+ return 1;
+}
+auto data_track = data_track_result.value();
-# Or run test executables directly
-./build-debug/bin/livekit_integration_tests
-./build-debug/bin/livekit_stress_tests
-
-# Run specific test suites
-./build-debug/bin/livekit_integration_tests --gtest_filter="*Rpc*"
-./build-debug/bin/livekit_stress_tests --gtest_filter="*MaxPayloadStress*"
-```
-
-### Test Types
-
-| Executable | Description |
-|------------|-------------|
-| `livekit_integration_tests` | Quick tests (~1-2 minutes) for SDK functionality |
-| `livekit_stress_tests` | Long-running tests (configurable, default 1 hour) |
-
-### Integration & Stress Test Environment Variables
-
-The integration and stress test suites (data tracks, RPC, media multistream,
-etc.) require a LiveKit server and two participant tokens:
-
-```bash
-# Required
-export LIVEKIT_URL="ws://localhost:7880" # or wss://your-server.livekit.cloud
-export LIVEKIT_TOKEN_A=""
-export LIVEKIT_TOKEN_B=""
-
-# Optional (for stress tests)
-export RPC_STRESS_DURATION_SECONDS=3600 # Test duration (default: 1 hour)
-export RPC_STRESS_CALLER_THREADS=4 # Concurrent caller threads (default: 4)
-```
-
-**Generate tokens for the test suites:**
-
-The easiest path is to source the helper script, which will mint both
-participant tokens against a local `livekit-server --dev` and export
-`LIVEKIT_TOKEN_A`, `LIVEKIT_TOKEN_B`, and `LIVEKIT_URL` for the current shell:
-
-```bash
-source .token_helpers/set_data_track_test_tokens.bash
-```
-
-To generate tokens manually instead (e.g. against a non-default server):
-
-```bash
-export LIVEKIT_TOKEN_A="$(lk token create --api-key devkey --api-secret secret -i cpp-test-a \
- --join --valid-for 99999h --room cpp_data_track_test \
- --grant '{"canPublish":true,"canSubscribe":true,"canPublishData":true}' \
- --token-only)"
-export LIVEKIT_TOKEN_B="$(lk token create --api-key devkey --api-secret secret -i cpp-test-b \
- --join --valid-for 99999h --room cpp_data_track_test \
- --grant '{"canPublish":true,"canSubscribe":true,"canPublishData":true}' \
- --token-only)"
+// Release the participant to reduce scope.
+participant.reset();
```
-### Test Coverage
-
-- **SDK Initialization**: Initialize/shutdown lifecycle
-- **Room**: Room creation, options, connection
-- **Audio Frame**: Frame creation, manipulation, edge cases
-- **RPC**: Round-trip calls, max payload (15KB), timeouts, errors, concurrent calls
-- **Stress Tests**: High throughput, bidirectional RPC, memory pressure
-
-## Recommended Setup
-### macOS
-```bash
-brew install cmake protobuf rust
-```
+**Publish video and data track frames every 100ms**
-### Ubuntu / Debian
-```bash
-sudo apt update
-sudo apt install -y cmake protobuf-compiler build-essential
-curl https://sh.rustup.rs -sSf | sh
-```
+```cpp
+int count = 0;
-## Development Tips
-### Update Rust version
-```bash
-cd client-sdk-cpp
-git fetch origin
-git switch -c try-rust-main origin/main
+while (true)
+{
+ // Create a 640x480 RGBA video frame.
+ auto vf = livekit::VideoFrame::create(640, 480, livekit::VideoBufferType::RGBA);
-# Sync submodule URLs and check out what origin/main pins (recursively):
-git submodule sync --recursive
-git submodule update --init --recursive --checkout
+ // Capture the frame. This publishes the frame on VideoTrack camera0.
+ video_source->captureFrame(vf);
-# Now, in case the nested submodule under yuv-sys didn’t materialize, force it explicitly:
-cd ..
-git -C client-sdk-rust/yuv-sys submodule sync --recursive
-git -C client-sdk-rust/yuv-sys submodule update --init --recursive --checkout
+ const std::string message = "hello #" + std::to_string(count);
+ const auto now_microsec =
+ std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count();
-# Sanity check:
-git submodule status --recursive
-```
+ // Create and push stamped DataTrackFrame.
+ const livekit::DataTrackFrame frame{
+ std::vector(message.begin(), message.end()),
+ now_microsec,
+ };
-### If yuv-sys fails to build
-```bash
-cargo clean -p yuv-sys
-cargo build -p yuv-sys -vv
-```
+ // Optionally, capture the result.
+ auto push_result = data_track->tryPush(frame);
+ if (!push_result) {
+ const auto& error = push_result.error();
+ std::cerr << "[warn] Failed to push data frame: code=" << static_cast(error.code)
+ << " message=" << error.message << "\n";
+ }
-### Full clean (Rust + C++ build folders)
-In some cases, you may need to perform a full clean that deletes all build artifacts from both the Rust and C++ folders, plus the local install folder:
-```bash
-./build.sh clean-all
+ ++count;
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+}
```
-## Quality Checks
+The receiver side setup is the same, except now we set callbacks to the relevant tracks.
-This SDK leverages various tools and checks to ensure the highest quality of the code.
+**Initialize LiveKit and Connect to the room**
-### Clang Tools
-
-- `clang-tidy`: static analysis checks to catch common C++ pitfalls. See [.clang-tidy](./.clang-tidy) for the list of current checks (enforced in CI on PR)
-- `clang-format`: code formatting and style consistency. See [.clang-format](./.clang-format) for the list of style configurations (enforced in CI on PR)
-
-> **Note**: For Windows, `clang-tidy` is not currently supported for this project because the Visual Studio CMake generator does not produce the compile_commands.json database that `clang-tidy` requires. Similarly, `clang-format` must be installed and run manually on Windows, referencing the root `.clang-format` file.
-
-To run locally, first install the following:
-
-**macOS:**
-
-```bash
-brew install llvm
-```
-
-This installs `clang-format`, `clang-tidy`, and `run-clang-tidy`. Homebrew may ask you to add `/opt/homebrew/opt/llvm/bin` to your `PATH`.
+```cpp
+#include "livekit/livekit.h"
-**Linux:**
+// Get your url and token from env vars, args, etc.
+const std::string url = "wss://hello.livekit.cloud";
+const std::string token = "receiver_token";
-```bash
-# Ubuntu / Debian:
-sudo apt-get install clang-format clang-tidy clang-tools
-```
+// Start the LiveKit SDK before creating rooms or tracks.
+livekit::initialize(livekit::LogLevel::Info);
-**Pre-commit hook**
+// Set your room options, here we use the defaults.
+livekit::RoomOptions options;
-```bash
-printf '#!/bin/sh\nFILES=$(git diff --cached --name-only --diff-filter=ACMR -- "*.c" "*.cc" "*.cpp" "*.cxx" "*.h" "*.hpp" "*.hxx")\n[ -z "$FILES" ] && exit 0\necho "$FILES" | xargs ./scripts/clang-format.sh --fix\necho "$FILES" | xargs git add\n' > .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
+// Create the room & connect to the room using a server URL and participant token.
+auto room = std::make_unique();
+if (!room->connect(url, token, options)) {
+ std::cerr << "Failed to connect to LiveKit\n";
+ return 1;
+}
```
-To run `clang-tidy`:
-
-1. Generate `compile_commands.json` and the protobuf headers via a release build:
+**Set callbacks for new video and data frames**
-```bash
-./build.sh release
-```
+```cpp
+// The identity of the participant sending video and data frames.
+const std::string sender_identity = "sender_identity";
-1. Run the clang-tidy wrapper, which uses the same file set, regex filters, and `.clang-tidy` config as CI:
+// Set the callback for new video frames from the sender's "camera0" VideoTrack.
+room->setOnVideoFrameCallback(sender_identity, "camera0", [](const livekit::VideoFrame& frame, std::int64_t) {
+ std::cout << "video frame: " << frame.width() << "x" << frame.height() << "\n";
+});
-```bash
-./scripts/clang-tidy.sh
+// Set the callback for new frames on the sender's "app-data" DataTrack.
+room->addOnDataFrameCallback(sender_identity, "app-data",
+ [](const std::vector& payload, std::optional) {
+ const std::string message(payload.begin(), payload.end());
+ std::cout << "data message: " << message << "\n";
+ });
```
-The wrapper forwards extra arguments to `run-clang-tidy`, examples below:
+For end-to-end samples and a fuller set of demos, see the [cpp-example-collection repo](https://github.com/livekit-examples/cpp-example-collection).
-```bash
-./scripts/clang-tidy.sh -j 4 # Change number of cores used
-./scripts/clang-tidy.sh -checks='-*,misc-const-correctness' # Only run certain checks
-./scripts/clang-tidy.sh -fix # Apply fixes automatically
-```
+## Features
-Output is captured to `clang-tidy.log` at the repo root. This is done as a convenience feature, as often times the terminal buffer is not large enough for all the output.
+- Connect to LiveKit rooms (Cloud or self-hosted)
+- Receive remote audio/video tracks
+- Publish local audio/video tracks
+- Data tracks (low-level) and data streams (high-level)
+- RPC between participants
+- End-to-end encryption (E2EE)
+- Hardware-accelerated codecs (via the underlying Rust SDK)
+- Chromium-style tracing for performance debugging
-To run `clang-format`:
+Supported platforms: **Linux** (x64, arm64), **macOS** (12.3+, Apple Silicon
+& Intel), **Windows** (x64).
-```bash
-./scripts/clang-format.sh
-```
+## Logging & tracing
-With no arguments, runs `clang-format` against every relevant file in the repository against defined `.clang-format` rules.
+The SDK uses a thin public logging API in `` with both
+compile-time and runtime filtering. Plug your own sink in (file, JSON,
+syslog, ROS2 `RCLCPP_*`) via `setLogCallback`. See
+[docs/logging.md](docs/logging.md).
-Pass flags or paths as needed, examples below:
+Chromium-format performance traces can be captured with `startTracing` /
+`stopTracing` and viewed in `chrome://tracing` or
+[ui.perfetto.dev](https://ui.perfetto.dev). See [docs/tracing.md](docs/tracing.md).
-```bash
-./scripts/clang-format.sh --fix # Rewrite files in place
-./scripts/clang-format.sh src/room.cpp include/livekit/room.h # Check just these files
-./scripts/clang-format.sh --fix src/room.cpp # Fix just this file
-```
+## Testing
-Output is captured to `clang-format.log` at the repo root.
+Integration and stress test suites live under `src/tests/`. Build them with
+`./build.sh debug-tests`, point `LIVEKIT_URL` + two participant tokens at a
+local `livekit-server --dev`, and run via `ctest` or directly. See
+[docs/testing.md](docs/testing.md).
-### Memory Checks
+## Developer tools
-Run valgrind on various examples or tests to check for memory leaks and other issues.
+`clang-tidy`, `clang-format`, `valgrind`, and Doxygen are all wired up via
+scripts under `scripts/`. Set up the pre-commit auto-formatter
+with:
```bash
-valgrind --leak-check=full ./build-debug/bin/livekit_integration_tests
-valgrind --leak-check=full ./build-debug/bin/livekit_stress_tests
+./scripts/install-pre-commit.sh
```
-## Running locally
+See [docs/tools.md](docs/tools.md).
-1. Install the livekit-server
-https://docs.livekit.io/transport/self-hosting/local/
-
-Start the livekit-server with data tracks enabled:
-```bash
-LIVEKIT_CONFIG="enable_data_tracks: true" livekit-server --dev
-```
+## Deprecation
-```bash
-# generate tokens, do for all participants
-lk token create \
- --api-key devkey \
- --api-secret secret \
- -i robot \
- --join \
- --valid-for 99999h \
- --room robo_room \
- --grant '{"canPublish":true,"canSubscribe":true,"canPublishData":true}'
-```
+| Component | Removed on |
+| ----------------------------------------------------------------- | ---------- |
+| `livekit_bridge` (bridge/ folder) — migrate to the base SDK | 2026-06-01 |
+| `setOn*FrameCallback` with `TrackSource` — use track name instead | 2026-06-01 |
+| Public-header symbols that don't follow `camelBack()` | 2026-06-01 |
-## Deprecation
+## Contributing
-- livekit_bridge (bridge/ folder) is deprecated. Avoid using it. Migrate to the base SDK. This will be removed on 06/01/2026.
-- setOn*FrameCallback with TrackSource is deprecated. Use track name instead. This will be removed on 06/01/2026.
-- All public headers that do not follow `camelBack()` case. This will be removed on 06/01/2026.
+PRs welcome. Issues: .
diff --git a/README_BUILD.md b/README_BUILD.md
deleted file mode 100644
index 0c061bb2..00000000
--- a/README_BUILD.md
+++ /dev/null
@@ -1,338 +0,0 @@
-# Build Guide
-
-## Prerequisites
-
-### Required Tools
-1. **CMake** (>= 3.20)
-2. **Rust and Cargo** - For building the Rust FFI layer
-3. **C++ Compiler**
- - Windows: Visual Studio 2019 or later
- - Linux: GCC 9+ or Clang 10+
- - macOS: Xcode 12+
-
-### Dependency Management
-
-This project uses different dependency management strategies per platform:
-
-| Platform | Package Manager | Dependencies |
-|----------|-----------------|-------------|
-| Windows | vcpkg (bundled) | protobuf, abseil (DLLs included in distribution) |
-| Linux | apt/dnf | `libprotobuf-dev libabsl-dev libssl-dev` |
-| macOS | Homebrew | `protobuf abseil` |
-
-#### Windows: Install vcpkg
-
-```powershell
-git clone https://github.com/microsoft/vcpkg.git
-cd vcpkg
-.\bootstrap-vcpkg.bat
-$env:VCPKG_ROOT = "$(Get-Location)"
-```
-
-#### Linux: Install Dependencies
-
-```bash
-# Ubuntu/Debian
-sudo apt update && sudo apt install -y \
- build-essential cmake ninja-build pkg-config \
- llvm-dev libclang-dev clang \
- libprotobuf-dev protobuf-compiler libabsl-dev \
- libssl-dev libva-dev libdrm-dev libgbm-dev libx11-dev libgl1-mesa-dev
-```
-
-#### macOS: Install Dependencies
-
-```bash
-brew install cmake ninja protobuf abseil
-```
-
-## Quick Start
-
-### Method 1: Using Build Scripts (Recommended)
-
-The project provides `build.cmd` (Windows) and `build.sh` (Linux/macOS) scripts for simplified building.
-
-**Windows:**
-```powershell
-# Set vcpkg root (required for Windows)
-$env:VCPKG_ROOT = "C:\path\to\vcpkg"
-
-# Build Release version
-.\build.cmd release
-
-# Build Release with examples
-.\build.cmd release-examples
-
-# Build Debug version
-.\build.cmd debug
-
-# Build Debug with examples
-.\build.cmd debug-examples
-
-# Clean build artifacts
-.\build.cmd clean
-
-# Full clean (C++ + Rust + generated files)
-.\build.cmd clean-all
-```
-
-**Linux:**
-```bash
-# Install system dependencies first (see Prerequisites above)
-
-# Build Release version
-./build.sh release
-
-# Build Release with examples
-./build.sh release-examples
-
-# Build Debug version
-./build.sh debug
-
-# Build Debug with examples
-./build.sh debug-examples
-
-# Clean build artifacts
-./build.sh clean
-
-# Full clean
-./build.sh clean-all
-```
-
-**macOS:**
-```bash
-# Install Homebrew dependencies first (see Prerequisites above)
-
-# Build Release version
-./build.sh release
-
-# Build Release with examples
-./build.sh release-examples
-
-# Build Debug version
-./build.sh debug
-
-# Build Debug with examples
-./build.sh debug-examples
-```
-
-#### Important Notes for Linux
-
-Before building on Linux (especially Ubuntu/WSL), you may need to set environment variables to avoid common build errors.
-
-**Set Build Environment Variables:**
-```bash
-# Suppress deprecated warnings from WebRTC (required for newer GCC versions)
-export CXXFLAGS="-Wno-deprecated-declarations"
-export CFLAGS="-Wno-deprecated-declarations"
-
-# Required for Rust bindgen to find libclang
-export LIBCLANG_PATH=/usr/lib/llvm-14/lib # Adjust version as needed
-```
-
-**Common Issues:**
-
-1. **Missing proto files or client-sdk-rust directory**
- - Solution: Initialize git submodules:
- ```bash
- git submodule update --init --recursive
- ```
-
-2. **Deprecated declaration errors during compilation**
- - Cause: Newer GCC versions (12/13/14) are stricter with WebRTC legacy code
- - Solution: Set `CXXFLAGS` and `CFLAGS` as shown above
-
-3. **Rust bindgen fails with "unable to find libclang"**
- - Cause: Rust bindgen cannot locate libclang library
- - Solution: Set `LIBCLANG_PATH` environment variable pointing to your LLVM installation
-
-### Method 2: Using vcpkg Manifest Mode
-
-vcpkg will automatically install all required dependencies based on `vcpkg.json`.
-
-**Windows:**
-```powershell
-# Configure project
-cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake
-
-# Build
-cmake --build build --config Release
-
-# Optional: Build with examples
-cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake -DLIVEKIT_BUILD_EXAMPLES=ON -DVCPKG_MANIFEST_FEATURES=examples
-cmake --build build --config Release
-```
-
-**Linux/macOS:**
-```bash
-# Configure project
-cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Release
-
-# Build
-cmake --build build
-
-# Optional: Build with examples
-cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Release -DLIVEKIT_BUILD_EXAMPLES=ON -DVCPKG_MANIFEST_FEATURES=examples
-cmake --build build
-```
-
-### Method 3: Manual Dependency Installation
-
-If you prefer not to use vcpkg, you can install dependencies manually:
-
-#### Required Dependencies
-- **Protobuf** (>= 5.29)
- - Windows: `vcpkg install protobuf:x64-windows`
- - Linux: `sudo apt install libprotobuf-dev protobuf-compiler`
- - macOS: `brew install protobuf`
-
-- **Abseil** (Required for Protobuf >= 6)
- - Windows: `vcpkg install abseil:x64-windows`
- - Linux: `sudo apt install libabsl-dev`
- - macOS: `brew install abseil`
-
-- **OpenSSL** (Linux only)
- - Linux: `sudo apt install libssl-dev`
-
-#### Build Command
-```bash
-cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
-cmake --build build
-```
-
-## Build Output
-
-After a successful build, you will find the following in the build directories:
-
-```
-build-release/ # Release build output
-├── lib/
-│ ├── livekit.lib / liblivekit.a # Main SDK static library
-│ ├── livekit_ffi.dll / .so / .dylib # Rust FFI dynamic library
-│ └── (Windows only: protobuf, abseil DLLs)
-├── include/ # Public headers (auto-synced)
-│ └── livekit/
-└── bin/ # Example executables (with examples enabled)
- ├── SimpleRoom
- ├── SimpleRpc
- ├── SimpleDataStream
- └── liblivekit_ffi.so / .dylib # (Linux/macOS: copied for runtime)
-```
-
-## Integrating into Your Project
-
-### Using CMake
-
-```cmake
-# Method 1: As a subdirectory
-add_subdirectory(path/to/client-sdk-cpp)
-target_link_libraries(your_target PRIVATE livekit)
-
-# Method 2: Using find_package (requires prior installation)
-find_package(livekit REQUIRED)
-target_link_libraries(your_target PRIVATE livekit)
-```
-
-### Manual Linking
-
-1. Add include path: `build/include`
-2. Link static library:
- - Windows: `build/lib/livekit.lib`
- - Linux: `build/lib/liblivekit.a`
- - macOS: `build/lib/liblivekit.a`
-3. Link/Deploy Rust FFI dynamic library:
- - Windows: Link `livekit_ffi.dll.lib`, deploy `livekit_ffi.dll` with your exe
- - Linux: Deploy `liblivekit_ffi.so` in same directory as your executable
- - macOS: Deploy `liblivekit_ffi.dylib` in same directory as your executable
-4. Link platform-specific system libraries
-
-> **Important**: On Linux/macOS, the `.so`/`.dylib` must be in the same directory as your executable (RPATH is set to `$ORIGIN` / `@executable_path`).
-
-**Windows system libraries:**
-```
-ntdll userenv winmm iphlpapi msdmo dmoguids wmcodecdspuuid
-ws2_32 secur32 bcrypt crypt32
-```
-
-**macOS frameworks:**
-```
-CoreAudio AudioToolbox CoreFoundation Security CoreGraphics
-CoreMedia VideoToolbox AVFoundation CoreVideo Foundation
-AppKit QuartzCore OpenGL IOSurface Metal MetalKit ScreenCaptureKit
-```
-
-**Linux libraries:**
-```
-OpenSSL::SSL OpenSSL::Crypto
-```
-
-## CMake Options
-
-| Option | Default | Description |
-|--------|---------|-------------|
-| `LIVEKIT_BUILD_EXAMPLES` | OFF | Build example applications |
-| `LIVEKIT_VERSION` | "0.1.0" | SDK version number |
-| `LIVEKIT_USE_VCPKG` | ON | Use vcpkg for dependency management |
-
-## Troubleshooting
-
-### Q: Rust code recompiles after modifying C++ code?
-A: This has been fixed. Rust code only recompiles when Rust source files change or the Rust library doesn't exist.
-
-### Q: Cannot find Protobuf or other dependencies?
-A: Make sure you're using the correct CMake toolchain file:
-```bash
--DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake
-```
-
-### Q: Linking fails on Linux?
-A: Ensure you have OpenSSL development packages installed:
-```bash
-sudo apt install libssl-dev
-```
-
-### Q: How to clean the build?
-A: Use the CMake-provided clean targets:
-```bash
-# Clean CMake build artifacts
-cmake --build build --target clean
-
-# Clean Rust build artifacts
-cmake --build build --target cargo_clean
-
-# Clean generated protobuf files
-cmake --build build --target clean_generated
-
-# Complete clean (including deletion of build directory)
-cmake --build build --target clean_all
-```
-
-## Example Applications
-
-Example applications are located in the `examples/` directory:
-
-- **SimpleRoom** - Basic room connection and audio/video handling
-- **SimpleRpc** - RPC call examples
-- **SimpleDataStream** - Data stream transmission examples
-
-Please refer to the README in each example directory for more details.
-
-## Platform-Specific Notes
-
-### Windows
-- Recommended: Visual Studio 2019 or later
-- Architecture: x64
-
-### macOS
-- Requires macOS 12.3+ (ScreenCaptureKit support)
-- Requires Xcode Command Line Tools
-
-### Linux
-- Tested on: Ubuntu 20.04+
-- Requires complete development toolchain
-
-## Support
-
-- GitHub Issues: https://github.com/livekit/client-sdk-cpp/issues
-- LiveKit Documentation: https://docs.livekit.io/
-
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 00000000..fd06e209
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,14 @@
+# Repository Documentation
+
+Additional documentation for the SDK.
+
+- [Building](building.md) — prerequisites, build scripts, CMake presets,
+ vcpkg, Docker, integration into your CMake project, troubleshooting.
+- [Logging](logging.md) — compile-time vs runtime filtering, log levels,
+ custom sinks (file, JSON, ROS2 `RCLCPP_*` macros).
+- [Tracing](tracing.md) — Chromium-format performance traces, viewing in
+ `chrome://tracing` and Perfetto.
+- [Testing](testing.md) — unit, integration, and stress test suites; env
+ vars; token-helper script for local `livekit-server --dev` runs.
+- [Developer tools](tools.md) — `clang-tidy`, `clang-format`, `valgrind`,
+ Doxygen, pre-commit hook, Rust submodule recovery tips.
diff --git a/docs/building.md b/docs/building.md
new file mode 100644
index 00000000..60f08a71
--- /dev/null
+++ b/docs/building.md
@@ -0,0 +1,366 @@
+# Building
+
+This document covers everything you need to build the LiveKit C++ SDK from
+source: prerequisites, cloning the repository, the build scripts, advanced
+CMake/vcpkg flows, and Docker.
+
+## Prerequisites
+
+### Common to all platforms
+
+- **CMake** ≥ 3.20
+- **Rust / Cargo** — latest stable toolchain (for building the Rust FFI layer).
+ Install via [rustup](https://rustup.rs/).
+- **Git LFS** — required for examples that pull test media assets.
+- **Protobuf** ≥ 5.29
+- **Abseil** — always required (used by Protobuf 5.x+)
+
+### Platform-specific toolchains
+
+| Platform | Compiler | Package manager |
+|----------|----------|-----------------|
+| Windows | Visual Studio 2019+ (MSBuild or Ninja) | vcpkg (see below) |
+| Linux | GCC 9+ or Clang 10+ | `apt` / `dnf` (or vcpkg) |
+| macOS | Xcode 12+ (macOS 12.3+ for ScreenCaptureKit) | Homebrew (or vcpkg) |
+
+## Clone the repository
+
+The SDK depends on the [`client-sdk-rust`](https://github.com/livekit/rust-sdks)
+submodule (recursive), so always clone with submodules:
+
+```bash
+# Option 1: clone with submodules in one step
+git clone --recurse-submodules https://github.com/livekit/client-sdk-cpp.git
+
+# Option 2: clone first, then initialize submodules
+git clone https://github.com/livekit/client-sdk-cpp.git
+cd client-sdk-cpp
+git submodule update --init --recursive
+
+# Pull Git LFS assets if you want to run the integration tests:
+git lfs pull
+```
+
+## Recommended setup
+
+These are the exact packages our CI uses. They will also work for examples.
+
+### macOS
+
+```bash
+brew install cmake ninja protobuf abseil rust
+```
+
+### Ubuntu / Debian
+
+```bash
+sudo apt update && sudo apt install -y \
+ build-essential cmake ninja-build pkg-config \
+ llvm-dev libclang-dev clang \
+ libprotobuf-dev protobuf-compiler libabsl-dev \
+ libssl-dev
+
+# Install Rust if you don't already have it
+curl https://sh.rustup.rs -sSf | sh
+```
+
+If you plan to build the [example collection](https://github.com/livekit-examples/cpp-example-collection)
+(SDL-based renderer + camera/mic capture), also install:
+
+```bash
+sudo apt install -y \
+ libva-dev libdrm-dev libgbm-dev libx11-dev libgl1-mesa-dev \
+ libxext-dev libxcomposite-dev libxdamage-dev libxfixes-dev \
+ libxrandr-dev libxi-dev libxkbcommon-dev \
+ libasound2-dev libpulse-dev \
+ libwayland-dev libdecor-0-dev
+```
+
+### Windows
+
+```powershell
+# Set VCPKG_ROOT once and bootstrap vcpkg
+git clone https://github.com/microsoft/vcpkg.git
+.\vcpkg\bootstrap-vcpkg.bat
+$env:VCPKG_ROOT = "$PWD\vcpkg"
+```
+
+CMake's vcpkg manifest mode (below) reads
+`vcpkg.json` and installs the rest automatically the first time you configure.
+
+## Build scripts (recommended)
+
+The repo ships with `build.sh` (Linux/macOS) and `build.cmd` (Windows) that
+wrap the right CMake preset for your platform and pick sensible defaults.
+
+**Linux/macOS:**
+```bash
+./build.sh release # Build Release
+./build.sh debug # Build Debug
+./build.sh release-examples # Release + examples
+./build.sh debug-examples # Debug + examples
+./build.sh release-tests # Release + tests
+./build.sh debug-tests # Debug + tests
+./build.sh release-all # Release + tests + examples
+./build.sh debug-all # Debug + tests + examples
+./build.sh clean # Clean CMake build artifacts + local-install
+./build.sh clean-all # Deep clean (C++ + Rust + local-install + generated files)
+```
+
+**Windows:**
+```powershell
+.\build.cmd release
+.\build.cmd debug
+.\build.cmd release-examples
+# ... same suffixes as build.sh
+```
+
+The build scripts pass an explicit job count to `cmake --build --parallel`. Set
+`CMAKE_BUILD_PARALLEL_LEVEL` to override the auto-detected logical CPU count.
+
+## Advanced: CMake presets
+
+For more control, drive CMake directly via the presets in
+[CMakePresets.json](https://github.com/livekit/client-sdk-cpp/blob/main/CMakePresets.json):
+
+```bash
+# Linux
+cmake --preset linux-release
+cmake --build --preset linux-release
+
+# macOS
+cmake --preset macos-release
+cmake --build --preset macos-release
+
+# Windows
+cmake --preset windows-release
+cmake --build --preset windows-release
+```
+
+Windows requires `VCPKG_ROOT` to be set:
+
+```powershell
+$env:VCPKG_ROOT = "C:\path\to\vcpkg"
+```
+
+## Advanced: vcpkg manifest mode
+
+vcpkg will automatically install all dependencies listed in
+[vcpkg.json](https://github.com/livekit/client-sdk-cpp/blob/main/vcpkg.json) the first time you configure with its toolchain
+file.
+
+**Windows:**
+```powershell
+cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=$env:VCPKG_ROOT\scripts\buildsystems\vcpkg.cmake
+cmake --build build --config Release
+
+# With examples:
+cmake -B build -S . `
+ -DCMAKE_TOOLCHAIN_FILE=$env:VCPKG_ROOT\scripts\buildsystems\vcpkg.cmake `
+ -DLIVEKIT_BUILD_EXAMPLES=ON -DVCPKG_MANIFEST_FEATURES=examples
+cmake --build build --config Release
+```
+
+**Linux/macOS:**
+```bash
+cmake -B build -S . \
+ -DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake \
+ -DCMAKE_BUILD_TYPE=Release
+cmake --build build
+
+# With examples:
+cmake -B build -S . \
+ -DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DLIVEKIT_BUILD_EXAMPLES=ON -DVCPKG_MANIFEST_FEATURES=examples
+cmake --build build
+```
+
+## Building with Docker
+
+The Docker setup is split into a reusable base image (toolchain + system
+deps) and an SDK image layered on top. **Tested on Linux only.**
+
+```bash
+docker build -t livekit-cpp-sdk-base . -f docker/Dockerfile.base
+docker build --build-arg BASE_IMAGE=livekit-cpp-sdk-base \
+ -t livekit-cpp-sdk . -f docker/Dockerfile.sdk
+docker run -it --network host livekit-cpp-sdk:latest bash
+```
+
+If you're authoring your own Dockerfile, mirror the `ENV` block in
+[docker/Dockerfile.base](https://github.com/livekit/client-sdk-cpp/blob/main/docker/Dockerfile.base):
+
+```bash
+export CC=$HOME/gcc-14/bin/gcc
+export CXX=$HOME/gcc-14/bin/g++
+export LD_LIBRARY_PATH=$HOME/gcc-14/lib64:$LD_LIBRARY_PATH
+export PATH=$HOME/.cargo/bin:$PATH
+export PATH=$HOME/cmake-3.31/bin:$PATH
+```
+
+## CMake options
+
+| Option | Default | Description |
+|--------|---------|-------------|
+| `LIVEKIT_BUILD_EXAMPLES` | OFF | Build example applications |
+| `LIVEKIT_USE_SYSTEM_PROTOBUF` | OFF | Use system Protobuf instead of vcpkg's |
+| `LIVEKIT_LOG_LEVEL` | `TRACE` | Compile-time log threshold (see [logging.md](logging.md)) |
+| `LIVEKIT_VERSION` | repo-derived | SDK version string baked into the binary |
+
+## Build output
+
+After a successful build:
+
+```
+build-release/
+├── lib/
+│ ├── liblivekit.{a,lib} # Main SDK static library
+│ ├── liblivekit_ffi.{so,dylib} # Rust FFI dynamic library
+│ └── livekit_ffi.dll, *.lib # (Windows) FFI DLL + import lib
+├── include/ # Public headers (auto-synced)
+│ └── livekit/
+└── bin/ # Example/test executables
+ └── liblivekit_ffi.{so,dylib} # (Linux/macOS: copied for runtime)
+```
+
+## Integrating into your project
+
+### Using CMake
+
+```cmake
+# Method 1: as a subdirectory
+add_subdirectory(path/to/client-sdk-cpp)
+target_link_libraries(your_target PRIVATE livekit)
+
+# Method 2: find_package (after install)
+find_package(livekit REQUIRED)
+target_link_libraries(your_target PRIVATE livekit)
+```
+
+### Using prebuilt releases
+
+The easiest way to consume the SDK without building from source is via
+the [cpp-example-collection](https://github.com/livekit-examples/cpp-example-collection)
+helper, which downloads a release tarball at CMake configure time:
+
+```cmake
+include(LiveKitSDK.cmake) # pins or auto-resolves a release
+```
+
+See the example collection's
+[`LiveKitSDK.cmake`](https://github.com/livekit-examples/cpp-example-collection/blob/main/cmake/LiveKitSDK.cmake)
+for the full pattern.
+
+### Manual linking
+
+1. Add include path: `build-release/include`
+2. Link the static SDK library:
+ - Windows: `build-release/lib/livekit.lib`
+ - Linux: `build-release/lib/liblivekit.a`
+ - macOS: `build-release/lib/liblivekit.a`
+3. Link/deploy the Rust FFI dynamic library:
+ - Windows: link `livekit_ffi.dll.lib`, deploy `livekit_ffi.dll` next to your `.exe`
+ - Linux: deploy `liblivekit_ffi.so` next to your executable
+ - macOS: deploy `liblivekit_ffi.dylib` next to your executable
+4. Link platform system libraries (see below).
+
+> **Important:** On Linux/macOS the FFI shared library must live next to the
+> executable. RPATH is set to `$ORIGIN` (Linux) / `@loader_path` (macOS).
+
+**Windows system libraries:**
+```
+ntdll userenv winmm iphlpapi msdmo dmoguids wmcodecdspuuid
+ws2_32 secur32 bcrypt crypt32
+```
+
+**macOS frameworks:**
+```
+CoreAudio AudioToolbox CoreFoundation Security CoreGraphics
+CoreMedia VideoToolbox AVFoundation CoreVideo Foundation
+AppKit QuartzCore OpenGL IOSurface Metal MetalKit ScreenCaptureKit
+```
+
+**Linux libraries:**
+```
+OpenSSL::SSL OpenSSL::Crypto
+```
+
+### Runtime dependencies of prebuilt artifacts
+
+Whether protobuf / abseil / openssl need to be installed on the target
+machine depends on how the SDK binary was built:
+
+- **Windows** release artifacts use vcpkg triplet `x64-windows-static-md` —
+ protobuf and abseil are statically linked into the DLLs; no runtime install
+ needed.
+- **macOS** release artifacts (from our CI) do **not** dynamically depend on
+ protobuf / abseil / openssl. You can verify with `otool -L liblivekit.dylib`.
+- **Linux** depends on packaging. Check with `ldd liblivekit_ffi.so`; if any
+ of those are listed, install the corresponding `-dev` (build) or runtime
+ package (`libprotobuf32` / `libabsl` / `libssl3`) as appropriate.
+
+## Troubleshooting
+
+### Missing proto files or `client-sdk-rust` directory
+
+Initialize submodules:
+```bash
+git submodule update --init --recursive
+```
+
+### Deprecated-declaration errors on Linux
+
+Newer GCC versions (12+) are stricter with the WebRTC legacy code in the
+Rust submodule. If `./build.sh release` errors with `-Werror=deprecated-declarations`,
+relax it for the build:
+
+```bash
+export CXXFLAGS="-Wno-deprecated-declarations"
+export CFLAGS="-Wno-deprecated-declarations"
+```
+
+### Rust bindgen fails with "unable to find libclang"
+
+Install `libclang-dev` (Ubuntu) or `llvm` (macOS Homebrew). bindgen normally
+discovers libclang from the system paths once `libclang-dev` is installed; if
+not, point `LIBCLANG_PATH` at your LLVM's `lib` directory (e.g.
+`/usr/lib/llvm-18/lib` on Ubuntu 24.04).
+
+### Rust code recompiles after C++ edits
+
+This was a historical issue; Rust only recompiles now when Rust source files
+change or the Rust library is missing.
+
+### Cannot find Protobuf or other dependencies
+
+Make sure you're passing the vcpkg toolchain file:
+```bash
+-DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake
+```
+
+### `clang-tidy` on Windows
+
+Not currently supported via our scripts — the Visual Studio (MSBuild) CMake
+generator doesn't produce `compile_commands.json`. The Ninja generator does;
+see [tools.md](tools.md).
+
+### How do I deep-clean?
+
+```bash
+./build.sh clean-all # C++ + Rust + local-install + generated files
+```
+
+Or via CMake targets:
+
+```bash
+cmake --build build --target clean # CMake artifacts
+cmake --build build --target cargo_clean # Rust artifacts
+cmake --build build --target clean_generated # Generated protobuf headers
+cmake --build build --target clean_all # Full clean
+```
+
+## Support
+
+- GitHub Issues:
+- LiveKit Docs:
diff --git a/docs/logging.md b/docs/logging.md
new file mode 100644
index 00000000..1889479c
--- /dev/null
+++ b/docs/logging.md
@@ -0,0 +1,85 @@
+# Logging
+
+The SDK uses [spdlog](https://github.com/gabime/spdlog) internally but does
+**not** expose it in public headers. All log output goes through a thin public
+API in ``.
+
+## Two-tier filtering
+
+| Tier | When | How | Cost |
+|------|------|-----|------|
+| **Compile-time** | CMake configure | `-DLIVEKIT_LOG_LEVEL=WARN` | Zero — calls below the level are stripped from the binary |
+| **Runtime** | Any time after `initialize()` | `livekit::setLogLevel(LogLevel::Warn)` | Minimal — a level check before formatting |
+
+### Compile-time level (`LIVEKIT_LOG_LEVEL`)
+
+Set once when you configure CMake. Calls below this threshold are completely
+removed by the preprocessor — no format-string evaluation, no function call.
+
+```bash
+# Development (default): keep everything available
+cmake -DLIVEKIT_LOG_LEVEL=TRACE ..
+
+# Release: strip TRACE / DEBUG / INFO
+cmake -DLIVEKIT_LOG_LEVEL=WARN ..
+
+# Production: only ERROR and CRITICAL survive
+cmake -DLIVEKIT_LOG_LEVEL=ERROR ..
+```
+
+Valid values: `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `CRITICAL`, `OFF`.
+
+### Runtime level (`setLogLevel`)
+
+Among the levels that survived compilation you can still filter at runtime
+without rebuilding:
+
+```cpp
+#include
+
+livekit::initialize(); // default level: Info
+livekit::setLogLevel(livekit::LogLevel::Debug); // show more detail
+livekit::setLogLevel(livekit::LogLevel::Warn); // suppress info chatter
+```
+
+## Custom log callback
+
+Replace the default stderr sink with your own handler. This is the integration
+point for frameworks like ROS2 (`RCLCPP_*` macros), Android logcat, or any
+structured-logging pipeline:
+
+```cpp
+#include
+
+livekit::initialize();
+livekit::setLogLevel(livekit::LogLevel::Trace);
+
+livekit::setLogCallback(
+ [](livekit::LogLevel level,
+ const std::string &logger_name,
+ const std::string &message) {
+ // Route to your framework, e.g.:
+ // RCLCPP_INFO(get_logger(), "[%s] %s", logger_name.c_str(), message.c_str());
+ myLogger.log(level, logger_name, message);
+ });
+
+// Pass nullptr to restore the default stderr sink:
+livekit::setLogCallback(nullptr);
+```
+
+See the [`logging_levels/custom_sinks.cpp`](https://github.com/livekit-examples/cpp-example-collection/blob/main/logging_levels/custom_sinks.cpp)
+example for three copy-paste-ready patterns: a **file logger**, **JSON
+structured lines**, and a **ROS2 bridge** that maps `LogLevel` to `RCLCPP_*`
+macros.
+
+## Available log levels
+
+| Level | Typical use |
+|-------|-------------|
+| `Trace` | Per-frame / per-packet detail (very noisy) |
+| `Debug` | Diagnostic info useful during development |
+| `Info` | Normal operational messages (connection, track events) |
+| `Warn` | Unexpected but recoverable situations |
+| `Error` | Failures that affect functionality |
+| `Critical` | Unrecoverable errors |
+| `Off` | Suppress all output |
diff --git a/docs/testing.md b/docs/testing.md
new file mode 100644
index 00000000..120baabb
--- /dev/null
+++ b/docs/testing.md
@@ -0,0 +1,112 @@
+# Testing
+
+The SDK includes integration and stress tests using
+[Google Test](https://github.com/google/googletest).
+
+## Building the test binaries
+
+**Linux/macOS:**
+```bash
+./build.sh debug-tests # Build Debug with tests
+./build.sh release-tests # Build Release with tests
+```
+
+**Windows:**
+```powershell
+.\build.cmd debug-tests
+.\build.cmd release-tests
+```
+
+## Running tests
+
+After building, run tests using `ctest` or invoke the binaries directly:
+
+```bash
+# Run all tests via ctest
+cd build-debug
+ctest --output-on-failure
+
+# Or run test executables directly
+./build-debug/bin/livekit_integration_tests
+./build-debug/bin/livekit_stress_tests
+
+# Run specific test suites
+./build-debug/bin/livekit_integration_tests --gtest_filter="*Rpc*"
+./build-debug/bin/livekit_stress_tests --gtest_filter="*MaxPayloadStress*"
+```
+
+__Note:__ The tests require tokens and a running LiveKit server. See the section below for details.
+
+## Test binaries
+
+| Executable | Description |
+|------------|-------------|
+| `livekit_unit_tests` | Pure unit tests (no server required) |
+| `livekit_integration_tests` | Quick tests (~1-2 minutes) for SDK functionality |
+| `livekit_stress_tests` | Long-running tests (configurable, default 1 hour) |
+
+## Running a local LiveKit server for tests
+
+The integration and stress suites need a running LiveKit server. The easiest
+path is `livekit-server --dev`, which uses the well-known dev API
+key/secret (`devkey` / `secret`).
+
+Install [`livekit-server`](https://docs.livekit.io/home/self-hosting/local/)
+and start it with data tracks enabled:
+
+```bash
+livekit-server --dev
+```
+
+## Environment variables
+
+The integration and stress test suites (data tracks, RPC, media multistream,
+etc.) require a server URL and two participant tokens:
+
+```bash
+# Required
+export LIVEKIT_URL="ws://localhost:7880" # or wss://your-server.livekit.cloud
+export LIVEKIT_TOKEN_A=""
+export LIVEKIT_TOKEN_B=""
+
+# Optional (for stress tests)
+export RPC_STRESS_DURATION_SECONDS=3600 # Test duration (default: 1 hour)
+export RPC_STRESS_CALLER_THREADS=4 # Concurrent caller threads (default: 4)
+```
+
+### Generating tokens for the test suites
+
+The easiest path is to source the helper script, which mints both
+participant tokens against a local `livekit-server --dev` and exports
+`LIVEKIT_TOKEN_A`, `LIVEKIT_TOKEN_B`, and `LIVEKIT_URL` for the current shell:
+
+```bash
+source .token_helpers/set_data_track_test_tokens.bash
+```
+
+To generate tokens manually (e.g. against a non-default server), install
+[`livekit-cli`](https://docs.livekit.io/home/cli/cli-setup/) and run:
+
+```bash
+export LIVEKIT_TOKEN_A="$(lk token create --api-key devkey --api-secret secret -i cpp-test-a \
+ --join --valid-for 99999h --room cpp_data_track_test \
+ --grant '{"canPublish":true,"canSubscribe":true,"canPublishData":true}' \
+ --token-only)"
+export LIVEKIT_TOKEN_B="$(lk token create --api-key devkey --api-secret secret -i cpp-test-b \
+ --join --valid-for 99999h --room cpp_data_track_test \
+ --grant '{"canPublish":true,"canSubscribe":true,"canPublishData":true}' \
+ --token-only)"
+```
+
+## Test coverage
+
+- **SDK initialization**: initialize / shutdown lifecycle.
+- **Room**: room creation, options, connection.
+- **Audio frame**: frame creation, manipulation, edge cases.
+- **RPC**: round-trip calls, max payload (15 KB), timeouts, errors, concurrent calls.
+- **Stress**: high throughput, bidirectional RPC, memory pressure.
+
+## Memory checks (valgrind)
+
+Run `valgrind` against the test binaries to check for memory leaks and other
+issues. See [tools.md](tools.md) for the recipe.
diff --git a/docs/tools.md b/docs/tools.md
new file mode 100644
index 00000000..b67826f5
--- /dev/null
+++ b/docs/tools.md
@@ -0,0 +1,163 @@
+# Developer tools
+
+This SDK uses several tools and checks to enforce code quality. All of these
+are also enforced in CI on PRs.
+
+## Clang tools
+
+- **`clang-tidy`** — static analysis. See [.clang-tidy](https://github.com/livekit/client-sdk-cpp/blob/main/.clang-tidy) for the
+ enabled checks. Enforced in CI on PR.
+- **`clang-format`** — code formatting and style consistency. See
+ [.clang-format](https://github.com/livekit/client-sdk-cpp/blob/main/.clang-format) for the rules. Enforced in CI on PR.
+
+> **Note (Windows):** `clang-tidy` is not currently driven by our scripts on
+> Windows. The MSBuild CMake generator doesn't emit
+> `compile_commands.json`, which `clang-tidy` requires. The Ninja generator
+> does, so manual invocation is possible. `clang-format` similarly needs to be
+> installed and run manually on Windows, pointing at the root `.clang-format`.
+
+### Install
+
+**macOS:**
+
+```bash
+brew install llvm
+```
+
+This installs `clang-format`, `clang-tidy`, and `run-clang-tidy`. Homebrew may
+ask you to add `/opt/homebrew/opt/llvm/bin` (Apple Silicon) or
+`/usr/local/opt/llvm/bin` (Intel) to your `PATH`.
+
+**Linux (Ubuntu/Debian):**
+
+```bash
+sudo apt-get install clang-format clang-tidy clang-tools
+```
+
+### Run `clang-tidy`
+
+1. Generate `compile_commands.json` and the protobuf headers via a release build:
+
+ ```bash
+ ./build.sh release
+ ```
+
+2. Run the wrapper, which uses the same file set, regex filters, and
+ `.clang-tidy` config as CI:
+
+ ```bash
+ ./scripts/clang-tidy.sh
+ ```
+
+The wrapper forwards extra arguments to `run-clang-tidy`:
+
+```bash
+./scripts/clang-tidy.sh -j 4 # Number of cores
+./scripts/clang-tidy.sh -checks='-*,misc-const-correctness' # Only specific checks
+./scripts/clang-tidy.sh -fix # Apply fixes
+```
+
+Output is captured to `clang-tidy.log` at the repo root, since the terminal
+buffer often can't hold all of it.
+
+### Run `clang-format`
+
+```bash
+./scripts/clang-format.sh
+```
+
+With no arguments, runs against every relevant file in the repository against
+the rules in `.clang-format`.
+
+```bash
+./scripts/clang-format.sh --fix # Rewrite files in place
+./scripts/clang-format.sh src/room.cpp include/livekit/room.h # Check just these files
+./scripts/clang-format.sh --fix src/room.cpp # Fix just this file
+```
+
+Output is captured to `clang-format.log` at the repo root.
+
+---
+
+## Pre-commit hook
+
+A simple pre-commit hook that auto-formats staged C/C++ files using the
+project's `.clang-format` rules:
+
+```bash
+./scripts/install-pre-commit.sh
+```
+
+This installs `.git/hooks/pre-commit`. Re-run after `git clone` on a fresh
+checkout.
+
+---
+
+## Memory checks (valgrind)
+
+Run `valgrind` against the integration or stress test binaries to check for
+memory leaks and other issues:
+
+```bash
+valgrind --leak-check=full ./build-debug/bin/livekit_integration_tests
+valgrind --leak-check=full ./build-debug/bin/livekit_stress_tests
+```
+
+`valgrind` is Linux-only. On macOS, use `leaks` or Instruments instead.
+
+---
+
+## API documentation (Doxygen)
+
+API reference is generated from headers using Doxygen. To rebuild locally:
+
+```bash
+./scripts/generate-docs.sh
+```
+
+Output lands under `docs/doxygen/html/`. The deployed reference is at
+[docs.livekit.io/reference/client-sdk-cpp/](https://docs.livekit.io/reference/client-sdk-cpp/).
+
+To view the generated documentation locally, open `docs/doxygen/html/index.html` in your browser.
+
+For details on the Doxygen configuration and CI pipeline, see the
+[doxygen/](https://github.com/livekit/client-sdk-cpp/tree/main/docs/doxygen) folder.
+
+---
+
+## Development tips
+
+### Bump the pinned Rust submodule
+
+```bash
+cd client-sdk-cpp
+git fetch origin
+git switch -c try-rust-main origin/main
+
+# Sync submodule URLs and check out what origin/main pins (recursively):
+git submodule sync --recursive
+git submodule update --init --recursive --checkout
+
+# If the nested submodule under yuv-sys didn't materialize, force it:
+git -C client-sdk-rust/yuv-sys submodule sync --recursive
+git -C client-sdk-rust/yuv-sys submodule update --init --recursive --checkout
+
+# Sanity check:
+git submodule status --recursive
+```
+
+### If `yuv-sys` fails to build
+
+```bash
+cargo clean -p yuv-sys
+cargo build -p yuv-sys -vv
+```
+
+### Full clean (Rust + C++ build folders)
+
+To delete all build artifacts from both Rust and C++ folders, plus the
+local-install folder:
+
+```bash
+./build.sh clean-all
+```
diff --git a/docs/tracing.md b/docs/tracing.md
new file mode 100644
index 00000000..f74fa75c
--- /dev/null
+++ b/docs/tracing.md
@@ -0,0 +1,35 @@
+# Tracing
+
+The SDK includes built-in support for [Chromium tracing](https://www.chromium.org/developers/how-tos/trace-event-profiling-tool/),
+allowing you to capture detailed performance traces for debugging and
+optimization.
+
+## Basic usage
+
+```cpp
+#include
+
+// Start tracing to a file
+livekit::startTracing("trace.json");
+
+// ... run your application ...
+
+// Stop tracing and flush to file
+livekit::stopTracing();
+```
+
+## Filtering by category
+
+You can optionally filter which categories to trace:
+
+```cpp
+// Trace only specific categories (supports wildcards)
+livekit::startTracing("trace.json", {"livekit.*", "webrtc.*"});
+```
+
+## Viewing traces
+
+Open the generated trace file in one of these viewers:
+
+- **Chrome**: navigate to `chrome://tracing` and click "Load" to open the trace file.
+- **Perfetto**: open [ui.perfetto.dev](https://ui.perfetto.dev) and drag-drop your trace file.
diff --git a/scripts/generate-docs.sh b/scripts/generate-docs.sh
index cd373f1f..22d442a3 100755
--- a/scripts/generate-docs.sh
+++ b/scripts/generate-docs.sh
@@ -154,7 +154,7 @@ cp "$repo_root/docs/doxygen/Doxyfile" "$doxyfile_tmp"
{
echo ""
echo "# Local override generated by scripts/generate-docs.sh"
- echo "INPUT = include/livekit ${mainpage_rel} README_BUILD.md"
+ echo "INPUT = include/livekit ${mainpage_rel} docs/README.md docs/building.md docs/logging.md docs/tracing.md docs/testing.md docs/tools.md"
echo "USE_MDFILE_AS_MAINPAGE = ${mainpage_rel}"
} >>"$doxyfile_tmp"
diff --git a/scripts/install-pre-commit.sh b/scripts/install-pre-commit.sh
new file mode 100755
index 00000000..fa37da84
--- /dev/null
+++ b/scripts/install-pre-commit.sh
@@ -0,0 +1,37 @@
+#!/usr/bin/env bash
+#
+# Copyright 2026 LiveKit
+#
+# 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.
+#
+# install-pre-commit.sh -- Install a git pre-commit hook that runs
+# `scripts/clang-format.sh --fix` on staged C/C++ files. Re-stages any
+# files the formatter rewrote so the commit includes the fixes.
+
+set -euo pipefail
+
+repo_root="$(git rev-parse --show-toplevel)"
+hook_path="${repo_root}/.git/hooks/pre-commit"
+
+cat >"${hook_path}" <<'HOOK'
+#!/bin/sh
+# Auto-format staged C/C++ files using ./scripts/clang-format.sh --fix.
+files=$(git diff --cached --name-only --diff-filter=ACMR \
+ -- "*.c" "*.cc" "*.cpp" "*.cxx" "*.h" "*.hpp" "*.hxx")
+[ -z "${files}" ] && exit 0
+echo "${files}" | xargs ./scripts/clang-format.sh --fix
+echo "${files}" | xargs git add
+HOOK
+
+chmod +x "${hook_path}"
+echo "Installed pre-commit hook at ${hook_path}"