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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 29 additions & 5 deletions include/livekit/room.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#pragma once

#include <chrono>
#include <cstdint>
#include <future>
#include <memory>
#include <mutex>
Expand Down Expand Up @@ -74,19 +76,41 @@ struct RoomOptions {
/// - remote audio/video frames
bool auto_subscribe = true;

/// Enable adaptive stream for subscribed video tracks.
///
/// When enabled, the SDK tells the server it may adjust the video layers sent
/// to this client based on what the application is currently rendering. This
/// lets the server pause or downscale subscribed video that is off-screen,
/// hidden, or only needed at a smaller size, reducing downstream bandwidth and
/// decode work. This affects media received by this room; use @ref dynacast
/// to control how this client publishes layers to others.
///
/// If unset, the Rust SDK default is used.
std::optional<bool> adaptive_stream;

/// Enable dynacast (server sends optimal layers depending on subscribers).
bool dynacast = false;

/// Optional end-to-end encryption settings.
std::optional<E2EEOptions> encryption;

/// Optional WebRTC configuration (ICE policy, servers, etc.)
std::optional<RtcConfig> rtc_config;

/// Number of retries for the initial room join after the first attempt.
///
/// If unset, the Rust SDK default is used.
std::optional<std::uint32_t> join_retries;

/// Enable single peer connection mode. When true, uses one RTCPeerConnection
/// for both publishing and subscribing instead of two separate connections.
/// Falls back to dual peer connection if the server doesn't support single PC.
bool single_peer_connection = true;

/// Optional WebRTC configuration (ICE policy, servers, etc.)
std::optional<RtcConfig> rtc_config;

/// Optional end-to-end encryption settings.
std::optional<E2EEOptions> encryption;
/// Timeout for each individual signal connection attempt.
///
/// If unset, the Rust SDK default is used.
std::optional<std::chrono::milliseconds> connect_timeout;
};

/// Represents a LiveKit room session.
Expand Down
68 changes: 24 additions & 44 deletions src/ffi_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@

#include <cassert>
#include <csignal>
#include <string>
#include <type_traits>

