From 50932d68eb89bfa3d3e0649ba04e89b15a661c05 Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 27 May 2026 14:27:05 -0600 Subject: [PATCH 01/27] Additional doc comments on critical sections --- include/livekit/audio_stream.h | 6 +-- include/livekit/data_track_error.h | 6 +++ include/livekit/data_track_stream.h | 1 + include/livekit/e2ee.h | 1 + include/livekit/local_participant.h | 3 +- include/livekit/local_track_publication.h | 1 + include/livekit/participant.h | 4 +- include/livekit/remote_participant.h | 1 + include/livekit/remote_track_publication.h | 1 + include/livekit/stats.h | 45 ++++++++++++++++++++++ include/livekit/track.h | 11 ++++-- include/livekit/video_frame.h | 1 + include/livekit/video_stream.h | 1 + 13 files changed, 71 insertions(+), 11 deletions(-) diff --git a/include/livekit/audio_stream.h b/include/livekit/audio_stream.h index 2cfa2501..65ffc208 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 + +/// 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..6bc08e92 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 +/// 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, }; +/// 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); }; +/// 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, }; +/// 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); }; +/// 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, }; +/// 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..502f60c2 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: + /// 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..494b859e 100644 --- a/include/livekit/e2ee.h +++ b/include/livekit/e2ee.h @@ -158,6 +158,7 @@ class LIVEKIT_API E2EEManager { KeyProviderOptions options_; }; + /// 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/local_participant.h b/include/livekit/local_participant.h index 608e5692..057630c1 100644 --- a/include/livekit/local_participant.h +++ b/include/livekit/local_participant.h @@ -43,6 +43,7 @@ class FfiClient; class Track; class LocalTrackPublication; +/// Data passed to a registered RPC method handler. struct RpcInvocationData { std::string request_id; std::string caller_identity; @@ -51,8 +52,6 @@ struct RpcInvocationData { }; /// Represents the local participant in a room. -/// -/// LocalParticipant, built on top of the participant.h base class. 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..ed8a6a4f 100644 --- a/include/livekit/local_track_publication.h +++ b/include/livekit/local_track_publication.h @@ -25,6 +25,7 @@ namespace proto { class OwnedTrackPublication; } +/// Publication metadata for a locally published media track. 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..c5eaaa51 100644 --- a/include/livekit/participant.h +++ b/include/livekit/participant.h @@ -27,8 +27,10 @@ namespace livekit { +/// Identifies the type of participant connected to a room. enum class ParticipantKind { Standard = 0, Ingress, Egress, Sip, Agent }; +/// 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..8a1d45c3 100644 --- a/include/livekit/remote_participant.h +++ b/include/livekit/remote_participant.h @@ -27,6 +27,7 @@ namespace livekit { class RemoteTrackPublication; +/// 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..116fa69f 100644 --- a/include/livekit/remote_track_publication.h +++ b/include/livekit/remote_track_publication.h @@ -25,6 +25,7 @@ namespace proto { class OwnedTrackPublication; } +/// Publication metadata for a remotely published media track. class LIVEKIT_API RemoteTrackPublication : public TrackPublication { public: /// Note, this RemoteTrackPublication is constructed internally only; diff --git a/include/livekit/stats.h b/include/livekit/stats.h index 6755edde..8c248b72 100644 --- a/include/livekit/stats.h +++ b/include/livekit/stats.h @@ -51,6 +51,7 @@ class CertificateStats; class StreamStats; } // namespace proto +/// State of a WebRTC data channel. enum class DataChannelState { Connecting, Open, @@ -59,6 +60,7 @@ enum class DataChannelState { Unknown, }; +/// Reason outbound media quality is currently limited. enum class QualityLimitationReason { None, Cpu, @@ -66,12 +68,14 @@ enum class QualityLimitationReason { Other, }; +/// ICE role used by a transport. enum class IceRole { Unknown, Controlling, Controlled, }; +/// DTLS transport state. enum class DtlsTransportState { New, Connecting, @@ -81,6 +85,7 @@ enum class DtlsTransportState { Unknown, }; +/// ICE transport state. enum class IceTransportState { New, Checking, @@ -92,12 +97,14 @@ enum class IceTransportState { Unknown, }; +/// DTLS role used by a transport. enum class DtlsRole { Client, Server, Unknown, }; +/// State of an ICE candidate pair. enum class IceCandidatePairState { Frozen, Waiting, @@ -107,6 +114,7 @@ enum class IceCandidatePairState { Unknown, }; +/// Type of ICE candidate. enum class IceCandidateType { Host, Srflx, @@ -115,6 +123,7 @@ enum class IceCandidateType { Unknown, }; +/// Transport protocol used by an ICE server. enum class IceServerTransportProtocol { Udp, Tcp, @@ -122,6 +131,7 @@ enum class IceServerTransportProtocol { Unknown, }; +/// TCP candidate type for an ICE candidate. enum class IceTcpCandidateType { Active, Passive, @@ -133,11 +143,13 @@ enum class IceTcpCandidateType { // Leaf stats types // ---------------------- +/// Base data shared by RTC stats records. struct RtcStatsData { std::string id; std::int64_t timestamp_ms; }; +/// 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; }; +/// 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; }; +/// Statistics for received RTP streams. struct ReceivedRtpStreamStats { std::uint64_t packets_received; std::int64_t packets_lost; double jitter; }; +/// 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; }; +/// Statistics for sent RTP streams. struct SentRtpStreamStats { std::uint64_t packets_sent; std::uint64_t bytes_sent; }; +/// 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; }; +/// 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; }; +/// 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; }; +/// Common statistics for a local media source. struct MediaSourceStats { std::string track_identifier; std::string kind; }; +/// 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; }; +/// 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. }; +/// Statistics for a peer connection. struct PeerConnectionStats { std::uint32_t data_channels_opened; std::uint32_t data_channels_closed; }; +/// 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; }; +/// 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; }; +/// 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; }; +/// 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; }; +/// 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; }; +/// Statistics for a media stream. struct StreamStats { std::string id; std::string stream_identifier; @@ -402,11 +431,13 @@ struct StreamStats { // High-level RtcStats wrapper // ---------------------- +/// Typed RTC stats wrapper for codec statistics. struct RtcCodecStats { RtcStatsData rtc; CodecStats codec; }; +/// Typed RTC stats wrapper for inbound RTP statistics. struct RtcInboundRtpStats { RtcStatsData rtc; RtpStreamStats stream; @@ -414,6 +445,7 @@ struct RtcInboundRtpStats { InboundRtpStreamStats inbound; }; +/// Typed RTC stats wrapper for outbound RTP statistics. struct RtcOutboundRtpStats { RtcStatsData rtc; RtpStreamStats stream; @@ -421,6 +453,7 @@ struct RtcOutboundRtpStats { OutboundRtpStreamStats outbound; }; +/// Typed RTC stats wrapper for remote inbound RTP statistics. struct RtcRemoteInboundRtpStats { RtcStatsData rtc; RtpStreamStats stream; @@ -428,6 +461,7 @@ struct RtcRemoteInboundRtpStats { RemoteInboundRtpStreamStats remote_inbound; }; +/// Typed RTC stats wrapper for remote outbound RTP statistics. struct RtcRemoteOutboundRtpStats { RtcStatsData rtc; RtpStreamStats stream; @@ -435,6 +469,7 @@ struct RtcRemoteOutboundRtpStats { RemoteOutboundRtpStreamStats remote_outbound; }; +/// Typed RTC stats wrapper for media source statistics. struct RtcMediaSourceStats { RtcStatsData rtc; MediaSourceStats source; @@ -442,46 +477,55 @@ struct RtcMediaSourceStats { VideoSourceStats video; }; +/// Typed RTC stats wrapper for audio playout statistics. struct RtcMediaPlayoutStats { RtcStatsData rtc; AudioPlayoutStats audio_playout; }; +/// Typed RTC stats wrapper for peer connection statistics. struct RtcPeerConnectionStats { RtcStatsData rtc; PeerConnectionStats pc; }; +/// Typed RTC stats wrapper for data channel statistics. struct RtcDataChannelStats { RtcStatsData rtc; DataChannelStats dc; }; +/// Typed RTC stats wrapper for transport statistics. struct RtcTransportStats { RtcStatsData rtc; TransportStats transport; }; +/// Typed RTC stats wrapper for ICE candidate pair statistics. struct RtcCandidatePairStats { RtcStatsData rtc; CandidatePairStats candidate_pair; }; +/// Typed RTC stats wrapper for local ICE candidate statistics. struct RtcLocalCandidateStats { RtcStatsData rtc; IceCandidateStats candidate; }; +/// Typed RTC stats wrapper for remote ICE candidate statistics. struct RtcRemoteCandidateStats { RtcStatsData rtc; IceCandidateStats candidate; }; +/// Typed RTC stats wrapper for certificate statistics. struct RtcCertificateStats { RtcStatsData rtc; CertificateStats certificate; }; +/// 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>; +/// Variant wrapper for typed RTC stats records. struct RtcStats { RtcStatsVariant stats; }; diff --git a/include/livekit/track.h b/include/livekit/track.h index 00c2d744..bbf6ba43 100644 --- a/include/livekit/track.h +++ b/include/livekit/track.h @@ -32,12 +32,14 @@ namespace livekit { class LocalTrackPublication; +/// Media kind for an audio or video track. enum class TrackKind { KIND_UNKNOWN = 0, KIND_AUDIO = 1, KIND_VIDEO = 2, }; +/// 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, }; +/// Stream state reported for a subscribed track. enum class StreamState { STATE_UNKNOWN = 0, STATE_ACTIVE = 1, STATE_PAUSED = 2, }; +/// 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, }; +/// Per-participant track subscription permission configuration. struct ParticipantTrackPermission { std::string participant_identity; std::optional allow_all; std::vector allowed_track_sids; }; -// ============================================================ -// Base Track -// ============================================================ +/// 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/video_frame.h b/include/livekit/video_frame.h index 79c2d016..0aa3e1d3 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 }; +/// 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..9c99fd02 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: + /// 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). From 7c472d91031b205deddb500c31503bacab4f791f Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 27 May 2026 16:49:24 -0600 Subject: [PATCH 02/27] Build helpers, better stage --- .github/workflows/make-release.yml | 4 + .github/workflows/publish-docs.yml | 75 +++++++++++++++++- .gitignore | 3 + docs/Doxyfile | 2 +- docs/customization/custom.css | 19 ++++- scripts/build-docs.sh | 118 +++++++++++++++++++++++++++++ 6 files changed, 215 insertions(+), 6 deletions(-) create mode 100755 scripts/build-docs.sh 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..1cd6e131 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -2,7 +2,29 @@ name: Publish docs on: workflow_dispatch: + inputs: + version: + description: 'Documentation version (for example, 0.1.0 or v0.1.0)' + required: false + type: string workflow_call: + inputs: + version: + description: 'Documentation version (for example, 0.1.0 or v0.1.0)' + required: false + type: string + # Dry-run on PRs that touch C++ code (or docs sources): build the docs to + # ensure they still generate, but skip the S3 upload / CloudFront invalidation. + pull_request: + branches: ["main"] + paths: + - include/** + - src/** + - examples/** + - docs/** + - README.md + - scripts/build-docs.sh + - .github/workflows/publish-docs.yml jobs: docs: @@ -19,10 +41,52 @@ jobs: sudo apt-get install -y doxygen graphviz - name: Build Docs (Doxygen) + id: build_docs + shell: bash + env: + INPUT_VERSION: ${{ inputs.version || github.event.inputs.version || '' }} run: | - doxygen docs/Doxyfile + 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/build-docs.sh "${args[@]}" + + - name: Print docs version + shell: bash + run: | + set -euo pipefail + + PROJECT_NUMBER="${{ steps.build_docs.outputs.project_number }}" + VERSION="${{ steps.build_docs.outputs.version }}" + + if [[ -z "$PROJECT_NUMBER" || -z "$VERSION" ]]; then + echo "ERROR: build_docs step did not emit version outputs." + exit 1 + fi + + echo "Docs project number: ${PROJECT_NUMBER}" + echo "Docs version: ${VERSION}" + + { + echo "### Docs build" + echo "" + echo "- Project number: \`${PROJECT_NUMBER}\`" + echo "- Version: \`${VERSION}\`" + if [[ "${{ github.event_name }}" == "pull_request" ]]; then + echo "- Mode: dry-run (PR build, not published)" + else + echo "- Mode: publish" + fi + } >>"$GITHUB_STEP_SUMMARY" - name: S3 Upload + if: github.event_name != 'pull_request' run: | DOCS_DIR="html" if [[ ! -d "$DOCS_DIR" ]]; then @@ -30,14 +94,19 @@ 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/${{ steps.build_docs.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 }} AWS_DEFAULT_REGION: "us-east-1" - name: Invalidate cloudfront cache + if: github.event_name != 'pull_request' run: | aws cloudfront create-invalidation --distribution-id EJJ40KLJ3TRY9 --paths "/client-sdk-cpp/*" env: diff --git a/.gitignore b/.gitignore index e6706760..f7d912c6 100644 --- a/.gitignore +++ b/.gitignore @@ -16,8 +16,11 @@ vcpkg_installed/ include/livekit/build.h received_green.avif docs/*.bak +# Doxygen docs/html/ docs/latex/ +html/ +latex/ .vs/ .vscode/ .cursor/ diff --git a/docs/Doxyfile b/docs/Doxyfile index ca0429d7..a0deeb79 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = "LiveKit C++ SDK" # 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 diff --git a/docs/customization/custom.css b/docs/customization/custom.css index aabf9936..5d6f3bec 100644 --- a/docs/customization/custom.css +++ b/docs/customization/custom.css @@ -8,6 +8,20 @@ html { --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 { @@ -20,8 +34,9 @@ html { } #projectnumber { - background-color: #eee; + 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; } @@ -48,4 +63,4 @@ ul.page-outline li.vis { text-align: unset; margin-right: 0; height: unset; -} \ No newline at end of file +} diff --git a/scripts/build-docs.sh b/scripts/build-docs.sh new file mode 100755 index 00000000..2e62ee5e --- /dev/null +++ b/scripts/build-docs.sh @@ -0,0 +1,118 @@ +#!/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/build-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 + + 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" +LIVEKIT_DOXYGEN_PROJECT_NUMBER="$project_number" doxygen docs/Doxyfile + +docs_index="${repo_root}/html/index.html" + +echo "==> Docs written to ${docs_index}" + +if [[ -n "${GITHUB_OUTPUT:-}" ]]; then + { + echo "version=${project_number#v}" + echo "project_number=${project_number}" + } >>"$GITHUB_OUTPUT" +fi From 9db39d4058f2381fdb222c40329a13ec851326dd Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 27 May 2026 16:54:50 -0600 Subject: [PATCH 03/27] Separate validate stage --- .github/workflows/publish-docs.yml | 92 +++++------------ .github/workflows/validate-docs.yml | 148 ++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 70 deletions(-) create mode 100644 .github/workflows/validate-docs.yml diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 1cd6e131..77d21837 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -13,80 +13,33 @@ on: description: 'Documentation version (for example, 0.1.0 or v0.1.0)' required: false type: string - # Dry-run on PRs that touch C++ code (or docs sources): build the docs to - # ensure they still generate, but skip the S3 upload / CloudFront invalidation. - pull_request: - branches: ["main"] - paths: - - include/** - - src/** - - examples/** - - docs/** - - README.md - - scripts/build-docs.sh - - .github/workflows/publish-docs.yml jobs: - docs: + # Reuse validate-docs.yml so the publish path runs exactly the same build/print + # logic that PR dry-runs run, and we ship the resulting html/ tree to the + # publish job below as a workflow artifact (avoids double-building). + validate: + name: Validate (build docs) + uses: ./.github/workflows/validate-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) - id: build_docs - shell: bash - env: - INPUT_VERSION: ${{ inputs.version || github.event.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/build-docs.sh "${args[@]}" - - - name: Print docs version - shell: bash - run: | - set -euo pipefail - - PROJECT_NUMBER="${{ steps.build_docs.outputs.project_number }}" - VERSION="${{ steps.build_docs.outputs.version }}" - - if [[ -z "$PROJECT_NUMBER" || -z "$VERSION" ]]; then - echo "ERROR: build_docs step did not emit version outputs." - exit 1 - fi - - echo "Docs project number: ${PROJECT_NUMBER}" - echo "Docs version: ${VERSION}" - - { - echo "### Docs build" - echo "" - echo "- Project number: \`${PROJECT_NUMBER}\`" - echo "- Version: \`${VERSION}\`" - if [[ "${{ github.event_name }}" == "pull_request" ]]; then - echo "- Mode: dry-run (PR build, not published)" - else - echo "- Mode: publish" - fi - } >>"$GITHUB_STEP_SUMMARY" + - name: Download docs artifact + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + with: + name: ${{ needs.validate.outputs.artifact_name }} + path: html - name: S3 Upload - if: github.event_name != 'pull_request' run: | DOCS_DIR="html" if [[ ! -d "$DOCS_DIR" ]]; then @@ -94,7 +47,7 @@ jobs: exit 1 fi - VERSIONED_PREFIX="s3://livekit-docs/client-sdk-cpp/${{ steps.build_docs.outputs.project_number }}" + 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. @@ -106,7 +59,6 @@ jobs: AWS_DEFAULT_REGION: "us-east-1" - name: Invalidate cloudfront cache - if: github.event_name != 'pull_request' run: | aws cloudfront create-invalidation --distribution-id EJJ40KLJ3TRY9 --paths "/client-sdk-cpp/*" env: diff --git a/.github/workflows/validate-docs.yml b/.github/workflows/validate-docs.yml new file mode 100644 index 00000000..f4799c8f --- /dev/null +++ b/.github/workflows/validate-docs.yml @@ -0,0 +1,148 @@ +name: Validate docs + +# Builds the Doxygen docs (and optionally uploads them as a workflow artifact) +# without publishing. Two roles: +# +# 1. Dry-run on every PR that touches C++ code (or other Doxygen inputs) so +# docs breakage is caught before merge. No artifact is uploaded. +# 2. Reusable workflow consumed by publish-docs.yml: builds the docs, prints +# the resolved version, and uploads the html/ directory as an artifact for +# a downstream publish job to consume. Avoids double-building the docs. +on: + pull_request: + branches: ["main"] + paths: + - include/** + - src/** + - examples/** + - docs/** + - README.md + - scripts/build-docs.sh + - .github/workflows/validate-docs.yml + - .github/workflows/publish-docs.yml + workflow_call: + inputs: + version: + description: 'Documentation version (for example, 0.1.0 or v0.1.0)' + required: false + type: string + upload_artifact: + description: 'Upload the built html/ tree as a workflow artifact' + required: false + type: boolean + default: false + 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 }} + version: + description: 'Bare semantic version with the leading v stripped (e.g., 1.2.3)' + value: ${{ jobs.validate.outputs.version }} + artifact_name: + description: 'Name of the uploaded docs artifact (empty when upload_artifact is false)' + value: ${{ jobs.validate.outputs.artifact_name }} + +jobs: + validate: + name: Build & verify docs + runs-on: ubuntu-latest + outputs: + project_number: ${{ steps.build_docs.outputs.project_number }} + version: ${{ steps.build_docs.outputs.version }} + artifact_name: ${{ steps.artifact_meta.outputs.name }} + 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) + 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/build-docs.sh "${args[@]}" + + - name: Print docs version + shell: bash + run: | + set -euo pipefail + + PROJECT_NUMBER="${{ steps.build_docs.outputs.project_number }}" + VERSION="${{ steps.build_docs.outputs.version }}" + + if [[ -z "$PROJECT_NUMBER" || -z "$VERSION" ]]; then + echo "ERROR: build_docs step did not emit version outputs." + exit 1 + fi + + echo "Docs project number: ${PROJECT_NUMBER}" + echo "Docs version: ${VERSION}" + + { + echo "### Docs build" + echo "" + echo "- Project number: \`${PROJECT_NUMBER}\`" + echo "- Version: \`${VERSION}\`" + if [[ "${{ github.event_name }}" == "pull_request" ]]; then + echo "- Mode: dry-run (PR build, not published)" + else + echo "- Mode: validate (reusable workflow)" + fi + } >>"$GITHUB_STEP_SUMMARY" + + - name: Verify docs were generated + shell: bash + run: | + set -euo pipefail + if [[ ! -f html/index.html ]]; then + echo "ERROR: Expected docs at html/index.html but file not found." + exit 1 + fi + + - name: Resolve artifact name + id: artifact_meta + if: inputs.upload_artifact + shell: bash + run: | + set -euo pipefail + NAME="${{ inputs.artifact_name }}" + if [[ -z "$NAME" ]]; then + echo "ERROR: artifact_name input is empty." + exit 1 + fi + echo "name=${NAME}" >>"$GITHUB_OUTPUT" + + - name: Upload docs artifact + if: inputs.upload_artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ${{ inputs.artifact_name }} + path: html/ + retention-days: ${{ inputs.artifact_retention_days }} + if-no-files-found: error From 8a5e19e5d59fe0e8c4463954c303e5195966a3a8 Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 27 May 2026 21:00:40 -0600 Subject: [PATCH 04/27] Try proper version --- .github/workflows/validate-docs.yml | 5 +++-- docs/index.md | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/validate-docs.yml b/.github/workflows/validate-docs.yml index f4799c8f..8db75c50 100644 --- a/.github/workflows/validate-docs.yml +++ b/.github/workflows/validate-docs.yml @@ -62,8 +62,9 @@ jobs: artifact_name: ${{ steps.artifact_meta.outputs.name }} 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/ + with: + fetch-depth: 1 + fetch-tags: true # for version detection # Doxygen is available on ubuntu-latest, but we install explicitly for stability - name: Install Doxygen diff --git a/docs/index.md b/docs/index.md index 2a8e5a28..0859b9ee 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,8 +2,6 @@ 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 From bf6c66c8292f957c704e1448b6b7ebf9cd5f3933 Mon Sep 17 00:00:00 2001 From: alan-george-lk Date: Wed, 27 May 2026 21:35:27 -0600 Subject: [PATCH 05/27] Potential fix for pull request finding 'CodeQL / Workflow does not contain permissions' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/publish-docs.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 77d21837..a716a8eb 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -14,6 +14,10 @@ on: required: false type: string +permissions: + contents: read + actions: read + jobs: # Reuse validate-docs.yml so the publish path runs exactly the same build/print # logic that PR dry-runs run, and we ship the resulting html/ tree to the From 348faca556438f677a798c08dae3f34efe885864 Mon Sep 17 00:00:00 2001 From: alan-george-lk Date: Wed, 27 May 2026 21:35:44 -0600 Subject: [PATCH 06/27] Potential fix for pull request finding 'CodeQL / Workflow does not contain permissions' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/validate-docs.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/validate-docs.yml b/.github/workflows/validate-docs.yml index 8db75c50..0ffbef40 100644 --- a/.github/workflows/validate-docs.yml +++ b/.github/workflows/validate-docs.yml @@ -1,4 +1,6 @@ name: Validate docs +permissions: + contents: read # Builds the Doxygen docs (and optionally uploads them as a workflow artifact) # without publishing. Two roles: From 53ac382fac30abcdf5c61ff273f89803cbe1e0cf Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 27 May 2026 21:54:31 -0600 Subject: [PATCH 07/27] Additional cleanup --- .../{validate-docs.yml => generate-docs.yml} | 40 ++++++++----------- .github/workflows/publish-docs.yml | 4 +- docs/Doxyfile | 2 +- docs/customization/custom.css | 14 +++++++ docs/index.md | 28 ++++++++++--- include/livekit/livekit.h | 2 + scripts/build-docs.sh | 5 +-- 7 files changed, 59 insertions(+), 36 deletions(-) rename .github/workflows/{validate-docs.yml => generate-docs.yml} (77%) diff --git a/.github/workflows/validate-docs.yml b/.github/workflows/generate-docs.yml similarity index 77% rename from .github/workflows/validate-docs.yml rename to .github/workflows/generate-docs.yml index 8db75c50..87001af1 100644 --- a/.github/workflows/validate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -18,19 +18,19 @@ on: - docs/** - README.md - scripts/build-docs.sh - - .github/workflows/validate-docs.yml + - .github/workflows/generate-docs.yml - .github/workflows/publish-docs.yml workflow_call: inputs: version: - description: 'Documentation version (for example, 0.1.0 or v0.1.0)' + description: 'Documentation version (e.g. v0.1.0)' required: false type: string upload_artifact: description: 'Upload the built html/ tree as a workflow artifact' required: false type: boolean - default: false + default: true artifact_name: description: 'Name to use for the uploaded docs artifact' required: false @@ -40,14 +40,11 @@ on: description: 'Retention (days) for the uploaded docs artifact' required: false type: number - default: 7 + default: 1 outputs: project_number: description: 'Doxygen PROJECT_NUMBER used for the build (e.g., v1.2.3)' value: ${{ jobs.validate.outputs.project_number }} - version: - description: 'Bare semantic version with the leading v stripped (e.g., 1.2.3)' - value: ${{ jobs.validate.outputs.version }} artifact_name: description: 'Name of the uploaded docs artifact (empty when upload_artifact is false)' value: ${{ jobs.validate.outputs.artifact_name }} @@ -58,13 +55,18 @@ jobs: runs-on: ubuntu-latest outputs: project_number: ${{ steps.build_docs.outputs.project_number }} - version: ${{ steps.build_docs.outputs.version }} artifact_name: ${{ steps.artifact_meta.outputs.name }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - fetch-depth: 1 - fetch-tags: true # for version detection + # fetch-depth: 0 is required so `git describe --tags` in + # scripts/build-docs.sh can walk back from HEAD to the nearest + # ancestor tag. A shallow clone (fetch-depth: 1) — even combined + # with fetch-tags: true — fetches the tag refs but not the + # connecting history, so `git describe` cannot reach any tag and + # falls back to a short SHA, producing v0.0.0-dev+ instead + # of the expected v--g. + fetch-depth: 0 # Doxygen is available on ubuntu-latest, but we install explicitly for stability - name: Install Doxygen @@ -95,26 +97,16 @@ jobs: set -euo pipefail PROJECT_NUMBER="${{ steps.build_docs.outputs.project_number }}" - VERSION="${{ steps.build_docs.outputs.version }}" - if [[ -z "$PROJECT_NUMBER" || -z "$VERSION" ]]; then - echo "ERROR: build_docs step did not emit version outputs." + if [[ -z "$PROJECT_NUMBER" ]]; then + echo "ERROR: build_docs step did not emit a project_number output." exit 1 fi - echo "Docs project number: ${PROJECT_NUMBER}" - echo "Docs version: ${VERSION}" + echo "Docs version: ${PROJECT_NUMBER}" { - echo "### Docs build" - echo "" - echo "- Project number: \`${PROJECT_NUMBER}\`" - echo "- Version: \`${VERSION}\`" - if [[ "${{ github.event_name }}" == "pull_request" ]]; then - echo "- Mode: dry-run (PR build, not published)" - else - echo "- Mode: validate (reusable workflow)" - fi + echo "Version: \`${PROJECT_NUMBER}\`" } >>"$GITHUB_STEP_SUMMARY" - name: Verify docs were generated diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 77d21837..a354d427 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -15,12 +15,12 @@ on: type: string jobs: - # Reuse validate-docs.yml so the publish path runs exactly the same build/print + # Reuse generate-docs.yml so the publish path runs exactly the same build/print # logic that PR dry-runs run, and we ship the resulting html/ tree to the # publish job below as a workflow artifact (avoids double-building). validate: name: Validate (build docs) - uses: ./.github/workflows/validate-docs.yml + uses: ./.github/workflows/generate-docs.yml with: version: ${{ inputs.version || github.event.inputs.version || '' }} upload_artifact: true diff --git a/docs/Doxyfile b/docs/Doxyfile index a0deeb79..78cc1275 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -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/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 diff --git a/docs/customization/custom.css b/docs/customization/custom.css index 5d6f3bec..a1744f25 100644 --- a/docs/customization/custom.css +++ b/docs/customization/custom.css @@ -64,3 +64,17 @@ ul.page-outline li.vis { 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; +} diff --git a/docs/index.md b/docs/index.md index 0859b9ee..56df9d37 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,4 +1,4 @@ -# LiveKit C++ Client SDK +# Overview Build real-time audio/video applications in C++ with LiveKit. @@ -100,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/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/scripts/build-docs.sh b/scripts/build-docs.sh index 2e62ee5e..e2977d61 100755 --- a/scripts/build-docs.sh +++ b/scripts/build-docs.sh @@ -111,8 +111,5 @@ docs_index="${repo_root}/html/index.html" echo "==> Docs written to ${docs_index}" if [[ -n "${GITHUB_OUTPUT:-}" ]]; then - { - echo "version=${project_number#v}" - echo "project_number=${project_number}" - } >>"$GITHUB_OUTPUT" + echo "project_number=${project_number}" >>"$GITHUB_OUTPUT" fi From 8f57e217945baf6264fd36f166f0496a7a3e3942 Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 27 May 2026 22:02:48 -0600 Subject: [PATCH 08/27] Fix output --- .github/workflows/generate-docs.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 1708aa09..70b7726d 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -1,4 +1,4 @@ -name: Validate docs +name: Generate docs permissions: contents: read @@ -32,7 +32,7 @@ on: description: 'Upload the built html/ tree as a workflow artifact' required: false type: boolean - default: true + default: false artifact_name: description: 'Name to use for the uploaded docs artifact' required: false @@ -62,12 +62,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: # fetch-depth: 0 is required so `git describe --tags` in - # scripts/build-docs.sh can walk back from HEAD to the nearest - # ancestor tag. A shallow clone (fetch-depth: 1) — even combined - # with fetch-tags: true — fetches the tag refs but not the - # connecting history, so `git describe` cannot reach any tag and - # falls back to a short SHA, producing v0.0.0-dev+ instead - # of the expected v--g. + # scripts/build-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 @@ -109,6 +104,7 @@ jobs: { echo "Version: \`${PROJECT_NUMBER}\`" + echo "Note: On a non-tag PR, the version will resolve to `--g`" } >>"$GITHUB_STEP_SUMMARY" - name: Verify docs were generated From a06848b0e482575e0912d8d3fc4aac9b24b43429 Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 27 May 2026 22:04:26 -0600 Subject: [PATCH 09/27] Fix render --- .github/workflows/generate-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 70b7726d..a34cc464 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -104,7 +104,7 @@ jobs: { echo "Version: \`${PROJECT_NUMBER}\`" - echo "Note: On a non-tag PR, the version will resolve to `--g`" + echo "Note: On a non-tag PR, the version will resolve to \`--g\`" } >>"$GITHUB_STEP_SUMMARY" - name: Verify docs were generated From a4c41e089cf688d2fd4a9ffc1c96d7e46aa8f130 Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 27 May 2026 22:21:53 -0600 Subject: [PATCH 10/27] Catch certain documentation issues --- .github/workflows/generate-docs.yml | 10 ++++++---- docs/Doxyfile | 13 ++----------- include/livekit/subscription_thread_dispatcher.h | 1 + scripts/{build-docs.sh => generate-docs.sh} | 12 +++++++++++- 4 files changed, 20 insertions(+), 16 deletions(-) rename scripts/{build-docs.sh => generate-docs.sh} (82%) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index a34cc464..10c6d53a 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -19,7 +19,7 @@ on: - examples/** - docs/** - README.md - - scripts/build-docs.sh + - scripts/generate-docs.sh - .github/workflows/generate-docs.yml - .github/workflows/publish-docs.yml workflow_call: @@ -62,7 +62,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: # fetch-depth: 0 is required so `git describe --tags` in - # scripts/build-docs.sh resolves the right version from the git history. + # 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 @@ -86,7 +86,7 @@ jobs: args+=(--version "${{ github.ref_name }}") fi - ./scripts/build-docs.sh "${args[@]}" + ./scripts/generate-docs.sh "${args[@]}" - name: Print docs version shell: bash @@ -104,7 +104,9 @@ jobs: { echo "Version: \`${PROJECT_NUMBER}\`" - echo "Note: On a non-tag PR, the version will resolve to \`--g\`" + echo "" + echo "> Note: On a non-tag/release run, the version will resolve to:" + echo " \`--g\`" } >>"$GITHUB_STEP_SUMMARY" - name: Verify docs were generated diff --git a/docs/Doxyfile b/docs/Doxyfile index 78cc1275..16129c11 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -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 @@ -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/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/scripts/build-docs.sh b/scripts/generate-docs.sh similarity index 82% rename from scripts/build-docs.sh rename to scripts/generate-docs.sh index e2977d61..23320638 100755 --- a/scripts/build-docs.sh +++ b/scripts/generate-docs.sh @@ -21,7 +21,7 @@ repo_root="$(cd "${script_dir}/.." && pwd -P)" usage() { cat <<'EOF' -Usage: ./scripts/build-docs.sh [--version VERSION] +Usage: ./scripts/generate-docs.sh [--version VERSION] Build Doxygen HTML docs with a visible project version. @@ -104,6 +104,16 @@ fi echo "==> Building Doxygen docs with PROJECT_NUMBER=${project_number}" cd "$repo_root" + +# Doxygen owns the failure decision: docs/Doxyfile sets +# WARN_IF_UNDOCUMENTED = NO (silences the ~400 "X is not documented" +# warnings about internal/private symbols) +# WARN_AS_ERROR = FAIL_ON_WARNINGS +# so any remaining warning (broken @ref, unknown @-command, unsupported HTML +# tag, malformed table, unreadable INPUT entry, etc.) fails this script via a +# non-zero exit. There is no per-warning toggle in Doxygen; if a category +# becomes too noisy to enforce, mute it at the WARN_IF_* level in the Doxyfile +# rather than adding pattern allowlists here. LIVEKIT_DOXYGEN_PROJECT_NUMBER="$project_number" doxygen docs/Doxyfile docs_index="${repo_root}/html/index.html" From 94170f69d6ee1bf20008b20e543b99d46d027cc2 Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 27 May 2026 22:31:25 -0600 Subject: [PATCH 11/27] Remove the g --- .github/workflows/generate-docs.yml | 2 +- scripts/generate-docs.sh | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 10c6d53a..b59cc004 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -106,7 +106,7 @@ jobs: echo "Version: \`${PROJECT_NUMBER}\`" echo "" echo "> Note: On a non-tag/release run, the version will resolve to:" - echo " \`--g\`" + echo " \`--\`" } >>"$GITHUB_STEP_SUMMARY" - name: Verify docs were generated diff --git a/scripts/generate-docs.sh b/scripts/generate-docs.sh index 23320638..2f5f246b 100755 --- a/scripts/generate-docs.sh +++ b/scripts/generate-docs.sh @@ -81,6 +81,18 @@ elif [[ -z "$project_number" ]]; then described="" fi + # git describe emits "--g[-dirty]" when HEAD is past the + # nearest ancestor tag. The leading 'g' on the abbreviated SHA is a + # git-describe convention (it once signified the originating SCM back + # when SVN/Hg interop mattered) and adds nothing for human readers of + # the docs version banner. Strip it so the rendered version reads as + # the more obvious "--[-dirty]". Untagged --always + # output (bare SHA) and on-tag output (no --g segment) are left + # untouched by this regex. + 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 From a56895274e460fd696a9fcb5a5407ccc88a5bbfe Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 28 May 2026 07:39:27 -0600 Subject: [PATCH 12/27] Drop 'g' from version --- include/livekit/room.h | 14 ++++++++++---- scripts/generate-docs.sh | 10 ++-------- 2 files changed, 12 insertions(+), 12 deletions(-) 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/scripts/generate-docs.sh b/scripts/generate-docs.sh index 2f5f246b..d9ad06d2 100755 --- a/scripts/generate-docs.sh +++ b/scripts/generate-docs.sh @@ -81,14 +81,8 @@ elif [[ -z "$project_number" ]]; then described="" fi - # git describe emits "--g[-dirty]" when HEAD is past the - # nearest ancestor tag. The leading 'g' on the abbreviated SHA is a - # git-describe convention (it once signified the originating SCM back - # when SVN/Hg interop mattered) and adds nothing for human readers of - # the docs version banner. Strip it so the rendered version reads as - # the more obvious "--[-dirty]". Untagged --always - # output (bare SHA) and on-tag output (no --g segment) are left - # untouched by this regex. + # 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 From 4fdd11fe7866862c09f00f94ec1c90b4b9e61a34 Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 28 May 2026 07:50:31 -0600 Subject: [PATCH 13/27] Document remaining classes via auto javadoc --- docs/Doxyfile | 2 +- include/livekit/e2ee.h | 14 ++++++++------ include/livekit/track_publication.h | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/Doxyfile b/docs/Doxyfile index 16129c11..1ca1bb39 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -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 diff --git a/include/livekit/e2ee.h b/include/livekit/e2ee.h index 494b859e..5c5cbbe2 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; diff --git a/include/livekit/track_publication.h b/include/livekit/track_publication.h index 1552b937..dd8fbd42 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. +/// 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). From 79b69659b97bdd00e867d917ef9d2adf356693a0 Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 28 May 2026 08:10:30 -0600 Subject: [PATCH 14/27] Workflow label --- .github/workflows/generate-docs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index b59cc004..65cfb2c7 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -53,7 +53,7 @@ on: jobs: validate: - name: Build & verify docs + name: Generate and verify docs runs-on: ubuntu-latest outputs: project_number: ${{ steps.build_docs.outputs.project_number }} @@ -71,7 +71,7 @@ jobs: sudo apt-get update sudo apt-get install -y doxygen graphviz - - name: Build Docs (Doxygen) + - name: Generate docs (Doxygen) id: build_docs shell: bash env: From 9c311653563a3c16e7a5d1ef3ea045d4f7c9fe11 Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 28 May 2026 08:30:56 -0600 Subject: [PATCH 15/27] Move to doxygen folder --- .github/workflows/generate-docs.yml | 11 ++-- .github/workflows/publish-docs.yml | 6 ++- .gitignore | 6 +-- AGENTS.md | 16 +++--- docs/{ => doxygen}/Doxyfile | 12 ++--- docs/{ => doxygen}/customization/custom.css | 0 .../customization/doxygen-awesome.css | 0 docs/{ => doxygen}/customization/favicon.ico | Bin docs/{ => doxygen}/customization/header.html | 0 docs/{ => doxygen}/index.md | 0 docs/guides/README.md | 49 ++++++++++++++++++ docs/layout.xml | 10 ---- scripts/generate-docs.sh | 6 +-- 13 files changed, 80 insertions(+), 36 deletions(-) rename docs/{ => doxygen}/Doxyfile (99%) rename docs/{ => doxygen}/customization/custom.css (100%) rename docs/{ => doxygen}/customization/doxygen-awesome.css (100%) rename docs/{ => doxygen}/customization/favicon.ico (100%) rename docs/{ => doxygen}/customization/header.html (100%) rename docs/{ => doxygen}/index.md (100%) create mode 100644 docs/guides/README.md delete mode 100644 docs/layout.xml diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 65cfb2c7..3b3508e5 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -8,7 +8,8 @@ permissions: # 1. Dry-run on every PR that touches C++ code (or other Doxygen inputs) so # docs breakage is caught before merge. No artifact is uploaded. # 2. Reusable workflow consumed by publish-docs.yml: builds the docs, prints -# the resolved version, and uploads the html/ directory as an artifact for +# the resolved version, and uploads the docs/doxygen/html/ directory as an +# artifact for # a downstream publish job to consume. Avoids double-building the docs. on: pull_request: @@ -29,7 +30,7 @@ on: required: false type: string upload_artifact: - description: 'Upload the built html/ tree as a workflow artifact' + description: 'Upload the built docs/doxygen/html/ tree as a workflow artifact' required: false type: boolean default: false @@ -113,8 +114,8 @@ jobs: shell: bash run: | set -euo pipefail - if [[ ! -f html/index.html ]]; then - echo "ERROR: Expected docs at html/index.html but file not found." + 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 @@ -136,6 +137,6 @@ jobs: uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ inputs.artifact_name }} - path: html/ + path: docs/doxygen/html/ retention-days: ${{ inputs.artifact_retention_days }} if-no-files-found: error diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 6f7284eb..4b92335a 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -20,8 +20,10 @@ permissions: jobs: # Reuse generate-docs.yml so the publish path runs exactly the same build/print - # logic that PR dry-runs run, and we ship the resulting html/ tree to the - # publish job below as a workflow artifact (avoids double-building). + # logic that PR dry-runs run, and we ship the resulting HTML tree (built into + # docs/doxygen/html/ on the builder) to the publish job below as a workflow + # artifact (avoids double-building). The artifact contains the *contents* of + # that directory, so the download path here can stay as the simpler "html". validate: name: Validate (build docs) uses: ./.github/workflows/generate-docs.yml diff --git a/.gitignore b/.gitignore index f7d912c6..c7fabe11 100644 --- a/.gitignore +++ b/.gitignore @@ -17,10 +17,8 @@ include/livekit/build.h received_green.avif docs/*.bak # Doxygen -docs/html/ -docs/latex/ -html/ -latex/ +docs/doxygen/html/ +docs/doxygen/latex/ .vs/ .vscode/ .cursor/ diff --git a/AGENTS.md b/AGENTS.md index 636f7e1e..3353b307 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. @@ -290,15 +292,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/docs/Doxyfile b/docs/doxygen/Doxyfile similarity index 99% rename from docs/Doxyfile rename to docs/doxygen/Doxyfile index 1ca1bb39..f5159a9f 100644 --- a/docs/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -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 @@ -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 +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 diff --git a/docs/customization/custom.css b/docs/doxygen/customization/custom.css similarity index 100% rename from docs/customization/custom.css rename to docs/doxygen/customization/custom.css 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 100% rename from docs/index.md rename to docs/doxygen/index.md diff --git a/docs/guides/README.md b/docs/guides/README.md new file mode 100644 index 00000000..dce60d3f --- /dev/null +++ b/docs/guides/README.md @@ -0,0 +1,49 @@ +# Guides + +Hand-written long-form documentation for the LiveKit C++ SDK. These +files are processed by Doxygen as additional pages and surface in the +generated docs alongside the auto-extracted API reference under +`include/livekit/`. + +This directory complements: + +- **`include/livekit/*.h`** — `///` doc comments for the API reference + (classes, structs, free functions, enums). +- **`docs/doxygen/index.md`** — the Doxygen mainpage / landing page + that links out to everything else. +- **`docs/doxygen/`** — Doxygen tool configuration (`Doxyfile`, theme + assets, mainpage). Nothing in there is intended for human reading + on its own (the mainpage uses Doxygen-only syntax like `@ref`). + +## What belongs here + +Topics that are too long for a `///` comment on a single type and too +narrative for the API reference. Examples: + +- Threading model and lifecycle conventions. +- End-to-end "publish a track" or "subscribe to a stream" walkthroughs. +- Error-handling philosophy (`Result` vs exceptions vs callbacks). +- E2EE / key provider deep dives. +- Migration guides between SDK versions. + +Anything that is genuinely *about a single symbol* belongs as a `///` +comment on that symbol instead, so it shows up inline in IDEs. + +## File convention + +- One topic per file. Filename in `kebab-case.md`. +- First line is a top-level `# Title` heading (becomes the Doxygen + page title). +- Use `///`-style Doxygen commands sparingly inside Markdown — prefer + plain Markdown where it works. +- Reference public symbols with `@ref livekit::FooBar` (classes/structs) + or `@ref livekit::someFreeFunction()` (free functions; the parens + are required for Doxygen to resolve the name). + +## Wiring into Doxygen + +Files in this directory are **not yet wired into the Doxyfile's +`INPUT`**. When the first real guide lands, add `docs/guides` to the +`INPUT =` line of `docs/doxygen/Doxyfile` so Doxygen processes the +folder. This `README.md` is intentionally excluded until then so it +doesn't render as a "README" page in the generated site. 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/scripts/generate-docs.sh b/scripts/generate-docs.sh index d9ad06d2..11e4a574 100755 --- a/scripts/generate-docs.sh +++ b/scripts/generate-docs.sh @@ -111,7 +111,7 @@ fi echo "==> Building Doxygen docs with PROJECT_NUMBER=${project_number}" cd "$repo_root" -# Doxygen owns the failure decision: docs/Doxyfile sets +# Doxygen owns the failure decision: docs/doxygen/Doxyfile sets # WARN_IF_UNDOCUMENTED = NO (silences the ~400 "X is not documented" # warnings about internal/private symbols) # WARN_AS_ERROR = FAIL_ON_WARNINGS @@ -120,9 +120,9 @@ cd "$repo_root" # non-zero exit. There is no per-warning toggle in Doxygen; if a category # becomes too noisy to enforce, mute it at the WARN_IF_* level in the Doxyfile # rather than adding pattern allowlists here. -LIVEKIT_DOXYGEN_PROJECT_NUMBER="$project_number" doxygen docs/Doxyfile +LIVEKIT_DOXYGEN_PROJECT_NUMBER="$project_number" doxygen docs/doxygen/Doxyfile -docs_index="${repo_root}/html/index.html" +docs_index="${repo_root}/docs/doxygen/html/index.html" echo "==> Docs written to ${docs_index}" From 8c7df7317e50bd2a51e9919e6bb67966e437fbb5 Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 28 May 2026 08:44:04 -0600 Subject: [PATCH 16/27] Use @brief on our changes --- include/livekit/audio_stream.h | 2 +- include/livekit/data_track_error.h | 12 +-- include/livekit/data_track_stream.h | 2 +- include/livekit/e2ee.h | 2 +- include/livekit/local_participant.h | 2 +- include/livekit/local_track_publication.h | 2 +- include/livekit/participant.h | 4 +- include/livekit/remote_participant.h | 2 +- include/livekit/remote_track_publication.h | 2 +- include/livekit/stats.h | 90 +++++++++++----------- include/livekit/track.h | 12 +-- include/livekit/track_publication.h | 2 +- include/livekit/video_frame.h | 2 +- include/livekit/video_stream.h | 2 +- 14 files changed, 69 insertions(+), 69 deletions(-) diff --git a/include/livekit/audio_stream.h b/include/livekit/audio_stream.h index 65ffc208..0729327b 100644 --- a/include/livekit/audio_stream.h +++ b/include/livekit/audio_stream.h @@ -40,7 +40,7 @@ class FfiEvent; // AudioFrame can throw in various places monitored by bugprone-exception-escape // Suppressing for now, would require significant refactor to fix -/// Event containing an audio frame received from an AudioStream. +/// @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 6bc08e92..ef7f1032 100644 --- a/include/livekit/data_track_error.h +++ b/include/livekit/data_track_error.h @@ -29,7 +29,7 @@ class LocalDataTrackTryPushError; class SubscribeDataTrackError; } // namespace proto -/// Error code returned when publishing a local data track fails. +/// @brief Error code returned when publishing a local data track fails. enum class PublishDataTrackErrorCode : std::uint32_t { UNKNOWN = 0, INVALID_HANDLE = 1, @@ -43,7 +43,7 @@ enum class PublishDataTrackErrorCode : std::uint32_t { INTERNAL = 9, }; -/// Error details returned when publishing a local data track fails. +/// @brief Error details returned when publishing a local data track fails. struct PublishDataTrackError { PublishDataTrackErrorCode code{PublishDataTrackErrorCode::UNKNOWN}; std::string message; @@ -51,7 +51,7 @@ struct PublishDataTrackError { LIVEKIT_API static PublishDataTrackError fromProto(const proto::PublishDataTrackError& error); }; -/// Error code returned when pushing a frame to a local data track fails. +/// @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, @@ -60,7 +60,7 @@ enum class LocalDataTrackTryPushErrorCode : std::uint32_t { INTERNAL = 4, }; -/// Error details returned when pushing a frame to a local data track fails. +/// @brief Error details returned when pushing a frame to a local data track fails. struct LocalDataTrackTryPushError { LocalDataTrackTryPushErrorCode code{LocalDataTrackTryPushErrorCode::UNKNOWN}; std::string message; @@ -68,7 +68,7 @@ struct LocalDataTrackTryPushError { LIVEKIT_API static LocalDataTrackTryPushError fromProto(const proto::LocalDataTrackTryPushError& error); }; -/// Error code returned when subscribing to a remote data track fails. +/// @brief Error code returned when subscribing to a remote data track fails. enum class SubscribeDataTrackErrorCode : std::uint32_t { UNKNOWN = 0, INVALID_HANDLE = 1, @@ -79,7 +79,7 @@ enum class SubscribeDataTrackErrorCode : std::uint32_t { INTERNAL = 6, }; -/// Error details returned when subscribing to a remote data track fails. +/// @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 502f60c2..f5f07a90 100644 --- a/include/livekit/data_track_stream.h +++ b/include/livekit/data_track_stream.h @@ -53,7 +53,7 @@ class DataTrackStreamReadResponse; /// } class LIVEKIT_API DataTrackStream { public: - /// Options for subscribing to a remote data track stream. + /// @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 5c5cbbe2..4bc81a26 100644 --- a/include/livekit/e2ee.h +++ b/include/livekit/e2ee.h @@ -160,7 +160,7 @@ class LIVEKIT_API E2EEManager { KeyProviderOptions options_; }; - /// Frame-level cryptor controls for one participant. + /// @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/local_participant.h b/include/livekit/local_participant.h index 057630c1..e2a20af3 100644 --- a/include/livekit/local_participant.h +++ b/include/livekit/local_participant.h @@ -43,7 +43,7 @@ class FfiClient; class Track; class LocalTrackPublication; -/// Data passed to a registered RPC method handler. +/// @brief Data passed to a registered RPC method handler. struct RpcInvocationData { std::string request_id; std::string caller_identity; diff --git a/include/livekit/local_track_publication.h b/include/livekit/local_track_publication.h index ed8a6a4f..bba399bf 100644 --- a/include/livekit/local_track_publication.h +++ b/include/livekit/local_track_publication.h @@ -25,7 +25,7 @@ namespace proto { class OwnedTrackPublication; } -/// Publication metadata for a locally published media track. +/// @brief Publication metadata for a locally published media track. 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 c5eaaa51..2b5857db 100644 --- a/include/livekit/participant.h +++ b/include/livekit/participant.h @@ -27,10 +27,10 @@ namespace livekit { -/// Identifies the type of participant connected to a room. +/// @brief Identifies the type of participant connected to a room. enum class ParticipantKind { Standard = 0, Ingress, Egress, Sip, Agent }; -/// Base class for local and remote room participants. +/// @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, diff --git a/include/livekit/remote_participant.h b/include/livekit/remote_participant.h index 8a1d45c3..1addd141 100644 --- a/include/livekit/remote_participant.h +++ b/include/livekit/remote_participant.h @@ -27,7 +27,7 @@ namespace livekit { class RemoteTrackPublication; -/// Represents a remote participant in a LiveKit room. +/// @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 116fa69f..afc375c6 100644 --- a/include/livekit/remote_track_publication.h +++ b/include/livekit/remote_track_publication.h @@ -25,7 +25,7 @@ namespace proto { class OwnedTrackPublication; } -/// Publication metadata for a remotely published media track. +/// @brief Publication metadata for a remotely published media track. class LIVEKIT_API RemoteTrackPublication : public TrackPublication { public: /// Note, this RemoteTrackPublication is constructed internally only; diff --git a/include/livekit/stats.h b/include/livekit/stats.h index 8c248b72..ce45d42e 100644 --- a/include/livekit/stats.h +++ b/include/livekit/stats.h @@ -51,7 +51,7 @@ class CertificateStats; class StreamStats; } // namespace proto -/// State of a WebRTC data channel. +/// @brief State of a WebRTC data channel. enum class DataChannelState { Connecting, Open, @@ -60,7 +60,7 @@ enum class DataChannelState { Unknown, }; -/// Reason outbound media quality is currently limited. +/// @brief Reason outbound media quality is currently limited. enum class QualityLimitationReason { None, Cpu, @@ -68,14 +68,14 @@ enum class QualityLimitationReason { Other, }; -/// ICE role used by a transport. +/// @brief ICE role used by a transport. enum class IceRole { Unknown, Controlling, Controlled, }; -/// DTLS transport state. +/// @brief DTLS transport state. enum class DtlsTransportState { New, Connecting, @@ -85,7 +85,7 @@ enum class DtlsTransportState { Unknown, }; -/// ICE transport state. +/// @brief ICE transport state. enum class IceTransportState { New, Checking, @@ -97,14 +97,14 @@ enum class IceTransportState { Unknown, }; -/// DTLS role used by a transport. +/// @brief DTLS role used by a transport. enum class DtlsRole { Client, Server, Unknown, }; -/// State of an ICE candidate pair. +/// @brief State of an ICE candidate pair. enum class IceCandidatePairState { Frozen, Waiting, @@ -114,7 +114,7 @@ enum class IceCandidatePairState { Unknown, }; -/// Type of ICE candidate. +/// @brief Type of ICE candidate. enum class IceCandidateType { Host, Srflx, @@ -123,7 +123,7 @@ enum class IceCandidateType { Unknown, }; -/// Transport protocol used by an ICE server. +/// @brief Transport protocol used by an ICE server. enum class IceServerTransportProtocol { Udp, Tcp, @@ -131,7 +131,7 @@ enum class IceServerTransportProtocol { Unknown, }; -/// TCP candidate type for an ICE candidate. +/// @brief TCP candidate type for an ICE candidate. enum class IceTcpCandidateType { Active, Passive, @@ -143,13 +143,13 @@ enum class IceTcpCandidateType { // Leaf stats types // ---------------------- -/// Base data shared by RTC stats records. +/// @brief Base data shared by RTC stats records. struct RtcStatsData { std::string id; std::int64_t timestamp_ms; }; -/// Codec statistics for an RTP stream. +/// @brief Codec statistics for an RTP stream. struct CodecStats { std::uint32_t payload_type; std::string transport_id; @@ -159,7 +159,7 @@ struct CodecStats { std::string sdp_fmtp_line; }; -/// Base statistics for an RTP stream. +/// @brief Base statistics for an RTP stream. struct RtpStreamStats { std::uint32_t ssrc; std::string kind; @@ -167,14 +167,14 @@ struct RtpStreamStats { std::string codec_id; }; -/// Statistics for received RTP streams. +/// @brief Statistics for received RTP streams. struct ReceivedRtpStreamStats { std::uint64_t packets_received; std::int64_t packets_lost; double jitter; }; -/// Statistics for inbound RTP media. +/// @brief Statistics for inbound RTP media. struct InboundRtpStreamStats { std::string track_identifier; std::string mid; @@ -231,13 +231,13 @@ struct InboundRtpStreamStats { std::uint32_t fec_ssrc; }; -/// Statistics for sent RTP streams. +/// @brief Statistics for sent RTP streams. struct SentRtpStreamStats { std::uint64_t packets_sent; std::uint64_t bytes_sent; }; -/// Statistics for outbound RTP media. +/// @brief Statistics for outbound RTP media. struct OutboundRtpStreamStats { std::string mid; std::string media_source_id; @@ -271,7 +271,7 @@ struct OutboundRtpStreamStats { std::string scalability_mode; }; -/// Statistics reported by a remote receiver for a local outbound RTP stream. +/// @brief Statistics reported by a remote receiver for a local outbound RTP stream. struct RemoteInboundRtpStreamStats { std::string local_id; double round_trip_time; @@ -280,7 +280,7 @@ struct RemoteInboundRtpStreamStats { std::uint64_t round_trip_time_measurements; }; -/// Statistics reported by a remote sender for a local inbound RTP stream. +/// @brief Statistics reported by a remote sender for a local inbound RTP stream. struct RemoteOutboundRtpStreamStats { std::string local_id; double remote_timestamp; @@ -290,13 +290,13 @@ struct RemoteOutboundRtpStreamStats { std::uint64_t round_trip_time_measurements; }; -/// Common statistics for a local media source. +/// @brief Common statistics for a local media source. struct MediaSourceStats { std::string track_identifier; std::string kind; }; -/// Statistics for a local audio source. +/// @brief Statistics for a local audio source. struct AudioSourceStats { double audio_level; double total_audio_energy; @@ -309,7 +309,7 @@ struct AudioSourceStats { std::uint64_t total_samples_captured; }; -/// Statistics for a local video source. +/// @brief Statistics for a local video source. struct VideoSourceStats { std::uint32_t width; std::uint32_t height; @@ -332,13 +332,13 @@ struct AudioPlayoutStats { std::uint64_t total_samples_count; ///< Total number of samples played out. }; -/// Statistics for a peer connection. +/// @brief Statistics for a peer connection. struct PeerConnectionStats { std::uint32_t data_channels_opened; std::uint32_t data_channels_closed; }; -/// Statistics for a WebRTC data channel. +/// @brief Statistics for a WebRTC data channel. struct DataChannelStats { std::string label; std::string protocol; @@ -350,7 +350,7 @@ struct DataChannelStats { std::uint64_t bytes_received; }; -/// Statistics for a WebRTC transport. +/// @brief Statistics for a WebRTC transport. struct TransportStats { std::uint64_t packets_sent; std::uint64_t packets_received; @@ -370,7 +370,7 @@ struct TransportStats { std::uint32_t selected_candidate_pair_changes; }; -/// Statistics for a selected or candidate ICE pair. +/// @brief Statistics for a selected or candidate ICE pair. struct CandidatePairStats { std::string transport_id; std::string local_candidate_id; @@ -396,7 +396,7 @@ struct CandidatePairStats { std::uint64_t bytes_discarded_on_send; }; -/// Statistics for a local or remote ICE candidate. +/// @brief Statistics for a local or remote ICE candidate. struct IceCandidateStats { std::string transport_id; std::string address; @@ -413,7 +413,7 @@ struct IceCandidateStats { std::optional tcp_type; }; -/// Statistics for a DTLS certificate. +/// @brief Statistics for a DTLS certificate. struct CertificateStats { std::string fingerprint; std::string fingerprint_algorithm; @@ -421,7 +421,7 @@ struct CertificateStats { std::string issuer_certificate_id; }; -/// Statistics for a media stream. +/// @brief Statistics for a media stream. struct StreamStats { std::string id; std::string stream_identifier; @@ -431,13 +431,13 @@ struct StreamStats { // High-level RtcStats wrapper // ---------------------- -/// Typed RTC stats wrapper for codec statistics. +/// @brief Typed RTC stats wrapper for codec statistics. struct RtcCodecStats { RtcStatsData rtc; CodecStats codec; }; -/// Typed RTC stats wrapper for inbound RTP statistics. +/// @brief Typed RTC stats wrapper for inbound RTP statistics. struct RtcInboundRtpStats { RtcStatsData rtc; RtpStreamStats stream; @@ -445,7 +445,7 @@ struct RtcInboundRtpStats { InboundRtpStreamStats inbound; }; -/// Typed RTC stats wrapper for outbound RTP statistics. +/// @brief Typed RTC stats wrapper for outbound RTP statistics. struct RtcOutboundRtpStats { RtcStatsData rtc; RtpStreamStats stream; @@ -453,7 +453,7 @@ struct RtcOutboundRtpStats { OutboundRtpStreamStats outbound; }; -/// Typed RTC stats wrapper for remote inbound RTP statistics. +/// @brief Typed RTC stats wrapper for remote inbound RTP statistics. struct RtcRemoteInboundRtpStats { RtcStatsData rtc; RtpStreamStats stream; @@ -461,7 +461,7 @@ struct RtcRemoteInboundRtpStats { RemoteInboundRtpStreamStats remote_inbound; }; -/// Typed RTC stats wrapper for remote outbound RTP statistics. +/// @brief Typed RTC stats wrapper for remote outbound RTP statistics. struct RtcRemoteOutboundRtpStats { RtcStatsData rtc; RtpStreamStats stream; @@ -469,7 +469,7 @@ struct RtcRemoteOutboundRtpStats { RemoteOutboundRtpStreamStats remote_outbound; }; -/// Typed RTC stats wrapper for media source statistics. +/// @brief Typed RTC stats wrapper for media source statistics. struct RtcMediaSourceStats { RtcStatsData rtc; MediaSourceStats source; @@ -477,55 +477,55 @@ struct RtcMediaSourceStats { VideoSourceStats video; }; -/// Typed RTC stats wrapper for audio playout statistics. +/// @brief Typed RTC stats wrapper for audio playout statistics. struct RtcMediaPlayoutStats { RtcStatsData rtc; AudioPlayoutStats audio_playout; }; -/// Typed RTC stats wrapper for peer connection statistics. +/// @brief Typed RTC stats wrapper for peer connection statistics. struct RtcPeerConnectionStats { RtcStatsData rtc; PeerConnectionStats pc; }; -/// Typed RTC stats wrapper for data channel statistics. +/// @brief Typed RTC stats wrapper for data channel statistics. struct RtcDataChannelStats { RtcStatsData rtc; DataChannelStats dc; }; -/// Typed RTC stats wrapper for transport statistics. +/// @brief Typed RTC stats wrapper for transport statistics. struct RtcTransportStats { RtcStatsData rtc; TransportStats transport; }; -/// Typed RTC stats wrapper for ICE candidate pair statistics. +/// @brief Typed RTC stats wrapper for ICE candidate pair statistics. struct RtcCandidatePairStats { RtcStatsData rtc; CandidatePairStats candidate_pair; }; -/// Typed RTC stats wrapper for local ICE candidate statistics. +/// @brief Typed RTC stats wrapper for local ICE candidate statistics. struct RtcLocalCandidateStats { RtcStatsData rtc; IceCandidateStats candidate; }; -/// Typed RTC stats wrapper for remote ICE candidate statistics. +/// @brief Typed RTC stats wrapper for remote ICE candidate statistics. struct RtcRemoteCandidateStats { RtcStatsData rtc; IceCandidateStats candidate; }; -/// Typed RTC stats wrapper for certificate statistics. +/// @brief Typed RTC stats wrapper for certificate statistics. struct RtcCertificateStats { RtcStatsData rtc; CertificateStats certificate; }; -/// Typed RTC stats wrapper for media stream statistics. +/// @brief Typed RTC stats wrapper for media stream statistics. struct RtcStreamStats { RtcStatsData rtc; StreamStats stream; @@ -539,7 +539,7 @@ using RtcStatsVariant = RtcDataChannelStats, RtcTransportStats, RtcCandidatePairStats, RtcLocalCandidateStats, RtcRemoteCandidateStats, RtcCertificateStats, RtcStreamStats>; -/// Variant wrapper for typed RTC stats records. +/// @brief Variant wrapper for typed RTC stats records. struct RtcStats { RtcStatsVariant stats; }; diff --git a/include/livekit/track.h b/include/livekit/track.h index bbf6ba43..544e82d4 100644 --- a/include/livekit/track.h +++ b/include/livekit/track.h @@ -32,14 +32,14 @@ namespace livekit { class LocalTrackPublication; -/// Media kind for an audio or video track. +/// @brief Media kind for an audio or video track. enum class TrackKind { KIND_UNKNOWN = 0, KIND_AUDIO = 1, KIND_VIDEO = 2, }; -/// Source category for a published track. +/// @brief Source category for a published track. enum class TrackSource { SOURCE_UNKNOWN = 0, SOURCE_CAMERA = 1, @@ -48,14 +48,14 @@ enum class TrackSource { SOURCE_SCREENSHARE_AUDIO = 4, }; -/// Stream state reported for a subscribed track. +/// @brief Stream state reported for a subscribed track. enum class StreamState { STATE_UNKNOWN = 0, STATE_ACTIVE = 1, STATE_PAUSED = 2, }; -/// Optional audio processing or encoding feature advertised for a track. +/// @brief Optional audio processing or encoding feature advertised for a track. enum class AudioTrackFeature { TF_STEREO = 0, TF_NO_DTX = 1, @@ -66,14 +66,14 @@ enum class AudioTrackFeature { TF_PRECONNECT_BUFFER = 6, }; -/// Per-participant track subscription permission configuration. +/// @brief Per-participant track subscription permission configuration. struct ParticipantTrackPermission { std::string participant_identity; std::optional allow_all; std::vector allowed_track_sids; }; -/// Base class for local and remote media tracks. +/// @brief Base class for local and remote media tracks. class LIVEKIT_API Track { public: virtual ~Track() = default; diff --git a/include/livekit/track_publication.h b/include/livekit/track_publication.h index dd8fbd42..c618cd7d 100644 --- a/include/livekit/track_publication.h +++ b/include/livekit/track_publication.h @@ -32,7 +32,7 @@ class Track; class LocalTrack; class RemoteTrack; -/// Base class for a track that has been published to a room. +/// @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 0aa3e1d3..e13c7b8d 100644 --- a/include/livekit/video_frame.h +++ b/include/livekit/video_frame.h @@ -28,7 +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 }; -/// Plane layout metadata for a VideoFrame buffer. +/// @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 9c99fd02..1554fee9 100644 --- a/include/livekit/video_stream.h +++ b/include/livekit/video_stream.h @@ -65,7 +65,7 @@ class FfiEvent; /// stream->close(); // optional, called automatically in destructor class LIVEKIT_API VideoStream { public: - /// Options for creating a decoded video frame stream. + /// @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). From c5d60f1b2cb6f0fc0d603248b449a226117133a3 Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 28 May 2026 08:49:43 -0600 Subject: [PATCH 17/27] Always upload docs --- .github/workflows/generate-docs.yml | 100 +++++++++++++++++++++++----- .github/workflows/publish-docs.yml | 5 +- docs/guides/README.md | 49 -------------- 3 files changed, 86 insertions(+), 68 deletions(-) delete mode 100644 docs/guides/README.md diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 3b3508e5..12931e74 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -2,15 +2,23 @@ name: Generate docs permissions: contents: read -# Builds the Doxygen docs (and optionally uploads them as a workflow artifact) -# without publishing. Two roles: +# Builds the Doxygen docs and uploads them as a workflow artifact (without +# publishing). Two roles: # # 1. Dry-run on every PR that touches C++ code (or other Doxygen inputs) so -# docs breakage is caught before merge. No artifact is uploaded. +# docs breakage is caught before merge. The artifact is attached to the +# workflow run so reviewers can download a preview. # 2. Reusable workflow consumed by publish-docs.yml: builds the docs, prints # the resolved version, and uploads the docs/doxygen/html/ directory as an -# artifact for -# a downstream publish job to consume. Avoids double-building the docs. +# artifact for a downstream publish job to consume. Avoids double-building +# the docs. +# +# Artifact layout: actions/upload-artifact@v4 uses the longest common path of +# all uploaded files as the artifact root. With `path: docs/doxygen/html/`, the +# artifact contains the *contents* of that directory at the artifact root +# (index.html, favicon.ico, …) -- NOT nested under docs/doxygen/html/. This +# means downstream consumers using `path: html` get files at html/index.html. +# The "Verify artifact layout" step below proves this empirically on every run. on: pull_request: branches: ["main"] @@ -33,23 +41,23 @@ on: description: 'Upload the built docs/doxygen/html/ tree as a workflow artifact' required: false type: boolean - default: false + 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' + description: 'Retention (days) for the uploaded docs artifact. Default tuned for PR-preview use.' required: false type: number - default: 1 + 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 when upload_artifact is false)' + description: 'Name of the uploaded docs artifact (empty if upload was skipped via upload_artifact: false)' value: ${{ jobs.validate.outputs.artifact_name }} jobs: @@ -119,24 +127,80 @@ jobs: exit 1 fi - - name: Resolve artifact name + # `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 + 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 - NAME="${{ inputs.artifact_name }}" - if [[ -z "$NAME" ]]; then - echo "ERROR: artifact_name input is empty." + if [[ -z "$INPUT_NAME" ]]; then + echo "ERROR: artifact name resolved to empty." exit 1 fi - echo "name=${NAME}" >>"$GITHUB_OUTPUT" + echo "name=${INPUT_NAME}" >>"$GITHUB_OUTPUT" + echo "retention=${INPUT_RETENTION}" >>"$GITHUB_OUTPUT" - name: Upload docs artifact - if: inputs.upload_artifact + if: steps.artifact_meta.outputs.name != '' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: ${{ inputs.artifact_name }} + name: ${{ steps.artifact_meta.outputs.name }} path: docs/doxygen/html/ - retention-days: ${{ inputs.artifact_retention_days }} + retention-days: ${{ steps.artifact_meta.outputs.retention }} if-no-files-found: error + + # Empirical verification that the artifact GitHub stores is laid out the + # way publish-docs.yml expects: download it back into a *separate* + # directory and confirm index.html lives at the top of that directory + # (not nested under docs/doxygen/html/). If actions/upload-artifact ever + # changes its "longest common path" behavior, this step fails loudly. + - name: Verify artifact layout + if: steps.artifact_meta.outputs.name != '' + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + with: + name: ${{ steps.artifact_meta.outputs.name }} + path: artifact-verify + + - name: Inspect downloaded artifact + if: steps.artifact_meta.outputs.name != '' + shell: bash + run: | + set -euo pipefail + + NAME="${{ steps.artifact_meta.outputs.name }}" + ROOT="artifact-verify" + + echo "Artifact '${NAME}' top-level contents:" + 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 index.html at the artifact root." + exit 1 + fi + + # Print a small sample to the step summary so the layout is visible + # without digging through the job log. + { + echo "### Docs artifact: \`${NAME}\`" + echo "" + echo "Uploaded ${TOTAL} files. Sample top-level entries:" + echo "" + echo '```' + (cd "$ROOT" && ls -1 | head -20) + echo '```' + echo "" + echo "Layout confirmed: \`index.html\` is at the artifact root," + echo "so \`publish-docs.yml\` downloading with \`path: html\` will land" + echo "files at \`html/index.html\`, \`html/favicon.ico\`, etc." + } >>"$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 4b92335a..c2440580 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -23,7 +23,10 @@ jobs: # logic that PR dry-runs run, and we ship the resulting HTML tree (built into # docs/doxygen/html/ on the builder) to the publish job below as a workflow # artifact (avoids double-building). The artifact contains the *contents* of - # that directory, so the download path here can stay as the simpler "html". + # that directory at its root (actions/upload-artifact uses the longest common + # path of the uploaded files as the artifact root), so the download step + # below lands them at html/index.html, html/favicon.ico, etc. The + # "Verify artifact layout" step in generate-docs.yml asserts this every run. validate: name: Validate (build docs) uses: ./.github/workflows/generate-docs.yml diff --git a/docs/guides/README.md b/docs/guides/README.md deleted file mode 100644 index dce60d3f..00000000 --- a/docs/guides/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# Guides - -Hand-written long-form documentation for the LiveKit C++ SDK. These -files are processed by Doxygen as additional pages and surface in the -generated docs alongside the auto-extracted API reference under -`include/livekit/`. - -This directory complements: - -- **`include/livekit/*.h`** — `///` doc comments for the API reference - (classes, structs, free functions, enums). -- **`docs/doxygen/index.md`** — the Doxygen mainpage / landing page - that links out to everything else. -- **`docs/doxygen/`** — Doxygen tool configuration (`Doxyfile`, theme - assets, mainpage). Nothing in there is intended for human reading - on its own (the mainpage uses Doxygen-only syntax like `@ref`). - -## What belongs here - -Topics that are too long for a `///` comment on a single type and too -narrative for the API reference. Examples: - -- Threading model and lifecycle conventions. -- End-to-end "publish a track" or "subscribe to a stream" walkthroughs. -- Error-handling philosophy (`Result` vs exceptions vs callbacks). -- E2EE / key provider deep dives. -- Migration guides between SDK versions. - -Anything that is genuinely *about a single symbol* belongs as a `///` -comment on that symbol instead, so it shows up inline in IDEs. - -## File convention - -- One topic per file. Filename in `kebab-case.md`. -- First line is a top-level `# Title` heading (becomes the Doxygen - page title). -- Use `///`-style Doxygen commands sparingly inside Markdown — prefer - plain Markdown where it works. -- Reference public symbols with `@ref livekit::FooBar` (classes/structs) - or `@ref livekit::someFreeFunction()` (free functions; the parens - are required for Doxygen to resolve the name). - -## Wiring into Doxygen - -Files in this directory are **not yet wired into the Doxyfile's -`INPUT`**. When the first real guide lands, add `docs/guides` to the -`INPUT =` line of `docs/doxygen/Doxyfile` so Doxygen processes the -folder. This `README.md` is intentionally excluded until then so it -doesn't render as a "README" page in the generated site. From 180128940c924fc72fda00c1f947efdb09278658 Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 28 May 2026 09:02:14 -0600 Subject: [PATCH 18/27] Get back html folder to avoid breaking publish stage --- .github/workflows/generate-docs.yml | 54 ++++++++++++++++++----------- .github/workflows/publish-docs.yml | 8 ----- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 12931e74..e903ffff 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -13,12 +13,15 @@ permissions: # artifact for a downstream publish job to consume. Avoids double-building # the docs. # -# Artifact layout: actions/upload-artifact@v4 uses the longest common path of +# Artifact layout: actions/upload-artifact@v4+ uses the longest common path of # all uploaded files as the artifact root. With `path: docs/doxygen/html/`, the # artifact contains the *contents* of that directory at the artifact root -# (index.html, favicon.ico, …) -- NOT nested under docs/doxygen/html/. This -# means downstream consumers using `path: html` get files at html/index.html. -# The "Verify artifact layout" step below proves this empirically on every run. +# (index.html, favicon.ico, …) -- NOT nested under docs/doxygen/html/. The +# `html/` prefix that publish-docs.yml relies on comes from the *download* +# step's `path: html` parameter, not from the artifact itself. The +# "Re-download artifact" + "Inspect downloaded artifact" steps below prove +# this end-to-end on every run by re-downloading with `path: html` and +# asserting `html/index.html` is present. on: pull_request: branches: ["main"] @@ -156,17 +159,19 @@ jobs: retention-days: ${{ steps.artifact_meta.outputs.retention }} if-no-files-found: error - # Empirical verification that the artifact GitHub stores is laid out the - # way publish-docs.yml expects: download it back into a *separate* - # directory and confirm index.html lives at the top of that directory - # (not nested under docs/doxygen/html/). If actions/upload-artifact ever - # changes its "longest common path" behavior, this step fails loudly. - - name: Verify artifact layout + # Empirical proof of the publish-docs.yml contract: re-download the + # just-uploaded artifact using the *same* `path: html` value that + # publish-docs.yml uses, and assert html/index.html exists. The artifact + # itself is flat (its root contains index.html, favicon.ico, …); the + # `html/` prefix comes purely from the download step's `path:`. If + # actions/upload-artifact ever changes its "longest common path" root + # behavior, this assertion fails loudly. + - name: Re-download artifact (mirrors publish-docs.yml exactly) if: steps.artifact_meta.outputs.name != '' uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: ${{ steps.artifact_meta.outputs.name }} - path: artifact-verify + path: html - name: Inspect downloaded artifact if: steps.artifact_meta.outputs.name != '' @@ -175,9 +180,11 @@ jobs: set -euo pipefail NAME="${{ steps.artifact_meta.outputs.name }}" - ROOT="artifact-verify" + ROOT="html" - echo "Artifact '${NAME}' top-level contents:" + 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 ' ') @@ -185,22 +192,27 @@ jobs: if [[ ! -f "${ROOT}/index.html" ]]; then echo "ERROR: ${ROOT}/index.html is missing -- artifact layout regressed." - echo " publish-docs.yml expects index.html at the artifact root." + echo " publish-docs.yml downloads with the same 'path: html' and" + echo " expects html/index.html." exit 1 fi - # Print a small sample to the step summary so the layout is visible - # without digging through the job log. + SAMPLE=$( (cd "$ROOT" && ls -1) | sed -n '1,20p' ) + { echo "### Docs artifact: \`${NAME}\`" echo "" - echo "Uploaded ${TOTAL} files. Sample top-level entries:" + echo "Uploaded ${TOTAL} files. The artifact's *contents* live at" + echo "the artifact root (there is no \`html/\` prefix inside the" + echo "artifact itself). The \`html/\` directory below comes from" + echo "this step's \`path: html\`, which mirrors \`publish-docs.yml\`:" echo "" echo '```' - (cd "$ROOT" && ls -1 | head -20) + echo "${SAMPLE}" echo '```' echo "" - echo "Layout confirmed: \`index.html\` is at the artifact root," - echo "so \`publish-docs.yml\` downloading with \`path: html\` will land" - echo "files at \`html/index.html\`, \`html/favicon.ico\`, etc." + echo "Layout confirmed: \`html/index.html\` is present, so the" + echo "\`aws s3 cp html/ s3://… --recursive\` step in" + echo "\`publish-docs.yml\` will publish \`index.html\`, \`favicon.ico\`," + echo "etc. at the bucket root." } >>"$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index c2440580..c170fcbd 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -19,14 +19,6 @@ permissions: actions: read jobs: - # Reuse generate-docs.yml so the publish path runs exactly the same build/print - # logic that PR dry-runs run, and we ship the resulting HTML tree (built into - # docs/doxygen/html/ on the builder) to the publish job below as a workflow - # artifact (avoids double-building). The artifact contains the *contents* of - # that directory at its root (actions/upload-artifact uses the longest common - # path of the uploaded files as the artifact root), so the download step - # below lands them at html/index.html, html/favicon.ico, etc. The - # "Verify artifact layout" step in generate-docs.yml asserts this every run. validate: name: Validate (build docs) uses: ./.github/workflows/generate-docs.yml From 3f98e2ec13af1debcf77127a5dc4a200832b9c68 Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 28 May 2026 09:16:38 -0600 Subject: [PATCH 19/27] Cleanup AI --- .github/workflows/generate-docs.yml | 33 +++-------------------------- 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index e903ffff..dbb515c1 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -159,14 +159,8 @@ jobs: retention-days: ${{ steps.artifact_meta.outputs.retention }} if-no-files-found: error - # Empirical proof of the publish-docs.yml contract: re-download the - # just-uploaded artifact using the *same* `path: html` value that - # publish-docs.yml uses, and assert html/index.html exists. The artifact - # itself is flat (its root contains index.html, favicon.ico, …); the - # `html/` prefix comes purely from the download step's `path:`. If - # actions/upload-artifact ever changes its "longest common path" root - # behavior, this assertion fails loudly. - - name: Re-download artifact (mirrors publish-docs.yml exactly) + # 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: @@ -192,27 +186,6 @@ jobs: if [[ ! -f "${ROOT}/index.html" ]]; then echo "ERROR: ${ROOT}/index.html is missing -- artifact layout regressed." - echo " publish-docs.yml downloads with the same 'path: html' and" - echo " expects html/index.html." + echo " publish-docs.yml expects html/index.html." exit 1 fi - - SAMPLE=$( (cd "$ROOT" && ls -1) | sed -n '1,20p' ) - - { - echo "### Docs artifact: \`${NAME}\`" - echo "" - echo "Uploaded ${TOTAL} files. The artifact's *contents* live at" - echo "the artifact root (there is no \`html/\` prefix inside the" - echo "artifact itself). The \`html/\` directory below comes from" - echo "this step's \`path: html\`, which mirrors \`publish-docs.yml\`:" - echo "" - echo '```' - echo "${SAMPLE}" - echo '```' - echo "" - echo "Layout confirmed: \`html/index.html\` is present, so the" - echo "\`aws s3 cp html/ s3://… --recursive\` step in" - echo "\`publish-docs.yml\` will publish \`index.html\`, \`favicon.ico\`," - echo "etc. at the bucket root." - } >>"$GITHUB_STEP_SUMMARY" From 31b7aaf44598cc24d941823ec14daa4da4cf2c34 Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 28 May 2026 09:21:22 -0600 Subject: [PATCH 20/27] Docs version --- .github/workflows/generate-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index dbb515c1..d9f0f6b0 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -115,7 +115,7 @@ jobs: echo "Docs version: ${PROJECT_NUMBER}" { - echo "Version: \`${PROJECT_NUMBER}\`" + echo "Docs version: \`${PROJECT_NUMBER}\`" echo "" echo "> Note: On a non-tag/release run, the version will resolve to:" echo " \`--\`" From caa8ffdb6d3624315a96596b85f83c065bc7c82d Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 28 May 2026 09:25:57 -0600 Subject: [PATCH 21/27] Cleanup AI comments --- .github/workflows/generate-docs.yml | 32 +++++++++-------------------- .github/workflows/publish-docs.yml | 4 ++-- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index d9f0f6b0..8b687463 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -2,26 +2,14 @@ name: Generate docs permissions: contents: read -# Builds the Doxygen docs and uploads them as a workflow artifact (without -# publishing). Two roles: -# -# 1. Dry-run on every PR that touches C++ code (or other Doxygen inputs) so -# docs breakage is caught before merge. The artifact is attached to the -# workflow run so reviewers can download a preview. -# 2. Reusable workflow consumed by publish-docs.yml: builds the docs, prints -# the resolved version, and uploads the docs/doxygen/html/ directory as an -# artifact for a downstream publish job to consume. Avoids double-building -# the docs. -# -# Artifact layout: actions/upload-artifact@v4+ uses the longest common path of -# all uploaded files as the artifact root. With `path: docs/doxygen/html/`, the -# artifact contains the *contents* of that directory at the artifact root -# (index.html, favicon.ico, …) -- NOT nested under docs/doxygen/html/. The -# `html/` prefix that publish-docs.yml relies on comes from the *download* -# step's `path: html` parameter, not from the artifact itself. The -# "Re-download artifact" + "Inspect downloaded artifact" steps below prove -# this end-to-end on every run by re-downloading with `path: html` and -# asserting `html/index.html` is present. +# 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"] @@ -41,7 +29,7 @@ on: required: false type: string upload_artifact: - description: 'Upload the built docs/doxygen/html/ tree as a workflow artifact' + description: 'Upload the generated docs folder as a workflow artifact' required: false type: boolean default: true @@ -51,7 +39,7 @@ on: type: string default: livekit-cpp-docs artifact_retention_days: - description: 'Retention (days) for the uploaded docs artifact. Default tuned for PR-preview use.' + description: 'Retention (days) for the uploaded docs artifact' required: false type: number default: 7 diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index c170fcbd..af55e60a 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -4,13 +4,13 @@ on: workflow_dispatch: inputs: version: - description: 'Documentation version (for example, 0.1.0 or v0.1.0)' + description: 'Documentation version (e.g. v0.1.0)' required: false type: string workflow_call: inputs: version: - description: 'Documentation version (for example, 0.1.0 or v0.1.0)' + description: 'Documentation version (e.g. v0.1.0)' required: false type: string From 327733e59531e2cd663a7b0203c97a24ef5bf7fc Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 28 May 2026 09:26:35 -0600 Subject: [PATCH 22/27] Additional cleanup --- scripts/generate-docs.sh | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/scripts/generate-docs.sh b/scripts/generate-docs.sh index 11e4a574..bf01e445 100755 --- a/scripts/generate-docs.sh +++ b/scripts/generate-docs.sh @@ -82,7 +82,7 @@ elif [[ -z "$project_number" ]]; then fi # git describe emits "--g[-dirty]" - # Strip unneeded "g" so it's easier to read. + # 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 @@ -111,15 +111,6 @@ fi echo "==> Building Doxygen docs with PROJECT_NUMBER=${project_number}" cd "$repo_root" -# Doxygen owns the failure decision: docs/doxygen/Doxyfile sets -# WARN_IF_UNDOCUMENTED = NO (silences the ~400 "X is not documented" -# warnings about internal/private symbols) -# WARN_AS_ERROR = FAIL_ON_WARNINGS -# so any remaining warning (broken @ref, unknown @-command, unsupported HTML -# tag, malformed table, unreadable INPUT entry, etc.) fails this script via a -# non-zero exit. There is no per-warning toggle in Doxygen; if a category -# becomes too noisy to enforce, mute it at the WARN_IF_* level in the Doxyfile -# rather than adding pattern allowlists here. LIVEKIT_DOXYGEN_PROJECT_NUMBER="$project_number" doxygen docs/doxygen/Doxyfile docs_index="${repo_root}/docs/doxygen/html/index.html" From d32e95cd1e1bd5b11bd028723b31b985232055e2 Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 28 May 2026 11:22:33 -0600 Subject: [PATCH 23/27] PR feedback --- include/livekit/local_participant.h | 2 +- include/livekit/local_track_publication.h | 2 +- include/livekit/remote_track_publication.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/livekit/local_participant.h b/include/livekit/local_participant.h index e2a20af3..9369a914 100644 --- a/include/livekit/local_participant.h +++ b/include/livekit/local_participant.h @@ -51,7 +51,7 @@ struct RpcInvocationData { double response_timeout_sec; // seconds }; -/// Represents the local participant in a room. +/// @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 bba399bf..412b4fb9 100644 --- a/include/livekit/local_track_publication.h +++ b/include/livekit/local_track_publication.h @@ -25,7 +25,7 @@ namespace proto { class OwnedTrackPublication; } -/// @brief Publication metadata for a locally published media track. +/// @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/remote_track_publication.h b/include/livekit/remote_track_publication.h index afc375c6..b6475612 100644 --- a/include/livekit/remote_track_publication.h +++ b/include/livekit/remote_track_publication.h @@ -25,7 +25,7 @@ namespace proto { class OwnedTrackPublication; } -/// @brief Publication metadata for a remotely published media track. +/// @brief A track published by a remote participant. class LIVEKIT_API RemoteTrackPublication : public TrackPublication { public: /// Note, this RemoteTrackPublication is constructed internally only; From ce0c35117810d20001dce450583e7626fe7e422d Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 28 May 2026 11:53:02 -0600 Subject: [PATCH 24/27] Additional cleanup --- .github/workflows/generate-docs.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 8b687463..52ea026e 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -16,9 +16,7 @@ on: paths: - include/** - src/** - - examples/** - docs/** - - README.md - scripts/generate-docs.sh - .github/workflows/generate-docs.yml - .github/workflows/publish-docs.yml From 5dcb25d03e4135cc8303cf891ee1a0732bae3a06 Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 28 May 2026 12:33:28 -0600 Subject: [PATCH 25/27] Embed README into doc reference --- README.md | 18 +++++----- docs/doxygen/customization/custom.css | 29 ++++++++++++++++ scripts/generate-docs.sh | 49 ++++++++++++++++++++++++++- 3 files changed, 86 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ec266b2f..25423886 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 ``` @@ -628,13 +628,13 @@ lk token create \
- - - - - - - + + + + + + +
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/docs/doxygen/customization/custom.css b/docs/doxygen/customization/custom.css index a1744f25..541e517c 100644 --- a/docs/doxygen/customization/custom.css +++ b/docs/doxygen/customization/custom.css @@ -78,3 +78,32 @@ span.tt, 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/scripts/generate-docs.sh b/scripts/generate-docs.sh index bf01e445..cd373f1f 100755 --- a/scripts/generate-docs.sh +++ b/scripts/generate-docs.sh @@ -111,7 +111,54 @@ fi echo "==> Building Doxygen docs with PROJECT_NUMBER=${project_number}" cd "$repo_root" -LIVEKIT_DOXYGEN_PROJECT_NUMBER="$project_number" doxygen docs/doxygen/Doxyfile +# 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" From 44e74b91e148c2af14878ef9cc06c8666c8911e3 Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 28 May 2026 13:51:45 -0600 Subject: [PATCH 26/27] Agents MD update, cleanup rendered docs, misc changes --- AGENTS.md | 9 +++++---- README.md | 37 ++++++++++++++++++++----------------- README_BUILD.md | 2 +- docs/doxygen/Doxyfile | 4 ++-- 4 files changed, 28 insertions(+), 24 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 3353b307..6663fdef 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -261,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. diff --git a/README.md b/README.md index 25423886..14274c64 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) @@ -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 @@ -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,7 +379,7 @@ 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). @@ -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,9 +621,11 @@ 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.
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/doxygen/Doxyfile b/docs/doxygen/Doxyfile index f5159a9f..718adf31 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -42,7 +42,7 @@ 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 @@ -54,7 +54,7 @@ PROJECT_NUMBER = $(LIVEKIT_DOXYGEN_PROJECT_NUMBER) # 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 From 6d7a3e3dfe87574c687337ab4185fe1e976069fd Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 28 May 2026 15:36:21 -0600 Subject: [PATCH 27/27] Hopefully fix generate docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 14274c64..665f0042 100644 --- a/README.md +++ b/README.md @@ -383,7 +383,7 @@ Open the generated trace file in one of these viewers: The SDK includes integration and stress tests using Google Test (gtest). -#### Build Tests +### Build Tests **Linux/macOS:** ```bash