Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
4fe2c18
addListener/removeListener
alan-george-lk May 12, 2026
14e3eab
Merge branch 'main' of github.com:livekit/client-sdk-cpp into feature…
alan-george-lk May 12, 2026
6e76a75
Enforce readability in CI
alan-george-lk May 12, 2026
a8fc5bd
Try deprecation approach
alan-george-lk May 12, 2026
e106c2d
Initial deprecation method work
alan-george-lk May 13, 2026
03551d3
Merge branch 'main' of github.com:livekit/client-sdk-cpp into feature…
alan-george-lk May 13, 2026
8256b14
Add exemption
alan-george-lk May 13, 2026
ff07d7a
Merge branch 'main' of github.com:livekit/client-sdk-cpp into feature…
alan-george-lk May 14, 2026
c271f9d
Deconflict local_participant methods
alan-george-lk May 14, 2026
1689fd9
Merge branch 'main' into feature/unified_method_style
alan-george-lk May 14, 2026
ed8d2ec
Agentic changes to match Python SDK
alan-george-lk May 14, 2026
f1a279c
Merge branch 'main' of github.com:livekit/client-sdk-cpp into feature…
alan-george-lk May 15, 2026
0541533
Friend class between participant and room, better matches Python SDK
alan-george-lk May 15, 2026
20d3892
Additional symbol renames
alan-george-lk May 15, 2026
edbdbe1
Merge branch 'feature/unified_method_style' of github.com:livekit/cli…
alan-george-lk May 15, 2026
0a815e9
Fix room exception string
alan-george-lk May 15, 2026
0c51eac
Merge branch 'main' of github.com:livekit/client-sdk-cpp into feature…
alan-george-lk May 15, 2026
bb6eaf7
Cleanup comments
alan-george-lk May 20, 2026
e18061e
Remove unneeded methods
alan-george-lk May 20, 2026
775191b
Merge branch 'main' into feature/unified_method_style
alan-george-lk May 20, 2026
c60a82a
Fix compilation issue
alan-george-lk May 20, 2026
d91f404
Use proper deprecated tags for doxygen
alan-george-lk May 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Checks: >
modernize-*,
readability-misleading-indentation,
readability-redundant-smartptr-get,
readability-identifier-naming,
-bugprone-easily-swappable-parameters,
-modernize-use-trailing-return-type,
-modernize-avoid-c-arrays,
Expand Down Expand Up @@ -38,3 +39,7 @@ FormatStyle: file
CheckOptions:
- key: modernize-use-nullptr.NullMacros
value: 'NULL'
- key: readability-identifier-naming.ClassCase
value: CamelCase
- key: readability-identifier-naming.MethodCase
value: camelBack
32 changes: 29 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ When making larger scale changes, check with the developer before committing to

The SDK has three categories of threads:

**FFI callback thread** — The Rust FFI layer calls `LivekitFfiCallback` from a Rust-managed thread (typically a Tokio runtime thread). This single entry point deserializes the `FfiEvent` and calls `FfiClient::PushEvent`, which:
**FFI callback thread** — The Rust FFI layer calls `LivekitFfiCallback` from a Rust-managed thread (typically a Tokio runtime thread). This single entry point deserializes the `FfiEvent` and calls `FfiClient::pushEvent`, which:
1. Completes any pending async `std::promise` matched by `async_id`.
2. Invokes all registered `FfiClient` listeners (including `Room::OnEvent`).
2. Invokes all registered `FfiClient` listeners (including `Room::onEvent`).

All `RoomDelegate` callbacks and stream handler callbacks (e.g., `registerTextStreamHandler`) are invoked on this FFI callback thread. **Handlers must not block**; spawn a background thread if synchronous work is needed.

**Per-subscription reader threads** — `SubscriptionThreadDispatcher` creates a dedicated `std::thread` for each active audio, video, or data track subscription. These threads block on `AudioStream::read()`, `VideoStream::read()`, or `DataTrackStream::read()` and invoke the registered `AudioFrameCallback`, `VideoFrameCallback`, or `DataFrameCallback` on that reader thread — not on the FFI callback thread. A hard limit of 20 concurrent reader threads is enforced.

