diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml new file mode 100644 index 00000000..52ea026e --- /dev/null +++ b/.github/workflows/generate-docs.yml @@ -0,0 +1,177 @@ +name: Generate docs +permissions: + contents: read + +# Generates Doxygen docs and uploads them as a workflow artifact. + +# This workflow is used to validate documentation before merge, for example catching +# things like broken @ref tags, missing @param tags, etc. + +# The artifact is attached to the workflow run so reviewers can download a preview, +# but also to verify the documentation artifact is valid for subsequent release runs +# that will publish them to the LiveKit docs web page: https://docs.livekit.io/reference/client-sdk-cpp/ +on: + pull_request: + branches: ["main"] + paths: + - include/** + - src/** + - docs/** + - scripts/generate-docs.sh + - .github/workflows/generate-docs.yml + - .github/workflows/publish-docs.yml + workflow_call: + inputs: + version: + description: 'Documentation version (e.g. v0.1.0)' + required: false + type: string + upload_artifact: + description: 'Upload the generated docs folder as a workflow artifact' + required: false + type: boolean + default: true + artifact_name: + description: 'Name to use for the uploaded docs artifact' + required: false + type: string + default: livekit-cpp-docs + artifact_retention_days: + description: 'Retention (days) for the uploaded docs artifact' + required: false + type: number + default: 7 + outputs: + project_number: + description: 'Doxygen PROJECT_NUMBER used for the build (e.g., v1.2.3)' + value: ${{ jobs.validate.outputs.project_number }} + artifact_name: + description: 'Name of the uploaded docs artifact (empty if upload was skipped via upload_artifact: false)' + value: ${{ jobs.validate.outputs.artifact_name }} + +jobs: + validate: + name: Generate and verify docs + runs-on: ubuntu-latest + outputs: + project_number: ${{ steps.build_docs.outputs.project_number }} + artifact_name: ${{ steps.artifact_meta.outputs.name }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + # fetch-depth: 0 is required so `git describe --tags` in + # scripts/generate-docs.sh resolves the right version from the git history. + fetch-depth: 0 + + # Doxygen is available on ubuntu-latest, but we install explicitly for stability + - name: Install Doxygen + run: | + sudo apt-get update + sudo apt-get install -y doxygen graphviz + + - name: Generate docs (Doxygen) + id: build_docs + shell: bash + env: + INPUT_VERSION: ${{ inputs.version || '' }} + run: | + set -euo pipefail + + args=() + if [[ -n "$INPUT_VERSION" ]]; then + args+=(--version "$INPUT_VERSION") + elif [[ "${{ github.ref_type }}" == "tag" ]]; then + args+=(--version "${{ github.ref_name }}") + fi + + ./scripts/generate-docs.sh "${args[@]}" + + - name: Print docs version + shell: bash + run: | + set -euo pipefail + + PROJECT_NUMBER="${{ steps.build_docs.outputs.project_number }}" + + if [[ -z "$PROJECT_NUMBER" ]]; then + echo "ERROR: build_docs step did not emit a project_number output." + exit 1 + fi + + echo "Docs version: ${PROJECT_NUMBER}" + + { + echo "Docs version: \`${PROJECT_NUMBER}\`" + echo "" + echo "> Note: On a non-tag/release run, the version will resolve to:" + echo " \`--\`" + } >>"$GITHUB_STEP_SUMMARY" + + - name: Verify docs were generated + shell: bash + run: | + set -euo pipefail + if [[ ! -f docs/doxygen/html/index.html ]]; then + echo "ERROR: Expected docs at docs/doxygen/html/index.html but file not found." + exit 1 + fi + + # `inputs.*` is only populated on `workflow_call`. On a direct + # `pull_request` trigger every `inputs.*` lookup is the empty string, so + # we OR-fold to the documented defaults to keep PR runs behaving the same + # as the workflow_call default of `upload_artifact: true`. + - name: Resolve artifact metadata + id: artifact_meta + if: inputs.upload_artifact || github.event_name == 'pull_request' + shell: bash + env: + INPUT_NAME: ${{ inputs.artifact_name || 'livekit-cpp-docs' }} + INPUT_RETENTION: ${{ inputs.artifact_retention_days || '7' }} + run: | + set -euo pipefail + if [[ -z "$INPUT_NAME" ]]; then + echo "ERROR: artifact name resolved to empty." + exit 1 + fi + echo "name=${INPUT_NAME}" >>"$GITHUB_OUTPUT" + echo "retention=${INPUT_RETENTION}" >>"$GITHUB_OUTPUT" + + - name: Upload docs artifact + if: steps.artifact_meta.outputs.name != '' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ${{ steps.artifact_meta.outputs.name }} + path: docs/doxygen/html/ + retention-days: ${{ steps.artifact_meta.outputs.retention }} + if-no-files-found: error + + # Simple check to make sure the documentation artifact is valid. + - name: Re-download artifact (publish workflow pre-validation) + if: steps.artifact_meta.outputs.name != '' + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + with: + name: ${{ steps.artifact_meta.outputs.name }} + path: html + + - name: Inspect downloaded artifact + if: steps.artifact_meta.outputs.name != '' + shell: bash + run: | + set -euo pipefail + + NAME="${{ steps.artifact_meta.outputs.name }}" + ROOT="html" + + echo "Artifact '${NAME}' extracted into '${ROOT}/'. Top-level contents:" + # `sed -n '1,Np'` reads the whole pipe before truncating its output, + # so the producer (ls) never hits SIGPIPE under `pipefail`. + ls -la "$ROOT" | sed -n '1,40p' + + TOTAL=$(find "$ROOT" -type f | wc -l | tr -d ' ') + echo "Total files: ${TOTAL}" + + if [[ ! -f "${ROOT}/index.html" ]]; then + echo "ERROR: ${ROOT}/index.html is missing -- artifact layout regressed." + echo " publish-docs.yml expects html/index.html." + exit 1 + fi diff --git a/.github/workflows/make-release.yml b/.github/workflows/make-release.yml index 0db805ab..bfce4586 100644 --- a/.github/workflows/make-release.yml +++ b/.github/workflows/make-release.yml @@ -341,6 +341,8 @@ jobs: permissions: contents: write if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch' + outputs: + version: ${{ steps.version.outputs.version }} steps: - name: Checkout @@ -434,4 +436,6 @@ jobs: name: Publish Documentation needs: release uses: ./.github/workflows/publish-docs.yml + with: + version: ${{ needs.release.outputs.version }} secrets: inherit diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index f302146c..af55e60a 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -2,25 +2,43 @@ name: Publish docs on: workflow_dispatch: + inputs: + version: + description: 'Documentation version (e.g. v0.1.0)' + required: false + type: string workflow_call: + inputs: + version: + description: 'Documentation version (e.g. v0.1.0)' + required: false + type: string + +permissions: + contents: read + actions: read jobs: - docs: + validate: + name: Validate (build docs) + uses: ./.github/workflows/generate-docs.yml + with: + version: ${{ inputs.version || github.event.inputs.version || '' }} + upload_artifact: true + # Suffix with run_id so concurrent publish runs cannot collide on the + # artifact namespace within the same repository. + artifact_name: livekit-cpp-docs-${{ github.run_id }} + + publish: + name: Publish (S3 + CloudFront) + needs: validate runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - # Note: submodules and LFS not needed for docs generation - # Doxygen only reads from include/, docs/, README.md, and examples/ - - # Doxygen is available on ubuntu-latest, but we install explicitly for stability - - name: Install Doxygen - run: | - sudo apt-get update - sudo apt-get install -y doxygen graphviz - - - name: Build Docs (Doxygen) - run: | - doxygen docs/Doxyfile + - name: Download docs artifact + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + with: + name: ${{ needs.validate.outputs.artifact_name }} + path: html - name: S3 Upload run: | @@ -30,8 +48,12 @@ jobs: exit 1 fi - # Upload to rolling latest (no versioned docs, always overwrite) - aws s3 cp "$DOCS_DIR"/ s3://livekit-docs/client-sdk-cpp --recursive + VERSIONED_PREFIX="s3://livekit-docs/client-sdk-cpp/${{ needs.validate.outputs.project_number }}" + LATEST_PREFIX="s3://livekit-docs/client-sdk-cpp" + + # Upload immutable versioned docs, then refresh rolling latest. + aws s3 cp "$DOCS_DIR"/ "$VERSIONED_PREFIX" --recursive + aws s3 cp "$DOCS_DIR"/ "$LATEST_PREFIX" --recursive env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} diff --git a/.gitignore b/.gitignore index e6706760..c7fabe11 100644 --- a/.gitignore +++ b/.gitignore @@ -16,8 +16,9 @@ vcpkg_installed/ include/livekit/build.h received_green.avif docs/*.bak -docs/html/ -docs/latex/ +# Doxygen +docs/doxygen/html/ +docs/doxygen/latex/ .vs/ .vscode/ .cursor/ diff --git a/AGENTS.md b/AGENTS.md index 636f7e1e..6663fdef 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -80,6 +80,8 @@ Be sure to update the directory layout in this file if the directory layout chan | `cmake/` | Build helpers (`protobuf.cmake`, `spdlog.cmake`, `LiveKitConfig.cmake.in`) | | `docker/` | Dockerfile for CI and SDK distribution images | | `scripts/` | Developer / CI helper scripts (e.g. `clang-tidy.sh`) | +| `docs/` | Documentation root. `docs/` holds hand-written long-form Markdown intended to also read well on GitHub. | +| `docs/doxygen/` | Doxygen tool config, theme assets, and Doxygen-only content (`Doxyfile`, `index.md` mainpage, `customization/*.css`, `customization/header.html`, `customization/favicon.ico`). Files here use Doxygen-only syntax (`@ref`, `@brief`, …) and are not intended for human reading on their own. | | `.github/workflows/` | GitHub Actions CI workflows | ### Key Types @@ -238,7 +240,7 @@ with the same library loaded elsewhere in the host process. ### Public API Documentation (Doxygen) The public API (`include/livekit/*.h`) is what consumers read first and is also -published as a Doxygen site (`docs/Doxyfile`, `.github/workflows/publish-docs.yml`). +published as a Doxygen site (`docs/doxygen/Doxyfile`, `.github/workflows/publish-docs.yml`). Every doc comment in `include/livekit/` must use the rules below, and PRs that add or modify public symbols are gated on these rules during review. @@ -259,12 +261,13 @@ add or modify public symbols are gated on these rules during review. #### Required tags +- Document class/structs succinctly using `@brief Description.` +- Document functions/methods/namespaces succinctly using `@brief Description.` - Document parameters using `@param name Description.` - Document non-void return values using `@return Description.` -- Document thrown exceptions using `@throws ExceptionType When/why it's - thrown.` Operations that can fail without throwing should return - `Result` (see Error Handling above) and the variants should be - documented in the doc block. +- Document thrown exceptions using `@throws ExceptionType When/why it's thrown.` + Operations that can fail without throwing should return `Result` + (see Error Handling above) and the variants should be documented in the doc block. - Free-text "Parameters:", "Returns:", "Throws:" sections in legacy comments must be converted to the corresponding `@param` / `@return` / `@throws` tags when the comment is touched. @@ -290,15 +293,17 @@ render a deprecation callout (see "Deprecating a public API" above). #### Verifying locally -Run Doxygen from the repository root to regenerate the HTML docs: +Run the docs build script from the repository root: ```bash -doxygen docs/Doxyfile +./scripts/generate-docs.sh ``` -The output lands in `docs/html/`. Doxygen warnings about "is not documented" -indicate undocumented public symbols — adding documentation to them is -encouraged but not strictly required by these rules. +The output lands in `docs/doxygen/html/index.html`. The Doxyfile sets +`WARN_IF_UNDOCUMENTED = NO` (the 400+ "X is not documented" warnings for +internal symbols are too noisy to enforce) and `WARN_AS_ERROR = FAIL_ON_WARNINGS`, +so any other warning (broken `@ref`, unknown `@command`, unsupported HTML tag, +malformed table, missing `@param` on a documented function, …) fails the build. ### Integer Types diff --git a/README.md b/README.md index ec266b2f..665f0042 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ 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 +## Requirements - **CMake** ≥ 3.20 - **Rust / Cargo** (latest stable toolchain) - **Git LFS** (required for examples) @@ -31,13 +31,13 @@ Use this SDK to add realtime video, audio and data features to your C++ app. By - **macOS:** `brew install protobuf` (protobuf 3.x) ### 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 +- **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 > **Note**: If the SDK was built with Protobuf 6.0+, you also need `libabsl-dev` (Linux) or `abseil` (macOS). -## 🧩 Clone the Repository +## Clone the Repository Make sure to initialize the Rust submodule (`client-sdk-rust`): @@ -54,7 +54,7 @@ git submodule update --init --recursive git lfs pull ``` -## ⚙️ BUILD +## Building ### Quick Build (Using Build Scripts) @@ -99,7 +99,7 @@ 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\. +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 @@ -135,7 +135,7 @@ 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)** +**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. @@ -155,7 +155,7 @@ export PATH=$HOME/.cargo/bin:$PATH export PATH=$HOME/cmake-3.31/bin:$PATH ``` -## 🧪 Run Example +## Run Example ### Prerequisites @@ -169,7 +169,7 @@ lk token create -r test -i your_own_identity --join --valid-for 99999h --dev -- ### SimpleRoom -```bash` +```bash ./build-release/cpp-example-collection-build/simple_room/SimpleRoom --url $URL --token ``` @@ -203,7 +203,7 @@ The SimpleRpc example demonstrates how to: - Handle success, application errors, unsupported methods, and timeouts - Observe round-trip times (RTT) for each RPC call -#### 🔑 Generate Tokens +#### 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 @@ -211,7 +211,7 @@ lk token create -r test -i greeter --join --valid-for 99999h --dev --room=your_o lk token create -r test -i math-genius --join --valid-for 99999h --dev --room=your_own_room ``` -#### ▶ Start Participants +#### 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 @@ -232,14 +232,14 @@ The caller will automatically: - 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 +#### 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 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 @@ -379,11 +379,11 @@ Open the generated trace file in one of these viewers: --- -## 🧪 Integration & Stress Tests +## Integration & Stress Tests The SDK includes integration and stress tests using Google Test (gtest). -#### Build Tests +### Build Tests **Linux/macOS:** ```bash @@ -469,7 +469,7 @@ export LIVEKIT_TOKEN_B="$(lk token create --api-key devkey --api-secret secret - - **RPC**: Round-trip calls, max payload (15KB), timeouts, errors, concurrent calls - **Stress Tests**: High throughput, bidirectional RPC, memory pressure -## 🧰 Recommended Setup +## Recommended Setup ### macOS ```bash brew install cmake protobuf rust @@ -482,7 +482,7 @@ sudo apt install -y cmake protobuf-compiler build-essential curl https://sh.rustup.rs -sSf | sh ``` -## 🛠️ Development Tips +## Development Tips ### Update Rust version ```bash cd client-sdk-cpp @@ -599,7 +599,8 @@ valgrind --leak-check=full ./build-debug/bin/livekit_integration_tests valgrind --leak-check=full ./build-debug/bin/livekit_stress_tests ``` -# Running locally +## Running locally + 1. Install the livekit-server https://docs.livekit.io/transport/self-hosting/local/ @@ -620,21 +621,23 @@ lk token create \ --grant '{"canPublish":true,"canSubscribe":true,"canPublishData":true}' ``` -# Deprecation +## Deprecation + - 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.
- - - - - - - + + + + + + +
LiveKit Ecosystem
Agents SDKsPython · Node.js
LiveKit SDKsBrowser · Swift · Android · Flutter · React Native · Rust · Node.js · Python · Unity · Unity (WebGL) · ESP32 · C++
Starter AppsPython Agent · TypeScript Agent · React App · SwiftUI App · Android App · Flutter App · React Native App · Web Embed
UI ComponentsReact · Android Compose · SwiftUI · Flutter
Server APIsNode.js · Golang · Ruby · Java/Kotlin · Python · Rust · PHP (community) · .NET (community)
ResourcesDocs · Docs MCP Server · CLI · LiveKit Cloud
LiveKit Server OSSLiveKit server · Egress · Ingress · SIP
Agents SDKsPython · Node.js
LiveKit SDKsBrowser · Swift · Android · Flutter · React Native · Rust · Node.js · Python · Unity · Unity (WebGL) · ESP32 · C++
Starter AppsPython Agent · TypeScript Agent · React App · SwiftUI App · Android App · Flutter App · React Native App · Web Embed
UI ComponentsReact · Android Compose · SwiftUI · Flutter
Server APIsNode.js · Golang · Ruby · Java/Kotlin · Python · Rust · PHP (community) · .NET (community)
ResourcesDocs · Docs MCP Server · CLI · LiveKit Cloud
LiveKit Server OSSLiveKit server · Egress · Ingress · SIP
CommunityDeveloper Community · Slack · X · YouTube
diff --git a/README_BUILD.md b/README_BUILD.md index 1c6b0608..0c061bb2 100644 --- a/README_BUILD.md +++ b/README_BUILD.md @@ -1,4 +1,4 @@ -# LiveKit C++ Client SDK - Build Guide +# Build Guide ## Prerequisites diff --git a/docs/customization/custom.css b/docs/customization/custom.css deleted file mode 100644 index aabf9936..00000000 --- a/docs/customization/custom.css +++ /dev/null @@ -1,51 +0,0 @@ -@import url("https://fonts.googleapis.com/css2?family=Fira+Code:wght@300..700&family=Public+Sans:ital,wght@0,100..900;1,100..900&display=swap"); - -html { - --font-family: "Public Sans", -apple-system, Helvetica Neue, sans-serif; - --font-family-monospace: "Fira Code", SFMono-Regular, SF Mono, Menlo, Consolas, - monospace; - --primary-color: rgb(10, 37, 159); - --primary-dark-color: rgb(22, 35, 89); - --primary-light-color: rgb(172, 175, 191); - --fragment-link: var(--primary-color); -} - -#main-nav { - border-bottom: none; -} - -#projectname { - display: flex; - gap: 0.35em; -} - -#projectnumber { - background-color: #eee; - border-radius: var(--border-radius-medium); - padding-right: 0.5em; - padding-left: 0.25em; -} - -#nav-sync { - display: none; -} - -#nav-tree { - border-color: var(--separator-color); -} - -#page-nav { - background-color: var(--side-nav-background); - border-left: none; -} - -ul.page-outline li.vis { - background-color: rgba(0, 0, 0, 0.025); -} - -.arrow { - vertical-align: unset; - text-align: unset; - margin-right: 0; - height: unset; -} \ No newline at end of file diff --git a/docs/Doxyfile b/docs/doxygen/Doxyfile similarity index 99% rename from docs/Doxyfile rename to docs/doxygen/Doxyfile index ca0429d7..718adf31 100644 --- a/docs/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -42,19 +42,19 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "LiveKit C++ SDK" +PROJECT_NAME = "LiveKit C++ Client SDK" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = +PROJECT_NUMBER = $(LIVEKIT_DOXYGEN_PROJECT_NUMBER) # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewers a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = "Real-time audio/video SDK for C++" +PROJECT_BRIEF = "Real-time audio/video/data SDK for C++" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 @@ -67,14 +67,14 @@ PROJECT_LOGO = # when the HTML document is shown. Doxygen will copy the logo to the output # directory. -PROJECT_ICON = +PROJECT_ICON = docs/doxygen/customization/favicon.ico # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where Doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = +OUTPUT_DIRECTORY = docs/doxygen # If the CREATE_SUBDIRS tag is set to YES then Doxygen will create up to 4096 # sub-directories (in 2 levels) under the output directory of each output format @@ -215,7 +215,7 @@ SHORT_NAMES = NO # explicit @brief command for a brief description.) # The default value is: NO. -JAVADOC_AUTOBRIEF = NO +JAVADOC_AUTOBRIEF = YES # If the JAVADOC_BANNER tag is set to YES then Doxygen will interpret a line # such as @@ -906,7 +906,7 @@ WARNINGS = YES # will automatically be disabled. # The default value is: YES. -WARN_IF_UNDOCUMENTED = YES +WARN_IF_UNDOCUMENTED = NO # If the WARN_IF_DOC_ERROR tag is set to YES, Doxygen will generate warnings for # potential errors in the documentation, such as documenting some parameters in @@ -963,7 +963,7 @@ WARN_LAYOUT_FILE = YES # Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. # The default value is: NO. -WARN_AS_ERROR = NO +WARN_AS_ERROR = FAIL_ON_WARNINGS # The WARN_FORMAT tag determines the format of the warning messages that Doxygen # can produce. The string should contain the $file, $line, and $text tags, which @@ -1005,7 +1005,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = include/livekit docs/index.md README.md examples/simple_rpc/README.md +INPUT = include/livekit docs/doxygen/index.md # This tag can be used to specify the character encoding of the source files # that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses @@ -1220,7 +1220,7 @@ FILTER_SOURCE_PATTERNS = # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the Doxygen output. -USE_MDFILE_AS_MAINPAGE = docs/index.md +USE_MDFILE_AS_MAINPAGE = docs/doxygen/index.md # If the IMPLICIT_DIR_DOCS tag is set to YES, any README.md file found in sub- # directories of the project's root, is used as the documentation for that sub- @@ -1429,8 +1429,8 @@ HTML_STYLESHEET = # documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_STYLESHEET = docs/customization/doxygen-awesome.css \ - docs/customization/custom.css +HTML_EXTRA_STYLESHEET = docs/doxygen/customization/doxygen-awesome.css \ + docs/doxygen/customization/custom.css # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note @@ -2932,15 +2932,6 @@ DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) support -# this, this feature is disabled by default. -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_MULTI_TARGETS = NO - # If the GENERATE_LEGEND tag is set to YES Doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. diff --git a/docs/doxygen/customization/custom.css b/docs/doxygen/customization/custom.css new file mode 100644 index 00000000..541e517c --- /dev/null +++ b/docs/doxygen/customization/custom.css @@ -0,0 +1,109 @@ +@import url("https://fonts.googleapis.com/css2?family=Fira+Code:wght@300..700&family=Public+Sans:ital,wght@0,100..900;1,100..900&display=swap"); + +html { + --font-family: "Public Sans", -apple-system, Helvetica Neue, sans-serif; + --font-family-monospace: "Fira Code", SFMono-Regular, SF Mono, Menlo, Consolas, + monospace; + --primary-color: rgb(10, 37, 159); + --primary-dark-color: rgb(22, 35, 89); + --primary-light-color: rgb(172, 175, 191); + --fragment-link: var(--primary-color); + --project-version-background: rgba(0, 0, 0, 0.06); + --project-version-foreground: var(--page-secondary-foreground-color); +} + +@media (prefers-color-scheme: dark) { + html:not(.light-mode) { + --project-version-background: rgba(255, 255, 255, 0.1); + --project-version-foreground: var(--page-secondary-foreground-color); + } +} + +html.dark-mode { + --project-version-background: rgba(255, 255, 255, 0.1); + --project-version-foreground: var(--page-secondary-foreground-color); +} + +#main-nav { + border-bottom: none; +} + +#projectname { + display: flex; + gap: 0.35em; +} + +#projectnumber { + background-color: var(--project-version-background); + border-radius: var(--border-radius-medium); + color: var(--project-version-foreground); + padding-right: 0.5em; + padding-left: 0.25em; +} + +#nav-sync { + display: none; +} + +#nav-tree { + border-color: var(--separator-color); +} + +#page-nav { + background-color: var(--side-nav-background); + border-left: none; +} + +ul.page-outline li.vis { + background-color: rgba(0, 0, 0, 0.025); +} + +.arrow { + vertical-align: unset; + text-align: unset; + margin-right: 0; + height: unset; +} + +/* Better inline-code styling, e.g. `code` spans */ +span.tt, +.tt { + display: inline; + font-family: var(--font-family-monospace); + font-size: var(--code-font-size) !important; + background: var(--code-background); + color: var(--code-foreground); + border: 1px solid var(--separator-color); + border-radius: var(--border-radius-small); + padding: 2px 6px; + white-space: normal; +} + +.doxygen-readme-banner img { + display: block; + width: 100%; + height: auto; +} + +.doxygen-banner-dark { + display: none !important; +} + +/* Follow Doxygen Awesome's theme toggles (system, forced dark, forced light). */ +@media (prefers-color-scheme: dark) { + html:not(.light-mode) .doxygen-banner-light { + display: none !important; + } + + html:not(.light-mode) .doxygen-banner-dark { + display: block !important; + } +} + +html.dark-mode .doxygen-banner-light { + display: none !important; +} + +html.dark-mode .doxygen-banner-dark { + display: block !important; +} diff --git a/docs/customization/doxygen-awesome.css b/docs/doxygen/customization/doxygen-awesome.css similarity index 100% rename from docs/customization/doxygen-awesome.css rename to docs/doxygen/customization/doxygen-awesome.css diff --git a/docs/customization/favicon.ico b/docs/doxygen/customization/favicon.ico similarity index 100% rename from docs/customization/favicon.ico rename to docs/doxygen/customization/favicon.ico diff --git a/docs/customization/header.html b/docs/doxygen/customization/header.html similarity index 100% rename from docs/customization/header.html rename to docs/doxygen/customization/header.html diff --git a/docs/index.md b/docs/doxygen/index.md similarity index 66% rename from docs/index.md rename to docs/doxygen/index.md index 2a8e5a28..56df9d37 100644 --- a/docs/index.md +++ b/docs/doxygen/index.md @@ -1,9 +1,7 @@ -# LiveKit C++ Client SDK +# Overview Build real-time audio/video applications in C++ with LiveKit. -> **Note:** This SDK is currently in Developer Preview. APIs may change before the stable release. - ## Quick Start ```cpp @@ -102,18 +100,36 @@ void shutdownLivekit() { See the [GitHub README](https://github.com/livekit/client-sdk-cpp#readme) for build instructions. **Requirements:** + - CMake ≥ 3.20 - Rust/Cargo (latest stable) - Platform: Windows, macOS, or Linux ## Examples -- [SimpleRoom](https://github.com/livekit/client-sdk-cpp/tree/main/examples/simple_room) - Basic room connection with audio/video -- [SimpleRpc](https://github.com/livekit/client-sdk-cpp/tree/main/examples/simple_rpc) - Remote procedure calls between participants -- [SimpleDataStream](https://github.com/livekit/client-sdk-cpp/tree/main/examples/simple_data_stream) - Send text and binary data streams +**Getting started** + +- [SimpleRoom](https://github.com/livekit-examples/cpp-example-collection/tree/main/simple_room) - Minimal room connection that publishes audio and video. +- [BasicRoom](https://github.com/livekit-examples/cpp-example-collection/tree/main/basic_room) - Publishes synthetic noise audio plus an RGB test pattern with capture loops; runs until Ctrl-C. +- [HelloLiveKit](https://github.com/livekit-examples/cpp-example-collection/tree/main/hello_livekit) - Two-process sender/receiver demo of publishing video and a data track from one app and subscribing in another. + +**Logging** + +- [LoggingLevels](https://github.com/livekit-examples/cpp-example-collection/tree/main/logging_levels) - Demonstrates @ref livekit::setLogLevel() and @ref livekit::setLogCallback(), including custom sinks for redirecting SDK logs. + +**RPC and data** + +- [SimpleRpc](https://github.com/livekit-examples/cpp-example-collection/tree/main/simple_rpc) - Remote procedure calls between participants. +- [SimpleDataStream](https://github.com/livekit-examples/cpp-example-collection/tree/main/simple_data_stream) - Send and receive text and binary data streams. +- [PingPong](https://github.com/livekit-examples/cpp-example-collection/tree/main/ping_pong) - Two-process round-trip over data tracks that prints RTT and one-way latency metrics. +- [SimpleJoystick](https://github.com/livekit-examples/cpp-example-collection/tree/main/simple_joystick) - Interactive sender/receiver: keyboard-driven joystick commands delivered via RPC, with auto-reconnect. + +**Advanced video** + +- [UserTimestampedVideo](https://github.com/livekit-examples/cpp-example-collection/tree/main/user_timestamped_video) - Producer/consumer pair showing per-frame `VideoFrameMetadata::user_timestamp_us` and the rich `setOnVideoFrameEventCallback` vs. legacy `setOnVideoFrameCallback` paths. ## Resources - [GitHub Repository](https://github.com/livekit/client-sdk-cpp) - [LiveKit Documentation](https://docs.livekit.io/) -- [Community Slack](https://livekit.io/join-slack) \ No newline at end of file +- [Community Slack](https://livekit.io/join-slack) diff --git a/docs/layout.xml b/docs/layout.xml deleted file mode 100644 index ae6ed399..00000000 --- a/docs/layout.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/include/livekit/audio_stream.h b/include/livekit/audio_stream.h index 2cfa2501..0729327b 100644 --- a/include/livekit/audio_stream.h +++ b/include/livekit/audio_stream.h @@ -36,13 +36,11 @@ namespace proto { class FfiEvent; } -/// @brief Event containing an audio frame received from an AudioStream. -/// -/// This struct wraps an AudioFrame and is used as the output type when -/// reading from an AudioStream. // NOLINTBEGIN(bugprone-exception-escape) // AudioFrame can throw in various places monitored by bugprone-exception-escape // Suppressing for now, would require significant refactor to fix + +/// @brief Event containing an audio frame received from an AudioStream. struct AudioFrameEvent { AudioFrame frame; ///< The decoded PCM audio frame. }; diff --git a/include/livekit/data_track_error.h b/include/livekit/data_track_error.h index d0e67b19..ef7f1032 100644 --- a/include/livekit/data_track_error.h +++ b/include/livekit/data_track_error.h @@ -29,6 +29,7 @@ class LocalDataTrackTryPushError; class SubscribeDataTrackError; } // namespace proto +/// @brief Error code returned when publishing a local data track fails. enum class PublishDataTrackErrorCode : std::uint32_t { UNKNOWN = 0, INVALID_HANDLE = 1, @@ -42,6 +43,7 @@ enum class PublishDataTrackErrorCode : std::uint32_t { INTERNAL = 9, }; +/// @brief Error details returned when publishing a local data track fails. struct PublishDataTrackError { PublishDataTrackErrorCode code{PublishDataTrackErrorCode::UNKNOWN}; std::string message; @@ -49,6 +51,7 @@ struct PublishDataTrackError { LIVEKIT_API static PublishDataTrackError fromProto(const proto::PublishDataTrackError& error); }; +/// @brief Error code returned when pushing a frame to a local data track fails. enum class LocalDataTrackTryPushErrorCode : std::uint32_t { UNKNOWN = 0, INVALID_HANDLE = 1, @@ -57,6 +60,7 @@ enum class LocalDataTrackTryPushErrorCode : std::uint32_t { INTERNAL = 4, }; +/// @brief Error details returned when pushing a frame to a local data track fails. struct LocalDataTrackTryPushError { LocalDataTrackTryPushErrorCode code{LocalDataTrackTryPushErrorCode::UNKNOWN}; std::string message; @@ -64,6 +68,7 @@ struct LocalDataTrackTryPushError { LIVEKIT_API static LocalDataTrackTryPushError fromProto(const proto::LocalDataTrackTryPushError& error); }; +/// @brief Error code returned when subscribing to a remote data track fails. enum class SubscribeDataTrackErrorCode : std::uint32_t { UNKNOWN = 0, INVALID_HANDLE = 1, @@ -74,6 +79,7 @@ enum class SubscribeDataTrackErrorCode : std::uint32_t { INTERNAL = 6, }; +/// @brief Error details returned when subscribing to a remote data track fails. struct SubscribeDataTrackError { SubscribeDataTrackErrorCode code{SubscribeDataTrackErrorCode::UNKNOWN}; std::string message; diff --git a/include/livekit/data_track_stream.h b/include/livekit/data_track_stream.h index 466677c8..f5f07a90 100644 --- a/include/livekit/data_track_stream.h +++ b/include/livekit/data_track_stream.h @@ -53,6 +53,7 @@ class DataTrackStreamReadResponse; /// } class LIVEKIT_API DataTrackStream { public: + /// @brief Options for subscribing to a remote data track stream. struct Options { /// Maximum frames buffered on the Rust side. Rust defaults to 16. std::optional buffer_size{std::nullopt}; diff --git a/include/livekit/e2ee.h b/include/livekit/e2ee.h index 295a68ab..4bc81a26 100644 --- a/include/livekit/e2ee.h +++ b/include/livekit/e2ee.h @@ -99,7 +99,7 @@ struct E2EEOptions { EncryptionType encryption_type = EncryptionType::GCM; // default & recommended }; -/// E2EE manager for a connected room. +/// @brief E2EE manager for a connected room. /// /// Lifetime: /// - Owned by Room. Applications must not construct E2EEManager directly. @@ -116,11 +116,13 @@ struct E2EEOptions { /// not required by the API shape (keys may be supplied later). class LIVEKIT_API E2EEManager { public: - /// If your application requires key rotation during the lifetime of a single - /// room or unique keys per participant (such as when implementing the MEGOLM - /// or MLS protocol), you' can do it via key provider and frame cryptor. refer - /// https://docs.livekit.io/home/client/encryption/#custom-key-provider doe - /// details + /// @brief Manages encryption keys used by the E2EE pipeline. + /// + /// Use this for key rotation during the lifetime of a single room or for + /// per-participant keys (e.g., when implementing the MEGOLM or MLS protocol) + /// via shared-key or participant-keyed APIs paired with the frame cryptor. + /// See https://docs.livekit.io/home/client/encryption/#custom-key-provider + /// for details. class LIVEKIT_API KeyProvider { public: ~KeyProvider() = default; @@ -158,6 +160,7 @@ class LIVEKIT_API E2EEManager { KeyProviderOptions options_; }; + /// @brief Frame-level cryptor controls for one participant. class LIVEKIT_API FrameCryptor { public: FrameCryptor(std::uint64_t room_handle, std::string participant_identity, int key_index, bool enabled); diff --git a/include/livekit/livekit.h b/include/livekit/livekit.h index f7906c42..d5ea02f5 100644 --- a/include/livekit/livekit.h +++ b/include/livekit/livekit.h @@ -40,6 +40,8 @@ #include "livekit/video_stream.h" #include "livekit/visibility.h" +/// @namespace livekit +/// @brief Public API for the LiveKit C++ Client SDK. namespace livekit { /// The log sink to use for SDK messages. diff --git a/include/livekit/local_participant.h b/include/livekit/local_participant.h index 608e5692..9369a914 100644 --- a/include/livekit/local_participant.h +++ b/include/livekit/local_participant.h @@ -43,6 +43,7 @@ class FfiClient; class Track; class LocalTrackPublication; +/// @brief Data passed to a registered RPC method handler. struct RpcInvocationData { std::string request_id; std::string caller_identity; @@ -50,9 +51,7 @@ struct RpcInvocationData { double response_timeout_sec; // seconds }; -/// Represents the local participant in a room. -/// -/// LocalParticipant, built on top of the participant.h base class. +/// @brief Represents the local participant in a room. class LIVEKIT_API LocalParticipant : public Participant { public: using PublicationMap = std::unordered_map>; diff --git a/include/livekit/local_track_publication.h b/include/livekit/local_track_publication.h index 50f5fd74..412b4fb9 100644 --- a/include/livekit/local_track_publication.h +++ b/include/livekit/local_track_publication.h @@ -25,6 +25,7 @@ namespace proto { class OwnedTrackPublication; } +/// @brief A track published by a local participant. class LIVEKIT_API LocalTrackPublication : public TrackPublication { public: /// Note, this LocalTrackPublication is constructed internally only; diff --git a/include/livekit/participant.h b/include/livekit/participant.h index 194ee4bf..2b5857db 100644 --- a/include/livekit/participant.h +++ b/include/livekit/participant.h @@ -27,8 +27,10 @@ namespace livekit { +/// @brief Identifies the type of participant connected to a room. enum class ParticipantKind { Standard = 0, Ingress, Egress, Sip, Agent }; +/// @brief Base class for local and remote room participants. class LIVEKIT_API Participant { public: Participant(FfiHandle handle, std::string sid, std::string name, std::string identity, std::string metadata, @@ -121,4 +123,4 @@ class LIVEKIT_API Participant { DisconnectReason reason_; }; -} // namespace livekit \ No newline at end of file +} // namespace livekit diff --git a/include/livekit/remote_participant.h b/include/livekit/remote_participant.h index f354b36b..1addd141 100644 --- a/include/livekit/remote_participant.h +++ b/include/livekit/remote_participant.h @@ -27,6 +27,7 @@ namespace livekit { class RemoteTrackPublication; +/// @brief Represents a remote participant in a LiveKit room. class LIVEKIT_API RemoteParticipant : public Participant { public: using PublicationMap = std::unordered_map>; diff --git a/include/livekit/remote_track_publication.h b/include/livekit/remote_track_publication.h index 2568a55d..b6475612 100644 --- a/include/livekit/remote_track_publication.h +++ b/include/livekit/remote_track_publication.h @@ -25,6 +25,7 @@ namespace proto { class OwnedTrackPublication; } +/// @brief A track published by a remote participant. class LIVEKIT_API RemoteTrackPublication : public TrackPublication { public: /// Note, this RemoteTrackPublication is constructed internally only; diff --git a/include/livekit/room.h b/include/livekit/room.h index cd08be0a..9568d9ce 100644 --- a/include/livekit/room.h +++ b/include/livekit/room.h @@ -108,15 +108,13 @@ class LIVEKIT_API Room { /// MyDelegate del; /// Room room; /// room.setDelegate(&del); + /// + /// @param delegate The RoomDelegate implementation to receive room lifecycle callbacks. void setDelegate(RoomDelegate* delegate); /// Connect to a LiveKit room using the given URL and token, applying the /// supplied connection options. /// - /// @param url WebSocket URL of the LiveKit server. - /// @param token Access token for authentication. - /// @param options Connection options controlling auto-subscribe, - /// dynacast, E2EE, and WebRTC configuration. /// Behavior: /// - Registers an FFI event listener *before* sending the connect request. /// - Sends a proto::FfiRequest::Connect with the URL, token, @@ -128,6 +126,11 @@ class LIVEKIT_API Room { /// RoomOptions defaults auto_subscribe = true. /// Without auto_subscribe enabled, remote tracks will NOT be subscribed /// automatically, and no remote audio/video will ever arrive. + /// + /// @param url WebSocket URL of the LiveKit server. + /// @param token Access token for authentication. + /// @param options Connection options controlling auto-subscribe, + /// dynacast, E2EE, and WebRTC configuration. bool connect(const std::string& url, const std::string& token, const RoomOptions& options); /// @deprecated Use connect() instead. @@ -215,12 +218,15 @@ class LIVEKIT_API Room { /// - The ByteStreamReader remains valid as long as the shared_ptr is held, /// preventing lifetime-related crashes when reading asynchronously. /// + /// @param topic The topic to register the byte stream handler for. + /// @param handler The ByteStreamHandler to invoke when a byte stream is received. /// @throws std::runtime_error if a handler is already registered for the topic. void registerByteStreamHandler(const std::string& topic, ByteStreamHandler handler); /// Unregister the byte stream handler for the given topic. /// /// If no handler exists for the topic, this function is a no-op. + /// @param topic The topic to unregister the byte stream handler for. void unregisterByteStreamHandler(const std::string& topic); /// Returns the room's E2EE manager, or nullptr if E2EE was not enabled at diff --git a/include/livekit/stats.h b/include/livekit/stats.h index 6755edde..ce45d42e 100644 --- a/include/livekit/stats.h +++ b/include/livekit/stats.h @@ -51,6 +51,7 @@ class CertificateStats; class StreamStats; } // namespace proto +/// @brief State of a WebRTC data channel. enum class DataChannelState { Connecting, Open, @@ -59,6 +60,7 @@ enum class DataChannelState { Unknown, }; +/// @brief Reason outbound media quality is currently limited. enum class QualityLimitationReason { None, Cpu, @@ -66,12 +68,14 @@ enum class QualityLimitationReason { Other, }; +/// @brief ICE role used by a transport. enum class IceRole { Unknown, Controlling, Controlled, }; +/// @brief DTLS transport state. enum class DtlsTransportState { New, Connecting, @@ -81,6 +85,7 @@ enum class DtlsTransportState { Unknown, }; +/// @brief ICE transport state. enum class IceTransportState { New, Checking, @@ -92,12 +97,14 @@ enum class IceTransportState { Unknown, }; +/// @brief DTLS role used by a transport. enum class DtlsRole { Client, Server, Unknown, }; +/// @brief State of an ICE candidate pair. enum class IceCandidatePairState { Frozen, Waiting, @@ -107,6 +114,7 @@ enum class IceCandidatePairState { Unknown, }; +/// @brief Type of ICE candidate. enum class IceCandidateType { Host, Srflx, @@ -115,6 +123,7 @@ enum class IceCandidateType { Unknown, }; +/// @brief Transport protocol used by an ICE server. enum class IceServerTransportProtocol { Udp, Tcp, @@ -122,6 +131,7 @@ enum class IceServerTransportProtocol { Unknown, }; +/// @brief TCP candidate type for an ICE candidate. enum class IceTcpCandidateType { Active, Passive, @@ -133,11 +143,13 @@ enum class IceTcpCandidateType { // Leaf stats types // ---------------------- +/// @brief Base data shared by RTC stats records. struct RtcStatsData { std::string id; std::int64_t timestamp_ms; }; +/// @brief Codec statistics for an RTP stream. struct CodecStats { std::uint32_t payload_type; std::string transport_id; @@ -147,6 +159,7 @@ struct CodecStats { std::string sdp_fmtp_line; }; +/// @brief Base statistics for an RTP stream. struct RtpStreamStats { std::uint32_t ssrc; std::string kind; @@ -154,12 +167,14 @@ struct RtpStreamStats { std::string codec_id; }; +/// @brief Statistics for received RTP streams. struct ReceivedRtpStreamStats { std::uint64_t packets_received; std::int64_t packets_lost; double jitter; }; +/// @brief Statistics for inbound RTP media. struct InboundRtpStreamStats { std::string track_identifier; std::string mid; @@ -216,11 +231,13 @@ struct InboundRtpStreamStats { std::uint32_t fec_ssrc; }; +/// @brief Statistics for sent RTP streams. struct SentRtpStreamStats { std::uint64_t packets_sent; std::uint64_t bytes_sent; }; +/// @brief Statistics for outbound RTP media. struct OutboundRtpStreamStats { std::string mid; std::string media_source_id; @@ -254,6 +271,7 @@ struct OutboundRtpStreamStats { std::string scalability_mode; }; +/// @brief Statistics reported by a remote receiver for a local outbound RTP stream. struct RemoteInboundRtpStreamStats { std::string local_id; double round_trip_time; @@ -262,6 +280,7 @@ struct RemoteInboundRtpStreamStats { std::uint64_t round_trip_time_measurements; }; +/// @brief Statistics reported by a remote sender for a local inbound RTP stream. struct RemoteOutboundRtpStreamStats { std::string local_id; double remote_timestamp; @@ -271,11 +290,13 @@ struct RemoteOutboundRtpStreamStats { std::uint64_t round_trip_time_measurements; }; +/// @brief Common statistics for a local media source. struct MediaSourceStats { std::string track_identifier; std::string kind; }; +/// @brief Statistics for a local audio source. struct AudioSourceStats { double audio_level; double total_audio_energy; @@ -288,6 +309,7 @@ struct AudioSourceStats { std::uint64_t total_samples_captured; }; +/// @brief Statistics for a local video source. struct VideoSourceStats { std::uint32_t width; std::uint32_t height; @@ -310,11 +332,13 @@ struct AudioPlayoutStats { std::uint64_t total_samples_count; ///< Total number of samples played out. }; +/// @brief Statistics for a peer connection. struct PeerConnectionStats { std::uint32_t data_channels_opened; std::uint32_t data_channels_closed; }; +/// @brief Statistics for a WebRTC data channel. struct DataChannelStats { std::string label; std::string protocol; @@ -326,6 +350,7 @@ struct DataChannelStats { std::uint64_t bytes_received; }; +/// @brief Statistics for a WebRTC transport. struct TransportStats { std::uint64_t packets_sent; std::uint64_t packets_received; @@ -345,6 +370,7 @@ struct TransportStats { std::uint32_t selected_candidate_pair_changes; }; +/// @brief Statistics for a selected or candidate ICE pair. struct CandidatePairStats { std::string transport_id; std::string local_candidate_id; @@ -370,6 +396,7 @@ struct CandidatePairStats { std::uint64_t bytes_discarded_on_send; }; +/// @brief Statistics for a local or remote ICE candidate. struct IceCandidateStats { std::string transport_id; std::string address; @@ -386,6 +413,7 @@ struct IceCandidateStats { std::optional tcp_type; }; +/// @brief Statistics for a DTLS certificate. struct CertificateStats { std::string fingerprint; std::string fingerprint_algorithm; @@ -393,6 +421,7 @@ struct CertificateStats { std::string issuer_certificate_id; }; +/// @brief Statistics for a media stream. struct StreamStats { std::string id; std::string stream_identifier; @@ -402,11 +431,13 @@ struct StreamStats { // High-level RtcStats wrapper // ---------------------- +/// @brief Typed RTC stats wrapper for codec statistics. struct RtcCodecStats { RtcStatsData rtc; CodecStats codec; }; +/// @brief Typed RTC stats wrapper for inbound RTP statistics. struct RtcInboundRtpStats { RtcStatsData rtc; RtpStreamStats stream; @@ -414,6 +445,7 @@ struct RtcInboundRtpStats { InboundRtpStreamStats inbound; }; +/// @brief Typed RTC stats wrapper for outbound RTP statistics. struct RtcOutboundRtpStats { RtcStatsData rtc; RtpStreamStats stream; @@ -421,6 +453,7 @@ struct RtcOutboundRtpStats { OutboundRtpStreamStats outbound; }; +/// @brief Typed RTC stats wrapper for remote inbound RTP statistics. struct RtcRemoteInboundRtpStats { RtcStatsData rtc; RtpStreamStats stream; @@ -428,6 +461,7 @@ struct RtcRemoteInboundRtpStats { RemoteInboundRtpStreamStats remote_inbound; }; +/// @brief Typed RTC stats wrapper for remote outbound RTP statistics. struct RtcRemoteOutboundRtpStats { RtcStatsData rtc; RtpStreamStats stream; @@ -435,6 +469,7 @@ struct RtcRemoteOutboundRtpStats { RemoteOutboundRtpStreamStats remote_outbound; }; +/// @brief Typed RTC stats wrapper for media source statistics. struct RtcMediaSourceStats { RtcStatsData rtc; MediaSourceStats source; @@ -442,46 +477,55 @@ struct RtcMediaSourceStats { VideoSourceStats video; }; +/// @brief Typed RTC stats wrapper for audio playout statistics. struct RtcMediaPlayoutStats { RtcStatsData rtc; AudioPlayoutStats audio_playout; }; +/// @brief Typed RTC stats wrapper for peer connection statistics. struct RtcPeerConnectionStats { RtcStatsData rtc; PeerConnectionStats pc; }; +/// @brief Typed RTC stats wrapper for data channel statistics. struct RtcDataChannelStats { RtcStatsData rtc; DataChannelStats dc; }; +/// @brief Typed RTC stats wrapper for transport statistics. struct RtcTransportStats { RtcStatsData rtc; TransportStats transport; }; +/// @brief Typed RTC stats wrapper for ICE candidate pair statistics. struct RtcCandidatePairStats { RtcStatsData rtc; CandidatePairStats candidate_pair; }; +/// @brief Typed RTC stats wrapper for local ICE candidate statistics. struct RtcLocalCandidateStats { RtcStatsData rtc; IceCandidateStats candidate; }; +/// @brief Typed RTC stats wrapper for remote ICE candidate statistics. struct RtcRemoteCandidateStats { RtcStatsData rtc; IceCandidateStats candidate; }; +/// @brief Typed RTC stats wrapper for certificate statistics. struct RtcCertificateStats { RtcStatsData rtc; CertificateStats certificate; }; +/// @brief Typed RTC stats wrapper for media stream statistics. struct RtcStreamStats { RtcStatsData rtc; StreamStats stream; @@ -495,6 +539,7 @@ using RtcStatsVariant = RtcDataChannelStats, RtcTransportStats, RtcCandidatePairStats, RtcLocalCandidateStats, RtcRemoteCandidateStats, RtcCertificateStats, RtcStreamStats>; +/// @brief Variant wrapper for typed RTC stats records. struct RtcStats { RtcStatsVariant stats; }; diff --git a/include/livekit/subscription_thread_dispatcher.h b/include/livekit/subscription_thread_dispatcher.h index 934e669c..f62b78ce 100644 --- a/include/livekit/subscription_thread_dispatcher.h +++ b/include/livekit/subscription_thread_dispatcher.h @@ -208,6 +208,7 @@ class LIVEKIT_API SubscriptionThreadDispatcher { /// /// @param participant_identity Identity of the remote participant. /// @param source Track source associated with the subscription. + /// @param track_name Track name associated with the subscription. /// @param track Subscribed remote track to read from. void handleTrackSubscribed(const std::string& participant_identity, TrackSource source, const std::string& track_name, const std::shared_ptr& track); diff --git a/include/livekit/track.h b/include/livekit/track.h index 00c2d744..544e82d4 100644 --- a/include/livekit/track.h +++ b/include/livekit/track.h @@ -32,12 +32,14 @@ namespace livekit { class LocalTrackPublication; +/// @brief Media kind for an audio or video track. enum class TrackKind { KIND_UNKNOWN = 0, KIND_AUDIO = 1, KIND_VIDEO = 2, }; +/// @brief Source category for a published track. enum class TrackSource { SOURCE_UNKNOWN = 0, SOURCE_CAMERA = 1, @@ -46,12 +48,14 @@ enum class TrackSource { SOURCE_SCREENSHARE_AUDIO = 4, }; +/// @brief Stream state reported for a subscribed track. enum class StreamState { STATE_UNKNOWN = 0, STATE_ACTIVE = 1, STATE_PAUSED = 2, }; +/// @brief Optional audio processing or encoding feature advertised for a track. enum class AudioTrackFeature { TF_STEREO = 0, TF_NO_DTX = 1, @@ -62,15 +66,14 @@ enum class AudioTrackFeature { TF_PRECONNECT_BUFFER = 6, }; +/// @brief Per-participant track subscription permission configuration. struct ParticipantTrackPermission { std::string participant_identity; std::optional allow_all; std::vector allowed_track_sids; }; -// ============================================================ -// Base Track -// ============================================================ +/// @brief Base class for local and remote media tracks. class LIVEKIT_API Track { public: virtual ~Track() = default; @@ -163,4 +166,4 @@ class LIVEKIT_API Track { std::optional mime_type_; }; -} // namespace livekit \ No newline at end of file +} // namespace livekit diff --git a/include/livekit/track_publication.h b/include/livekit/track_publication.h index 1552b937..c618cd7d 100644 --- a/include/livekit/track_publication.h +++ b/include/livekit/track_publication.h @@ -32,7 +32,7 @@ class Track; class LocalTrack; class RemoteTrack; -/// C++ TrackPublication. +/// @brief Base class for a track that has been published to a room. /// /// Wraps the immutable publication info plus an FFI handle, and /// holds a weak reference to the associated Track (if any). diff --git a/include/livekit/video_frame.h b/include/livekit/video_frame.h index 79c2d016..e13c7b8d 100644 --- a/include/livekit/video_frame.h +++ b/include/livekit/video_frame.h @@ -28,6 +28,7 @@ namespace livekit { /// Mirror of WebRTC video buffer type enum class VideoBufferType { RGBA = 0, ABGR, ARGB, BGRA, RGB24, I420, I420A, I422, I444, I010, NV12 }; +/// @brief Plane layout metadata for a VideoFrame buffer. struct VideoPlaneInfo { std::uintptr_t data_ptr; ///< pointer to plane data (for FFI) std::uint32_t stride; ///< bytes per row diff --git a/include/livekit/video_stream.h b/include/livekit/video_stream.h index 5ed03c78..1554fee9 100644 --- a/include/livekit/video_stream.h +++ b/include/livekit/video_stream.h @@ -65,6 +65,7 @@ class FfiEvent; /// stream->close(); // optional, called automatically in destructor class LIVEKIT_API VideoStream { public: + /// @brief Options for creating a decoded video frame stream. struct Options { /// Maximum number of VideoFrameEvent items buffered in the internal queue. /// 0 means "unbounded" (the queue can grow without limit). diff --git a/scripts/generate-docs.sh b/scripts/generate-docs.sh new file mode 100755 index 00000000..cd373f1f --- /dev/null +++ b/scripts/generate-docs.sh @@ -0,0 +1,169 @@ +#!/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. + +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +repo_root="$(cd "${script_dir}/.." && pwd -P)" + +usage() { + cat <<'EOF' +Usage: ./scripts/generate-docs.sh [--version VERSION] + +Build Doxygen HTML docs with a visible project version. + +Options: + --version VERSION + Version to display in generated docs. Accepts either "0.1.0" or + "v0.1.0"; the rendered Doxygen PROJECT_NUMBER will include "v". + -h, --help + Show this help and exit. + +When --version is omitted, the script uses LIVEKIT_DOXYGEN_PROJECT_NUMBER if +already set. Otherwise it derives a local version from git describe. +EOF +} + +version_arg="" + +while (($#)); do + case "$1" in + --version) + shift + if (($# == 0)); then + echo "ERROR: --version requires a value" >&2 + exit 2 + fi + version_arg="$1" + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "ERROR: unknown argument: $1" >&2 + usage >&2 + exit 2 + ;; + esac +done + +if ! command -v doxygen >/dev/null 2>&1; then + echo "ERROR: doxygen not found in PATH." >&2 + echo "Install: brew install doxygen graphviz (macOS)" >&2 + echo " apt install doxygen graphviz (Linux)" >&2 + exit 1 +fi + +project_number="${LIVEKIT_DOXYGEN_PROJECT_NUMBER:-}" + +if [[ -n "$version_arg" ]]; then + version="${version_arg#v}" + project_number="v${version}" +elif [[ -z "$project_number" ]]; then + if git -C "$repo_root" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + described="$(git -C "$repo_root" describe --tags --dirty --always 2>/dev/null || true)" + else + described="" + fi + + # git describe emits "--g[-dirty]" + # Strip unneeded "g" so it's easier to read + if [[ "$described" =~ ^(.+-[0-9]+)-g([0-9a-f]+)(.*)$ ]]; then + described="${BASH_REMATCH[1]}-${BASH_REMATCH[2]}${BASH_REMATCH[3]}" + fi + + if [[ -z "$described" ]]; then + project_number="v0.0.0-dev" + elif [[ "$described" == v* ]]; then + project_number="$described" + else + project_number="v0.0.0-dev+${described}" + fi +elif [[ "$project_number" != v* ]]; then + project_number="v${project_number}" +fi + +if [[ -z "$project_number" ]]; then + echo "ERROR: derived Doxygen project number is empty" >&2 + exit 1 +fi + +if [[ "$project_number" =~ [[:space:]] ]]; then + echo "ERROR: Doxygen project number contains whitespace: '$project_number'" >&2 + exit 1 +fi + +echo "==> Building Doxygen docs with PROJECT_NUMBER=${project_number}" +cd "$repo_root" + +# Build a Doxygen-only mainpage derived from README.md so README can remain +# GitHub-optimized while docs rendering stays strict under WARN_AS_ERROR. +mainpage_rel="docs/doxygen/README.doxygen.md" +mainpage_tmp="${repo_root}/${mainpage_rel}" +doxyfile_tmp="$(mktemp "${TMPDIR:-/tmp}/livekit-doxyfile.XXXXXX")" +cleanup() { + rm -f "$mainpage_tmp" "$doxyfile_tmp" +} +trap cleanup EXIT + +python3 - "$repo_root/README.md" "$mainpage_tmp" <<'PY' +import pathlib +import re +import sys + +readme_path = pathlib.Path(sys.argv[1]) +out_path = pathlib.Path(sys.argv[2]) +text = readme_path.read_text(encoding="utf-8") + +# Doxygen's Markdown parser rejects /. Replace the banner block +# with a docs-only dark/light pair controlled by CSS in custom.css. +banner_replacement = """ + +
+ The LiveKit icon, the name of the repository and some sample code in the background. + The LiveKit icon, the name of the repository and some sample code in the background. +
+ +""" +text = re.sub( + r".*?", + banner_replacement, + text, + flags=re.DOTALL, +) + +out_path.write_text(text, encoding="utf-8") +PY + +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 "USE_MDFILE_AS_MAINPAGE = ${mainpage_rel}" +} >>"$doxyfile_tmp" + +LIVEKIT_DOXYGEN_PROJECT_NUMBER="$project_number" doxygen "$doxyfile_tmp" + +docs_index="${repo_root}/docs/doxygen/html/index.html" + +echo "==> Docs written to ${docs_index}" + +if [[ -n "${GITHUB_OUTPUT:-}" ]]; then + echo "project_number=${project_number}" >>"$GITHUB_OUTPUT" +fi