#include "data_track.pb.h"
#include "e2ee.pb.h"
#include "ffi.pb.h"
#include "livekit/build.h"
#include "livekit/data_track_error.h"
#include "livekit/e2ee.h"
#include "livekit/ffi_handle.h"
#include "livekit/room.h"
#include "livekit/rpc_error.h"
Expand All @@ -36,11 +36,27 @@
namespace livekit {

namespace {

inline void logAndThrow(const std::string& error_msg) {
LK_LOG_ERROR("LiveKit SDK Error: {}", error_msg);
throw std::runtime_error(error_msg);
}

// Helper for debug logging of optional values
const auto optional_to_string = [](const auto& value) -> std::string {
if (!value) {
return "<unset>";
}
using Value = std::decay_t<decltype(*value)>;
if constexpr (std::is_same_v<Value, bool>) {
return *value ? "true" : "false";
} else if constexpr (std::is_same_v<Value, std::chrono::milliseconds>) {
return std::to_string(value->count());
} else {
return std::to_string(*value);
}
};

Result<proto::OwnedDataTrackStream, SubscribeDataTrackError> subscribeDataTrackFailure(SubscribeDataTrackErrorCode code,
const std::string& message) {
LK_LOG_WARN("Subscribe data track failed: code={} message={}", static_cast<std::uint32_t>(code), message);
Expand Down Expand Up @@ -331,50 +347,14 @@ std::future<proto::ConnectCallback> FfiClient::connectAsync(const std::string& u
connect->set_url(url);
connect->set_token(token);
connect->set_request_async_id(async_id);
auto* opts = connect->mutable_options();
opts->set_auto_subscribe(options.auto_subscribe);
opts->set_dynacast(options.dynacast);
opts->set_single_peer_connection(options.single_peer_connection);
connect->mutable_options()->CopyFrom(toProto(options));

LK_LOG_DEBUG(
"[FfiClient] connectAsync: auto_subscribe={}, dynacast={}, "
"single_peer_connection={}",
options.auto_subscribe, options.dynacast, options.single_peer_connection);

// --- E2EE / encryption (optional) ---
if (options.encryption.has_value()) {
const E2EEOptions& e2ee = *options.encryption;
const auto& kpo = e2ee.key_provider_options;

auto* enc = opts->mutable_encryption();
enc->set_encryption_type(static_cast<proto::EncryptionType>(e2ee.encryption_type));
enc->mutable_key_provider_options()->CopyFrom(toProto(kpo));
}

// --- RTC configuration (optional) ---
if (options.rtc_config.has_value()) {
const RtcConfig& rc = *options.rtc_config;
auto* rtc = opts->mutable_rtc_config();

rtc->set_ice_transport_type(static_cast<proto::IceTransportType>(rc.ice_transport_type));
rtc->set_continual_gathering_policy(static_cast<proto::ContinualGatheringPolicy>(rc.continual_gathering_policy));

for (const IceServer& ice : rc.ice_servers) {
auto* s = rtc->add_ice_servers();

// proto: repeated string urls = 1
if (!ice.url.empty()) {
s->add_urls(ice.url);
}
if (!ice.username.empty()) {
s->set_username(ice.username);
}
if (!ice.credential.empty()) {
// proto: password = 3
s->set_password(ice.credential);
}
}
}
"[FfiClient] connectAsync: auto_subscribe={}, adaptive_stream={}, dynacast={}, "
"single_peer_connection={}, join_retries={}, connect_timeout_ms={}",
options.auto_subscribe, optional_to_string(options.adaptive_stream), options.dynacast,
options.single_peer_connection, optional_to_string(options.join_retries),
optional_to_string(options.connect_timeout));

try {
const proto::FfiResponse resp = sendRequest(req);
Expand Down
45 changes: 45 additions & 0 deletions src/room_proto_converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "room_proto_converter.h"

#include "livekit/data_stream.h"
#include "livekit/room.h"
#include "room.pb.h"

namespace livekit {
Expand Down Expand Up @@ -402,6 +403,50 @@ proto::KeyProviderOptions toProto(const KeyProviderOptions& in) {
return out;
}

proto::RoomOptions toProto(const RoomOptions& in) {
proto::RoomOptions out;
out.set_auto_subscribe(in.auto_subscribe);
if (in.adaptive_stream) {
out.set_adaptive_stream(*in.adaptive_stream);
}
out.set_dynacast(in.dynacast);

if (in.encryption) {
auto* encryption = out.mutable_encryption();
encryption->set_encryption_type(static_cast<proto::EncryptionType>(in.encryption->encryption_type));
encryption->mutable_key_provider_options()->CopyFrom(toProto(in.encryption->key_provider_options));
}

if (in.rtc_config) {
auto* rtc = out.mutable_rtc_config();
rtc->set_ice_transport_type(static_cast<proto::IceTransportType>(in.rtc_config->ice_transport_type));
rtc->set_continual_gathering_policy(
static_cast<proto::ContinualGatheringPolicy>(in.rtc_config->continual_gathering_policy));

for (const IceServer& ice : in.rtc_config->ice_servers) {
auto* server = rtc->add_ice_servers();
if (!ice.url.empty()) {
server->add_urls(ice.url);
}
if (!ice.username.empty()) {
server->set_username(ice.username);
}
if (!ice.credential.empty()) {
server->set_password(ice.credential);
}
}
}

if (in.join_retries) {
out.set_join_retries(*in.join_retries);
}
out.set_single_peer_connection(in.single_peer_connection);
if (in.connect_timeout) {
out.set_connect_timeout_ms(static_cast<std::uint64_t>(in.connect_timeout->count()));
}
return out;
}

proto::AudioEncoding toProto(const AudioEncodingOptions& in) {
proto::AudioEncoding msg;
msg.set_max_bitrate(in.max_bitrate);
Expand Down
2 changes: 2 additions & 0 deletions src/room_proto_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ namespace livekit {
enum class RpcErrorCode;
class RemoteParticipant;
struct ByteStreamInfo;
struct RoomOptions;
struct TextStreamInfo;

// --------- basic helper conversions ---------
Expand Down Expand Up @@ -73,6 +74,7 @@ LIVEKIT_INTERNAL_API RoomMovedEvent roomMovedFromProto(const proto::RoomInfo& in
// --------- room options conversions ---------

LIVEKIT_INTERNAL_API proto::KeyProviderOptions toProto(const KeyProviderOptions& in);
LIVEKIT_INTERNAL_API proto::RoomOptions toProto(const RoomOptions& in);

LIVEKIT_INTERNAL_API proto::AudioEncoding toProto(const AudioEncodingOptions& in);
LIVEKIT_INTERNAL_API AudioEncodingOptions fromProto(const proto::AudioEncoding& in);
Expand Down
Loading
Loading