**Application threads** — The calling thread for public API methods such as `Room::Connect`, `LocalParticipant::publishTrack`, `AudioSource::captureFrame`, etc. These may block while waiting for FFI responses or future completion.
**Application threads** — The calling thread for public API methods such as `Room::connect`, `LocalParticipant::publishTrack`, `AudioSource::captureFrame`, etc. These may block while waiting for FFI responses or future completion.

#### Thread-safety guarantees

Expand Down Expand Up @@ -146,6 +146,32 @@ All source files must have the LiveKit Apache 2.0 copyright header. Use the curr
- `lk_log.h` lives under `src/` (internal). The public-facing logging API is `include/livekit/logging.h`.
- spdlog must not appear in any public header or installed header.

#### Deprecating a public API

When superseding a public API (renaming, replacing, or removing in a future major
version), every retained back-compat shim must carry **both** annotations:

1. The C++11 `[[deprecated("...")]]` attribute so the compiler warns at every
call site. The message should name the replacement (e.g.
`"AudioFrame::sample_rate is deprecated; use AudioFrame::sampleRate instead"`).
2. A Doxygen `/// @deprecated Use <newName>() instead.` line immediately above
the attribute so the generated docs render a deprecation callout and add the
symbol to Doxygen's auto-generated *Deprecated List* page. Doxygen does not
read the C++ attribute, so this line is required even though it duplicates
information.

Example:

```cpp
/// @deprecated Use sampleRate() instead.
[[deprecated("AudioFrame::sample_rate is deprecated; use AudioFrame::sampleRate instead")]]
int sample_rate() const noexcept { return sampleRate(); }
```

Keep the prose consistent: `Use <newName>() instead.` Per-symbol deprecations
must use `///` (not `//`); only section-level asides (e.g. "Deprecated public
mutators" group headers) may stay as plain `//` comments.

### Include Conventions

- **Public headers (`include/livekit/*.h`) must include other public headers
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/data_track_throughput/consumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ int main(int argc, char* argv[]) {
room_options.dynacast = false;

std::cout << "Connecting to " << options.url << std::endl;
if (!room.Connect(options.url, options.token, room_options)) {
if (!room.connect(options.url, options.token, room_options)) {
throw std::runtime_error("Failed to connect to LiveKit room");
}

Expand Down
2 changes: 1 addition & 1 deletion benchmarks/data_track_throughput/producer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ int main(int argc, char* argv[]) {
room_options.dynacast = false;

std::cout << "Connecting to " << options.url << std::endl;
if (!room.Connect(options.url, options.token, room_options)) {
if (!room.connect(options.url, options.token, room_options)) {
throw std::runtime_error("Failed to connect to LiveKit room");
}

Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ bool initializeLivekit(const std::string& url, const std::string& token) {
livekit::RoomOptions options;
options.auto_subscribe = true;
options.dynacast = false;
if (!room_->Connect(url, token, options)) {
if (!room_->connect(url, token, options)) {
std::cerr << "Failed to connect\n";
livekit::shutdown();
return false;
Expand Down
40 changes: 34 additions & 6 deletions include/livekit/audio_frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,22 +69,50 @@ class LIVEKIT_API AudioFrame {
std::vector<std::int16_t>& data() noexcept { return data_; }

/// Number of samples in the buffer (per all channels).
std::size_t total_samples() const noexcept { return data_.size(); }
std::size_t totalSamples() const noexcept { return data_.size(); }

/// Sample rate in Hz.
int sample_rate() const noexcept { return sample_rate_; }
int sampleRate() const noexcept { return sample_rate_; }

/// Number of channels.
int num_channels() const noexcept { return num_channels_; }
int numChannels() const noexcept { return num_channels_; }

/// Samples per channel.
int samples_per_channel() const noexcept { return samples_per_channel_; }
int samplesPerChannel() const noexcept { return samples_per_channel_; }

/// Duration in seconds (samples_per_channel / sample_rate).
/// Duration in seconds (samplesPerChannel / sampleRate).
double duration() const noexcept;

/// A human-readable description.
std::string to_string() const;
std::string toString() const;

/// @deprecated Use totalSamples() instead.
[[deprecated("AudioFrame::total_samples is deprecated; use AudioFrame::totalSamples instead")]]
std::size_t total_samples() const noexcept { // NOLINT(readability-identifier-naming)
return totalSamples();
}

/// @deprecated Use sampleRate() instead.
[[deprecated("AudioFrame::sample_rate is deprecated; use AudioFrame::sampleRate instead")]]
int sample_rate() const noexcept { // NOLINT(readability-identifier-naming)
return sampleRate();
}

/// @deprecated Use numChannels() instead.
[[deprecated("AudioFrame::num_channels is deprecated; use AudioFrame::numChannels instead")]]
int num_channels() const noexcept { // NOLINT(readability-identifier-naming)
return numChannels();
}

/// @deprecated Use samplesPerChannel() instead.
[[deprecated("AudioFrame::samples_per_channel is deprecated; use AudioFrame::samplesPerChannel instead")]]
int samples_per_channel() const noexcept { // NOLINT(readability-identifier-naming)
return samplesPerChannel();
}

/// @deprecated Use toString() instead.
[[deprecated("AudioFrame::to_string is deprecated; use AudioFrame::toString instead")]]
std::string to_string() const; // NOLINT(readability-identifier-naming)

protected:
// Build a proto AudioFrameBufferInfo pointing at this frame’s data.
Expand Down
2 changes: 1 addition & 1 deletion include/livekit/audio_processing_module.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ class LIVEKIT_API AudioProcessingModule {
bool valid() const noexcept { return handle_.valid(); }

/// Get the underlying FFI handle ID (used internally).
std::uint64_t ffi_handle_id() const noexcept { return static_cast<std::uint64_t>(handle_.get()); }
std::uint64_t ffiHandleId() const noexcept { return static_cast<std::uint64_t>(handle_.get()); }

FfiHandle handle_;
};
Expand Down
24 changes: 21 additions & 3 deletions include/livekit/audio_source.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,31 @@ class LIVEKIT_API AudioSource {
AudioSource& operator=(AudioSource&&) noexcept = default;

/// The sample rate of the audio source in Hz.
int sample_rate() const noexcept { return sample_rate_; }
int sampleRate() const noexcept { return sample_rate_; }

/// The number of audio channels.
int num_channels() const noexcept { return num_channels_; }
int numChannels() const noexcept { return num_channels_; }

/// Underlying FFI handle ID used in FFI requests.
std::uint64_t ffi_handle_id() const noexcept { return static_cast<std::uint64_t>(handle_.get()); }
std::uint64_t ffiHandleId() const noexcept { return static_cast<std::uint64_t>(handle_.get()); }

/// @deprecated Use sampleRate() instead.
[[deprecated("AudioSource::sample_rate is deprecated; use AudioSource::sampleRate instead")]]
int sample_rate() const noexcept { // NOLINT(readability-identifier-naming)
return sampleRate();
}

/// @deprecated Use numChannels() instead.
[[deprecated("AudioSource::num_channels is deprecated; use AudioSource::numChannels instead")]]
int num_channels() const noexcept { // NOLINT(readability-identifier-naming)
return numChannels();
}

/// @deprecated Use ffiHandleId() instead.
[[deprecated("AudioSource::ffi_handle_id is deprecated; use AudioSource::ffiHandleId instead")]]
std::uint64_t ffi_handle_id() const noexcept { // NOLINT(readability-identifier-naming)
return ffiHandleId();
}

/// Current duration of queued audio (in seconds).
double queuedDuration() const noexcept;
Expand Down
6 changes: 6 additions & 0 deletions include/livekit/local_audio_track.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,13 @@ class LIVEKIT_API LocalAudioTrack : public Track {

/// Returns a human-readable string representation of the track,
/// including its SID and name. Useful for debugging and logging.
std::string toString() const;

/// @deprecated Use toString() instead.
// NOLINTBEGIN(readability-identifier-naming)
[[deprecated("LocalAudioTrack::to_string is deprecated; use LocalAudioTrack::toString instead")]]
std::string to_string() const;
// NOLINTEND(readability-identifier-naming)

/// Returns the publication that owns this track, or nullptr if the track is
/// not published.
Expand Down
2 changes: 1 addition & 1 deletion include/livekit/local_data_track.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class LIVEKIT_API LocalDataTrack {

explicit LocalDataTrack(const proto::OwnedLocalDataTrack& owned);

uintptr_t ffi_handle_id() const noexcept { return handle_.get(); }
uintptr_t ffiHandleId() const noexcept { return handle_.get(); }

/** RAII wrapper for the Rust-owned FFI resource. */
FfiHandle handle_;
Expand Down
33 changes: 29 additions & 4 deletions include/livekit/local_participant.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,37 @@ class LIVEKIT_API LocalParticipant : public Participant {
*/
void publishDtmf(int code, const std::string& digit);

// -------------------------------------------------------------------------
// Metadata APIs (set metadata / name / attributes)
// -------------------------------------------------------------------------

/**
* Update this participant's metadata on the server.
*
* Sends an FFI request to the LiveKit server to change the metadata
* associated with the local participant. Other participants will be
* notified via \c onParticipantMetadataChanged.
*
* Note: this requires \c canUpdateOwnMetadata permission.
*/
void setMetadata(const std::string& metadata);

/**
* Update this participant's display name on the server.
*
* Sends an FFI request to the LiveKit server to change the name
* associated with the local participant. Other participants will be
* notified via \c onParticipantNameChanged.
*
* Note: this requires \c canUpdateOwnMetadata permission.
*/
void setName(const std::string& name);
Comment thread
alan-george-lk marked this conversation as resolved.

/**
* Update this participant's attributes on the server.
*
* Sends an FFI request to the LiveKit server to replace the attribute
* map for the local participant. Other participants will be notified
* via \c onParticipantAttributesChanged.
*
* Note: this requires \c canUpdateOwnMetadata permission.
*/
void setAttributes(const std::unordered_map<std::string, std::string>& attributes);

/**
Expand Down
6 changes: 6 additions & 0 deletions include/livekit/local_video_track.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,13 @@ class LIVEKIT_API LocalVideoTrack : public Track {

/// Returns a human-readable string representation of the track,
/// including its SID and name. Useful for debugging and logging.
std::string toString() const;

/// @deprecated Use toString() instead.
// NOLINTBEGIN(readability-identifier-naming)
[[deprecated("LocalVideoTrack::to_string is deprecated; use LocalVideoTrack::toString instead")]]
std::string to_string() const;
// NOLINTEND(readability-identifier-naming)

/// Returns the publication that owns this track, or nullptr if the track is
/// not published.
Expand Down
62 changes: 54 additions & 8 deletions include/livekit/participant.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,63 @@ class LIVEKIT_API Participant {

uintptr_t ffiHandleId() const noexcept { return handle_.get(); }

// Setters (caller ensures threading)
void set_name(std::string name) noexcept { name_ = std::move(name); }
void set_metadata(std::string metadata) noexcept { metadata_ = std::move(metadata); }
void set_attributes(std::unordered_map<std::string, std::string> attrs) noexcept { attributes_ = std::move(attrs); }
void set_attribute(const std::string& key, const std::string& value) { attributes_[key] = value; }
void remove_attribute(const std::string& key) { attributes_.erase(key); }
void set_kind(ParticipantKind kind) noexcept { kind_ = kind; }
void set_disconnect_reason(DisconnectReason reason) noexcept { reason_ = reason; }
// ---------------------------------------------------------------------------
// Deprecated public mutators
// ---------------------------------------------------------------------------

// NOLINTBEGIN(readability-identifier-naming)

/// @deprecated Use setName() instead.
[[deprecated("Participant::set_name is deprecated; use LocalParticipant::setName instead")]]
Comment thread
alan-george-lk marked this conversation as resolved.
void set_name(std::string name) noexcept {
name_ = std::move(name);
}

/// @deprecated Use setMetadata() instead.
[[deprecated("Participant::set_metadata is deprecated; use LocalParticipant::setMetadata instead")]]
void set_metadata(std::string metadata) noexcept {
metadata_ = std::move(metadata);
}

/// @deprecated Use setAttributes() instead.
[[deprecated("Participant::set_attributes is deprecated; use LocalParticipant::setAttributes instead")]]
void set_attributes(std::unordered_map<std::string, std::string> attrs) noexcept {
attributes_ = std::move(attrs);
}

/// @deprecated Use setAttribute() instead.
[[deprecated("Participant::set_attribute is deprecated; use LocalParticipant::setAttributes instead")]]
void set_attribute(const std::string& key, const std::string& value) {
attributes_[key] = value;
}

/// @deprecated Use removeAttribute() instead.
[[deprecated("Participant::remove_attribute is deprecated; use LocalParticipant::setAttributes instead")]]
void remove_attribute(const std::string& key) {
attributes_.erase(key);
}

/// @deprecated Kind is server-determined and not user-settable; this mutator will be removed.
[[deprecated("Participant::set_kind is deprecated; Kind is server-determined and not user-settable")]]
void set_kind(ParticipantKind kind) noexcept {
kind_ = kind;
}

/// @deprecated DisconnectReason is server-determined and not user-settable; this mutator will be removed.
[[deprecated(
"Participant::set_disconnect_reason is deprecated; DisconnectReason is server-determined and not "
"user-settable")]]
void set_disconnect_reason(DisconnectReason reason) noexcept {
reason_ = reason;
}

// NOLINTEND(readability-identifier-naming)

protected:
virtual std::shared_ptr<TrackPublication> findTrackPublication(const std::string& sid) const = 0;

/// Room class is a friend to set the internal state of the participant
/// Avoids awkward additional setter methods
friend class Room;

private:
Expand Down
6 changes: 6 additions & 0 deletions include/livekit/remote_audio_track.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,13 @@ class LIVEKIT_API RemoteAudioTrack : public Track {

/// Returns a concise, human-readable string summarizing the track,
/// including its SID and name. Useful for debugging and logging.
std::string toString() const;

/// @deprecated Use toString() instead.
// NOLINTBEGIN(readability-identifier-naming)
[[deprecated("RemoteAudioTrack::to_string is deprecated; use RemoteAudioTrack::toString instead")]]
std::string to_string() const;
// NOLINTEND(readability-identifier-naming)
};

} // namespace livekit
4 changes: 2 additions & 2 deletions include/livekit/remote_data_track.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class RemoteDataTrack {

#ifdef LIVEKIT_TEST_ACCESS
/// Test-only accessor for exercising lower-level FFI subscription paths.
uintptr_t testFfiHandleId() const noexcept { return ffi_handle_id(); }
uintptr_t testFfiHandleId() const noexcept { return ffiHandleId(); }
#endif

/**
Expand All @@ -86,7 +86,7 @@ class RemoteDataTrack {

explicit RemoteDataTrack(const proto::OwnedRemoteDataTrack& owned);

uintptr_t ffi_handle_id() const noexcept { return handle_.get(); }
uintptr_t ffiHandleId() const noexcept { return handle_.get(); }
/** RAII wrapper for the Rust-owned FFI resource. */
FfiHandle handle_;

Expand Down
6 changes: 6 additions & 0 deletions include/livekit/remote_participant.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ class LIVEKIT_API RemoteParticipant : public Participant {
// Optional: non-const access if you want to mutate in-place.
PublicationMap& mutableTrackPublications() noexcept { return track_publications_; }

std::string toString() const;

/// @deprecated Use toString() instead.
// NOLINTBEGIN(readability-identifier-naming)
[[deprecated("RemoteParticipant::to_string is deprecated; use RemoteParticipant::toString instead")]]
std::string to_string() const;
// NOLINTEND(readability-identifier-naming)

protected:
// Called by Room events like kTrackMuted. This is internal plumbing and not
Expand Down
Loading
Loading