From 2018d294880f4e3882db6ac26471a6be801e33fe Mon Sep 17 00:00:00 2001 From: Stephen DeRosa Date: Thu, 12 Feb 2026 15:41:20 -0700 Subject: [PATCH 01/13] Bridge RPC: Mute/unmute working examples with audio/video RPCManager: bridge object for managing default and custom RPC calls bridge_rpc examples RPCManager tests --- .gitignore | 2 + bridge/src/rpc_manager.cpp | 140 +++++++++++++++ bridge/src/rpc_manager.h | 144 ++++++++++++++++ bridge/tests/test_rpc_manager.cpp | 274 ++++++++++++++++++++++++++++++ 4 files changed, 560 insertions(+) create mode 100644 bridge/src/rpc_manager.cpp create mode 100644 bridge/src/rpc_manager.h create mode 100644 bridge/tests/test_rpc_manager.cpp diff --git a/.gitignore b/.gitignore index 8a37fba5..d98ce162 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,5 @@ lib/ *.dll *.exe livekit.log + +examples/realsense-livekit/ diff --git a/bridge/src/rpc_manager.cpp b/bridge/src/rpc_manager.cpp new file mode 100644 index 00000000..6f792ea2 --- /dev/null +++ b/bridge/src/rpc_manager.cpp @@ -0,0 +1,140 @@ +/* + * 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. + */ + +/// @file rpc_manager.cpp +/// @brief Implementation of RpcManager. + +#include "rpc_manager.h" +#include "livekit_bridge/rpc_constants.h" + +#include "livekit/local_participant.h" +#include "livekit/rpc_error.h" + +#include +#include + +namespace livekit_bridge { + +RpcManager::RpcManager(TrackActionFn track_action_fn) + : track_action_fn_(std::move(track_action_fn)) {} + +void RpcManager::enable(livekit::LocalParticipant *lp) { + assert(lp != nullptr); + lp_ = lp; + enableBuiltInHandlers(); +} + +void RpcManager::disable() { + if (lp_) { + disableBuiltInHandlers(); + } + lp_ = nullptr; +} + +// --------------------------------------------------------------- +// Generic RPC +// --------------------------------------------------------------- + +std::string +RpcManager::performRpc(const std::string &destination_identity, + const std::string &method, const std::string &payload, + const std::optional &response_timeout) { + assert(lp_ != nullptr); + return lp_->performRpc(destination_identity, method, payload, + response_timeout); +} + +// --------------------------------------------------------------- +// User-registered handlers +// --------------------------------------------------------------- + +void RpcManager::registerRpcMethod( + const std::string &method_name, + livekit::LocalParticipant::RpcHandler handler) { + assert(lp_ != nullptr); + lp_->registerRpcMethod(method_name, std::move(handler)); +} + +void RpcManager::unregisterRpcMethod(const std::string &method_name) { + assert(lp_ != nullptr); + lp_->unregisterRpcMethod(method_name); +} + +// --------------------------------------------------------------- +// Built-in outgoing convenience (track control) +// --------------------------------------------------------------- + +void RpcManager::requestTrackMute(const std::string &destination_identity, + const std::string &track_name) { + namespace tc = rpc::track_control; + performRpc(destination_identity, tc::kMethod, + tc::formatPayload(tc::kActionMute, track_name), std::nullopt); +} + +void RpcManager::requestTrackUnmute(const std::string &destination_identity, + const std::string &track_name) { + namespace tc = rpc::track_control; + performRpc(destination_identity, tc::kMethod, + tc::formatPayload(tc::kActionUnmute, track_name), std::nullopt); +} + +// --------------------------------------------------------------- +// Built-in handler registration +// --------------------------------------------------------------- + +void RpcManager::enableBuiltInHandlers() { + assert(lp_ != nullptr); + lp_->registerRpcMethod(rpc::track_control::kMethod, + [this](const livekit::RpcInvocationData &data) + -> std::optional { + return handleTrackControlRpc(data); + }); +} + +void RpcManager::disableBuiltInHandlers() { + assert(lp_ != nullptr); + lp_->unregisterRpcMethod(rpc::track_control::kMethod); +} + +// --------------------------------------------------------------- +// Built-in handler: track control +// --------------------------------------------------------------- + +std::optional +RpcManager::handleTrackControlRpc(const livekit::RpcInvocationData &data) { + namespace tc = rpc::track_control; + + std::cout << "[RpcManager] Handling track control RPC: " << data.payload + << "\n"; + auto delim = data.payload.find(tc::kDelimiter); + if (delim == std::string::npos || delim == 0) { + throw livekit::RpcError( + livekit::RpcError::ErrorCode::APPLICATION_ERROR, + "invalid payload format, expected \":\""); + } + std::string action = data.payload.substr(0, delim); + std::string track_name = data.payload.substr(delim + 1); + + if (action != tc::kActionMute && action != tc::kActionUnmute) { + throw livekit::RpcError(livekit::RpcError::ErrorCode::APPLICATION_ERROR, + "unknown action: " + action); + } + + track_action_fn_(action, track_name); + return tc::kResponseOk; +} + +} // namespace livekit_bridge diff --git a/bridge/src/rpc_manager.h b/bridge/src/rpc_manager.h new file mode 100644 index 00000000..a43b5c67 --- /dev/null +++ b/bridge/src/rpc_manager.h @@ -0,0 +1,144 @@ +/* + * 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. + */ + +/// @file rpc_manager.h +/// @brief Internal RPC manager that owns all RPC concerns for the bridge. + +#pragma once + +#include "livekit/local_participant.h" + +#include +#include +#include +#include + +namespace livekit { +struct RpcInvocationData; +} // namespace livekit + +namespace livekit_bridge { + +namespace test { +class RpcManagerTest; +} // namespace test + +/** + * Owns all RPC concerns for the LiveKitBridge: built-in handler registration + * and dispatch, user-registered custom handlers, and outgoing RPC calls. + * + * The manager is bound to a LocalParticipant via enable() and unbound via + * disable(). All public methods require the manager to be enabled (i.e., + * enable() has been called and disable() has not). + * + * Built-in handlers (e.g., track-control) are automatically registered on + * enable() and unregistered on disable(). User-registered handlers are + * forwarded directly to the underlying LocalParticipant. + * + * Not part of the public API; lives in bridge/src/. + */ +class RpcManager { +public: + /// Callback the bridge provides to execute a track action + /// (mute/unmute/release). Throws livekit::RpcError if the track is not found + /// or the action is invalid. + using TrackActionFn = std::function; + + explicit RpcManager(TrackActionFn track_action_fn); + + /// Bind to a LocalParticipant and register all built-in RPC handlers. + /// @pre @p lp must be non-null and remain valid until disable() is called. + void enable(livekit::LocalParticipant *lp); + + /// Unregister built-in handlers and unbind from the LocalParticipant. + void disable(); + + /// Whether the manager is currently bound to a LocalParticipant. + bool isEnabled() const { return lp_ != nullptr; } + + // -- Generic RPC -- + + /// @brief Perform an RPC call to a remote participant. + /// @param destination_identity Identity of the destination participant. + /// @param method Name of the RPC method to invoke. + /// @param payload Request payload to send to the remote + /// handler. + /// @param response_timeout Optional timeout in seconds for receiving + /// a response. If not set, the server default + /// timeout (15 seconds) is used. + /// @return The response payload returned by the remote handler. + /// @throws if the LocalParticipant performRpc fails. + std::string performRpc(const std::string &destination_identity, + const std::string &method, const std::string &payload, + const std::optional &response_timeout); + + // -- User-registered handlers -- + /// @brief Register a handler for an incoming RPC method. + /// @param method_name Name of the RPC method to handle. + /// @param handler Callback to execute when an invocation is received. + /// The handler may return an optional response payload + /// or throw an RpcError to signal failure. + /// @throws if the LocalParticipant registerRpcMethod fails. + void registerRpcMethod(const std::string &method_name, + livekit::LocalParticipant::RpcHandler handler); + + /// @brief Unregister a handler for an incoming RPC method. + /// @param method_name Name of the RPC method to unregister. + /// @throws if the LocalParticipant unregisterRpcMethod fails. + void unregisterRpcMethod(const std::string &method_name); + + // -- Built-in outgoing convenience (track control) -- + + /// @brief Request a remote participant to mute a published track. + /// @param destination_identity Identity of the remote participant. + /// @param track_name Name of the track to mute. + /// @throws if the LocalParticipant requestTrackMute fails. + void requestTrackMute(const std::string &destination_identity, + const std::string &track_name); + /// @brief Request a remote participant to unmute a published track. + /// @param destination_identity Identity of the remote participant. + /// @param track_name Name of the track to unmute. + /// @throws if the LocalParticipant requestTrackUnmute fails. + void requestTrackUnmute(const std::string &destination_identity, + const std::string &track_name); + +private: + friend class test::RpcManagerTest; + + /// @brief Enable built-in handlers. + /// @throws if the LocalParticipant registerRpcMethod fails. + void enableBuiltInHandlers(); + + /// @brief Disable built-in handlers. + /// @throws if the LocalParticipant unregisterRpcMethod fails. + void disableBuiltInHandlers(); + + /// @brief Handle a track control RPC. + /// @param data The RPC invocation data. + /// @return The response payload returned by the remote handler. + /// @throws if the RPC is invalid or the track is not found. + std::optional + handleTrackControlRpc(const livekit::RpcInvocationData &data); + + /// Callback to execute a track action RPC + TrackActionFn track_action_fn_; + + /// The LocalParticipant bound to the manager. + livekit::LocalParticipant *lp_ = nullptr; +}; + +} // namespace livekit_bridge diff --git a/bridge/tests/test_rpc_manager.cpp b/bridge/tests/test_rpc_manager.cpp new file mode 100644 index 00000000..5666c58c --- /dev/null +++ b/bridge/tests/test_rpc_manager.cpp @@ -0,0 +1,274 @@ +/* + * 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. + */ + +/// @file test_rpc_manager.cpp +/// @brief Unit tests for RpcManager. + +#include + +#include "livekit_bridge/rpc_constants.h" +#include "rpc_manager.h" + +#include "livekit/local_participant.h" +#include "livekit/rpc_error.h" + +#include +#include + +namespace livekit_bridge { +namespace test { + +// Records (action, track_name) pairs passed to the TrackActionFn callback. +struct TrackActionRecord { + std::string action; + std::string track_name; +}; + +class RpcManagerTest : public ::testing::Test { +protected: + std::vector recorded_actions_; + + std::unique_ptr makeManager() { + return std::make_unique( + [this](const std::string &action, const std::string &track_name) { + recorded_actions_.push_back({action, track_name}); + }); + } + + std::unique_ptr makeThrowingManager() { + return std::make_unique([](const std::string &, + const std::string &track_name) { + throw livekit::RpcError(livekit::RpcError::ErrorCode::APPLICATION_ERROR, + "track not found: " + track_name); + }); + } + + // Helper: call the private handleTrackControlRpc with a given payload. + std::optional + callHandler(RpcManager &mgr, const std::string &payload, + const std::string &caller = "test-caller") { + livekit::RpcInvocationData data; + data.request_id = "test-request-id"; + data.caller_identity = caller; + data.payload = payload; + data.response_timeout_sec = 10.0; + return mgr.handleTrackControlRpc(data); + } +}; + +// ============================================================================ +// Construction & lifecycle +// ============================================================================ + +TEST_F(RpcManagerTest, InitiallyDisabled) { + auto mgr = makeManager(); + EXPECT_FALSE(mgr->isEnabled()); +} + +TEST_F(RpcManagerTest, DisableOnAlreadyDisabledIsNoOp) { + auto mgr = makeManager(); + EXPECT_NO_THROW(mgr->disable()); + EXPECT_FALSE(mgr->isEnabled()); +} + +TEST_F(RpcManagerTest, DisableMultipleTimesIsIdempotent) { + auto mgr = makeManager(); + EXPECT_NO_THROW({ + mgr->disable(); + mgr->disable(); + mgr->disable(); + }); +} + +TEST_F(RpcManagerTest, DestructorWithoutEnableIsSafe) { + EXPECT_NO_THROW({ auto mgr = makeManager(); }); +} + +// ============================================================================ +// handleTrackControlRpc — payload parsing +// ============================================================================ + +TEST_F(RpcManagerTest, ValidMutePayload) { + auto mgr = makeManager(); + auto result = callHandler(*mgr, "mute:my-track"); + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result.value(), rpc::track_control::kResponseOk); + + ASSERT_EQ(recorded_actions_.size(), 1u); + EXPECT_EQ(recorded_actions_[0].action, "mute"); + EXPECT_EQ(recorded_actions_[0].track_name, "my-track"); +} + +TEST_F(RpcManagerTest, ValidUnmutePayload) { + auto mgr = makeManager(); + auto result = callHandler(*mgr, "unmute:cam"); + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result.value(), rpc::track_control::kResponseOk); + + ASSERT_EQ(recorded_actions_.size(), 1u); + EXPECT_EQ(recorded_actions_[0].action, "unmute"); + EXPECT_EQ(recorded_actions_[0].track_name, "cam"); +} + +TEST_F(RpcManagerTest, TrackNameWithColons) { + auto mgr = makeManager(); + auto result = callHandler(*mgr, "mute:track:with:colons"); + + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(recorded_actions_.size(), 1u); + EXPECT_EQ(recorded_actions_[0].action, "mute"); + EXPECT_EQ(recorded_actions_[0].track_name, "track:with:colons"); +} + +TEST_F(RpcManagerTest, TrackNameWithSpaces) { + auto mgr = makeManager(); + auto result = callHandler(*mgr, "unmute:my track name"); + + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(recorded_actions_.size(), 1u); + EXPECT_EQ(recorded_actions_[0].action, "unmute"); + EXPECT_EQ(recorded_actions_[0].track_name, "my track name"); +} + +// ============================================================================ +// handleTrackControlRpc — invalid payloads +// ============================================================================ + +TEST_F(RpcManagerTest, EmptyPayloadThrows) { + auto mgr = makeManager(); + EXPECT_THROW(callHandler(*mgr, ""), livekit::RpcError); + EXPECT_TRUE(recorded_actions_.empty()); +} + +TEST_F(RpcManagerTest, NoDelimiterThrows) { + auto mgr = makeManager(); + EXPECT_THROW(callHandler(*mgr, "mutetrack"), livekit::RpcError); + EXPECT_TRUE(recorded_actions_.empty()); +} + +TEST_F(RpcManagerTest, LeadingDelimiterThrows) { + auto mgr = makeManager(); + EXPECT_THROW(callHandler(*mgr, ":track"), livekit::RpcError); + EXPECT_TRUE(recorded_actions_.empty()); +} + +TEST_F(RpcManagerTest, UnknownActionThrows) { + auto mgr = makeManager(); + EXPECT_THROW(callHandler(*mgr, "pause:cam"), livekit::RpcError); + EXPECT_TRUE(recorded_actions_.empty()); +} + +TEST_F(RpcManagerTest, CaseSensitiveAction) { + auto mgr = makeManager(); + EXPECT_THROW(callHandler(*mgr, "MUTE:cam"), livekit::RpcError); + EXPECT_THROW(callHandler(*mgr, "Mute:cam"), livekit::RpcError); + EXPECT_TRUE(recorded_actions_.empty()); +} + +// ============================================================================ +// handleTrackControlRpc — TrackActionFn propagation +// ============================================================================ + +TEST_F(RpcManagerTest, TrackActionFnExceptionPropagates) { + auto mgr = makeThrowingManager(); + + try { + callHandler(*mgr, "mute:nonexistent"); + FAIL() << "Expected RpcError to propagate from TrackActionFn"; + } catch (const livekit::RpcError &e) { + EXPECT_EQ(e.code(), static_cast( + livekit::RpcError::ErrorCode::APPLICATION_ERROR)); + EXPECT_NE(std::string(e.message()).find("nonexistent"), std::string::npos) + << "Error message should contain the track name"; + } +} + +TEST_F(RpcManagerTest, MultipleCallsAccumulate) { + auto mgr = makeManager(); + + callHandler(*mgr, "mute:audio"); + callHandler(*mgr, "unmute:audio"); + callHandler(*mgr, "mute:video"); + + ASSERT_EQ(recorded_actions_.size(), 3u); + EXPECT_EQ(recorded_actions_[0].action, "mute"); + EXPECT_EQ(recorded_actions_[0].track_name, "audio"); + EXPECT_EQ(recorded_actions_[1].action, "unmute"); + EXPECT_EQ(recorded_actions_[1].track_name, "audio"); + EXPECT_EQ(recorded_actions_[2].action, "mute"); + EXPECT_EQ(recorded_actions_[2].track_name, "video"); +} + +// ============================================================================ +// handleTrackControlRpc — caller identity forwarded +// ============================================================================ + +TEST_F(RpcManagerTest, CallerIdentityPassedThrough) { + auto mgr = makeManager(); + livekit::RpcInvocationData data; + data.request_id = "req-1"; + data.caller_identity = "remote-robot"; + data.payload = "mute:mic"; + data.response_timeout_sec = 5.0; + + auto result = mgr->handleTrackControlRpc(data); + + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(recorded_actions_.size(), 1u); + EXPECT_EQ(recorded_actions_[0].action, "mute"); + EXPECT_EQ(recorded_actions_[0].track_name, "mic"); +} + +// ============================================================================ +// rpc_constants — formatPayload +// ============================================================================ + +TEST_F(RpcManagerTest, FormatPayloadMute) { + namespace tc = rpc::track_control; + std::string payload = tc::formatPayload(tc::kActionMute, "cam"); + EXPECT_EQ(payload, "mute:cam"); +} + +TEST_F(RpcManagerTest, FormatPayloadUnmute) { + namespace tc = rpc::track_control; + std::string payload = tc::formatPayload(tc::kActionUnmute, "mic"); + EXPECT_EQ(payload, "unmute:mic"); +} + +TEST_F(RpcManagerTest, FormatPayloadEmptyTrackName) { + namespace tc = rpc::track_control; + std::string payload = tc::formatPayload(tc::kActionMute, ""); + EXPECT_EQ(payload, "mute:"); +} + +TEST_F(RpcManagerTest, FormatPayloadRoundTrip) { + namespace tc = rpc::track_control; + std::string track_name = "some-track-123"; + std::string payload = tc::formatPayload(tc::kActionMute, track_name); + + auto mgr = makeManager(); + auto result = callHandler(*mgr, payload); + + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(recorded_actions_.size(), 1u); + EXPECT_EQ(recorded_actions_[0].action, tc::kActionMute); + EXPECT_EQ(recorded_actions_[0].track_name, track_name); +} + +} // namespace test +} // namespace livekit_bridge From 76199c6a7b64b74f61ad306d51d7927502100680 Mon Sep 17 00:00:00 2001 From: Stephen DeRosa Date: Thu, 5 Mar 2026 09:37:23 -0700 Subject: [PATCH 02/13] requestTrackMute/Unmute -> requestRemoteTrackMute/Unmute . Make the trackActionFn action input an Enum --- bridge/src/rpc_manager.cpp | 20 ++++++++++++-------- bridge/src/rpc_manager.h | 19 ++++++++++--------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/bridge/src/rpc_manager.cpp b/bridge/src/rpc_manager.cpp index 6f792ea2..99ef1cd9 100644 --- a/bridge/src/rpc_manager.cpp +++ b/bridge/src/rpc_manager.cpp @@ -29,7 +29,7 @@ namespace livekit_bridge { RpcManager::RpcManager(TrackActionFn track_action_fn) - : track_action_fn_(std::move(track_action_fn)) {} + : track_action_fn_(std::move(track_action_fn)), lp_(nullptr) {} void RpcManager::enable(livekit::LocalParticipant *lp) { assert(lp != nullptr); @@ -77,15 +77,15 @@ void RpcManager::unregisterRpcMethod(const std::string &method_name) { // Built-in outgoing convenience (track control) // --------------------------------------------------------------- -void RpcManager::requestTrackMute(const std::string &destination_identity, - const std::string &track_name) { +void RpcManager::requestRemoteTrackMute(const std::string &destination_identity, + const std::string &track_name) { namespace tc = rpc::track_control; performRpc(destination_identity, tc::kMethod, tc::formatPayload(tc::kActionMute, track_name), std::nullopt); } -void RpcManager::requestTrackUnmute(const std::string &destination_identity, - const std::string &track_name) { +void RpcManager::requestRemoteTrackUnmute( + const std::string &destination_identity, const std::string &track_name) { namespace tc = rpc::track_control; performRpc(destination_identity, tc::kMethod, tc::formatPayload(tc::kActionUnmute, track_name), std::nullopt); @@ -125,15 +125,19 @@ RpcManager::handleTrackControlRpc(const livekit::RpcInvocationData &data) { livekit::RpcError::ErrorCode::APPLICATION_ERROR, "invalid payload format, expected \":\""); } - std::string action = data.payload.substr(0, delim); - std::string track_name = data.payload.substr(delim + 1); + const std::string action = data.payload.substr(0, delim); + const std::string track_name = data.payload.substr(delim + 1); if (action != tc::kActionMute && action != tc::kActionUnmute) { throw livekit::RpcError(livekit::RpcError::ErrorCode::APPLICATION_ERROR, "unknown action: " + action); } - track_action_fn_(action, track_name); + const auto action_enum = action == tc::kActionMute + ? rpc::track_control::Action::kActionMute + : rpc::track_control::Action::kActionUnmute; + + track_action_fn_(action_enum, track_name); return tc::kResponseOk; } diff --git a/bridge/src/rpc_manager.h b/bridge/src/rpc_manager.h index a43b5c67..71bdeeb6 100644 --- a/bridge/src/rpc_manager.h +++ b/bridge/src/rpc_manager.h @@ -20,6 +20,7 @@ #pragma once #include "livekit/local_participant.h" +#include "livekit_bridge/rpc_constants.h" #include #include @@ -55,8 +56,8 @@ class RpcManager { /// Callback the bridge provides to execute a track action /// (mute/unmute/release). Throws livekit::RpcError if the track is not found /// or the action is invalid. - using TrackActionFn = std::function; + using TrackActionFn = std::function; explicit RpcManager(TrackActionFn track_action_fn); @@ -106,15 +107,15 @@ class RpcManager { /// @brief Request a remote participant to mute a published track. /// @param destination_identity Identity of the remote participant. /// @param track_name Name of the track to mute. - /// @throws if the LocalParticipant requestTrackMute fails. - void requestTrackMute(const std::string &destination_identity, - const std::string &track_name); + /// @throws if the LocalParticipant requestRemoteTrackMute fails. + void requestRemoteTrackMute(const std::string &destination_identity, + const std::string &track_name); /// @brief Request a remote participant to unmute a published track. /// @param destination_identity Identity of the remote participant. /// @param track_name Name of the track to unmute. - /// @throws if the LocalParticipant requestTrackUnmute fails. - void requestTrackUnmute(const std::string &destination_identity, - const std::string &track_name); + /// @throws if the LocalParticipant requestRemoteTrackUnmute fails. + void requestRemoteTrackUnmute(const std::string &destination_identity, + const std::string &track_name); private: friend class test::RpcManagerTest; @@ -138,7 +139,7 @@ class RpcManager { TrackActionFn track_action_fn_; /// The LocalParticipant bound to the manager. - livekit::LocalParticipant *lp_ = nullptr; + livekit::LocalParticipant *lp_; }; } // namespace livekit_bridge From f624d8afdfecc2657249685477b423715652949d Mon Sep 17 00:00:00 2001 From: Stephen DeRosa Date: Tue, 10 Mar 2026 10:39:16 -0600 Subject: [PATCH 03/13] fix --- bridge/src/rpc_manager.cpp | 144 ---------------- bridge/src/rpc_manager.h | 145 ---------------- bridge/tests/test_rpc_manager.cpp | 274 ------------------------------ 3 files changed, 563 deletions(-) delete mode 100644 bridge/src/rpc_manager.cpp delete mode 100644 bridge/src/rpc_manager.h delete mode 100644 bridge/tests/test_rpc_manager.cpp diff --git a/bridge/src/rpc_manager.cpp b/bridge/src/rpc_manager.cpp deleted file mode 100644 index 99ef1cd9..00000000 --- a/bridge/src/rpc_manager.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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. - */ - -/// @file rpc_manager.cpp -/// @brief Implementation of RpcManager. - -#include "rpc_manager.h" -#include "livekit_bridge/rpc_constants.h" - -#include "livekit/local_participant.h" -#include "livekit/rpc_error.h" - -#include -#include - -namespace livekit_bridge { - -RpcManager::RpcManager(TrackActionFn track_action_fn) - : track_action_fn_(std::move(track_action_fn)), lp_(nullptr) {} - -void RpcManager::enable(livekit::LocalParticipant *lp) { - assert(lp != nullptr); - lp_ = lp; - enableBuiltInHandlers(); -} - -void RpcManager::disable() { - if (lp_) { - disableBuiltInHandlers(); - } - lp_ = nullptr; -} - -// --------------------------------------------------------------- -// Generic RPC -// --------------------------------------------------------------- - -std::string -RpcManager::performRpc(const std::string &destination_identity, - const std::string &method, const std::string &payload, - const std::optional &response_timeout) { - assert(lp_ != nullptr); - return lp_->performRpc(destination_identity, method, payload, - response_timeout); -} - -// --------------------------------------------------------------- -// User-registered handlers -// --------------------------------------------------------------- - -void RpcManager::registerRpcMethod( - const std::string &method_name, - livekit::LocalParticipant::RpcHandler handler) { - assert(lp_ != nullptr); - lp_->registerRpcMethod(method_name, std::move(handler)); -} - -void RpcManager::unregisterRpcMethod(const std::string &method_name) { - assert(lp_ != nullptr); - lp_->unregisterRpcMethod(method_name); -} - -// --------------------------------------------------------------- -// Built-in outgoing convenience (track control) -// --------------------------------------------------------------- - -void RpcManager::requestRemoteTrackMute(const std::string &destination_identity, - const std::string &track_name) { - namespace tc = rpc::track_control; - performRpc(destination_identity, tc::kMethod, - tc::formatPayload(tc::kActionMute, track_name), std::nullopt); -} - -void RpcManager::requestRemoteTrackUnmute( - const std::string &destination_identity, const std::string &track_name) { - namespace tc = rpc::track_control; - performRpc(destination_identity, tc::kMethod, - tc::formatPayload(tc::kActionUnmute, track_name), std::nullopt); -} - -// --------------------------------------------------------------- -// Built-in handler registration -// --------------------------------------------------------------- - -void RpcManager::enableBuiltInHandlers() { - assert(lp_ != nullptr); - lp_->registerRpcMethod(rpc::track_control::kMethod, - [this](const livekit::RpcInvocationData &data) - -> std::optional { - return handleTrackControlRpc(data); - }); -} - -void RpcManager::disableBuiltInHandlers() { - assert(lp_ != nullptr); - lp_->unregisterRpcMethod(rpc::track_control::kMethod); -} - -// --------------------------------------------------------------- -// Built-in handler: track control -// --------------------------------------------------------------- - -std::optional -RpcManager::handleTrackControlRpc(const livekit::RpcInvocationData &data) { - namespace tc = rpc::track_control; - - std::cout << "[RpcManager] Handling track control RPC: " << data.payload - << "\n"; - auto delim = data.payload.find(tc::kDelimiter); - if (delim == std::string::npos || delim == 0) { - throw livekit::RpcError( - livekit::RpcError::ErrorCode::APPLICATION_ERROR, - "invalid payload format, expected \":\""); - } - const std::string action = data.payload.substr(0, delim); - const std::string track_name = data.payload.substr(delim + 1); - - if (action != tc::kActionMute && action != tc::kActionUnmute) { - throw livekit::RpcError(livekit::RpcError::ErrorCode::APPLICATION_ERROR, - "unknown action: " + action); - } - - const auto action_enum = action == tc::kActionMute - ? rpc::track_control::Action::kActionMute - : rpc::track_control::Action::kActionUnmute; - - track_action_fn_(action_enum, track_name); - return tc::kResponseOk; -} - -} // namespace livekit_bridge diff --git a/bridge/src/rpc_manager.h b/bridge/src/rpc_manager.h deleted file mode 100644 index 71bdeeb6..00000000 --- a/bridge/src/rpc_manager.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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. - */ - -/// @file rpc_manager.h -/// @brief Internal RPC manager that owns all RPC concerns for the bridge. - -#pragma once - -#include "livekit/local_participant.h" -#include "livekit_bridge/rpc_constants.h" - -#include -#include -#include -#include - -namespace livekit { -struct RpcInvocationData; -} // namespace livekit - -namespace livekit_bridge { - -namespace test { -class RpcManagerTest; -} // namespace test - -/** - * Owns all RPC concerns for the LiveKitBridge: built-in handler registration - * and dispatch, user-registered custom handlers, and outgoing RPC calls. - * - * The manager is bound to a LocalParticipant via enable() and unbound via - * disable(). All public methods require the manager to be enabled (i.e., - * enable() has been called and disable() has not). - * - * Built-in handlers (e.g., track-control) are automatically registered on - * enable() and unregistered on disable(). User-registered handlers are - * forwarded directly to the underlying LocalParticipant. - * - * Not part of the public API; lives in bridge/src/. - */ -class RpcManager { -public: - /// Callback the bridge provides to execute a track action - /// (mute/unmute/release). Throws livekit::RpcError if the track is not found - /// or the action is invalid. - using TrackActionFn = std::function; - - explicit RpcManager(TrackActionFn track_action_fn); - - /// Bind to a LocalParticipant and register all built-in RPC handlers. - /// @pre @p lp must be non-null and remain valid until disable() is called. - void enable(livekit::LocalParticipant *lp); - - /// Unregister built-in handlers and unbind from the LocalParticipant. - void disable(); - - /// Whether the manager is currently bound to a LocalParticipant. - bool isEnabled() const { return lp_ != nullptr; } - - // -- Generic RPC -- - - /// @brief Perform an RPC call to a remote participant. - /// @param destination_identity Identity of the destination participant. - /// @param method Name of the RPC method to invoke. - /// @param payload Request payload to send to the remote - /// handler. - /// @param response_timeout Optional timeout in seconds for receiving - /// a response. If not set, the server default - /// timeout (15 seconds) is used. - /// @return The response payload returned by the remote handler. - /// @throws if the LocalParticipant performRpc fails. - std::string performRpc(const std::string &destination_identity, - const std::string &method, const std::string &payload, - const std::optional &response_timeout); - - // -- User-registered handlers -- - /// @brief Register a handler for an incoming RPC method. - /// @param method_name Name of the RPC method to handle. - /// @param handler Callback to execute when an invocation is received. - /// The handler may return an optional response payload - /// or throw an RpcError to signal failure. - /// @throws if the LocalParticipant registerRpcMethod fails. - void registerRpcMethod(const std::string &method_name, - livekit::LocalParticipant::RpcHandler handler); - - /// @brief Unregister a handler for an incoming RPC method. - /// @param method_name Name of the RPC method to unregister. - /// @throws if the LocalParticipant unregisterRpcMethod fails. - void unregisterRpcMethod(const std::string &method_name); - - // -- Built-in outgoing convenience (track control) -- - - /// @brief Request a remote participant to mute a published track. - /// @param destination_identity Identity of the remote participant. - /// @param track_name Name of the track to mute. - /// @throws if the LocalParticipant requestRemoteTrackMute fails. - void requestRemoteTrackMute(const std::string &destination_identity, - const std::string &track_name); - /// @brief Request a remote participant to unmute a published track. - /// @param destination_identity Identity of the remote participant. - /// @param track_name Name of the track to unmute. - /// @throws if the LocalParticipant requestRemoteTrackUnmute fails. - void requestRemoteTrackUnmute(const std::string &destination_identity, - const std::string &track_name); - -private: - friend class test::RpcManagerTest; - - /// @brief Enable built-in handlers. - /// @throws if the LocalParticipant registerRpcMethod fails. - void enableBuiltInHandlers(); - - /// @brief Disable built-in handlers. - /// @throws if the LocalParticipant unregisterRpcMethod fails. - void disableBuiltInHandlers(); - - /// @brief Handle a track control RPC. - /// @param data The RPC invocation data. - /// @return The response payload returned by the remote handler. - /// @throws if the RPC is invalid or the track is not found. - std::optional - handleTrackControlRpc(const livekit::RpcInvocationData &data); - - /// Callback to execute a track action RPC - TrackActionFn track_action_fn_; - - /// The LocalParticipant bound to the manager. - livekit::LocalParticipant *lp_; -}; - -} // namespace livekit_bridge diff --git a/bridge/tests/test_rpc_manager.cpp b/bridge/tests/test_rpc_manager.cpp deleted file mode 100644 index 5666c58c..00000000 --- a/bridge/tests/test_rpc_manager.cpp +++ /dev/null @@ -1,274 +0,0 @@ -/* - * 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. - */ - -/// @file test_rpc_manager.cpp -/// @brief Unit tests for RpcManager. - -#include - -#include "livekit_bridge/rpc_constants.h" -#include "rpc_manager.h" - -#include "livekit/local_participant.h" -#include "livekit/rpc_error.h" - -#include -#include - -namespace livekit_bridge { -namespace test { - -// Records (action, track_name) pairs passed to the TrackActionFn callback. -struct TrackActionRecord { - std::string action; - std::string track_name; -}; - -class RpcManagerTest : public ::testing::Test { -protected: - std::vector recorded_actions_; - - std::unique_ptr makeManager() { - return std::make_unique( - [this](const std::string &action, const std::string &track_name) { - recorded_actions_.push_back({action, track_name}); - }); - } - - std::unique_ptr makeThrowingManager() { - return std::make_unique([](const std::string &, - const std::string &track_name) { - throw livekit::RpcError(livekit::RpcError::ErrorCode::APPLICATION_ERROR, - "track not found: " + track_name); - }); - } - - // Helper: call the private handleTrackControlRpc with a given payload. - std::optional - callHandler(RpcManager &mgr, const std::string &payload, - const std::string &caller = "test-caller") { - livekit::RpcInvocationData data; - data.request_id = "test-request-id"; - data.caller_identity = caller; - data.payload = payload; - data.response_timeout_sec = 10.0; - return mgr.handleTrackControlRpc(data); - } -}; - -// ============================================================================ -// Construction & lifecycle -// ============================================================================ - -TEST_F(RpcManagerTest, InitiallyDisabled) { - auto mgr = makeManager(); - EXPECT_FALSE(mgr->isEnabled()); -} - -TEST_F(RpcManagerTest, DisableOnAlreadyDisabledIsNoOp) { - auto mgr = makeManager(); - EXPECT_NO_THROW(mgr->disable()); - EXPECT_FALSE(mgr->isEnabled()); -} - -TEST_F(RpcManagerTest, DisableMultipleTimesIsIdempotent) { - auto mgr = makeManager(); - EXPECT_NO_THROW({ - mgr->disable(); - mgr->disable(); - mgr->disable(); - }); -} - -TEST_F(RpcManagerTest, DestructorWithoutEnableIsSafe) { - EXPECT_NO_THROW({ auto mgr = makeManager(); }); -} - -// ============================================================================ -// handleTrackControlRpc — payload parsing -// ============================================================================ - -TEST_F(RpcManagerTest, ValidMutePayload) { - auto mgr = makeManager(); - auto result = callHandler(*mgr, "mute:my-track"); - - ASSERT_TRUE(result.has_value()); - EXPECT_EQ(result.value(), rpc::track_control::kResponseOk); - - ASSERT_EQ(recorded_actions_.size(), 1u); - EXPECT_EQ(recorded_actions_[0].action, "mute"); - EXPECT_EQ(recorded_actions_[0].track_name, "my-track"); -} - -TEST_F(RpcManagerTest, ValidUnmutePayload) { - auto mgr = makeManager(); - auto result = callHandler(*mgr, "unmute:cam"); - - ASSERT_TRUE(result.has_value()); - EXPECT_EQ(result.value(), rpc::track_control::kResponseOk); - - ASSERT_EQ(recorded_actions_.size(), 1u); - EXPECT_EQ(recorded_actions_[0].action, "unmute"); - EXPECT_EQ(recorded_actions_[0].track_name, "cam"); -} - -TEST_F(RpcManagerTest, TrackNameWithColons) { - auto mgr = makeManager(); - auto result = callHandler(*mgr, "mute:track:with:colons"); - - ASSERT_TRUE(result.has_value()); - ASSERT_EQ(recorded_actions_.size(), 1u); - EXPECT_EQ(recorded_actions_[0].action, "mute"); - EXPECT_EQ(recorded_actions_[0].track_name, "track:with:colons"); -} - -TEST_F(RpcManagerTest, TrackNameWithSpaces) { - auto mgr = makeManager(); - auto result = callHandler(*mgr, "unmute:my track name"); - - ASSERT_TRUE(result.has_value()); - ASSERT_EQ(recorded_actions_.size(), 1u); - EXPECT_EQ(recorded_actions_[0].action, "unmute"); - EXPECT_EQ(recorded_actions_[0].track_name, "my track name"); -} - -// ============================================================================ -// handleTrackControlRpc — invalid payloads -// ============================================================================ - -TEST_F(RpcManagerTest, EmptyPayloadThrows) { - auto mgr = makeManager(); - EXPECT_THROW(callHandler(*mgr, ""), livekit::RpcError); - EXPECT_TRUE(recorded_actions_.empty()); -} - -TEST_F(RpcManagerTest, NoDelimiterThrows) { - auto mgr = makeManager(); - EXPECT_THROW(callHandler(*mgr, "mutetrack"), livekit::RpcError); - EXPECT_TRUE(recorded_actions_.empty()); -} - -TEST_F(RpcManagerTest, LeadingDelimiterThrows) { - auto mgr = makeManager(); - EXPECT_THROW(callHandler(*mgr, ":track"), livekit::RpcError); - EXPECT_TRUE(recorded_actions_.empty()); -} - -TEST_F(RpcManagerTest, UnknownActionThrows) { - auto mgr = makeManager(); - EXPECT_THROW(callHandler(*mgr, "pause:cam"), livekit::RpcError); - EXPECT_TRUE(recorded_actions_.empty()); -} - -TEST_F(RpcManagerTest, CaseSensitiveAction) { - auto mgr = makeManager(); - EXPECT_THROW(callHandler(*mgr, "MUTE:cam"), livekit::RpcError); - EXPECT_THROW(callHandler(*mgr, "Mute:cam"), livekit::RpcError); - EXPECT_TRUE(recorded_actions_.empty()); -} - -// ============================================================================ -// handleTrackControlRpc — TrackActionFn propagation -// ============================================================================ - -TEST_F(RpcManagerTest, TrackActionFnExceptionPropagates) { - auto mgr = makeThrowingManager(); - - try { - callHandler(*mgr, "mute:nonexistent"); - FAIL() << "Expected RpcError to propagate from TrackActionFn"; - } catch (const livekit::RpcError &e) { - EXPECT_EQ(e.code(), static_cast( - livekit::RpcError::ErrorCode::APPLICATION_ERROR)); - EXPECT_NE(std::string(e.message()).find("nonexistent"), std::string::npos) - << "Error message should contain the track name"; - } -} - -TEST_F(RpcManagerTest, MultipleCallsAccumulate) { - auto mgr = makeManager(); - - callHandler(*mgr, "mute:audio"); - callHandler(*mgr, "unmute:audio"); - callHandler(*mgr, "mute:video"); - - ASSERT_EQ(recorded_actions_.size(), 3u); - EXPECT_EQ(recorded_actions_[0].action, "mute"); - EXPECT_EQ(recorded_actions_[0].track_name, "audio"); - EXPECT_EQ(recorded_actions_[1].action, "unmute"); - EXPECT_EQ(recorded_actions_[1].track_name, "audio"); - EXPECT_EQ(recorded_actions_[2].action, "mute"); - EXPECT_EQ(recorded_actions_[2].track_name, "video"); -} - -// ============================================================================ -// handleTrackControlRpc — caller identity forwarded -// ============================================================================ - -TEST_F(RpcManagerTest, CallerIdentityPassedThrough) { - auto mgr = makeManager(); - livekit::RpcInvocationData data; - data.request_id = "req-1"; - data.caller_identity = "remote-robot"; - data.payload = "mute:mic"; - data.response_timeout_sec = 5.0; - - auto result = mgr->handleTrackControlRpc(data); - - ASSERT_TRUE(result.has_value()); - ASSERT_EQ(recorded_actions_.size(), 1u); - EXPECT_EQ(recorded_actions_[0].action, "mute"); - EXPECT_EQ(recorded_actions_[0].track_name, "mic"); -} - -// ============================================================================ -// rpc_constants — formatPayload -// ============================================================================ - -TEST_F(RpcManagerTest, FormatPayloadMute) { - namespace tc = rpc::track_control; - std::string payload = tc::formatPayload(tc::kActionMute, "cam"); - EXPECT_EQ(payload, "mute:cam"); -} - -TEST_F(RpcManagerTest, FormatPayloadUnmute) { - namespace tc = rpc::track_control; - std::string payload = tc::formatPayload(tc::kActionUnmute, "mic"); - EXPECT_EQ(payload, "unmute:mic"); -} - -TEST_F(RpcManagerTest, FormatPayloadEmptyTrackName) { - namespace tc = rpc::track_control; - std::string payload = tc::formatPayload(tc::kActionMute, ""); - EXPECT_EQ(payload, "mute:"); -} - -TEST_F(RpcManagerTest, FormatPayloadRoundTrip) { - namespace tc = rpc::track_control; - std::string track_name = "some-track-123"; - std::string payload = tc::formatPayload(tc::kActionMute, track_name); - - auto mgr = makeManager(); - auto result = callHandler(*mgr, payload); - - ASSERT_TRUE(result.has_value()); - ASSERT_EQ(recorded_actions_.size(), 1u); - EXPECT_EQ(recorded_actions_[0].action, tc::kActionMute); - EXPECT_EQ(recorded_actions_[0].track_name, track_name); -} - -} // namespace test -} // namespace livekit_bridge From 4a1a33e225a394cf0374ec2b9ab541cfbd9394df Mon Sep 17 00:00:00 2001 From: Stephen DeRosa Date: Tue, 10 Mar 2026 12:11:44 -0600 Subject: [PATCH 04/13] session_manager --- CMakeLists.txt | 2 +- examples/bridge_human_robot/human.cpp | 4 +- examples/bridge_human_robot/robot.cpp | 6 +- examples/bridge_mute_unmute/README.md | 2 +- examples/bridge_mute_unmute/caller.cpp | 4 +- examples/bridge_mute_unmute/receiver.cpp | 4 +- examples/bridge_rpc/README.md | 2 +- examples/bridge_rpc/custom_caller.cpp | 4 +- examples/bridge_rpc/custom_receiver.cpp | 4 +- .../session_manager}/CMakeLists.txt | 22 ++-- {bridge => src/session_manager}/README.md | 32 ++--- .../livekit_bridge/managed_audio_track.h | 32 ++--- .../livekit_bridge/managed_video_track.h | 32 ++--- .../include/livekit_bridge/rpc_constants.h | 0 .../include/livekit_bridge/session_manager.h | 50 ++++---- .../src/bridge_room_delegate.cpp | 2 +- .../src/bridge_room_delegate.h | 14 +- .../src/managed_audio_track.cpp | 32 ++--- .../src/managed_video_track.cpp | 34 ++--- .../session_manager}/src/rpc_controller.cpp | 0 .../session_manager}/src/rpc_controller.h | 4 +- .../session_manager/src/session_manager.cpp | 120 +++++++++--------- .../session_manager}/tests/CMakeLists.txt | 0 .../test_session_manager_rpc_roundtrip.cpp | 24 ++-- .../tests/test_callback_key.cpp | 10 +- .../tests/test_managed_audio_track.cpp | 42 +++--- .../tests/test_managed_video_track.cpp | 40 +++--- .../tests/test_rpc_controller.cpp | 0 .../tests/test_session_manager.cpp | 52 ++++---- 29 files changed, 288 insertions(+), 286 deletions(-) rename {bridge => src/session_manager}/CMakeLists.txt (68%) rename {bridge => src/session_manager}/README.md (90%) rename bridge/include/livekit_bridge/bridge_audio_track.h => src/session_manager/include/livekit_bridge/managed_audio_track.h (84%) rename bridge/include/livekit_bridge/bridge_video_track.h => src/session_manager/include/livekit_bridge/managed_video_track.h (83%) rename {bridge => src/session_manager}/include/livekit_bridge/rpc_constants.h (100%) rename bridge/include/livekit_bridge/livekit_bridge.h => src/session_manager/include/livekit_bridge/session_manager.h (93%) rename {bridge => src/session_manager}/src/bridge_room_delegate.cpp (97%) rename {bridge => src/session_manager}/src/bridge_room_delegate.h (75%) rename bridge/src/bridge_audio_track.cpp => src/session_manager/src/managed_audio_track.cpp (76%) rename bridge/src/bridge_video_track.cpp => src/session_manager/src/managed_video_track.cpp (75%) rename {bridge => src/session_manager}/src/rpc_controller.cpp (100%) rename {bridge => src/session_manager}/src/rpc_controller.h (97%) rename bridge/src/livekit_bridge.cpp => src/session_manager/src/session_manager.cpp (82%) rename {bridge => src/session_manager}/tests/CMakeLists.txt (100%) rename bridge/tests/integration/test_bridge_rpc_roundtrip.cpp => src/session_manager/tests/integration/test_session_manager_rpc_roundtrip.cpp (96%) rename {bridge => src/session_manager}/tests/test_callback_key.cpp (94%) rename bridge/tests/test_bridge_audio_track.cpp => src/session_manager/tests/test_managed_audio_track.cpp (66%) rename bridge/tests/test_bridge_video_track.cpp => src/session_manager/tests/test_managed_video_track.cpp (65%) rename {bridge => src/session_manager}/tests/test_rpc_controller.cpp (100%) rename bridge/tests/test_livekit_bridge.cpp => src/session_manager/tests/test_session_manager.cpp (81%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 39d06fad..739bfc00 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -680,7 +680,7 @@ install(FILES # ------------------------------------------------------------------------ # Build the LiveKit C++ bridge before examples (human_robot depends on it) -add_subdirectory(bridge) +add_subdirectory(src/session_manager) # ---- Examples ---- # add_subdirectory(examples) diff --git a/examples/bridge_human_robot/human.cpp b/examples/bridge_human_robot/human.cpp index 434deca6..23e95e81 100644 --- a/examples/bridge_human_robot/human.cpp +++ b/examples/bridge_human_robot/human.cpp @@ -46,7 +46,7 @@ #include "livekit/audio_frame.h" #include "livekit/track.h" #include "livekit/video_frame.h" -#include "livekit_bridge/livekit_bridge.h" +#include "livekit_bridge/session_manager.h" #include "lk_log.h" #include "sdl_media.h" @@ -192,7 +192,7 @@ int main(int argc, char *argv[]) { std::mutex speaker_mutex; // ----- Connect to LiveKit ----- - livekit_bridge::LiveKitBridge bridge; + livekit_bridge::SessionManager bridge; std::cout << "[human] Connecting to " << url << " ...\n"; livekit::RoomOptions options; options.auto_subscribe = true; diff --git a/examples/bridge_human_robot/robot.cpp b/examples/bridge_human_robot/robot.cpp index 18d07b92..9a2e52ee 100644 --- a/examples/bridge_human_robot/robot.cpp +++ b/examples/bridge_human_robot/robot.cpp @@ -36,7 +36,7 @@ #include "livekit/audio_frame.h" #include "livekit/track.h" #include "livekit/video_frame.h" -#include "livekit_bridge/livekit_bridge.h" +#include "livekit_bridge/session_manager.h" #include "lk_log.h" #include "sdl_media.h" @@ -357,7 +357,7 @@ int main(int argc, char *argv[]) { } // ----- Connect to LiveKit ----- - livekit_bridge::LiveKitBridge bridge; + livekit_bridge::SessionManager bridge; LK_LOG_INFO("[robot] Connecting to {} ...", url); livekit::RoomOptions options; options.auto_subscribe = true; @@ -377,7 +377,7 @@ int main(int argc, char *argv[]) { constexpr int kSimWidth = 480; constexpr int kSimHeight = 320; - std::shared_ptr mic; + std::shared_ptr mic; if (use_mic) { mic = bridge.createAudioTrack("robot-mic", kSampleRate, kChannels, livekit::TrackSource::SOURCE_MICROPHONE); diff --git a/examples/bridge_mute_unmute/README.md b/examples/bridge_mute_unmute/README.md index 6dcd5443..60051e38 100644 --- a/examples/bridge_mute_unmute/README.md +++ b/examples/bridge_mute_unmute/README.md @@ -1,6 +1,6 @@ # Bridge Mute/Unmute Example -Demonstrates remote track control using the `LiveKitBridge` built-in +Demonstrates remote track control using the `SessionManager` built-in track-control RPC. A **receiver** publishes audio and video tracks, and a **caller** subscribes to them and toggles mute/unmute every few seconds. diff --git a/examples/bridge_mute_unmute/caller.cpp b/examples/bridge_mute_unmute/caller.cpp index a47b2e11..c7d3b5b2 100644 --- a/examples/bridge_mute_unmute/caller.cpp +++ b/examples/bridge_mute_unmute/caller.cpp @@ -35,7 +35,7 @@ #include "livekit/rpc_error.h" #include "livekit/track.h" #include "livekit/video_frame.h" -#include "livekit_bridge/livekit_bridge.h" +#include "livekit_bridge/session_manager.h" #include "sdl_media.h" #include @@ -146,7 +146,7 @@ int main(int argc, char *argv[]) { std::mutex speaker_mutex; // ----- Connect to LiveKit ----- - livekit_bridge::LiveKitBridge bridge; + livekit_bridge::SessionManager bridge; std::cout << "[caller] Connecting to " << url << " ...\n"; livekit::RoomOptions options; diff --git a/examples/bridge_mute_unmute/receiver.cpp b/examples/bridge_mute_unmute/receiver.cpp index 1abafbc9..c642809a 100644 --- a/examples/bridge_mute_unmute/receiver.cpp +++ b/examples/bridge_mute_unmute/receiver.cpp @@ -34,7 +34,7 @@ */ #include "livekit/track.h" -#include "livekit_bridge/livekit_bridge.h" +#include "livekit_bridge/session_manager.h" #include "sdl_media.h" #include @@ -83,7 +83,7 @@ int main(int argc, char *argv[]) { } // ----- Connect to LiveKit ----- - livekit_bridge::LiveKitBridge bridge; + livekit_bridge::SessionManager bridge; std::cout << "[receiver] Connecting to " << url << " ...\n"; livekit::RoomOptions options; diff --git a/examples/bridge_rpc/README.md b/examples/bridge_rpc/README.md index 8969619e..19025b18 100644 --- a/examples/bridge_rpc/README.md +++ b/examples/bridge_rpc/README.md @@ -1,7 +1,7 @@ # Bridge RPC Example A minimal example of custom user-registered RPC methods using the -`LiveKitBridge` high-level API. +`SessionManager` high-level API. Two headless executables — **BridgeRpcReceiver** and **BridgeRpcCaller** — connect to the same LiveKit room. The receiver registers a `"print"` RPC diff --git a/examples/bridge_rpc/custom_caller.cpp b/examples/bridge_rpc/custom_caller.cpp index 4ff5d355..f5f37b20 100644 --- a/examples/bridge_rpc/custom_caller.cpp +++ b/examples/bridge_rpc/custom_caller.cpp @@ -31,7 +31,7 @@ */ #include "livekit/rpc_error.h" -#include "livekit_bridge/livekit_bridge.h" +#include "livekit_bridge/session_manager.h" #include #include @@ -65,7 +65,7 @@ int main(int argc, char *argv[]) { std::signal(SIGINT, handleSignal); - livekit_bridge::LiveKitBridge bridge; + livekit_bridge::SessionManager bridge; std::cout << "[caller] Connecting to " << url << " ...\n"; livekit::RoomOptions options; diff --git a/examples/bridge_rpc/custom_receiver.cpp b/examples/bridge_rpc/custom_receiver.cpp index a98cbd3b..f924fdd8 100644 --- a/examples/bridge_rpc/custom_receiver.cpp +++ b/examples/bridge_rpc/custom_receiver.cpp @@ -28,7 +28,7 @@ * lk token create --join --room --identity receiver --valid-for 24h */ -#include "livekit_bridge/livekit_bridge.h" +#include "livekit_bridge/session_manager.h" #include #include @@ -62,7 +62,7 @@ int main(int argc, char *argv[]) { std::signal(SIGINT, handleSignal); - livekit_bridge::LiveKitBridge bridge; + livekit_bridge::SessionManager bridge; std::cout << "[receiver] Connecting to " << url << " ...\n"; livekit::RoomOptions options; diff --git a/bridge/CMakeLists.txt b/src/session_manager/CMakeLists.txt similarity index 68% rename from bridge/CMakeLists.txt rename to src/session_manager/CMakeLists.txt index 25d88aa0..853341d8 100644 --- a/bridge/CMakeLists.txt +++ b/src/session_manager/CMakeLists.txt @@ -1,14 +1,14 @@ cmake_minimum_required(VERSION 3.20) -project(livekit_bridge LANGUAGES CXX) +project(session_manager LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -add_library(livekit_bridge SHARED - src/livekit_bridge.cpp - src/bridge_audio_track.cpp - src/bridge_video_track.cpp +add_library(session_manager SHARED + src/session_manager.cpp + src/managed_audio_track.cpp + src/managed_video_track.cpp src/bridge_room_delegate.cpp src/bridge_room_delegate.h src/rpc_controller.cpp @@ -16,10 +16,10 @@ add_library(livekit_bridge SHARED ) if(WIN32) - set_target_properties(livekit_bridge PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) + set_target_properties(session_manager PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) endif() -target_include_directories(livekit_bridge +target_include_directories(session_manager PUBLIC $ $ @@ -30,22 +30,22 @@ target_include_directories(livekit_bridge # Link against the main livekit SDK library (which transitively provides # include paths for livekit/*.h and links livekit_ffi). -target_link_libraries(livekit_bridge +target_link_libraries(session_manager PUBLIC livekit PRIVATE spdlog::spdlog ) -target_compile_definitions(livekit_bridge +target_compile_definitions(session_manager PRIVATE SPDLOG_ACTIVE_LEVEL=${_SPDLOG_ACTIVE_LEVEL} ) if(MSVC) - target_compile_options(livekit_bridge PRIVATE /permissive- /Zc:__cplusplus /W4) + target_compile_options(session_manager PRIVATE /permissive- /Zc:__cplusplus /W4) else() - target_compile_options(livekit_bridge PRIVATE -Wall -Wextra -Wpedantic) + target_compile_options(session_manager PRIVATE -Wall -Wextra -Wpedantic) endif() # --- Tests --- diff --git a/bridge/README.md b/src/session_manager/README.md similarity index 90% rename from bridge/README.md rename to src/session_manager/README.md index cebb09b2..f3d7821d 100644 --- a/bridge/README.md +++ b/src/session_manager/README.md @@ -11,13 +11,13 @@ Since this is an extention of the LiveKit C++ SDK, go through the LiveKit C++ SD ## Usage Overview ```cpp -#include "livekit_bridge/livekit_bridge.h" +#include "livekit_bridge/session_manager.h" #include "livekit/audio_frame.h" #include "livekit/video_frame.h" #include "livekit/track.h" // 1. Connect -livekit_bridge::LiveKitBridge bridge; +livekit_bridge::SessionManager bridge; livekit::RoomOptions options; options.auto_subscribe = true; // automatically subscribe to all remote tracks options.dynacast = false; @@ -80,12 +80,12 @@ TODO(sderosa): add instructions on how to use the bridge in your own CMake proje ``` Your Application | | - | pushFrame() -----> BridgeAudioTrack | (sending to remote participants) - | pushFrame() -----> BridgeVideoTrack | + | pushFrame() -----> ManagedAudioTrack | (sending to remote participants) + | pushFrame() -----> ManagedVideoTrack | | | | callback() <------ Reader Thread | (receiving from remote participants) | | - +------- LiveKitBridge -----------------+ + +------- SessionManager ----------------+ | LiveKit Room | @@ -94,9 +94,9 @@ Your Application ### Core Components -**`LiveKitBridge`** -- The main entry point. Owns the full room lifecycle: SDK initialization, room connection, track publishing, and frame callback management. +**`SessionManager`** -- The main entry point. Owns the full room lifecycle: SDK initialization, room connection, track publishing, and frame callback management. -**`BridgeAudioTrack` / `BridgeVideoTrack`** -- RAII handles for published local tracks. Created via `createAudioTrack()` / `createVideoTrack()`. When the `shared_ptr` is dropped, the track is automatically unpublished and all underlying SDK resources are freed. Call `pushFrame()` to send audio/video data to remote participants. +**`ManagedAudioTrack` / `ManagedVideoTrack`** -- RAII handles for published local tracks. Created via `createAudioTrack()` / `createVideoTrack()`. When the `shared_ptr` is dropped, the track is automatically unpublished and all underlying SDK resources are freed. Call `pushFrame()` to send audio/video data to remote participants. **`BridgeRoomDelegate`** -- Internal (not part of the public API; lives in `src/`). Listens for `onTrackSubscribed` / `onTrackUnsubscribed` events from the LiveKit SDK and wires up reader threads automatically. @@ -108,7 +108,7 @@ When a remote participant publishes an audio or video track and the bridge subsc In short: -- **Sending** (you -> remote): `BridgeAudioTrack::pushFrame()` / `BridgeVideoTrack::pushFrame()` +- **Sending** (you -> remote): `ManagedAudioTrack::pushFrame()` / `ManagedVideoTrack::pushFrame()` - **Receiving** (remote -> you): reader threads invoke your registered callbacks Reader threads are managed entirely by the bridge. They are created when a matching remote track is subscribed, and torn down (stream closed, thread joined) when the track is unsubscribed, the callback is unregistered, or `disconnect()` is called. @@ -133,21 +133,21 @@ bridge.connect(url, token, options); ### Thread Safety -- `LiveKitBridge` uses a mutex to protect the callback map and active reader state. +- `SessionManager` uses a mutex to protect the callback map and active reader state. - Frame callbacks fire on background reader threads. If your callback accesses shared application state, you are responsible for synchronization. - `disconnect()` closes all streams and joins all reader threads before returning -- it is safe to destroy the bridge immediately after. ## API Reference -### `LiveKitBridge` +### `SessionManager` | Method | Description | |---|---| | `connect(url, token, options)` | Connect to a LiveKit room. Initializes the SDK, creates a Room, and connects with auto-subscribe enabled. | | `disconnect()` | Disconnect and release all resources. Joins all reader threads. Safe to call multiple times. | | `isConnected()` | Returns whether the bridge is currently connected. | -| `createAudioTrack(name, sample_rate, num_channels, source)` | Create and publish a local audio track with the given `TrackSource` (e.g. `SOURCE_MICROPHONE`, `SOURCE_SCREENSHARE_AUDIO`). Returns an RAII `shared_ptr`. | -| `createVideoTrack(name, width, height, source)` | Create and publish a local video track with the given `TrackSource` (e.g. `SOURCE_CAMERA`, `SOURCE_SCREENSHARE`). Returns an RAII `shared_ptr`. | +| `createAudioTrack(name, sample_rate, num_channels, source)` | Create and publish a local audio track with the given `TrackSource` (e.g. `SOURCE_MICROPHONE`, `SOURCE_SCREENSHARE_AUDIO`). Returns an RAII `shared_ptr`. | +| `createVideoTrack(name, width, height, source)` | Create and publish a local video track with the given `TrackSource` (e.g. `SOURCE_CAMERA`, `SOURCE_SCREENSHARE`). Returns an RAII `shared_ptr`. | | `setOnAudioFrameCallback(identity, source, callback)` | Register a callback for audio frames from a specific remote participant + track source. | | `setOnVideoFrameCallback(identity, source, callback)` | Register a callback for video frames from a specific remote participant + track source. | | `clearOnAudioFrameCallback(identity, source)` | Clear the audio callback for a specific remote participant + track source. Stops and joins the reader thread if active. | @@ -158,7 +158,7 @@ bridge.connect(url, token, options); | `requestRemoteTrackMute(identity, track_name)` | Ask a remote participant to mute a track by name. Throws `livekit::RpcError` on failure. | | `requestRemoteTrackUnmute(identity, track_name)` | Ask a remote participant to unmute a track by name. Throws `livekit::RpcError` on failure. | -### `BridgeAudioTrack` +### `ManagedAudioTrack` | Method | Description | |---|---| @@ -167,7 +167,7 @@ bridge.connect(url, token, options); | `release()` | Explicitly unpublish and free resources. Called automatically by the destructor. | | `name()` / `sampleRate()` / `numChannels()` | Accessors for track configuration. | -### `BridgeVideoTrack` +### `ManagedVideoTrack` | Method | Description | |---|---| @@ -227,8 +227,8 @@ The human will print periodic summaries like: The bridge includes a unit test suite built with [Google Test](https://github.com/google/googletest). Tests cover 1. `CallbackKey` hashing/equality, -2. `BridgeAudioTrack`/`BridgeVideoTrack` state management, and -3. `LiveKitBridge` pre-connection behaviour (callback registration, error handling). +2. `ManagedAudioTrack`/`ManagedVideoTrack` state management, and +3. `SessionManager` pre-connection behaviour (callback registration, error handling). ### Building and running tests diff --git a/bridge/include/livekit_bridge/bridge_audio_track.h b/src/session_manager/include/livekit_bridge/managed_audio_track.h similarity index 84% rename from bridge/include/livekit_bridge/bridge_audio_track.h rename to src/session_manager/include/livekit_bridge/managed_audio_track.h index 5683e069..afed94ee 100644 --- a/bridge/include/livekit_bridge/bridge_audio_track.h +++ b/src/session_manager/include/livekit_bridge/managed_audio_track.h @@ -14,7 +14,7 @@ * limitations under the License. */ -/// @file bridge_audio_track.h +/// @file managed_audio_track.h /// @brief Handle for a published local audio track. #pragma once @@ -35,13 +35,13 @@ class LocalParticipant; namespace livekit_bridge { namespace test { -class BridgeAudioTrackTest; +class ManagedAudioTrackTest; } // namespace test /** * Handle to a published local audio track. * - * Created via LiveKitBridge::createAudioTrack(). The bridge retains a + * Created via SessionManager::createAudioTrack(). The manager retains a * reference to every track it creates and will automatically release all * tracks when disconnect() is called. To unpublish a track mid-session, * call release() explicitly; dropping the shared_ptr alone is not @@ -62,13 +62,13 @@ class BridgeAudioTrackTest; * mic->mute(); * mic->release(); // unpublishes the track mid-session */ -class BridgeAudioTrack { +class ManagedAudioTrack { public: - ~BridgeAudioTrack(); + ~ManagedAudioTrack(); // Non-copyable - BridgeAudioTrack(const BridgeAudioTrack &) = delete; - BridgeAudioTrack &operator=(const BridgeAudioTrack &) = delete; + ManagedAudioTrack(const ManagedAudioTrack &) = delete; + ManagedAudioTrack &operator=(const ManagedAudioTrack &) = delete; /** * Push a PCM audio frame to the track. @@ -118,19 +118,19 @@ class BridgeAudioTrack { * * After this call, pushFrame() returns false and mute()/unmute() are * no-ops. Called automatically by the destructor and by - * LiveKitBridge::disconnect(). Safe to call multiple times (idempotent). + * SessionManager::disconnect(). Safe to call multiple times (idempotent). */ void release(); private: - friend class LiveKitBridge; - friend class test::BridgeAudioTrackTest; - - BridgeAudioTrack(std::string name, int sample_rate, int num_channels, - std::shared_ptr source, - std::shared_ptr track, - std::shared_ptr publication, - livekit::LocalParticipant *participant); + friend class SessionManager; + friend class test::ManagedAudioTrackTest; + + ManagedAudioTrack(std::string name, int sample_rate, int num_channels, + std::shared_ptr source, + std::shared_ptr track, + std::shared_ptr publication, + livekit::LocalParticipant *participant); mutable std::mutex mutex_; std::string name_; diff --git a/bridge/include/livekit_bridge/bridge_video_track.h b/src/session_manager/include/livekit_bridge/managed_video_track.h similarity index 83% rename from bridge/include/livekit_bridge/bridge_video_track.h rename to src/session_manager/include/livekit_bridge/managed_video_track.h index 8057b7a9..d3f21567 100644 --- a/bridge/include/livekit_bridge/bridge_video_track.h +++ b/src/session_manager/include/livekit_bridge/managed_video_track.h @@ -14,7 +14,7 @@ * limitations under the License. */ -/// @file bridge_video_track.h +/// @file managed_video_track.h /// @brief Handle for a published local video track. #pragma once @@ -35,13 +35,13 @@ class LocalParticipant; namespace livekit_bridge { namespace test { -class BridgeVideoTrackTest; +class ManagedVideoTrackTest; } // namespace test /** * Handle to a published local video track. * - * Created via LiveKitBridge::createVideoTrack(). The bridge retains a + * Created via SessionManager::createVideoTrack(). The manager retains a * reference to every track it creates and will automatically release all * tracks when disconnect() is called. To unpublish a track mid-session, * call release() explicitly; dropping the shared_ptr alone is not @@ -62,13 +62,13 @@ class BridgeVideoTrackTest; * cam->mute(); * cam->release(); // unpublishes the track mid-session */ -class BridgeVideoTrack { +class ManagedVideoTrack { public: - ~BridgeVideoTrack(); + ~ManagedVideoTrack(); // Non-copyable - BridgeVideoTrack(const BridgeVideoTrack &) = delete; - BridgeVideoTrack &operator=(const BridgeVideoTrack &) = delete; + ManagedVideoTrack(const ManagedVideoTrack &) = delete; + ManagedVideoTrack &operator=(const ManagedVideoTrack &) = delete; /** * Push an RGBA video frame to the track. @@ -116,19 +116,19 @@ class BridgeVideoTrack { * * After this call, pushFrame() returns false and mute()/unmute() are * no-ops. Called automatically by the destructor and by - * LiveKitBridge::disconnect(). Safe to call multiple times (idempotent). + * SessionManager::disconnect(). Safe to call multiple times (idempotent). */ void release(); private: - friend class LiveKitBridge; - friend class test::BridgeVideoTrackTest; - - BridgeVideoTrack(std::string name, int width, int height, - std::shared_ptr source, - std::shared_ptr track, - std::shared_ptr publication, - livekit::LocalParticipant *participant); + friend class SessionManager; + friend class test::ManagedVideoTrackTest; + + ManagedVideoTrack(std::string name, int width, int height, + std::shared_ptr source, + std::shared_ptr track, + std::shared_ptr publication, + livekit::LocalParticipant *participant); mutable std::mutex mutex_; std::string name_; diff --git a/bridge/include/livekit_bridge/rpc_constants.h b/src/session_manager/include/livekit_bridge/rpc_constants.h similarity index 100% rename from bridge/include/livekit_bridge/rpc_constants.h rename to src/session_manager/include/livekit_bridge/rpc_constants.h diff --git a/bridge/include/livekit_bridge/livekit_bridge.h b/src/session_manager/include/livekit_bridge/session_manager.h similarity index 93% rename from bridge/include/livekit_bridge/livekit_bridge.h rename to src/session_manager/include/livekit_bridge/session_manager.h index f366c10d..39270966 100644 --- a/bridge/include/livekit_bridge/livekit_bridge.h +++ b/src/session_manager/include/livekit_bridge/session_manager.h @@ -14,13 +14,13 @@ * limitations under the License. */ -/// @file livekit_bridge.h +/// @file session_manager.h /// @brief High-level bridge API for the LiveKit C++ SDK. #pragma once -#include "livekit_bridge/bridge_audio_track.h" -#include "livekit_bridge/bridge_video_track.h" +#include "livekit_bridge/managed_audio_track.h" +#include "livekit_bridge/managed_video_track.h" #include "livekit_bridge/rpc_constants.h" #include "livekit/local_participant.h" @@ -53,7 +53,7 @@ class RpcController; namespace test { class CallbackKeyTest; -class LiveKitBridgeTest; +class SessionManagerTest; } // namespace test /// Callback type for incoming audio frames. @@ -81,24 +81,24 @@ using VideoFrameCallback = std::functionpushFrame(pcm_data, samples_per_channel); * cam->pushFrame(rgba_data, timestamp_us); * - * bridge.setOnAudioFrameCallback("remote-participant", + * manager.setOnAudioFrameCallback("remote-participant", * livekit::TrackSource::SOURCE_MICROPHONE, * [](const livekit::AudioFrame& f) { process(f); }); * - * bridge.setOnVideoFrameCallback("remote-participant", + * manager.setOnVideoFrameCallback("remote-participant", * livekit::TrackSource::SOURCE_CAMERA, * [](const livekit::VideoFrame& f, int64_t ts) { render(f); }); * @@ -106,18 +106,18 @@ using VideoFrameCallback = std::functionrelease(); * * // Disconnect releases all remaining tracks and tears down the room: - * bridge.disconnect(); + * manager.disconnect(); */ -class LiveKitBridge { +class SessionManager { public: - LiveKitBridge(); - ~LiveKitBridge(); + SessionManager(); + ~SessionManager(); // Non-copyable, non-movable (owns threads, callbacks, room) - LiveKitBridge(const LiveKitBridge &) = delete; - LiveKitBridge &operator=(const LiveKitBridge &) = delete; - LiveKitBridge(LiveKitBridge &&) = delete; - LiveKitBridge &operator=(LiveKitBridge &&) = delete; + SessionManager(const SessionManager &) = delete; + SessionManager &operator=(const SessionManager &) = delete; + SessionManager(SessionManager &&) = delete; + SessionManager &operator=(SessionManager &&) = delete; // --------------------------------------------------------------- // Connection @@ -179,7 +179,7 @@ class LiveKitBridge { * @return Shared pointer to the published audio track handle (never null). * @throws std::runtime_error if the bridge is not connected. */ - std::shared_ptr + std::shared_ptr createAudioTrack(const std::string &name, int sample_rate, int num_channels, livekit::TrackSource source); @@ -203,7 +203,7 @@ class LiveKitBridge { * @return Shared pointer to the published video track handle (never null). * @throws std::runtime_error if the bridge is not connected. */ - std::shared_ptr + std::shared_ptr createVideoTrack(const std::string &name, int width, int height, livekit::TrackSource source); @@ -329,7 +329,7 @@ class LiveKitBridge { /** * Request a remote participant to mute a published track. * - * The remote participant must be a LiveKitBridge instance (which + * The remote participant must be a SessionManager instance (which * automatically registers the built-in track-control RPC handler). * * @param destination_identity Identity of the remote participant. @@ -342,7 +342,7 @@ class LiveKitBridge { /** * Request a remote participant to unmute a published track. * - * The remote participant must be a LiveKitBridge instance (which + * The remote participant must be a SessionManager instance (which * automatically registers the built-in track-control RPC handler). * * @param destination_identity Identity of the remote participant. @@ -355,7 +355,7 @@ class LiveKitBridge { private: friend class BridgeRoomDelegate; friend class test::CallbackKeyTest; - friend class test::LiveKitBridgeTest; + friend class test::SessionManagerTest; /// Composite key for the callback map: (participant_identity, source). /// Only one callback can exist per key -- re-registering overwrites. @@ -434,9 +434,9 @@ class LiveKitBridge { /// All tracks created by this bridge. The bridge retains a shared_ptr so /// it can force-release every track on disconnect() before the room is /// destroyed, preventing dangling @c participant_ pointers. - std::vector> published_audio_tracks_; + std::vector> published_audio_tracks_; /// @copydoc published_audio_tracks_ - std::vector> published_video_tracks_; + std::vector> published_video_tracks_; }; } // namespace livekit_bridge diff --git a/bridge/src/bridge_room_delegate.cpp b/src/session_manager/src/bridge_room_delegate.cpp similarity index 97% rename from bridge/src/bridge_room_delegate.cpp rename to src/session_manager/src/bridge_room_delegate.cpp index 118627c6..c42ba4e3 100644 --- a/bridge/src/bridge_room_delegate.cpp +++ b/src/session_manager/src/bridge_room_delegate.cpp @@ -22,7 +22,7 @@ #include "livekit/remote_participant.h" #include "livekit/remote_track_publication.h" #include "livekit/track.h" -#include "livekit_bridge/livekit_bridge.h" +#include "livekit_bridge/session_manager.h" namespace livekit_bridge { diff --git a/bridge/src/bridge_room_delegate.h b/src/session_manager/src/bridge_room_delegate.h similarity index 75% rename from bridge/src/bridge_room_delegate.h rename to src/session_manager/src/bridge_room_delegate.h index 8f5dd311..04dcfa8b 100644 --- a/bridge/src/bridge_room_delegate.h +++ b/src/session_manager/src/bridge_room_delegate.h @@ -15,7 +15,7 @@ */ /// @file bridge_room_delegate.h -/// @brief Internal RoomDelegate forwarding SDK events to LiveKitBridge. +/// @brief Internal RoomDelegate forwarding SDK events to SessionManager. #pragma once @@ -23,29 +23,29 @@ namespace livekit_bridge { -class LiveKitBridge; +class SessionManager; /** - * Internal RoomDelegate that forwards SDK room events to the LiveKitBridge. + * Internal RoomDelegate that forwards SDK room events to the SessionManager. * * Handles track subscribe/unsubscribe lifecycle. Not part of the public API, * so its in src/ instead of include/. */ class BridgeRoomDelegate : public livekit::RoomDelegate { public: - explicit BridgeRoomDelegate(LiveKitBridge &bridge) : bridge_(bridge) {} + explicit BridgeRoomDelegate(SessionManager &bridge) : bridge_(bridge) {} - /// Forwards a track-subscribed event to LiveKitBridge::onTrackSubscribed(). + /// Forwards a track-subscribed event to SessionManager::onTrackSubscribed(). void onTrackSubscribed(livekit::Room &room, const livekit::TrackSubscribedEvent &ev) override; /// Forwards a track-unsubscribed event to - /// LiveKitBridge::onTrackUnsubscribed(). + /// SessionManager::onTrackUnsubscribed(). void onTrackUnsubscribed(livekit::Room &room, const livekit::TrackUnsubscribedEvent &ev) override; private: - LiveKitBridge &bridge_; + SessionManager &bridge_; }; } // namespace livekit_bridge diff --git a/bridge/src/bridge_audio_track.cpp b/src/session_manager/src/managed_audio_track.cpp similarity index 76% rename from bridge/src/bridge_audio_track.cpp rename to src/session_manager/src/managed_audio_track.cpp index 654129cc..91b8a7ad 100644 --- a/bridge/src/bridge_audio_track.cpp +++ b/src/session_manager/src/managed_audio_track.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -/// @file bridge_audio_track.cpp -/// @brief Implementation of BridgeAudioTrack. +/// @file managed_audio_track.cpp +/// @brief Implementation of ManagedAudioTrack. -#include "livekit_bridge/bridge_audio_track.h" +#include "livekit_bridge/managed_audio_track.h" #include "livekit/audio_frame.h" #include "livekit/audio_source.h" @@ -31,7 +31,7 @@ namespace livekit_bridge { -BridgeAudioTrack::BridgeAudioTrack( +ManagedAudioTrack::ManagedAudioTrack( std::string name, int sample_rate, int num_channels, std::shared_ptr source, std::shared_ptr track, @@ -42,10 +42,10 @@ BridgeAudioTrack::BridgeAudioTrack( track_(std::move(track)), publication_(std::move(publication)), participant_(participant) {} -BridgeAudioTrack::~BridgeAudioTrack() { release(); } +ManagedAudioTrack::~ManagedAudioTrack() { release(); } -bool BridgeAudioTrack::pushFrame(const std::vector &data, - int samples_per_channel, int timeout_ms) { +bool ManagedAudioTrack::pushFrame(const std::vector &data, + int samples_per_channel, int timeout_ms) { livekit::AudioFrame frame(std::vector(data.begin(), data.end()), sample_rate_, num_channels_, samples_per_channel); @@ -57,14 +57,14 @@ bool BridgeAudioTrack::pushFrame(const std::vector &data, try { source_->captureFrame(frame, timeout_ms); } catch (const std::exception &e) { - LK_LOG_ERROR("BridgeAudioTrack captureFrame error: {}", e.what()); + LK_LOG_ERROR("ManagedAudioTrack captureFrame error: {}", e.what()); return false; } return true; } -bool BridgeAudioTrack::pushFrame(const std::int16_t *data, - int samples_per_channel, int timeout_ms) { +bool ManagedAudioTrack::pushFrame(const std::int16_t *data, + int samples_per_channel, int timeout_ms) { const int total_samples = samples_per_channel * num_channels_; livekit::AudioFrame frame( std::vector(data, data + total_samples), sample_rate_, @@ -78,32 +78,32 @@ bool BridgeAudioTrack::pushFrame(const std::int16_t *data, try { source_->captureFrame(frame, timeout_ms); } catch (const std::exception &e) { - LK_LOG_ERROR("BridgeAudioTrack captureFrame error: {}", e.what()); + LK_LOG_ERROR("ManagedAudioTrack captureFrame error: {}", e.what()); return false; } return true; } -void BridgeAudioTrack::mute() { +void ManagedAudioTrack::mute() { std::lock_guard lock(mutex_); if (!released_ && track_) { track_->mute(); } } -void BridgeAudioTrack::unmute() { +void ManagedAudioTrack::unmute() { std::lock_guard lock(mutex_); if (!released_ && track_) { track_->unmute(); } } -bool BridgeAudioTrack::isReleased() const noexcept { +bool ManagedAudioTrack::isReleased() const noexcept { std::lock_guard lock(mutex_); return released_; } -void BridgeAudioTrack::release() { +void ManagedAudioTrack::release() { std::lock_guard lock(mutex_); if (released_) { return; @@ -116,7 +116,7 @@ void BridgeAudioTrack::release() { participant_->unpublishTrack(publication_->sid()); } catch (...) { // Best-effort cleanup; ignore errors during teardown - LK_LOG_WARN("BridgeAudioTrack unpublishTrack error, continuing with " + LK_LOG_WARN("ManagedAudioTrack unpublishTrack error, continuing with " "cleanup"); } } diff --git a/bridge/src/bridge_video_track.cpp b/src/session_manager/src/managed_video_track.cpp similarity index 75% rename from bridge/src/bridge_video_track.cpp rename to src/session_manager/src/managed_video_track.cpp index d78d2322..5bea1c32 100644 --- a/bridge/src/bridge_video_track.cpp +++ b/src/session_manager/src/managed_video_track.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -/// @file bridge_video_track.cpp -/// @brief Implementation of BridgeVideoTrack. +/// @file managed_video_track.cpp +/// @brief Implementation of ManagedVideoTrack. -#include "livekit_bridge/bridge_video_track.h" +#include "livekit_bridge/managed_video_track.h" #include "livekit/local_participant.h" #include "livekit/local_track_publication.h" @@ -31,7 +31,7 @@ namespace livekit_bridge { -BridgeVideoTrack::BridgeVideoTrack( +ManagedVideoTrack::ManagedVideoTrack( std::string name, int width, int height, std::shared_ptr source, std::shared_ptr track, @@ -41,10 +41,10 @@ BridgeVideoTrack::BridgeVideoTrack( source_(std::move(source)), track_(std::move(track)), publication_(std::move(publication)), participant_(participant) {} -BridgeVideoTrack::~BridgeVideoTrack() { release(); } +ManagedVideoTrack::~ManagedVideoTrack() { release(); } -bool BridgeVideoTrack::pushFrame(const std::vector &rgba, - std::int64_t timestamp_us) { +bool ManagedVideoTrack::pushFrame(const std::vector &rgba, + std::int64_t timestamp_us) { livekit::VideoFrame frame( width_, height_, livekit::VideoBufferType::RGBA, std::vector(rgba.begin(), rgba.end())); @@ -57,15 +57,15 @@ bool BridgeVideoTrack::pushFrame(const std::vector &rgba, try { source_->captureFrame(frame, timestamp_us); } catch (const std::exception &e) { - LK_LOG_ERROR("BridgeVideoTrack captureFrame error: {}", e.what()); + LK_LOG_ERROR("ManagedVideoTrack captureFrame error: {}", e.what()); return false; } return true; } -bool BridgeVideoTrack::pushFrame(const std::uint8_t *rgba, - std::size_t rgba_size, - std::int64_t timestamp_us) { +bool ManagedVideoTrack::pushFrame(const std::uint8_t *rgba, + std::size_t rgba_size, + std::int64_t timestamp_us) { livekit::VideoFrame frame(width_, height_, livekit::VideoBufferType::RGBA, std::vector(rgba, rgba + rgba_size)); @@ -77,32 +77,32 @@ bool BridgeVideoTrack::pushFrame(const std::uint8_t *rgba, try { source_->captureFrame(frame, timestamp_us); } catch (const std::exception &e) { - LK_LOG_ERROR("BridgeVideoTrack captureFrame error: {}", e.what()); + LK_LOG_ERROR("ManagedVideoTrack captureFrame error: {}", e.what()); return false; } return true; } -void BridgeVideoTrack::mute() { +void ManagedVideoTrack::mute() { std::lock_guard lock(mutex_); if (!released_ && track_) { track_->mute(); } } -void BridgeVideoTrack::unmute() { +void ManagedVideoTrack::unmute() { std::lock_guard lock(mutex_); if (!released_ && track_) { track_->unmute(); } } -bool BridgeVideoTrack::isReleased() const noexcept { +bool ManagedVideoTrack::isReleased() const noexcept { std::lock_guard lock(mutex_); return released_; } -void BridgeVideoTrack::release() { +void ManagedVideoTrack::release() { std::lock_guard lock(mutex_); if (released_) { return; @@ -115,7 +115,7 @@ void BridgeVideoTrack::release() { participant_->unpublishTrack(publication_->sid()); } catch (...) { // Best-effort cleanup; ignore errors during teardown - LK_LOG_WARN("BridgeVideoTrack unpublishTrack error, continuing with " + LK_LOG_WARN("ManagedVideoTrack unpublishTrack error, continuing with " "cleanup"); } } diff --git a/bridge/src/rpc_controller.cpp b/src/session_manager/src/rpc_controller.cpp similarity index 100% rename from bridge/src/rpc_controller.cpp rename to src/session_manager/src/rpc_controller.cpp diff --git a/bridge/src/rpc_controller.h b/src/session_manager/src/rpc_controller.h similarity index 97% rename from bridge/src/rpc_controller.h rename to src/session_manager/src/rpc_controller.h index 97d096dd..4abe4011 100644 --- a/bridge/src/rpc_controller.h +++ b/src/session_manager/src/rpc_controller.h @@ -38,7 +38,7 @@ class RpcControllerTest; } // namespace test /** - * Owns all RPC concerns for the LiveKitBridge: built-in handler registration + * Owns all RPC concerns for the SessionManager: built-in handler registration * and dispatch, user-registered custom handlers, and outgoing RPC calls. * * The controller is bound to a LocalParticipant via enable() and unbound via @@ -49,7 +49,7 @@ class RpcControllerTest; * enable() and unregistered on disable(). User-registered handlers are * forwarded directly to the underlying LocalParticipant. * - * Not part of the public API; lives in bridge/src/. + * Not part of the public API; lives in session_manager/src/. */ class RpcController { public: diff --git a/bridge/src/livekit_bridge.cpp b/src/session_manager/src/session_manager.cpp similarity index 82% rename from bridge/src/livekit_bridge.cpp rename to src/session_manager/src/session_manager.cpp index b6c495c1..7c290ead 100644 --- a/bridge/src/livekit_bridge.cpp +++ b/src/session_manager/src/session_manager.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -/// @file livekit_bridge.cpp -/// @brief Implementation of the LiveKitBridge high-level API. +/// @file session_manager.cpp +/// @brief Implementation of the SessionManager high-level API. -#include "livekit_bridge/livekit_bridge.h" +#include "livekit_bridge/session_manager.h" #include "bridge_room_delegate.h" #include "livekit_bridge/rpc_constants.h" #include "rpc_controller.h" @@ -47,12 +47,12 @@ namespace livekit_bridge { // CallbackKey // --------------------------------------------------------------- -bool LiveKitBridge::CallbackKey::operator==(const CallbackKey &o) const { +bool SessionManager::CallbackKey::operator==(const CallbackKey &o) const { return identity == o.identity && source == o.source; } std::size_t -LiveKitBridge::CallbackKeyHash::operator()(const CallbackKey &k) const { +SessionManager::CallbackKeyHash::operator()(const CallbackKey &k) const { std::size_t h1 = std::hash{}(k.identity); std::size_t h2 = std::hash{}(static_cast(k.source)); return h1 ^ (h2 << 1); @@ -62,7 +62,7 @@ LiveKitBridge::CallbackKeyHash::operator()(const CallbackKey &k) const { // Construction / Destruction // --------------------------------------------------------------- -LiveKitBridge::LiveKitBridge() +SessionManager::SessionManager() : connected_(false), connecting_(false), sdk_initialized_(false), rpc_controller_(std::make_unique( [this](const rpc::track_control::Action &action, @@ -70,14 +70,14 @@ LiveKitBridge::LiveKitBridge() executeTrackAction(action, track_name); })) {} -LiveKitBridge::~LiveKitBridge() { disconnect(); } +SessionManager::~SessionManager() { disconnect(); } // --------------------------------------------------------------- // Connection // --------------------------------------------------------------- -bool LiveKitBridge::connect(const std::string &url, const std::string &token, - const livekit::RoomOptions &options) { +bool SessionManager::connect(const std::string &url, const std::string &token, + const livekit::RoomOptions &options) { // ---- Phase 1: quick check under lock ---- { std::lock_guard lock(mutex_); @@ -137,7 +137,7 @@ bool LiveKitBridge::connect(const std::string &url, const std::string &token, return true; } -void LiveKitBridge::disconnect() { +void SessionManager::disconnect() { // Disable the RPC controller before tearing down the room. This unregisters // built-in handlers while the LocalParticipant is still alive. if (rpc_controller_ && rpc_controller_->isEnabled()) { @@ -214,7 +214,7 @@ void LiveKitBridge::disconnect() { } } -bool LiveKitBridge::isConnected() const { +bool SessionManager::isConnected() const { std::lock_guard lock(mutex_); return connected_; } @@ -223,9 +223,9 @@ bool LiveKitBridge::isConnected() const { // Track creation (publishing) // --------------------------------------------------------------- -std::shared_ptr -LiveKitBridge::createAudioTrack(const std::string &name, int sample_rate, - int num_channels, livekit::TrackSource source) { +std::shared_ptr +SessionManager::createAudioTrack(const std::string &name, int sample_rate, + int num_channels, livekit::TrackSource source) { std::lock_guard lock(mutex_); if (!connected_ || !room_) { @@ -251,16 +251,17 @@ LiveKitBridge::createAudioTrack(const std::string &name, int sample_rate, auto publication = lp->publishTrack(track, opts); // 4. Wrap in handle and retain a reference - auto bridge_track = std::shared_ptr(new BridgeAudioTrack( + auto bridge_track = + std::shared_ptr(new ManagedAudioTrack( name, sample_rate, num_channels, std::move(audio_source), std::move(track), std::move(publication), lp)); published_audio_tracks_.emplace_back(bridge_track); return bridge_track; } -std::shared_ptr -LiveKitBridge::createVideoTrack(const std::string &name, int width, int height, - livekit::TrackSource source) { +std::shared_ptr +SessionManager::createVideoTrack(const std::string &name, int width, int height, + livekit::TrackSource source) { std::lock_guard lock(mutex_); if (!connected_ || !room_) { @@ -285,9 +286,9 @@ LiveKitBridge::createVideoTrack(const std::string &name, int width, int height, auto publication = lp->publishTrack(track, opts); // 4. Wrap in handle and retain a reference - auto bridge_track = std::shared_ptr( - new BridgeVideoTrack(name, width, height, std::move(video_source), - std::move(track), std::move(publication), lp)); + auto bridge_track = std::shared_ptr( + new ManagedVideoTrack(name, width, height, std::move(video_source), + std::move(track), std::move(publication), lp)); published_video_tracks_.emplace_back(bridge_track); return bridge_track; } @@ -296,7 +297,7 @@ LiveKitBridge::createVideoTrack(const std::string &name, int width, int height, // Incoming frame callbacks // --------------------------------------------------------------- -void LiveKitBridge::setOnAudioFrameCallback( +void SessionManager::setOnAudioFrameCallback( const std::string &participant_identity, livekit::TrackSource source, AudioFrameCallback callback) { std::lock_guard lock(mutex_); @@ -316,7 +317,7 @@ void LiveKitBridge::setOnAudioFrameCallback( // be picked up. } -void LiveKitBridge::setOnVideoFrameCallback( +void SessionManager::setOnVideoFrameCallback( const std::string &participant_identity, livekit::TrackSource source, VideoFrameCallback callback) { std::lock_guard lock(mutex_); @@ -325,7 +326,7 @@ void LiveKitBridge::setOnVideoFrameCallback( video_callbacks_[key] = std::move(callback); } -void LiveKitBridge::clearOnAudioFrameCallback( +void SessionManager::clearOnAudioFrameCallback( const std::string &participant_identity, livekit::TrackSource source) { std::thread thread_to_join; { @@ -339,7 +340,7 @@ void LiveKitBridge::clearOnAudioFrameCallback( } } -void LiveKitBridge::clearOnVideoFrameCallback( +void SessionManager::clearOnVideoFrameCallback( const std::string &participant_identity, livekit::TrackSource source) { std::thread thread_to_join; { @@ -358,9 +359,10 @@ void LiveKitBridge::clearOnVideoFrameCallback( // --------------------------------------------------------------- std::optional -LiveKitBridge::performRpc(const std::string &destination_identity, - const std::string &method, const std::string &payload, - const std::optional &response_timeout) { +SessionManager::performRpc(const std::string &destination_identity, + const std::string &method, + const std::string &payload, + const std::optional &response_timeout) { if (!isConnected()) { return std::nullopt; @@ -370,18 +372,18 @@ LiveKitBridge::performRpc(const std::string &destination_identity, return rpc_controller_->performRpc(destination_identity, method, payload, response_timeout); } catch (const std::exception &e) { - std::cerr << "[LiveKitBridge] Exception: " << e.what() << "\n"; + std::cerr << "[SessionManager] Exception: " << e.what() << "\n"; return std::nullopt; } catch (const std::runtime_error &e) { - std::cerr << "[LiveKitBridge] Runtime error: " << e.what() << "\n"; + std::cerr << "[SessionManager] Runtime error: " << e.what() << "\n"; return std::nullopt; } catch (const livekit::RpcError &e) { - std::cerr << "[LiveKitBridge] RPC error: " << e.what() << "\n"; + std::cerr << "[SessionManager] RPC error: " << e.what() << "\n"; return std::nullopt; } } -bool LiveKitBridge::registerRpcMethod( +bool SessionManager::registerRpcMethod( const std::string &method_name, livekit::LocalParticipant::RpcHandler handler) { @@ -392,18 +394,18 @@ bool LiveKitBridge::registerRpcMethod( rpc_controller_->registerRpcMethod(method_name, std::move(handler)); return true; } catch (const std::exception &e) { - std::cerr << "[LiveKitBridge] Exception: " << e.what() << "\n"; + std::cerr << "[SessionManager] Exception: " << e.what() << "\n"; return false; } catch (const std::runtime_error &e) { - std::cerr << "[LiveKitBridge] Runtime error: " << e.what() << "\n"; + std::cerr << "[SessionManager] Runtime error: " << e.what() << "\n"; return false; } catch (const livekit::RpcError &e) { - std::cerr << "[LiveKitBridge] RPC error: " << e.what() << "\n"; + std::cerr << "[SessionManager] RPC error: " << e.what() << "\n"; return false; } } -bool LiveKitBridge::unregisterRpcMethod(const std::string &method_name) { +bool SessionManager::unregisterRpcMethod(const std::string &method_name) { if (!isConnected()) { return false; } @@ -411,18 +413,18 @@ bool LiveKitBridge::unregisterRpcMethod(const std::string &method_name) { rpc_controller_->unregisterRpcMethod(method_name); return true; } catch (const std::exception &e) { - std::cerr << "[LiveKitBridge] Exception: " << e.what() << "\n"; + std::cerr << "[SessionManager] Exception: " << e.what() << "\n"; return false; } catch (const std::runtime_error &e) { - std::cerr << "[LiveKitBridge] Runtime error: " << e.what() << "\n"; + std::cerr << "[SessionManager] Runtime error: " << e.what() << "\n"; return false; } catch (const livekit::RpcError &e) { - std::cerr << "[LiveKitBridge] RPC error: " << e.what() << "\n"; + std::cerr << "[SessionManager] RPC error: " << e.what() << "\n"; return false; } } -bool LiveKitBridge::requestRemoteTrackMute( +bool SessionManager::requestRemoteTrackMute( const std::string &destination_identity, const std::string &track_name) { if (!isConnected()) { return false; @@ -431,18 +433,18 @@ bool LiveKitBridge::requestRemoteTrackMute( rpc_controller_->requestRemoteTrackMute(destination_identity, track_name); return true; } catch (const std::exception &e) { - std::cerr << "[LiveKitBridge] Exception: " << e.what() << "\n"; + std::cerr << "[SessionManager] Exception: " << e.what() << "\n"; return false; } catch (const std::runtime_error &e) { - std::cerr << "[LiveKitBridge] Runtime error: " << e.what() << "\n"; + std::cerr << "[SessionManager] Runtime error: " << e.what() << "\n"; return false; } catch (const livekit::RpcError &e) { - std::cerr << "[LiveKitBridge] RPC error: " << e.what() << "\n"; + std::cerr << "[SessionManager] RPC error: " << e.what() << "\n"; return false; } } -bool LiveKitBridge::requestRemoteTrackUnmute( +bool SessionManager::requestRemoteTrackUnmute( const std::string &destination_identity, const std::string &track_name) { if (!isConnected()) { return false; @@ -451,13 +453,13 @@ bool LiveKitBridge::requestRemoteTrackUnmute( rpc_controller_->requestRemoteTrackUnmute(destination_identity, track_name); return true; } catch (const std::exception &e) { - std::cerr << "[LiveKitBridge] Exception: " << e.what() << "\n"; + std::cerr << "[SessionManager] Exception: " << e.what() << "\n"; return false; } catch (const std::runtime_error &e) { - std::cerr << "[LiveKitBridge] Runtime error: " << e.what() << "\n"; + std::cerr << "[SessionManager] Runtime error: " << e.what() << "\n"; return false; } catch (const livekit::RpcError &e) { - std::cerr << "[LiveKitBridge] RPC error: " << e.what() << "\n"; + std::cerr << "[SessionManager] RPC error: " << e.what() << "\n"; return false; } } @@ -466,8 +468,8 @@ bool LiveKitBridge::requestRemoteTrackUnmute( // Track action callback for RpcController // --------------------------------------------------------------- -void LiveKitBridge::executeTrackAction(const rpc::track_control::Action &action, - const std::string &track_name) { +void SessionManager::executeTrackAction(const rpc::track_control::Action &action, + const std::string &track_name) { std::lock_guard lock(mutex_); for (auto &track : published_audio_tracks_) { @@ -500,7 +502,7 @@ void LiveKitBridge::executeTrackAction(const rpc::track_control::Action &action, // Internal: track subscribe / unsubscribe from delegate // --------------------------------------------------------------- -void LiveKitBridge::onTrackSubscribed( +void SessionManager::onTrackSubscribed( const std::string &participant_identity, livekit::TrackSource source, const std::shared_ptr &track) { std::thread old_thread; @@ -530,8 +532,8 @@ void LiveKitBridge::onTrackSubscribed( } } -void LiveKitBridge::onTrackUnsubscribed(const std::string &participant_identity, - livekit::TrackSource source) { +void SessionManager::onTrackUnsubscribed( + const std::string &participant_identity, livekit::TrackSource source) { std::thread thread_to_join; { std::lock_guard lock(mutex_); @@ -547,7 +549,7 @@ void LiveKitBridge::onTrackUnsubscribed(const std::string &participant_identity, // Internal: reader thread management // --------------------------------------------------------------- -std::thread LiveKitBridge::extractReaderThread(const CallbackKey &key) { +std::thread SessionManager::extractReaderThread(const CallbackKey &key) { // Caller must hold mutex_. // Closes the stream and extracts the thread for the caller to join. auto it = active_readers_.find(key); @@ -571,9 +573,9 @@ std::thread LiveKitBridge::extractReaderThread(const CallbackKey &key) { } std::thread -LiveKitBridge::startAudioReader(const CallbackKey &key, - const std::shared_ptr &track, - AudioFrameCallback cb) { +SessionManager::startAudioReader(const CallbackKey &key, + const std::shared_ptr &track, + AudioFrameCallback cb) { // Caller must hold mutex_. // Returns the old reader thread (if any) for the caller to join outside // the lock. @@ -611,9 +613,9 @@ LiveKitBridge::startAudioReader(const CallbackKey &key, } std::thread -LiveKitBridge::startVideoReader(const CallbackKey &key, - const std::shared_ptr &track, - VideoFrameCallback cb) { +SessionManager::startVideoReader(const CallbackKey &key, + const std::shared_ptr &track, + VideoFrameCallback cb) { // Caller must hold mutex_. // Returns the old reader thread (if any) for the caller to join outside // the lock. diff --git a/bridge/tests/CMakeLists.txt b/src/session_manager/tests/CMakeLists.txt similarity index 100% rename from bridge/tests/CMakeLists.txt rename to src/session_manager/tests/CMakeLists.txt diff --git a/bridge/tests/integration/test_bridge_rpc_roundtrip.cpp b/src/session_manager/tests/integration/test_session_manager_rpc_roundtrip.cpp similarity index 96% rename from bridge/tests/integration/test_bridge_rpc_roundtrip.cpp rename to src/session_manager/tests/integration/test_session_manager_rpc_roundtrip.cpp index e649450a..f3390501 100644 --- a/bridge/tests/integration/test_bridge_rpc_roundtrip.cpp +++ b/src/session_manager/tests/integration/test_session_manager_rpc_roundtrip.cpp @@ -33,8 +33,8 @@ TEST_F(BridgeRpcRoundtripTest, BasicRpcRoundTrip) { std::cout << "\n=== Bridge RPC Round-Trip Test ===" << std::endl; - LiveKitBridge caller; - LiveKitBridge receiver; + SessionManager caller; + SessionManager receiver; ASSERT_TRUE(connectPair(caller, receiver)); @@ -88,8 +88,8 @@ TEST_F(BridgeRpcRoundtripTest, RpcErrorPropagation) { std::cout << "\n=== Bridge RPC Error Propagation Test ===" << std::endl; - LiveKitBridge caller; - LiveKitBridge receiver; + SessionManager caller; + SessionManager receiver; ASSERT_TRUE(connectPair(caller, receiver)); @@ -129,8 +129,8 @@ TEST_F(BridgeRpcRoundtripTest, UnregisteredMethod) { std::cout << "\n=== Bridge RPC Unsupported Method Test ===" << std::endl; - LiveKitBridge caller; - LiveKitBridge receiver; + SessionManager caller; + SessionManager receiver; ASSERT_TRUE(connectPair(caller, receiver)); @@ -167,8 +167,8 @@ TEST_F(BridgeRemoteTrackControlTest, RemoteMuteAudioTrack) { std::cout << "\n=== Bridge Remote Mute Audio Track Test ===" << std::endl; - LiveKitBridge publisher; - LiveKitBridge controller; + SessionManager publisher; + SessionManager controller; ASSERT_TRUE(connectPair(controller, publisher)); @@ -206,8 +206,8 @@ TEST_F(BridgeRemoteTrackControlTest, RemoteMuteVideoTrack) { std::cout << "\n=== Bridge Remote Mute Video Track Test ===" << std::endl; - LiveKitBridge publisher; - LiveKitBridge controller; + SessionManager publisher; + SessionManager controller; ASSERT_TRUE(connectPair(controller, publisher)); @@ -243,8 +243,8 @@ TEST_F(BridgeRemoteTrackControlTest, RemoteMuteNonexistentTrack) { std::cout << "\n=== Bridge Remote Mute Nonexistent Track Test ===" << std::endl; - LiveKitBridge publisher; - LiveKitBridge controller; + SessionManager publisher; + SessionManager controller; ASSERT_TRUE(connectPair(controller, publisher)); diff --git a/bridge/tests/test_callback_key.cpp b/src/session_manager/tests/test_callback_key.cpp similarity index 94% rename from bridge/tests/test_callback_key.cpp rename to src/session_manager/tests/test_callback_key.cpp index 29974f6f..258e2bb0 100644 --- a/bridge/tests/test_callback_key.cpp +++ b/src/session_manager/tests/test_callback_key.cpp @@ -15,10 +15,10 @@ */ /// @file test_callback_key.cpp -/// @brief Unit tests for LiveKitBridge::CallbackKey hash and equality. +/// @brief Unit tests for SessionManager::CallbackKey hash and equality. #include -#include +#include #include @@ -29,10 +29,10 @@ namespace test { class CallbackKeyTest : public ::testing::Test { protected: - // Type aliases for convenience -- these are private types in LiveKitBridge, + // Type aliases for convenience -- these are private types in SessionManager, // accessible via the friend declaration. - using CallbackKey = LiveKitBridge::CallbackKey; - using CallbackKeyHash = LiveKitBridge::CallbackKeyHash; + using CallbackKey = SessionManager::CallbackKey; + using CallbackKeyHash = SessionManager::CallbackKeyHash; }; TEST_F(CallbackKeyTest, EqualKeysCompareEqual) { diff --git a/bridge/tests/test_bridge_audio_track.cpp b/src/session_manager/tests/test_managed_audio_track.cpp similarity index 66% rename from bridge/tests/test_bridge_audio_track.cpp rename to src/session_manager/tests/test_managed_audio_track.cpp index 8e7274e9..3ec31a22 100644 --- a/bridge/tests/test_bridge_audio_track.cpp +++ b/src/session_manager/tests/test_managed_audio_track.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -/// @file test_bridge_audio_track.cpp -/// @brief Unit tests for BridgeAudioTrack. +/// @file test_managed_audio_track.cpp +/// @brief Unit tests for ManagedAudioTrack. #include -#include +#include #include #include @@ -27,25 +27,25 @@ namespace livekit_bridge { namespace test { -class BridgeAudioTrackTest : public ::testing::Test { +class ManagedAudioTrackTest : public ::testing::Test { protected: - /// Create a BridgeAudioTrack with null SDK objects for pure-logic testing. + /// Create a ManagedAudioTrack with null SDK objects for pure-logic testing. /// The track is usable for accessor and state management tests but will /// crash if pushFrame / mute / unmute try to dereference SDK pointers /// on a non-released track. - static BridgeAudioTrack createNullTrack(const std::string &name = "mic", - int sample_rate = 48000, - int num_channels = 2) { - return BridgeAudioTrack(name, sample_rate, num_channels, - nullptr, // source - nullptr, // track - nullptr, // publication - nullptr // participant + static ManagedAudioTrack createNullTrack(const std::string &name = "mic", + int sample_rate = 48000, + int num_channels = 2) { + return ManagedAudioTrack(name, sample_rate, num_channels, + nullptr, // source + nullptr, // track + nullptr, // publication + nullptr // participant ); } }; -TEST_F(BridgeAudioTrackTest, AccessorsReturnConstructionValues) { +TEST_F(ManagedAudioTrackTest, AccessorsReturnConstructionValues) { auto track = createNullTrack("test-mic", 16000, 1); EXPECT_EQ(track.name(), "test-mic") << "Name should match construction value"; @@ -53,14 +53,14 @@ TEST_F(BridgeAudioTrackTest, AccessorsReturnConstructionValues) { EXPECT_EQ(track.numChannels(), 1) << "Channel count should match"; } -TEST_F(BridgeAudioTrackTest, InitiallyNotReleased) { +TEST_F(ManagedAudioTrackTest, InitiallyNotReleased) { auto track = createNullTrack(); EXPECT_FALSE(track.isReleased()) << "Track should not be released immediately after construction"; } -TEST_F(BridgeAudioTrackTest, ReleaseMarksTrackAsReleased) { +TEST_F(ManagedAudioTrackTest, ReleaseMarksTrackAsReleased) { auto track = createNullTrack(); track.release(); @@ -69,7 +69,7 @@ TEST_F(BridgeAudioTrackTest, ReleaseMarksTrackAsReleased) { << "Track should be released after calling release()"; } -TEST_F(BridgeAudioTrackTest, DoubleReleaseIsIdempotent) { +TEST_F(ManagedAudioTrackTest, DoubleReleaseIsIdempotent) { auto track = createNullTrack(); track.release(); @@ -78,7 +78,7 @@ TEST_F(BridgeAudioTrackTest, DoubleReleaseIsIdempotent) { EXPECT_TRUE(track.isReleased()); } -TEST_F(BridgeAudioTrackTest, PushFrameAfterReleaseReturnsFalse) { +TEST_F(ManagedAudioTrackTest, PushFrameAfterReleaseReturnsFalse) { auto track = createNullTrack(); track.release(); @@ -88,7 +88,7 @@ TEST_F(BridgeAudioTrackTest, PushFrameAfterReleaseReturnsFalse) { << "pushFrame (vector) on a released track should return false"; } -TEST_F(BridgeAudioTrackTest, PushFrameRawPointerAfterReleaseReturnsFalse) { +TEST_F(ManagedAudioTrackTest, PushFrameRawPointerAfterReleaseReturnsFalse) { auto track = createNullTrack(); track.release(); @@ -98,7 +98,7 @@ TEST_F(BridgeAudioTrackTest, PushFrameRawPointerAfterReleaseReturnsFalse) { << "pushFrame (raw pointer) on a released track should return false"; } -TEST_F(BridgeAudioTrackTest, MuteOnReleasedTrackDoesNotCrash) { +TEST_F(ManagedAudioTrackTest, MuteOnReleasedTrackDoesNotCrash) { auto track = createNullTrack(); track.release(); @@ -106,7 +106,7 @@ TEST_F(BridgeAudioTrackTest, MuteOnReleasedTrackDoesNotCrash) { << "mute() on a released track should be a no-op"; } -TEST_F(BridgeAudioTrackTest, UnmuteOnReleasedTrackDoesNotCrash) { +TEST_F(ManagedAudioTrackTest, UnmuteOnReleasedTrackDoesNotCrash) { auto track = createNullTrack(); track.release(); diff --git a/bridge/tests/test_bridge_video_track.cpp b/src/session_manager/tests/test_managed_video_track.cpp similarity index 65% rename from bridge/tests/test_bridge_video_track.cpp rename to src/session_manager/tests/test_managed_video_track.cpp index 08517b02..3659f4bc 100644 --- a/bridge/tests/test_bridge_video_track.cpp +++ b/src/session_manager/tests/test_managed_video_track.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -/// @file test_bridge_video_track.cpp -/// @brief Unit tests for BridgeVideoTrack. +/// @file test_managed_video_track.cpp +/// @brief Unit tests for ManagedVideoTrack. #include -#include +#include #include #include @@ -27,21 +27,21 @@ namespace livekit_bridge { namespace test { -class BridgeVideoTrackTest : public ::testing::Test { +class ManagedVideoTrackTest : public ::testing::Test { protected: - /// Create a BridgeVideoTrack with null SDK objects for pure-logic testing. - static BridgeVideoTrack createNullTrack(const std::string &name = "cam", - int width = 1280, int height = 720) { - return BridgeVideoTrack(name, width, height, - nullptr, // source - nullptr, // track - nullptr, // publication - nullptr // participant + /// Create a ManagedVideoTrack with null SDK objects for pure-logic testing. + static ManagedVideoTrack createNullTrack(const std::string &name = "cam", + int width = 1280, int height = 720) { + return ManagedVideoTrack(name, width, height, + nullptr, // source + nullptr, // track + nullptr, // publication + nullptr // participant ); } }; -TEST_F(BridgeVideoTrackTest, AccessorsReturnConstructionValues) { +TEST_F(ManagedVideoTrackTest, AccessorsReturnConstructionValues) { auto track = createNullTrack("test-cam", 640, 480); EXPECT_EQ(track.name(), "test-cam") << "Name should match construction value"; @@ -49,14 +49,14 @@ TEST_F(BridgeVideoTrackTest, AccessorsReturnConstructionValues) { EXPECT_EQ(track.height(), 480) << "Height should match"; } -TEST_F(BridgeVideoTrackTest, InitiallyNotReleased) { +TEST_F(ManagedVideoTrackTest, InitiallyNotReleased) { auto track = createNullTrack(); EXPECT_FALSE(track.isReleased()) << "Track should not be released immediately after construction"; } -TEST_F(BridgeVideoTrackTest, ReleaseMarksTrackAsReleased) { +TEST_F(ManagedVideoTrackTest, ReleaseMarksTrackAsReleased) { auto track = createNullTrack(); track.release(); @@ -65,7 +65,7 @@ TEST_F(BridgeVideoTrackTest, ReleaseMarksTrackAsReleased) { << "Track should be released after calling release()"; } -TEST_F(BridgeVideoTrackTest, DoubleReleaseIsIdempotent) { +TEST_F(ManagedVideoTrackTest, DoubleReleaseIsIdempotent) { auto track = createNullTrack(); track.release(); @@ -74,7 +74,7 @@ TEST_F(BridgeVideoTrackTest, DoubleReleaseIsIdempotent) { EXPECT_TRUE(track.isReleased()); } -TEST_F(BridgeVideoTrackTest, PushFrameAfterReleaseReturnsFalse) { +TEST_F(ManagedVideoTrackTest, PushFrameAfterReleaseReturnsFalse) { auto track = createNullTrack(); track.release(); @@ -84,7 +84,7 @@ TEST_F(BridgeVideoTrackTest, PushFrameAfterReleaseReturnsFalse) { << "pushFrame (vector) on a released track should return false"; } -TEST_F(BridgeVideoTrackTest, PushFrameRawPointerAfterReleaseReturnsFalse) { +TEST_F(ManagedVideoTrackTest, PushFrameRawPointerAfterReleaseReturnsFalse) { auto track = createNullTrack(); track.release(); @@ -94,7 +94,7 @@ TEST_F(BridgeVideoTrackTest, PushFrameRawPointerAfterReleaseReturnsFalse) { << "pushFrame (raw pointer) on a released track should return false"; } -TEST_F(BridgeVideoTrackTest, MuteOnReleasedTrackDoesNotCrash) { +TEST_F(ManagedVideoTrackTest, MuteOnReleasedTrackDoesNotCrash) { auto track = createNullTrack(); track.release(); @@ -102,7 +102,7 @@ TEST_F(BridgeVideoTrackTest, MuteOnReleasedTrackDoesNotCrash) { << "mute() on a released track should be a no-op"; } -TEST_F(BridgeVideoTrackTest, UnmuteOnReleasedTrackDoesNotCrash) { +TEST_F(ManagedVideoTrackTest, UnmuteOnReleasedTrackDoesNotCrash) { auto track = createNullTrack(); track.release(); diff --git a/bridge/tests/test_rpc_controller.cpp b/src/session_manager/tests/test_rpc_controller.cpp similarity index 100% rename from bridge/tests/test_rpc_controller.cpp rename to src/session_manager/tests/test_rpc_controller.cpp diff --git a/bridge/tests/test_livekit_bridge.cpp b/src/session_manager/tests/test_session_manager.cpp similarity index 81% rename from bridge/tests/test_livekit_bridge.cpp rename to src/session_manager/tests/test_session_manager.cpp index 38b1d7d3..3f50b7a8 100644 --- a/bridge/tests/test_livekit_bridge.cpp +++ b/src/session_manager/tests/test_session_manager.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -/// @file test_livekit_bridge.cpp -/// @brief Unit tests for LiveKitBridge. +/// @file test_session_manager.cpp +/// @brief Unit tests for SessionManager. #include -#include +#include #include @@ -27,7 +27,7 @@ namespace livekit_bridge { namespace test { -class LiveKitBridgeTest : public ::testing::Test { +class SessionManagerTest : public ::testing::Test { protected: // No SetUp/TearDown needed -- we test the bridge without initializing // the LiveKit SDK, since we only exercise pre-connection behaviour. @@ -37,15 +37,15 @@ class LiveKitBridgeTest : public ::testing::Test { // Initial state // ============================================================================ -TEST_F(LiveKitBridgeTest, InitiallyNotConnected) { - LiveKitBridge bridge; +TEST_F(SessionManagerTest, InitiallyNotConnected) { + SessionManager bridge; EXPECT_FALSE(bridge.isConnected()) << "Bridge should not be connected immediately after construction"; } -TEST_F(LiveKitBridgeTest, DisconnectBeforeConnectIsNoOp) { - LiveKitBridge bridge; +TEST_F(SessionManagerTest, DisconnectBeforeConnectIsNoOp) { + SessionManager bridge; EXPECT_NO_THROW(bridge.disconnect()) << "disconnect() on an unconnected bridge should be a safe no-op"; @@ -53,8 +53,8 @@ TEST_F(LiveKitBridgeTest, DisconnectBeforeConnectIsNoOp) { EXPECT_FALSE(bridge.isConnected()); } -TEST_F(LiveKitBridgeTest, MultipleDisconnectsAreIdempotent) { - LiveKitBridge bridge; +TEST_F(SessionManagerTest, MultipleDisconnectsAreIdempotent) { + SessionManager bridge; EXPECT_NO_THROW({ bridge.disconnect(); @@ -63,10 +63,10 @@ TEST_F(LiveKitBridgeTest, MultipleDisconnectsAreIdempotent) { }) << "Multiple disconnect() calls should be safe"; } -TEST_F(LiveKitBridgeTest, DestructorOnUnconnectedBridgeIsSafe) { +TEST_F(SessionManagerTest, DestructorOnUnconnectedBridgeIsSafe) { // Just verify no crash when the bridge is destroyed without connecting. EXPECT_NO_THROW({ - LiveKitBridge bridge; + SessionManager bridge; // bridge goes out of scope here }); } @@ -75,8 +75,8 @@ TEST_F(LiveKitBridgeTest, DestructorOnUnconnectedBridgeIsSafe) { // Track creation before connection // ============================================================================ -TEST_F(LiveKitBridgeTest, CreateAudioTrackBeforeConnectThrows) { - LiveKitBridge bridge; +TEST_F(SessionManagerTest, CreateAudioTrackBeforeConnectThrows) { + SessionManager bridge; EXPECT_THROW(bridge.createAudioTrack("mic", 48000, 2, livekit::TrackSource::SOURCE_MICROPHONE), @@ -84,8 +84,8 @@ TEST_F(LiveKitBridgeTest, CreateAudioTrackBeforeConnectThrows) { << "createAudioTrack should throw when not connected"; } -TEST_F(LiveKitBridgeTest, CreateVideoTrackBeforeConnectThrows) { - LiveKitBridge bridge; +TEST_F(SessionManagerTest, CreateVideoTrackBeforeConnectThrows) { + SessionManager bridge; EXPECT_THROW(bridge.createVideoTrack("cam", 1280, 720, livekit::TrackSource::SOURCE_CAMERA), @@ -97,8 +97,8 @@ TEST_F(LiveKitBridgeTest, CreateVideoTrackBeforeConnectThrows) { // Callback registration (pre-connection, pure map operations) // ============================================================================ -TEST_F(LiveKitBridgeTest, RegisterAndUnregisterAudioCallbackDoesNotCrash) { - LiveKitBridge bridge; +TEST_F(SessionManagerTest, RegisterAndUnregisterAudioCallbackDoesNotCrash) { + SessionManager bridge; EXPECT_NO_THROW({ bridge.setOnAudioFrameCallback("remote-participant", @@ -111,8 +111,8 @@ TEST_F(LiveKitBridgeTest, RegisterAndUnregisterAudioCallbackDoesNotCrash) { "even without a connection"; } -TEST_F(LiveKitBridgeTest, RegisterAndUnregisterVideoCallbackDoesNotCrash) { - LiveKitBridge bridge; +TEST_F(SessionManagerTest, RegisterAndUnregisterVideoCallbackDoesNotCrash) { + SessionManager bridge; EXPECT_NO_THROW({ bridge.setOnVideoFrameCallback( @@ -125,8 +125,8 @@ TEST_F(LiveKitBridgeTest, RegisterAndUnregisterVideoCallbackDoesNotCrash) { "even without a connection"; } -TEST_F(LiveKitBridgeTest, UnregisterNonExistentCallbackIsNoOp) { - LiveKitBridge bridge; +TEST_F(SessionManagerTest, UnregisterNonExistentCallbackIsNoOp) { + SessionManager bridge; EXPECT_NO_THROW({ bridge.clearOnAudioFrameCallback("nonexistent", @@ -136,8 +136,8 @@ TEST_F(LiveKitBridgeTest, UnregisterNonExistentCallbackIsNoOp) { }) << "Unregistering a callback that was never registered should be a no-op"; } -TEST_F(LiveKitBridgeTest, MultipleRegistrationsSameKeyOverwrites) { - LiveKitBridge bridge; +TEST_F(SessionManagerTest, MultipleRegistrationsSameKeyOverwrites) { + SessionManager bridge; int call_count = 0; @@ -156,8 +156,8 @@ TEST_F(LiveKitBridgeTest, MultipleRegistrationsSameKeyOverwrites) { "alice", livekit::TrackSource::SOURCE_MICROPHONE)); } -TEST_F(LiveKitBridgeTest, RegisterCallbacksForMultipleParticipants) { - LiveKitBridge bridge; +TEST_F(SessionManagerTest, RegisterCallbacksForMultipleParticipants) { + SessionManager bridge; EXPECT_NO_THROW({ bridge.setOnAudioFrameCallback("alice", From c8c1a34a49a7626de449c4d1698c6eb11009f288 Mon Sep 17 00:00:00 2001 From: Stephen DeRosa Date: Tue, 10 Mar 2026 13:09:59 -0600 Subject: [PATCH 05/13] livekit_bridge -> session_manager --- CMakeLists.txt | 18 ++- docker/Dockerfile | 1 - examples/CMakeLists.txt | 144 +++++++----------- .../{ => base_sdk}/logging_levels/README.md | 0 .../logging_levels/basic_usage.cpp | 0 .../logging_levels/custom_sinks.cpp | 4 +- .../simple_data_stream/main.cpp | 0 .../simple_joystick/json_utils.cpp | 0 .../simple_joystick/json_utils.h | 0 .../simple_joystick/receiver.cpp | 0 .../{ => base_sdk}/simple_joystick/sender.cpp | 0 .../{ => base_sdk}/simple_joystick/utils.cpp | 0 .../{ => base_sdk}/simple_joystick/utils.h | 0 .../simple_room/fallback_capture.cpp | 0 .../simple_room/fallback_capture.h | 0 examples/{ => base_sdk}/simple_room/main.cpp | 0 examples/{ => base_sdk}/simple_rpc/README.md | 0 examples/{ => base_sdk}/simple_rpc/main.cpp | 0 .../human.cpp | 18 +-- .../robot.cpp | 27 ++-- .../README.md | 16 +- .../caller.cpp | 30 ++-- .../receiver.cpp | 26 ++-- examples/{bridge_rpc => rpc}/README.md | 8 +- .../{bridge_rpc => rpc}/custom_caller.cpp | 21 ++- .../{bridge_rpc => rpc}/custom_receiver.cpp | 20 +-- .../session_manager}/managed_audio_track.h | 10 +- .../session_manager}/managed_video_track.h | 10 +- .../session_manager}/rpc_constants.h | 10 +- .../session_manager}/session_manager.h | 60 ++++---- src/session_manager/CMakeLists.txt | 61 -------- src/session_manager/README.md | 77 ++++------ .../{src => }/managed_audio_track.cpp | 6 +- .../{src => }/managed_video_track.cpp | 6 +- .../{src => }/rpc_controller.cpp | 14 +- .../{src => }/rpc_controller.h | 11 +- .../{src => }/session_manager.cpp | 48 +++--- ....cpp => session_manager_room_delegate.cpp} | 20 +-- ...gate.h => session_manager_room_delegate.h} | 12 +- .../session_manager}/CMakeLists.txt | 55 +++---- .../test_session_manager_rpc_roundtrip.cpp | 41 ++--- .../session_manager}/test_callback_key.cpp | 6 +- .../test_managed_audio_track.cpp | 6 +- .../test_managed_video_track.cpp | 6 +- .../session_manager}/test_rpc_controller.cpp | 12 +- .../session_manager}/test_session_manager.cpp | 114 +++++++------- 46 files changed, 401 insertions(+), 517 deletions(-) rename examples/{ => base_sdk}/logging_levels/README.md (100%) rename examples/{ => base_sdk}/logging_levels/basic_usage.cpp (100%) rename examples/{ => base_sdk}/logging_levels/custom_sinks.cpp (98%) rename examples/{ => base_sdk}/simple_data_stream/main.cpp (100%) rename examples/{ => base_sdk}/simple_joystick/json_utils.cpp (100%) rename examples/{ => base_sdk}/simple_joystick/json_utils.h (100%) rename examples/{ => base_sdk}/simple_joystick/receiver.cpp (100%) rename examples/{ => base_sdk}/simple_joystick/sender.cpp (100%) rename examples/{ => base_sdk}/simple_joystick/utils.cpp (100%) rename examples/{ => base_sdk}/simple_joystick/utils.h (100%) rename examples/{ => base_sdk}/simple_room/fallback_capture.cpp (100%) rename examples/{ => base_sdk}/simple_room/fallback_capture.h (100%) rename examples/{ => base_sdk}/simple_room/main.cpp (100%) rename examples/{ => base_sdk}/simple_rpc/README.md (100%) rename examples/{ => base_sdk}/simple_rpc/main.cpp (100%) rename examples/{bridge_human_robot => human_robot}/human.cpp (97%) rename examples/{bridge_human_robot => human_robot}/robot.cpp (96%) rename examples/{bridge_mute_unmute => mute_unmute}/README.md (86%) rename examples/{bridge_mute_unmute => mute_unmute}/caller.cpp (92%) rename examples/{bridge_mute_unmute => mute_unmute}/receiver.cpp (91%) rename examples/{bridge_rpc => rpc}/README.md (95%) rename examples/{bridge_rpc => rpc}/custom_caller.cpp (86%) rename examples/{bridge_rpc => rpc}/custom_receiver.cpp (86%) rename {src/session_manager/include/livekit_bridge => include/session_manager}/managed_audio_track.h (94%) rename {src/session_manager/include/livekit_bridge => include/session_manager}/managed_video_track.h (94%) rename {src/session_manager/include/livekit_bridge => include/session_manager}/rpc_constants.h (86%) rename {src/session_manager/include/livekit_bridge => include/session_manager}/session_manager.h (88%) delete mode 100644 src/session_manager/CMakeLists.txt rename src/session_manager/{src => }/managed_audio_track.cpp (97%) rename src/session_manager/{src => }/managed_video_track.cpp (97%) rename src/session_manager/{src => }/rpc_controller.cpp (91%) rename src/session_manager/{src => }/rpc_controller.h (96%) rename src/session_manager/{src => }/session_manager.cpp (94%) rename src/session_manager/{src/bridge_room_delegate.cpp => session_manager_room_delegate.cpp} (72%) rename src/session_manager/{src/bridge_room_delegate.h => session_manager_room_delegate.h} (83%) rename src/{session_manager/tests => tests/session_manager}/CMakeLists.txt (55%) rename src/{session_manager/tests => tests/session_manager}/integration/test_session_manager_rpc_roundtrip.cpp (87%) rename src/{session_manager/tests => tests/session_manager}/test_callback_key.cpp (97%) rename src/{session_manager/tests => tests/session_manager}/test_managed_audio_track.cpp (97%) rename src/{session_manager/tests => tests/session_manager}/test_managed_video_track.cpp (96%) rename src/{session_manager/tests => tests/session_manager}/test_rpc_controller.cpp (96%) rename src/{session_manager/tests => tests/session_manager}/test_session_manager.cpp (57%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 739bfc00..3de39910 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") option(LIVEKIT_BUILD_EXAMPLES "Build LiveKit examples" OFF) option(LIVEKIT_BUILD_TESTS "Build LiveKit tests" OFF) -option(LIVEKIT_BUILD_BRIDGE "Build LiveKit Bridge (simplified high-level API)" OFF) + # vcpkg is only used on Windows; Linux/macOS use system package managers if(WIN32) @@ -353,6 +353,13 @@ add_library(livekit SHARED src/remote_video_track.cpp src/video_utils.cpp src/video_utils.h + src/session_manager/session_manager.cpp + src/session_manager/managed_audio_track.cpp + src/session_manager/managed_video_track.cpp + src/session_manager/session_manager_room_delegate.cpp + src/session_manager/session_manager_room_delegate.h + src/session_manager/rpc_controller.cpp + src/session_manager/rpc_controller.h ) if(WIN32) set_target_properties(livekit PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) @@ -367,6 +374,7 @@ target_include_directories(livekit $ PRIVATE ${LIVEKIT_ROOT_DIR}/src + ${LIVEKIT_ROOT_DIR}/src/session_manager ${RUST_ROOT}/livekit-ffi/include ${PROTO_BINARY_DIR} ) @@ -679,19 +687,13 @@ install(FILES # ------------------------------------------------------------------------ -# Build the LiveKit C++ bridge before examples (human_robot depends on it) -add_subdirectory(src/session_manager) - -# ---- Examples ---- -# add_subdirectory(examples) - - if(LIVEKIT_BUILD_EXAMPLES) add_subdirectory(examples) endif() if(LIVEKIT_BUILD_TESTS) add_subdirectory(src/tests) + add_subdirectory(src/tests/session_manager) endif() add_custom_target(clean_generated diff --git a/docker/Dockerfile b/docker/Dockerfile index 0f42ff0b..6848ab3e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -104,7 +104,6 @@ WORKDIR /client-sdk-cpp RUN mkdir -p /client-sdk-cpp COPY src /client-sdk-cpp/src COPY include /client-sdk-cpp/include -COPY bridge /client-sdk-cpp/bridge COPY build.sh /client-sdk-cpp/build.sh COPY CMakePresets.json /client-sdk-cpp/CMakePresets.json COPY build.cmd /client-sdk-cpp/build.cmd diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 2eb68be1..6e250323 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -45,36 +45,26 @@ set(EXAMPLES_ALL SimpleDataStream LoggingLevelsBasicUsage LoggingLevelsCustomSinks - BridgeRobot - BridgeHuman - BridgeMuteCaller - BridgeMuteReceiver - BridgeRpcCaller - BridgeRpcReceiver -) - -# Bridge examples (need livekit_bridge DLL/shared lib in addition to livekit_ffi) -set(EXAMPLES_BRIDGE - BridgeRobot - BridgeHuman - BridgeMuteCaller - BridgeMuteReceiver - BridgeRpcCaller - BridgeRpcReceiver + HumanRobotRobot + HumanRobotHuman + MuteUnmuteCaller + MuteUnmuteReceiver + RpcCaller + RpcReceiver ) # Examples that use SDL3 (need SDL3 lib copied on Linux; SimpleRoom is handled separately above) set(EXAMPLES_NEED_SDL3 - BridgeRobot - BridgeHuman - BridgeMuteCaller - BridgeMuteReceiver + HumanRobotRobot + HumanRobotHuman + MuteUnmuteCaller + MuteUnmuteReceiver ) add_executable(SimpleRoom - simple_room/main.cpp - simple_room/fallback_capture.cpp - simple_room/fallback_capture.h + base_sdk/simple_room/main.cpp + base_sdk/simple_room/fallback_capture.cpp + base_sdk/simple_room/fallback_capture.h ${EXAMPLES_COMMON_DIR}/sdl_media.cpp ${EXAMPLES_COMMON_DIR}/sdl_media.h ${EXAMPLES_COMMON_DIR}/sdl_media_manager.cpp @@ -88,7 +78,7 @@ add_executable(SimpleRoom target_include_directories(SimpleRoom PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS} ${EXAMPLES_COMMON_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/simple_room + ${CMAKE_CURRENT_SOURCE_DIR}/base_sdk/simple_room ) target_link_libraries(SimpleRoom @@ -140,7 +130,7 @@ if(NOT nlohmann_json_FOUND) endif() add_executable(SimpleRpc - simple_rpc/main.cpp + base_sdk/simple_rpc/main.cpp ) target_include_directories(SimpleRpc PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS}) @@ -155,14 +145,14 @@ target_link_libraries(SimpleRpc # --- SimpleJoystick example (sender + receiver executables with shared json_utils) --- add_library(simple_joystick_json_utils STATIC - simple_joystick/json_utils.cpp - simple_joystick/json_utils.h - simple_joystick/utils.cpp - simple_joystick/utils.h + base_sdk/simple_joystick/json_utils.cpp + base_sdk/simple_joystick/json_utils.h + base_sdk/simple_joystick/utils.cpp + base_sdk/simple_joystick/utils.h ) target_include_directories(simple_joystick_json_utils PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/simple_joystick + ${CMAKE_CURRENT_SOURCE_DIR}/base_sdk/simple_joystick ) target_link_libraries(simple_joystick_json_utils @@ -171,7 +161,7 @@ target_link_libraries(simple_joystick_json_utils ) add_executable(SimpleJoystickReceiver - simple_joystick/receiver.cpp + base_sdk/simple_joystick/receiver.cpp ) target_include_directories(SimpleJoystickReceiver PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS}) @@ -184,7 +174,7 @@ target_link_libraries(SimpleJoystickReceiver ) add_executable(SimpleJoystickSender - simple_joystick/sender.cpp + base_sdk/simple_joystick/sender.cpp ) target_include_directories(SimpleJoystickSender PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS}) @@ -199,7 +189,7 @@ target_link_libraries(SimpleJoystickSender # --- LoggingLevelsBasicUsage example --- add_executable(LoggingLevelsBasicUsage - logging_levels/basic_usage.cpp + base_sdk/logging_levels/basic_usage.cpp ) target_include_directories(LoggingLevelsBasicUsage PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS}) @@ -213,7 +203,7 @@ target_link_libraries(LoggingLevelsBasicUsage # --- LoggingLevelsCustomSinks example --- add_executable(LoggingLevelsCustomSinks - logging_levels/custom_sinks.cpp + base_sdk/logging_levels/custom_sinks.cpp ) target_link_libraries(LoggingLevelsCustomSinks @@ -224,7 +214,7 @@ target_link_libraries(LoggingLevelsCustomSinks # --- SimpleDataStream example --- add_executable(SimpleDataStream - simple_data_stream/main.cpp + base_sdk/simple_data_stream/main.cpp ) target_include_directories(SimpleDataStream PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS}) @@ -243,67 +233,69 @@ add_custom_command( $/data ) -# --- bridge_human_robot examples (robot + human; use livekit_bridge and SDL3) --- +# --- human_robot examples (robot + human; use session_manager and SDL3) --- -add_executable(BridgeRobot - bridge_human_robot/robot.cpp +add_executable(HumanRobotRobot + human_robot/robot.cpp ${EXAMPLES_COMMON_DIR}/sdl_media.cpp ${EXAMPLES_COMMON_DIR}/sdl_media.h ) -target_include_directories(BridgeRobot PRIVATE +target_include_directories(HumanRobotRobot PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS} ${EXAMPLES_COMMON_DIR} ) -target_link_libraries(BridgeRobot PRIVATE livekit_bridge spdlog::spdlog SDL3::SDL3) +target_link_libraries(HumanRobotRobot PRIVATE livekit spdlog::spdlog SDL3::SDL3) -add_executable(BridgeHuman - bridge_human_robot/human.cpp +add_executable(HumanRobotHuman + human_robot/human.cpp ${EXAMPLES_COMMON_DIR}/sdl_media.cpp ${EXAMPLES_COMMON_DIR}/sdl_media.h ) -target_include_directories(BridgeHuman PRIVATE +target_include_directories(HumanRobotHuman PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS} ${EXAMPLES_COMMON_DIR} ) -target_link_libraries(BridgeHuman PRIVATE livekit_bridge spdlog::spdlog SDL3::SDL3) +target_link_libraries(HumanRobotHuman PRIVATE livekit spdlog::spdlog SDL3::SDL3) -# --- bridge_rpc examples (headless custom RPC caller + receiver) --- +# --- rpc examples (headless custom RPC caller + receiver) --- -add_executable(BridgeRpcCaller - bridge_rpc/custom_caller.cpp +add_executable(RpcCaller + rpc/custom_caller.cpp ) -target_link_libraries(BridgeRpcCaller PRIVATE livekit_bridge) +target_include_directories(RpcCaller PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS}) +target_link_libraries(RpcCaller PRIVATE livekit) -add_executable(BridgeRpcReceiver - bridge_rpc/custom_receiver.cpp +add_executable(RpcReceiver + rpc/custom_receiver.cpp ) -target_link_libraries(BridgeRpcReceiver PRIVATE livekit_bridge) +target_include_directories(RpcReceiver PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS}) +target_link_libraries(RpcReceiver PRIVATE livekit) -# --- bridge_mute_unmute examples (caller uses SDL3 for A/V playback; receiver is headless) --- +# --- mute_unmute examples (caller uses SDL3 for A/V playback; receiver is headless) --- -add_executable(BridgeMuteCaller - bridge_mute_unmute/caller.cpp +add_executable(MuteUnmuteCaller + mute_unmute/caller.cpp ${EXAMPLES_COMMON_DIR}/sdl_media.cpp ${EXAMPLES_COMMON_DIR}/sdl_media.h ) -target_include_directories(BridgeMuteCaller PRIVATE +target_include_directories(MuteUnmuteCaller PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS} ${EXAMPLES_COMMON_DIR} ) -target_link_libraries(BridgeMuteCaller PRIVATE livekit_bridge spdlog::spdlog SDL3::SDL3) +target_link_libraries(MuteUnmuteCaller PRIVATE livekit spdlog::spdlog SDL3::SDL3) -add_executable(BridgeMuteReceiver - bridge_mute_unmute/receiver.cpp +add_executable(MuteUnmuteReceiver + mute_unmute/receiver.cpp ${EXAMPLES_COMMON_DIR}/sdl_media.cpp ${EXAMPLES_COMMON_DIR}/sdl_media.h ) -target_include_directories(BridgeMuteReceiver PRIVATE +target_include_directories(MuteUnmuteReceiver PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS} ${EXAMPLES_COMMON_DIR} ) -target_link_libraries(BridgeMuteReceiver PRIVATE livekit_bridge spdlog::spdlog SDL3::SDL3) +ßtarget_link_libraries(MuteUnmuteReceiver PRIVATE livekit spdlog::spdlog SDL3::SDL3) -# Copy SDL3 shared library to bridge example output directories +# Copy SDL3 shared library to examples output directories if(UNIX AND NOT APPLE) foreach(_target ${EXAMPLES_NEED_SDL3}) add_custom_command(TARGET ${_target} POST_BUILD @@ -346,19 +338,6 @@ if(WIN32) add_dependencies(${EXAMPLE} copy_ffi_dll_to_bin) endforeach() - # Bridge examples also need livekit_bridge.dll (single copy to bin/) - set(BRIDGE_DLL "livekit_bridge.dll") - add_custom_command(OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${BRIDGE_DLL} - COMMAND ${CMAKE_COMMAND} -E copy_if_different - $ - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${BRIDGE_DLL} - DEPENDS livekit_bridge - COMMENT "Copying livekit_bridge DLL to examples output directory" - ) - add_custom_target(copy_bridge_dll_to_bin ALL DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${BRIDGE_DLL}) - foreach(EXAMPLE ${EXAMPLES_BRIDGE}) - add_dependencies(${EXAMPLE} copy_bridge_dll_to_bin) - endforeach() endif() # Linux/macOS: Copy shared library to examples output directory (single copy to avoid parallel POST_BUILD races) @@ -382,21 +361,4 @@ if(UNIX) add_dependencies(${EXAMPLE} copy_ffi_to_bin) endforeach() - # Bridge examples also need livekit_bridge shared library (single copy to bin/) - if(APPLE) - set(BRIDGE_SHARED_LIB "liblivekit_bridge.dylib") - else() - set(BRIDGE_SHARED_LIB "liblivekit_bridge.so") - endif() - add_custom_command(OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${BRIDGE_SHARED_LIB} - COMMAND ${CMAKE_COMMAND} -E copy_if_different - $ - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${BRIDGE_SHARED_LIB} - DEPENDS livekit_bridge - COMMENT "Copying livekit_bridge to examples output directory" - ) - add_custom_target(copy_bridge_to_bin ALL DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${BRIDGE_SHARED_LIB}) - foreach(EXAMPLE ${EXAMPLES_BRIDGE}) - add_dependencies(${EXAMPLE} copy_bridge_to_bin) - endforeach() endif() \ No newline at end of file diff --git a/examples/logging_levels/README.md b/examples/base_sdk/logging_levels/README.md similarity index 100% rename from examples/logging_levels/README.md rename to examples/base_sdk/logging_levels/README.md diff --git a/examples/logging_levels/basic_usage.cpp b/examples/base_sdk/logging_levels/basic_usage.cpp similarity index 100% rename from examples/logging_levels/basic_usage.cpp rename to examples/base_sdk/logging_levels/basic_usage.cpp diff --git a/examples/logging_levels/custom_sinks.cpp b/examples/base_sdk/logging_levels/custom_sinks.cpp similarity index 98% rename from examples/logging_levels/custom_sinks.cpp rename to examples/base_sdk/logging_levels/custom_sinks.cpp index 40ddcb4b..f1bc46e3 100644 --- a/examples/logging_levels/custom_sinks.cpp +++ b/examples/base_sdk/logging_levels/custom_sinks.cpp @@ -225,7 +225,7 @@ void runJsonSinkDemo() { void runRos2SinkDemo() { std::cout << "\n=== ROS2 bridge sink (stubbed) ===\n\n"; - const std::string node_name = "livekit_bridge_node"; + const std::string node_name = "session_manager_node"; livekit::LogCallback ros2Sink = [&node_name](livekit::LogLevel level, const std::string &logger_name, @@ -251,7 +251,7 @@ void runRos2SinkDemo() { break; } - // Mimic: [INFO] [1719500000.123] [livekit_bridge_node]: [livekit] msg + // Mimic: [INFO] [1719500000.123] [session_manager_node]: [livekit] msg auto epoch_s = std::chrono::duration( std::chrono::system_clock::now().time_since_epoch()) .count(); diff --git a/examples/simple_data_stream/main.cpp b/examples/base_sdk/simple_data_stream/main.cpp similarity index 100% rename from examples/simple_data_stream/main.cpp rename to examples/base_sdk/simple_data_stream/main.cpp diff --git a/examples/simple_joystick/json_utils.cpp b/examples/base_sdk/simple_joystick/json_utils.cpp similarity index 100% rename from examples/simple_joystick/json_utils.cpp rename to examples/base_sdk/simple_joystick/json_utils.cpp diff --git a/examples/simple_joystick/json_utils.h b/examples/base_sdk/simple_joystick/json_utils.h similarity index 100% rename from examples/simple_joystick/json_utils.h rename to examples/base_sdk/simple_joystick/json_utils.h diff --git a/examples/simple_joystick/receiver.cpp b/examples/base_sdk/simple_joystick/receiver.cpp similarity index 100% rename from examples/simple_joystick/receiver.cpp rename to examples/base_sdk/simple_joystick/receiver.cpp diff --git a/examples/simple_joystick/sender.cpp b/examples/base_sdk/simple_joystick/sender.cpp similarity index 100% rename from examples/simple_joystick/sender.cpp rename to examples/base_sdk/simple_joystick/sender.cpp diff --git a/examples/simple_joystick/utils.cpp b/examples/base_sdk/simple_joystick/utils.cpp similarity index 100% rename from examples/simple_joystick/utils.cpp rename to examples/base_sdk/simple_joystick/utils.cpp diff --git a/examples/simple_joystick/utils.h b/examples/base_sdk/simple_joystick/utils.h similarity index 100% rename from examples/simple_joystick/utils.h rename to examples/base_sdk/simple_joystick/utils.h diff --git a/examples/simple_room/fallback_capture.cpp b/examples/base_sdk/simple_room/fallback_capture.cpp similarity index 100% rename from examples/simple_room/fallback_capture.cpp rename to examples/base_sdk/simple_room/fallback_capture.cpp diff --git a/examples/simple_room/fallback_capture.h b/examples/base_sdk/simple_room/fallback_capture.h similarity index 100% rename from examples/simple_room/fallback_capture.h rename to examples/base_sdk/simple_room/fallback_capture.h diff --git a/examples/simple_room/main.cpp b/examples/base_sdk/simple_room/main.cpp similarity index 100% rename from examples/simple_room/main.cpp rename to examples/base_sdk/simple_room/main.cpp diff --git a/examples/simple_rpc/README.md b/examples/base_sdk/simple_rpc/README.md similarity index 100% rename from examples/simple_rpc/README.md rename to examples/base_sdk/simple_rpc/README.md diff --git a/examples/simple_rpc/main.cpp b/examples/base_sdk/simple_rpc/main.cpp similarity index 100% rename from examples/simple_rpc/main.cpp rename to examples/base_sdk/simple_rpc/main.cpp diff --git a/examples/bridge_human_robot/human.cpp b/examples/human_robot/human.cpp similarity index 97% rename from examples/bridge_human_robot/human.cpp rename to examples/human_robot/human.cpp index 23e95e81..85618568 100644 --- a/examples/bridge_human_robot/human.cpp +++ b/examples/human_robot/human.cpp @@ -46,9 +46,9 @@ #include "livekit/audio_frame.h" #include "livekit/track.h" #include "livekit/video_frame.h" -#include "livekit_bridge/session_manager.h" #include "lk_log.h" #include "sdl_media.h" +#include "session_manager/session_manager.h" #include @@ -84,7 +84,7 @@ struct LatestVideoFrame { static LatestVideoFrame g_latest_video; /// Store a video frame for the main loop to render. -/// Called from bridge callbacks when their source is the active selection. +/// Called from session manager callbacks when their source is selected. static void renderFrame(const livekit::VideoFrame &frame) { const std::uint8_t *src = frame.data(); const std::size_t size = frame.dataSize(); @@ -192,11 +192,11 @@ int main(int argc, char *argv[]) { std::mutex speaker_mutex; // ----- Connect to LiveKit ----- - livekit_bridge::SessionManager bridge; + session_manager::SessionManager sm; std::cout << "[human] Connecting to " << url << " ...\n"; livekit::RoomOptions options; options.auto_subscribe = true; - if (!bridge.connect(url, token, options)) { + if (!sm.connect(url, token, options)) { LK_LOG_ERROR("[human] Failed to connect."); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); @@ -231,7 +231,7 @@ int main(int argc, char *argv[]) { // ----- set audio callbacks ----- // Real mic (SOURCE_MICROPHONE) -- plays only when 'w' is selected - bridge.setOnAudioFrameCallback( + sm.setOnAudioFrameCallback( "robot", livekit::TrackSource::SOURCE_MICROPHONE, [playAudio, no_audio](const livekit::AudioFrame &frame) { g_audio_frames.fetch_add(1, std::memory_order_relaxed); @@ -243,7 +243,7 @@ int main(int argc, char *argv[]) { // Sim audio / siren (SOURCE_SCREENSHARE_AUDIO) -- plays only when 's' is // selected - bridge.setOnAudioFrameCallback( + sm.setOnAudioFrameCallback( "robot", livekit::TrackSource::SOURCE_SCREENSHARE_AUDIO, [playAudio, no_audio](const livekit::AudioFrame &frame) { g_audio_frames.fetch_add(1, std::memory_order_relaxed); @@ -255,7 +255,7 @@ int main(int argc, char *argv[]) { // ----- set video callbacks ----- // Webcam feed (SOURCE_CAMERA) -- renders only when 'w' is selected - bridge.setOnVideoFrameCallback( + sm.setOnVideoFrameCallback( "robot", livekit::TrackSource::SOURCE_CAMERA, [](const livekit::VideoFrame &frame, std::int64_t /*timestamp_us*/) { g_video_frames.fetch_add(1, std::memory_order_relaxed); @@ -266,7 +266,7 @@ int main(int argc, char *argv[]) { }); // Sim frame feed (SOURCE_SCREENSHARE) -- renders only when 's' is selected - bridge.setOnVideoFrameCallback( + sm.setOnVideoFrameCallback( "robot", livekit::TrackSource::SOURCE_SCREENSHARE, [](const livekit::VideoFrame &frame, std::int64_t /*timestamp_us*/) { g_video_frames.fetch_add(1, std::memory_order_relaxed); @@ -404,7 +404,7 @@ int main(int argc, char *argv[]) { if (input_thread.joinable()) input_thread.detach(); - bridge.disconnect(); + sm.disconnect(); { std::lock_guard lock(speaker_mutex); diff --git a/examples/bridge_human_robot/robot.cpp b/examples/human_robot/robot.cpp similarity index 96% rename from examples/bridge_human_robot/robot.cpp rename to examples/human_robot/robot.cpp index 9a2e52ee..388dd8e7 100644 --- a/examples/bridge_human_robot/robot.cpp +++ b/examples/human_robot/robot.cpp @@ -36,9 +36,9 @@ #include "livekit/audio_frame.h" #include "livekit/track.h" #include "livekit/video_frame.h" -#include "livekit_bridge/session_manager.h" #include "lk_log.h" #include "sdl_media.h" +#include "session_manager/session_manager.h" #include @@ -357,11 +357,11 @@ int main(int argc, char *argv[]) { } // ----- Connect to LiveKit ----- - livekit_bridge::SessionManager bridge; + session_manager::SessionManager sm; LK_LOG_INFO("[robot] Connecting to {} ...", url); livekit::RoomOptions options; options.auto_subscribe = true; - if (!bridge.connect(url, token, options)) { + if (!sm.connect(url, token, options)) { LK_LOG_ERROR("[robot] Failed to connect."); SDL_Quit(); return 1; @@ -377,19 +377,18 @@ int main(int argc, char *argv[]) { constexpr int kSimWidth = 480; constexpr int kSimHeight = 320; - std::shared_ptr mic; + std::shared_ptr mic; if (use_mic) { - mic = bridge.createAudioTrack("robot-mic", kSampleRate, kChannels, - livekit::TrackSource::SOURCE_MICROPHONE); + mic = sm.createAudioTrack("robot-mic", kSampleRate, kChannels, + livekit::TrackSource::SOURCE_MICROPHONE); } auto sim_audio = - bridge.createAudioTrack("robot-sim-audio", kSampleRate, kChannels, - livekit::TrackSource::SOURCE_SCREENSHARE_AUDIO); - auto cam = bridge.createVideoTrack("robot-cam", kWidth, kHeight, - livekit::TrackSource::SOURCE_CAMERA); - auto sim_cam = - bridge.createVideoTrack("robot-sim-frame", kSimWidth, kSimHeight, - livekit::TrackSource::SOURCE_SCREENSHARE); + sm.createAudioTrack("robot-sim-audio", kSampleRate, kChannels, + livekit::TrackSource::SOURCE_SCREENSHARE_AUDIO); + auto cam = sm.createVideoTrack("robot-cam", kWidth, kHeight, + livekit::TrackSource::SOURCE_CAMERA); + auto sim_cam = sm.createVideoTrack("robot-sim-frame", kSimWidth, kSimHeight, + livekit::TrackSource::SOURCE_SCREENSHARE); LK_LOG_INFO("[robot] Publishing {} sim audio ({} Hz, {} ch), cam + sim frame " "({}x{} / {}x{}).", use_mic ? "mic + " : "(no mic) ", kSampleRate, kChannels, kWidth, @@ -654,7 +653,7 @@ int main(int argc, char *argv[]) { sim_audio.reset(); cam.reset(); sim_cam.reset(); - bridge.disconnect(); + sm.disconnect(); SDL_Quit(); LK_LOG_INFO("[robot] Done."); diff --git a/examples/bridge_mute_unmute/README.md b/examples/mute_unmute/README.md similarity index 86% rename from examples/bridge_mute_unmute/README.md rename to examples/mute_unmute/README.md index 60051e38..94bec9c9 100644 --- a/examples/bridge_mute_unmute/README.md +++ b/examples/mute_unmute/README.md @@ -1,4 +1,4 @@ -# Bridge Mute/Unmute Example +# Mute/Unmute Example Demonstrates remote track control using the `SessionManager` built-in track-control RPC. A **receiver** publishes audio and video tracks, and a @@ -8,8 +8,8 @@ track-control RPC. A **receiver** publishes audio and video tracks, and a | Executable | Role | |-----------------------|------| -| **BridgeMuteReceiver** | Publishes an audio track (`"mic"`) and a video track (`"cam"`) using SDL3 hardware capture when available, falling back to silence and solid-color frames otherwise. The bridge automatically registers a built-in `lk.bridge.track-control` RPC handler on connect. | -| **BridgeMuteCaller** | Subscribes to the receiver's mic and cam tracks, renders them via SDL3 (speaker + window), and periodically calls `requestRemoteTrackMute` / `requestRemoteTrackUnmute` to toggle both tracks. | +| **MuteUnmuteReceiver** | Publishes an audio track (`"mic"`) and a video track (`"cam"`) using SDL3 hardware capture when available, falling back to silence and solid-color frames otherwise. The SessionManager automatically registers a built-in `lk.session_manager.track-control` RPC handler on connect. | +| **MuteUnmuteCaller** | Subscribes to the receiver's mic and cam tracks, renders them via SDL3 (speaker + window), and periodically calls `requestRemoteTrackMute` / `requestRemoteTrackUnmute` to toggle both tracks. | When the caller mutes a track, the receiver's `LocalAudioTrack::mute()` or `LocalVideoTrack::mute()` is invoked via RPC, which signals the LiveKit @@ -30,17 +30,17 @@ Start the receiver first, then the caller: ```bash # Terminal 1 -LIVEKIT_URL=wss://... LIVEKIT_TOKEN= ./build-release/bin/BridgeMuteReceiver +LIVEKIT_URL=wss://... LIVEKIT_TOKEN= ./build-release/bin/MuteUnmuteReceiver # Terminal 2 -LIVEKIT_URL=wss://... LIVEKIT_TOKEN= ./build-release/bin/BridgeMuteCaller +LIVEKIT_URL=wss://... LIVEKIT_TOKEN= ./build-release/bin/MuteUnmuteCaller ``` The caller also accepts an optional third argument for the receiver's identity (defaults to `"receiver"`): ```bash -./build-release/bin/BridgeMuteCaller wss://... my-receiver +./build-release/bin/MuteUnmuteCaller wss://... my-receiver ``` ## Sample output @@ -48,7 +48,7 @@ identity (defaults to `"receiver"`): ### Receiver ``` -./build-release/bin/BridgeMuteReceiver +./build-release/bin/MuteUnmuteReceiver [receiver] Connecting to wss://sderosasandbox-15g80zq7.livekit.cloud ... [receiver] Connected. cs.state() is 1 connection_state_ is 1 @@ -66,7 +66,7 @@ cs.state() is 1 connection_state_ is 1 ### Caller ``` -./build-release/bin/BridgeMuteCaller +./build-release/bin/MuteUnmuteCaller [caller] Connecting to wss://sderosasandbox-15g80zq7.livekit.cloud ... cs.state() is 1 connection_state_ is 1 [caller] Connected. diff --git a/examples/bridge_mute_unmute/caller.cpp b/examples/mute_unmute/caller.cpp similarity index 92% rename from examples/bridge_mute_unmute/caller.cpp rename to examples/mute_unmute/caller.cpp index c7d3b5b2..be1bc1ba 100644 --- a/examples/bridge_mute_unmute/caller.cpp +++ b/examples/mute_unmute/caller.cpp @@ -15,7 +15,7 @@ */ /* - * Caller (controller) for the bridge mute/unmute example. + * Caller (controller) for the SessionManager mute/unmute example. * * Connects to the same room as the receiver, subscribes to the receiver's * "mic" and "cam" tracks, and renders them via SDL3 (speaker + window). @@ -23,8 +23,8 @@ * so you can see and hear the tracks go silent and come back. * * Usage: - * BridgeMuteCaller [receiver-identity] - * LIVEKIT_URL=... LIVEKIT_TOKEN=... BridgeMuteCaller [receiver-identity] + * MuteUnmuteCaller [receiver-identity] + * LIVEKIT_URL=... LIVEKIT_TOKEN=... MuteUnmuteCaller [receiver-identity] * * The token must grant a different identity (e.g. "caller"). Generate with: * lk token create --api-key --api-secret \ @@ -35,8 +35,8 @@ #include "livekit/rpc_error.h" #include "livekit/track.h" #include "livekit/video_frame.h" -#include "livekit_bridge/session_manager.h" #include "sdl_media.h" +#include "session_manager/session_manager.h" #include @@ -103,8 +103,8 @@ int main(int argc, char *argv[]) { } if (url.empty() || token.empty()) { std::cerr - << "Usage: BridgeMuteCaller [receiver-identity]\n" - << " or: LIVEKIT_URL=... LIVEKIT_TOKEN=... BridgeMuteCaller " + << "Usage: MuteUnmuteCaller [receiver-identity]\n" + << " or: LIVEKIT_URL=... LIVEKIT_TOKEN=... MuteUnmuteCaller " "[receiver-identity]\n" << "Default receiver-identity: \"receiver\"\n"; return 1; @@ -146,13 +146,13 @@ int main(int argc, char *argv[]) { std::mutex speaker_mutex; // ----- Connect to LiveKit ----- - livekit_bridge::SessionManager bridge; + session_manager::SessionManager sm; std::cout << "[caller] Connecting to " << url << " ...\n"; livekit::RoomOptions options; options.auto_subscribe = true; - if (!bridge.connect(url, token, options)) { + if (!sm.connect(url, token, options)) { std::cerr << "[caller] Failed to connect.\n"; SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); @@ -164,7 +164,7 @@ int main(int argc, char *argv[]) { << "\"\n"; // ----- Subscribe to receiver's audio ----- - bridge.setOnAudioFrameCallback( + sm.setOnAudioFrameCallback( receiver_identity, livekit::TrackSource::SOURCE_MICROPHONE, [&speaker, &speaker_mutex](const livekit::AudioFrame &frame) { const auto &samples = frame.data(); @@ -187,7 +187,7 @@ int main(int argc, char *argv[]) { }); // ----- Subscribe to receiver's video ----- - bridge.setOnVideoFrameCallback( + sm.setOnVideoFrameCallback( receiver_identity, livekit::TrackSource::SOURCE_CAMERA, [](const livekit::VideoFrame &frame, std::int64_t /*timestamp_us*/) { storeFrame(frame); @@ -214,10 +214,10 @@ int main(int argc, char *argv[]) { // Toggle audio track "mic" try { if (currently_muted) { - bridge.requestRemoteTrackUnmute(receiver_identity, "mic"); + sm.requestRemoteTrackUnmute(receiver_identity, "mic"); std::cout << "[caller] mic: unmuted OK\n"; } else { - bridge.requestRemoteTrackMute(receiver_identity, "mic"); + sm.requestRemoteTrackMute(receiver_identity, "mic"); std::cout << "[caller] mic: muted OK\n"; } } catch (const livekit::RpcError &e) { @@ -230,10 +230,10 @@ int main(int argc, char *argv[]) { // Toggle video track "cam" try { if (currently_muted) { - bridge.requestRemoteTrackUnmute(receiver_identity, "cam"); + sm.requestRemoteTrackUnmute(receiver_identity, "cam"); std::cout << "[caller] cam: unmuted OK\n"; } else { - bridge.requestRemoteTrackMute(receiver_identity, "cam"); + sm.requestRemoteTrackMute(receiver_identity, "cam"); std::cout << "[caller] cam: muted OK\n"; } } catch (const livekit::RpcError &e) { @@ -319,7 +319,7 @@ int main(int argc, char *argv[]) { if (toggle_thread.joinable()) toggle_thread.join(); - bridge.disconnect(); + sm.disconnect(); { std::lock_guard lock(speaker_mutex); diff --git a/examples/bridge_mute_unmute/receiver.cpp b/examples/mute_unmute/receiver.cpp similarity index 91% rename from examples/bridge_mute_unmute/receiver.cpp rename to examples/mute_unmute/receiver.cpp index c642809a..5f4f9090 100644 --- a/examples/bridge_mute_unmute/receiver.cpp +++ b/examples/mute_unmute/receiver.cpp @@ -15,7 +15,7 @@ */ /* - * Receiver (publisher) for the bridge mute/unmute example. + * Receiver (publisher) for the mute/unmute example. * * Publishes an audio track ("mic") and a video track ("cam"), then enables * remote track control so that a remote caller can mute/unmute them via RPC. @@ -25,8 +25,8 @@ * frames (video). * * Usage: - * BridgeMuteReceiver - * LIVEKIT_URL=... LIVEKIT_TOKEN=... BridgeMuteReceiver + * MuteUnmuteReceiver + * LIVEKIT_URL=... LIVEKIT_TOKEN=... MuteUnmuteReceiver * * The token must grant identity "receiver". Generate one with: * lk token create --api-key --api-secret \ @@ -34,8 +34,8 @@ */ #include "livekit/track.h" -#include "livekit_bridge/session_manager.h" #include "sdl_media.h" +#include "session_manager/session_manager.h" #include @@ -69,8 +69,8 @@ int main(int argc, char *argv[]) { } if (url.empty() || token.empty()) { std::cerr - << "Usage: BridgeMuteReceiver \n" - << " or: LIVEKIT_URL=... LIVEKIT_TOKEN=... BridgeMuteReceiver\n"; + << "Usage: MuteUnmuteReceiver \n" + << " or: LIVEKIT_URL=... LIVEKIT_TOKEN=... MuteUnmuteReceiver\n"; return 1; } @@ -83,13 +83,13 @@ int main(int argc, char *argv[]) { } // ----- Connect to LiveKit ----- - livekit_bridge::SessionManager bridge; + session_manager::SessionManager sm; std::cout << "[receiver] Connecting to " << url << " ...\n"; livekit::RoomOptions options; options.auto_subscribe = true; - if (!bridge.connect(url, token, options)) { + if (!sm.connect(url, token, options)) { std::cerr << "[receiver] Failed to connect.\n"; SDL_Quit(); return 1; @@ -101,10 +101,10 @@ int main(int argc, char *argv[]) { constexpr int kWidth = 1280; constexpr int kHeight = 720; - auto mic = bridge.createAudioTrack("mic", kSampleRate, kChannels, - livekit::TrackSource::SOURCE_MICROPHONE); - auto cam = bridge.createVideoTrack("cam", kWidth, kHeight, - livekit::TrackSource::SOURCE_CAMERA); + auto mic = sm.createAudioTrack("mic", kSampleRate, kChannels, + livekit::TrackSource::SOURCE_MICROPHONE); + auto cam = sm.createVideoTrack("cam", kWidth, kHeight, + livekit::TrackSource::SOURCE_CAMERA); std::cout << "[receiver] Published audio track \"mic\" and video track " "\"cam\".\n"; @@ -258,7 +258,7 @@ int main(int argc, char *argv[]) { mic.reset(); cam.reset(); - bridge.disconnect(); + sm.disconnect(); SDL_Quit(); std::cout << "[receiver] Done.\n"; diff --git a/examples/bridge_rpc/README.md b/examples/rpc/README.md similarity index 95% rename from examples/bridge_rpc/README.md rename to examples/rpc/README.md index 19025b18..8178404f 100644 --- a/examples/bridge_rpc/README.md +++ b/examples/rpc/README.md @@ -1,9 +1,9 @@ -# Bridge RPC Example +# RPC Example A minimal example of custom user-registered RPC methods using the `SessionManager` high-level API. -Two headless executables — **BridgeRpcReceiver** and **BridgeRpcCaller** — +Two headless executables — **RpcReceiver** and **RpcCaller** — connect to the same LiveKit room. The receiver registers a `"print"` RPC method that logs the caller's message and sleeps for a variable duration before responding. The caller sends a numbered message every ~1 second and @@ -37,10 +37,10 @@ Start the receiver first, then the caller: ```bash # Terminal 1 -LIVEKIT_URL=wss://... LIVEKIT_TOKEN= ./build-release/bin/BridgeRpcReceiver +LIVEKIT_URL=wss://... LIVEKIT_TOKEN= ./build-release/bin/RpcReceiver # Terminal 2 -LIVEKIT_URL=wss://... LIVEKIT_TOKEN= ./build-release/bin/BridgeRpcCaller +LIVEKIT_URL=wss://... LIVEKIT_TOKEN= ./build-release/bin/RpcCaller ``` ## Sample output diff --git a/examples/bridge_rpc/custom_caller.cpp b/examples/rpc/custom_caller.cpp similarity index 86% rename from examples/bridge_rpc/custom_caller.cpp rename to examples/rpc/custom_caller.cpp index f5f37b20..bae614be 100644 --- a/examples/bridge_rpc/custom_caller.cpp +++ b/examples/rpc/custom_caller.cpp @@ -15,7 +15,7 @@ */ /* - * Caller for the bridge_rpc example. + * Caller for the rpc example. * * Connects to a LiveKit room as "caller" and sends a string to the * receiver's custom "print" RPC method every second. The receiver @@ -23,15 +23,15 @@ * calls will take noticeably longer to return. * * Usage: - * BridgeRpcCaller - * LIVEKIT_URL=... LIVEKIT_TOKEN=... BridgeRpcCaller + * RpcCaller + * LIVEKIT_URL=... LIVEKIT_TOKEN=... RpcCaller * * Generate a token with: * lk token create --join --room --identity caller --valid-for 24h */ #include "livekit/rpc_error.h" -#include "livekit_bridge/session_manager.h" +#include "session_manager/session_manager.h" #include #include @@ -58,18 +58,18 @@ int main(int argc, char *argv[]) { token = e; } if (url.empty() || token.empty()) { - std::cerr << "Usage: BridgeRpcCaller \n" - << " or: LIVEKIT_URL=... LIVEKIT_TOKEN=... BridgeRpcCaller\n"; + std::cerr << "Usage: RpcCaller \n" + << " or: LIVEKIT_URL=... LIVEKIT_TOKEN=... RpcCaller\n"; return 1; } std::signal(SIGINT, handleSignal); - livekit_bridge::SessionManager bridge; + session_manager::SessionManager sm; std::cout << "[caller] Connecting to " << url << " ...\n"; livekit::RoomOptions options; - if (!bridge.connect(url, token, options)) { + if (!sm.connect(url, token, options)) { std::cerr << "[caller] Failed to connect.\n"; return 1; } @@ -89,8 +89,7 @@ int main(int argc, char *argv[]) { auto t0 = std::chrono::steady_clock::now(); try { - auto response = - bridge.performRpc("receiver", "print", message, std::nullopt); + auto response = sm.performRpc("receiver", "print", message, std::nullopt); auto elapsed = std::chrono::duration_cast( std::chrono::steady_clock::now() - t0) .count(); @@ -116,7 +115,7 @@ int main(int argc, char *argv[]) { } std::cout << "[caller] Shutting down...\n"; - bridge.disconnect(); + sm.disconnect(); std::cout << "[caller] Done.\n"; return 0; } diff --git a/examples/bridge_rpc/custom_receiver.cpp b/examples/rpc/custom_receiver.cpp similarity index 86% rename from examples/bridge_rpc/custom_receiver.cpp rename to examples/rpc/custom_receiver.cpp index f924fdd8..8c3a1814 100644 --- a/examples/bridge_rpc/custom_receiver.cpp +++ b/examples/rpc/custom_receiver.cpp @@ -15,20 +15,20 @@ */ /* - * Receiver for the bridge_rpc example. + * Receiver for the rpc example. * * Connects to a LiveKit room as "receiver", registers a custom RPC method * called "print", and prints whatever string the caller sends. * * Usage: - * BridgeRpcReceiver - * LIVEKIT_URL=... LIVEKIT_TOKEN=... BridgeRpcReceiver + * RpcReceiver + * LIVEKIT_URL=... LIVEKIT_TOKEN=... RpcReceiver * * Generate a token with: * lk token create --join --room --identity receiver --valid-for 24h */ -#include "livekit_bridge/session_manager.h" +#include "session_manager/session_manager.h" #include #include @@ -55,18 +55,18 @@ int main(int argc, char *argv[]) { token = e; } if (url.empty() || token.empty()) { - std::cerr << "Usage: BridgeRpcReceiver \n" - << " or: LIVEKIT_URL=... LIVEKIT_TOKEN=... BridgeRpcReceiver\n"; + std::cerr << "Usage: RpcReceiver \n" + << " or: LIVEKIT_URL=... LIVEKIT_TOKEN=... RpcReceiver\n"; return 1; } std::signal(SIGINT, handleSignal); - livekit_bridge::SessionManager bridge; + session_manager::SessionManager sm; std::cout << "[receiver] Connecting to " << url << " ...\n"; livekit::RoomOptions options; - if (!bridge.connect(url, token, options)) { + if (!sm.connect(url, token, options)) { std::cerr << "[receiver] Failed to connect.\n"; return 1; } @@ -74,7 +74,7 @@ int main(int argc, char *argv[]) { std::atomic call_count{0}; - bridge.registerRpcMethod( + sm.registerRpcMethod( "print", [&call_count](const livekit::RpcInvocationData &data) -> std::optional { @@ -107,7 +107,7 @@ int main(int argc, char *argv[]) { } std::cout << "[receiver] Shutting down...\n"; - bridge.disconnect(); + sm.disconnect(); std::cout << "[receiver] Done.\n"; return 0; } diff --git a/src/session_manager/include/livekit_bridge/managed_audio_track.h b/include/session_manager/managed_audio_track.h similarity index 94% rename from src/session_manager/include/livekit_bridge/managed_audio_track.h rename to include/session_manager/managed_audio_track.h index afed94ee..2432f426 100644 --- a/src/session_manager/include/livekit_bridge/managed_audio_track.h +++ b/include/session_manager/managed_audio_track.h @@ -32,7 +32,7 @@ class LocalTrackPublication; class LocalParticipant; } // namespace livekit -namespace livekit_bridge { +namespace session_manager { namespace test { class ManagedAudioTrackTest; @@ -45,9 +45,9 @@ class ManagedAudioTrackTest; * reference to every track it creates and will automatically release all * tracks when disconnect() is called. To unpublish a track mid-session, * call release() explicitly; dropping the shared_ptr alone is not - * sufficient because the bridge still holds a reference. + * sufficient because the SessionManager still holds a reference. * - * After release() (whether called explicitly or by the bridge on + * After release() (whether called explicitly or by the SessionManager on * disconnect), pushFrame() returns false and mute()/unmute() become * no-ops. The track object remains valid but inert. * @@ -56,7 +56,7 @@ class ManagedAudioTrackTest; * pushFrame() concurrently from multiple threads. * * Usage: - * auto mic = bridge.createAudioTrack("mic", 48000, 2, + * auto mic = session_manager.createAudioTrack("mic", 48000, 2, * livekit::TrackSource::SOURCE_MICROPHONE); * mic->pushFrame(pcm_data, samples_per_channel); * mic->mute(); @@ -144,4 +144,4 @@ class ManagedAudioTrack { livekit::LocalParticipant *participant_ = nullptr; // not owned }; -} // namespace livekit_bridge +} // namespace session_manager diff --git a/src/session_manager/include/livekit_bridge/managed_video_track.h b/include/session_manager/managed_video_track.h similarity index 94% rename from src/session_manager/include/livekit_bridge/managed_video_track.h rename to include/session_manager/managed_video_track.h index d3f21567..4a28b606 100644 --- a/src/session_manager/include/livekit_bridge/managed_video_track.h +++ b/include/session_manager/managed_video_track.h @@ -32,7 +32,7 @@ class LocalTrackPublication; class LocalParticipant; } // namespace livekit -namespace livekit_bridge { +namespace session_manager { namespace test { class ManagedVideoTrackTest; @@ -45,9 +45,9 @@ class ManagedVideoTrackTest; * reference to every track it creates and will automatically release all * tracks when disconnect() is called. To unpublish a track mid-session, * call release() explicitly; dropping the shared_ptr alone is not - * sufficient because the bridge still holds a reference. + * sufficient because the SessionManager still holds a reference. * - * After release() (whether called explicitly or by the bridge on + * After release() (whether called explicitly or by the SessionManager on * disconnect), pushFrame() returns false and mute()/unmute() become * no-ops. The track object remains valid but inert. * @@ -56,7 +56,7 @@ class ManagedVideoTrackTest; * pushFrame() concurrently from multiple threads. * * Usage: - * auto cam = bridge.createVideoTrack("cam", 1280, 720, + * auto cam = session_manager.createVideoTrack("cam", 1280, 720, * livekit::TrackSource::SOURCE_CAMERA); * cam->pushFrame(rgba_data, timestamp_us); * cam->mute(); @@ -142,4 +142,4 @@ class ManagedVideoTrack { livekit::LocalParticipant *participant_ = nullptr; // not owned }; -} // namespace livekit_bridge +} // namespace session_manager diff --git a/src/session_manager/include/livekit_bridge/rpc_constants.h b/include/session_manager/rpc_constants.h similarity index 86% rename from src/session_manager/include/livekit_bridge/rpc_constants.h rename to include/session_manager/rpc_constants.h index 4bd81093..6b5deaab 100644 --- a/src/session_manager/include/livekit_bridge/rpc_constants.h +++ b/include/session_manager/rpc_constants.h @@ -15,13 +15,13 @@ */ /// @file rpc_constants.h -/// @brief Constants for built-in bridge RPC methods. +/// @brief Constants for built-in SessionManager RPC methods. #pragma once #include -namespace livekit_bridge { +namespace session_manager { namespace rpc { /// Built-in RPC method name used by remote track control. @@ -33,8 +33,8 @@ namespace track_control { enum class Action { kActionMute, kActionUnmute }; -/// RPC method name registered by the bridge for remote track control. -constexpr const char *kMethod = "lk.bridge.track-control"; +/// RPC method name registered by the SessionManager for remote track control. +constexpr const char *kMethod = "lk.session_manager.track-control"; /// Payload action strings. constexpr const char *kActionMute = "mute"; @@ -60,4 +60,4 @@ inline std::string formatPayload(const char *action, } // namespace track_control } // namespace rpc -} // namespace livekit_bridge +} // namespace session_manager diff --git a/src/session_manager/include/livekit_bridge/session_manager.h b/include/session_manager/session_manager.h similarity index 88% rename from src/session_manager/include/livekit_bridge/session_manager.h rename to include/session_manager/session_manager.h index 39270966..f739c900 100644 --- a/src/session_manager/include/livekit_bridge/session_manager.h +++ b/include/session_manager/session_manager.h @@ -15,13 +15,13 @@ */ /// @file session_manager.h -/// @brief High-level bridge API for the LiveKit C++ SDK. +/// @brief High-level SessionManager API for the LiveKit C++ SDK. #pragma once -#include "livekit_bridge/managed_audio_track.h" -#include "livekit_bridge/managed_video_track.h" -#include "livekit_bridge/rpc_constants.h" +#include "session_manager/managed_audio_track.h" +#include "session_manager/managed_video_track.h" +#include "session_manager/rpc_constants.h" #include "livekit/local_participant.h" #include "livekit/room.h" @@ -46,9 +46,9 @@ class Track; enum class TrackSource; } // namespace livekit -namespace livekit_bridge { +namespace session_manager { -class BridgeRoomDelegate; +class SessionManagerRoomDelegate; class RpcController; namespace test { @@ -68,12 +68,12 @@ using VideoFrameCallback = std::function; /** - * High-level bridge to the LiveKit C++ SDK. + * High-level SessionManager to the LiveKit C++ SDK. * * Owns the full room lifecycle: initialize SDK, create Room, connect, * publish tracks, and manage incoming frame callbacks. * - * The bridge retains a shared_ptr to every track it creates. On + * The SessionManager retains a shared_ptr to every track it creates. On * disconnect(), all tracks are released (unpublished) before the room * is torn down, guaranteeing safe teardown order. To unpublish a track * mid-session, call release() on the track explicitly; dropping the @@ -131,7 +131,7 @@ class SessionManager { * succeeds or fails. auto_subscribe is enabled so that remote tracks * are subscribed automatically. * - * If the bridge is already connected, returns true immediately. + * If the SessionManager is already connected, returns true immediately. * If another thread is already in the process of connecting, returns * false without blocking. * @@ -152,7 +152,7 @@ class SessionManager { */ void disconnect(); - /// Whether the bridge is currently connected to a room. + /// Whether the SessionManager is currently connected to a room. bool isConnected() const; // --------------------------------------------------------------- @@ -162,12 +162,12 @@ class SessionManager { /** * Create and publish a local audio track. * - * The bridge retains a reference to the track internally. To unpublish - * mid-session, call release() on the returned track. All surviving + * The SessionManager retains a reference to the track internally. To + * unpublish mid-session, call release() on the returned track. All surviving * tracks are automatically released on disconnect(). * - * @pre The bridge must be connected (via connect()). Calling this on a - * disconnected bridge is a programming error. + * @pre The SessionManager must be connected (via connect()). Calling this on + * a disconnected SessionManager is a programming error. * * @param name Human-readable track name. * @param sample_rate Sample rate in Hz (e.g. 48000). @@ -177,7 +177,7 @@ class SessionManager { * publish multiple audio tracks from the same * participant that can be independently subscribed to. * @return Shared pointer to the published audio track handle (never null). - * @throws std::runtime_error if the bridge is not connected. + * @throws std::runtime_error if the SessionManager is not connected. */ std::shared_ptr createAudioTrack(const std::string &name, int sample_rate, int num_channels, @@ -186,12 +186,12 @@ class SessionManager { /** * Create and publish a local video track. * - * The bridge retains a reference to the track internally. To unpublish - * mid-session, call release() on the returned track. All surviving + * The SessionManager retains a reference to the track internally. To + * unpublish mid-session, call release() on the returned track. All surviving * tracks are automatically released on disconnect(). * - * @pre The bridge must be connected (via connect()). Calling this on a - * disconnected bridge is a programming error. + * @pre The SessionManager must be connected (via connect()). Calling this on + * a disconnected SessionManager is a programming error. * * @param name Human-readable track name. * @param width Video width in pixels. @@ -201,7 +201,7 @@ class SessionManager { * multiple video tracks from the same participant that * can be independently subscribed to. * @return Shared pointer to the published video track handle (never null). - * @throws std::runtime_error if the bridge is not connected. + * @throws std::runtime_error if the SessionManager is not connected. */ std::shared_ptr createVideoTrack(const std::string &name, int width, int height, @@ -285,7 +285,7 @@ class SessionManager { * @param response_timeout Optional timeout in seconds. If not set, * the server default (15 s) is used. * @return The response payload returned by the remote handler. nullptr if the - * RPC call fails, or the bridge is not connected. + * RPC call fails, or the SessionManager is not connected. */ std::optional performRpc(const std::string &destination_identity, const std::string &method, @@ -296,7 +296,7 @@ class SessionManager { * Register a handler for incoming RPC method invocations. * * When a remote participant calls the given @p method_name on this - * participant, the bridge invokes @p handler. The handler may return + * participant, the SessionManager invokes @p handler. The handler may return * an optional response payload or throw a @c livekit::RpcError to * signal failure to the caller. * @@ -353,7 +353,7 @@ class SessionManager { const std::string &track_name); private: - friend class BridgeRoomDelegate; + friend class SessionManagerRoomDelegate; friend class test::CallbackKeyTest; friend class test::SessionManagerTest; @@ -378,12 +378,12 @@ class SessionManager { bool is_audio = false; }; - /// Called by BridgeRoomDelegate when a remote track is subscribed. + /// Called by SessionManagerRoomDelegate when a remote track is subscribed. void onTrackSubscribed(const std::string &participant_identity, livekit::TrackSource source, const std::shared_ptr &track); - /// Called by BridgeRoomDelegate when a remote track is unsubscribed. + /// Called by SessionManagerRoomDelegate when a remote track is unsubscribed. void onTrackUnsubscribed(const std::string &participant_identity, livekit::TrackSource source); @@ -417,7 +417,7 @@ class SessionManager { static constexpr int kMaxActiveReaders = 20; std::unique_ptr room_; - std::unique_ptr delegate_; + std::unique_ptr delegate_; std::unique_ptr rpc_controller_; /// Registered callbacks (may be registered before tracks are subscribed). @@ -431,12 +431,12 @@ class SessionManager { std::unordered_map active_readers_; - /// All tracks created by this bridge. The bridge retains a shared_ptr so - /// it can force-release every track on disconnect() before the room is - /// destroyed, preventing dangling @c participant_ pointers. + /// All tracks created by this SessionManager. The SessionManager retains a + /// shared_ptr so it can force-release every track on disconnect() before the + /// room is destroyed, preventing dangling @c participant_ pointers. std::vector> published_audio_tracks_; /// @copydoc published_audio_tracks_ std::vector> published_video_tracks_; }; -} // namespace livekit_bridge +} // namespace session_manager diff --git a/src/session_manager/CMakeLists.txt b/src/session_manager/CMakeLists.txt deleted file mode 100644 index 853341d8..00000000 --- a/src/session_manager/CMakeLists.txt +++ /dev/null @@ -1,61 +0,0 @@ -cmake_minimum_required(VERSION 3.20) - -project(session_manager LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -add_library(session_manager SHARED - src/session_manager.cpp - src/managed_audio_track.cpp - src/managed_video_track.cpp - src/bridge_room_delegate.cpp - src/bridge_room_delegate.h - src/rpc_controller.cpp - src/rpc_controller.h -) - -if(WIN32) - set_target_properties(session_manager PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) -endif() - -target_include_directories(session_manager - PUBLIC - $ - $ - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/src - ${LIVEKIT_ROOT_DIR}/src -) - -# Link against the main livekit SDK library (which transitively provides -# include paths for livekit/*.h and links livekit_ffi). -target_link_libraries(session_manager - PUBLIC - livekit - PRIVATE - spdlog::spdlog -) - -target_compile_definitions(session_manager - PRIVATE - SPDLOG_ACTIVE_LEVEL=${_SPDLOG_ACTIVE_LEVEL} -) - -if(MSVC) - target_compile_options(session_manager PRIVATE /permissive- /Zc:__cplusplus /W4) -else() - target_compile_options(session_manager PRIVATE -Wall -Wextra -Wpedantic) -endif() - -# --- Tests --- -# Bridge tests default to OFF. They are automatically enabled when the parent -# SDK tests are enabled (LIVEKIT_BUILD_TESTS=ON), e.g. via ./build.sh debug-tests. -option(LIVEKIT_BRIDGE_BUILD_TESTS "Build bridge unit tests" OFF) - -if(LIVEKIT_BRIDGE_BUILD_TESTS OR LIVEKIT_BUILD_TESTS) - add_subdirectory(tests) -endif() - -# Bridge examples (robot + human) are built from examples/CMakeLists.txt -# when LIVEKIT_BUILD_EXAMPLES is ON; see examples/bridge_human_robot/. diff --git a/src/session_manager/README.md b/src/session_manager/README.md index f3d7821d..b75c7692 100644 --- a/src/session_manager/README.md +++ b/src/session_manager/README.md @@ -1,32 +1,28 @@ -# LiveKit Bridge +# LiveKit SessionManager -A simplified, high-level C++ wrapper around the [LiveKit C++ SDK](../README.md). The bridge abstracts away room lifecycle management, track creation, publishing, and subscription boilerplate so that external codebases can interface with LiveKit in just a few lines. It is intended that this library will be used to bridge the LiveKit C++ SDK into other SDKs such as, but not limited to, Foxglove, ROS, and Rerun. - -It is intended that this library closely matches the style of the core LiveKit C++ SDK. - -# Prerequisites -Since this is an extention of the LiveKit C++ SDK, go through the LiveKit C++ SDK installation instructions first: -*__[LiveKit C++ SDK](../README.md)__* +A simplified, high-level C++ wrapper around the [LiveKit C++ SDK](../README.md). +The SessionManager abstracts away room lifecycle management, track creation, publishing, and subscription boilerplate so that external codebases can interface with LiveKit in just a few lines. +It is intended that this library will be used to integrate the LiveKit C++ SDK into other SDKs such as, but not limited to, Foxglove, ROS, and Rerun. ## Usage Overview ```cpp -#include "livekit_bridge/session_manager.h" +#include "session_manager/session_manager.h" #include "livekit/audio_frame.h" #include "livekit/video_frame.h" #include "livekit/track.h" // 1. Connect -livekit_bridge::SessionManager bridge; +session_manager::SessionManager sm; livekit::RoomOptions options; options.auto_subscribe = true; // automatically subscribe to all remote tracks options.dynacast = false; -bridge.connect("wss://my-server.livekit.cloud", token, options); +sm.connect("wss://my-server.livekit.cloud", token, options); // 2. Create outgoing tracks (RAII-managed) -auto mic = bridge.createAudioTrack("mic", 48000, 2, +auto mic = sm.createAudioTrack("mic", 48000, 2, livekit::TrackSource::SOURCE_MICROPHONE); // name, sample_rate, channels, source -auto cam = bridge.createVideoTrack("cam", 1280, 720, +auto cam = sm.createVideoTrack("cam", 1280, 720, livekit::TrackSource::SOURCE_CAMERA); // name, width, height, source // 3. Push frames to remote participants @@ -34,25 +30,25 @@ mic->pushFrame(pcm_data, samples_per_channel); cam->pushFrame(rgba_data, timestamp_us); // 4. Receive frames from a remote participant -bridge.setOnAudioFrameCallback("remote-peer", livekit::TrackSource::SOURCE_MICROPHONE, +sm.setOnAudioFrameCallback("remote-peer", livekit::TrackSource::SOURCE_MICROPHONE, [](const livekit::AudioFrame& frame) { // Called on a background reader thread }); -bridge.setOnVideoFrameCallback("remote-peer", livekit::TrackSource::SOURCE_CAMERA, +sm.setOnVideoFrameCallback("remote-peer", livekit::TrackSource::SOURCE_CAMERA, [](const livekit::VideoFrame& frame, int64_t timestamp_us) { // Called on a background reader thread }); // 5. RPC (Remote Procedure Call) -bridge.registerRpcMethod("greet", +sm.registerRpcMethod("greet", [](const livekit::RpcInvocationData& data) -> std::optional { return "Hello, " + data.caller_identity + "!"; }); -std::string response = bridge.performRpc("remote-peer", "greet", ""); +std::string response = sm.performRpc("remote-peer", "greet", ""); -bridge.unregisterRpcMethod("greet"); +sm.unregisterRpcMethod("greet"); // Controller side: send commands to the publisher controller_bridge.requestRemoteTrackMute("robot-1", "mic"); // mute audio track "mic" @@ -61,18 +57,9 @@ controller_bridge.requestRemoteTrackUnmute("robot-1", "mic"); // unmute it // 7. Cleanup is automatic (RAII), or explicit: mic.reset(); // unpublishes the audio track cam.reset(); // unpublishes the video track -bridge.disconnect(); +sm.disconnect(); ``` -## Building - -The bridge is a component of the `client-sdk-cpp` build. See the "⚙️ BUILD" section of the [LiveKit C++ SDK README](../README.md) for instructions on how to build the bridge. - -This produces `liblivekit_bridge` (shared library) and optional `robot_stub`, `human_stub`, `robot`, and `human` executables. - -### Using the bridge in your own CMake project -TODO(sderosa): add instructions on how to use the bridge in your own CMake project. - ## Architecture ### Data Flow Overview @@ -98,24 +85,24 @@ Your Application **`ManagedAudioTrack` / `ManagedVideoTrack`** -- RAII handles for published local tracks. Created via `createAudioTrack()` / `createVideoTrack()`. When the `shared_ptr` is dropped, the track is automatically unpublished and all underlying SDK resources are freed. Call `pushFrame()` to send audio/video data to remote participants. -**`BridgeRoomDelegate`** -- Internal (not part of the public API; lives in `src/`). Listens for `onTrackSubscribed` / `onTrackUnsubscribed` events from the LiveKit SDK and wires up reader threads automatically. +**`SessionManagerRoomDelegate`** -- Internal (not part of the public API; lives in `src/`). Listens for `onTrackSubscribed` / `onTrackUnsubscribed` events from the LiveKit SDK and wires up reader threads automatically. ### What is a Reader? A **reader** is a background thread that receives decoded media frames from a remote participant. -When a remote participant publishes an audio or video track and the bridge subscribes to it (auto-subscribe is enabled by default), the bridge creates an `AudioStream` or `VideoStream` from that track and spins up a dedicated thread. This thread loops on `stream->read()`, which blocks until a new frame arrives. Each received frame is forwarded to the user's registered callback. +When a remote participant publishes an audio or video track and the SessionManager subscribes to it (auto-subscribe is enabled by default), the SessionManager creates an `AudioStream` or `VideoStream` from that track and spins up a dedicated thread. This thread loops on `stream->read()`, which blocks until a new frame arrives. Each received frame is forwarded to the user's registered callback. In short: - **Sending** (you -> remote): `ManagedAudioTrack::pushFrame()` / `ManagedVideoTrack::pushFrame()` - **Receiving** (remote -> you): reader threads invoke your registered callbacks -Reader threads are managed entirely by the bridge. They are created when a matching remote track is subscribed, and torn down (stream closed, thread joined) when the track is unsubscribed, the callback is unregistered, or `disconnect()` is called. +Reader threads are managed entirely by the SessionManager. They are created when a matching remote track is subscribed, and torn down (stream closed, thread joined) when the track is unsubscribed, the callback is unregistered, or `disconnect()` is called. ### Callback Registration Timing -Callbacks are keyed by `(participant_identity, track_source)`. You can register them **before** the remote participant has joined the room. The bridge stores the callback and automatically wires it up when the matching track is subscribed. +Callbacks are keyed by `(participant_identity, track_source)`. You can register them **before** the remote participant has joined the room. The SessionManager stores the callback and automatically wires it up when the matching track is subscribed. > **Note:** Only one callback may be set per `(participant_identity, track_source)` pair. Calling `setOnAudioFrameCallback` or `setOnVideoFrameCallback` again with the same identity and source will silently replace the previous callback. If you need to fan-out a single stream to multiple consumers, do so inside your callback. @@ -124,10 +111,10 @@ This means the typical pattern is: ```cpp // Register first, connect second -- or register after connect but before // the remote participant joins. -bridge.setOnAudioFrameCallback("robot-1", livekit::TrackSource::SOURCE_MICROPHONE, my_callback); +sm.setOnAudioFrameCallback("robot-1", livekit::TrackSource::SOURCE_MICROPHONE, my_callback); livekit::RoomOptions options; options.auto_subscribe = true; -bridge.connect(url, token, options); +sm.connect(url, token, options); // When robot-1 joins and publishes a mic track, my_callback starts firing. ``` @@ -135,7 +122,7 @@ bridge.connect(url, token, options); - `SessionManager` uses a mutex to protect the callback map and active reader state. - Frame callbacks fire on background reader threads. If your callback accesses shared application state, you are responsible for synchronization. -- `disconnect()` closes all streams and joins all reader threads before returning -- it is safe to destroy the bridge immediately after. +- `disconnect()` closes all streams and joins all reader threads before returning -- it is safe to destroy the SessionManager immediately after. ## API Reference @@ -145,7 +132,7 @@ bridge.connect(url, token, options); |---|---| | `connect(url, token, options)` | Connect to a LiveKit room. Initializes the SDK, creates a Room, and connects with auto-subscribe enabled. | | `disconnect()` | Disconnect and release all resources. Joins all reader threads. Safe to call multiple times. | -| `isConnected()` | Returns whether the bridge is currently connected. | +| `isConnected()` | Returns whether the SessionManager is currently connected. | | `createAudioTrack(name, sample_rate, num_channels, source)` | Create and publish a local audio track with the given `TrackSource` (e.g. `SOURCE_MICROPHONE`, `SOURCE_SCREENSHARE_AUDIO`). Returns an RAII `shared_ptr`. | | `createVideoTrack(name, width, height, source)` | Create and publish a local video track with the given `TrackSource` (e.g. `SOURCE_CAMERA`, `SOURCE_SCREENSHARE`). Returns an RAII `shared_ptr`. | | `setOnAudioFrameCallback(identity, source, callback)` | Register a callback for audio frames from a specific remote participant + track source. | @@ -225,14 +212,14 @@ The human will print periodic summaries like: ## Testing -The bridge includes a unit test suite built with [Google Test](https://github.com/google/googletest). Tests cover +The SessionManager includes a unit test suite built with [Google Test](https://github.com/google/googletest). Tests cover 1. `CallbackKey` hashing/equality, 2. `ManagedAudioTrack`/`ManagedVideoTrack` state management, and 3. `SessionManager` pre-connection behaviour (callback registration, error handling). ### Building and running tests -Bridge tests are automatically included when you build with the `debug-tests` or `release-tests` command: +SessionManager tests are automatically included when you build with the `debug-tests` or `release-tests` command: ```bash ./build.sh debug-tests @@ -241,21 +228,21 @@ Bridge tests are automatically included when you build with the `debug-tests` or Then run them directly: ```bash -./build-debug/bin/livekit_bridge_tests +./build-debug/bin/session_manager_tests ``` -### Standalone bridge tests only +### Standalone SessionManager tests only -If you want to build bridge tests independently (without the parent SDK tests), set `LIVEKIT_BRIDGE_BUILD_TESTS=ON`: +If you want to build SessionManager tests independently (without the parent SDK tests), set `SESSION_MANAGER_BUILD_TESTS=ON`: ```bash -cmake --preset macos-debug -DLIVEKIT_BRIDGE_BUILD_TESTS=ON -cmake --build build-debug --target livekit_bridge_tests +cmake --preset macos-debug -DSESSION_MANAGER_BUILD_TESTS=ON +cmake --build build-debug --target session_manager_tests ``` ## Limitations -The bridge is designed for simplicity and currently only supports limited audio and video features. It does not expose: +The SessionManager is designed for simplicity and currently only supports limited audio and video features. It does not expose: - We dont support all events defined in the RoomDelegate interface. - E2EE configuration @@ -265,4 +252,4 @@ The bridge is designed for simplicity and currently only supports limited audio - Custom `RoomOptions` or `TrackPublishOptions` - **One callback per (participant, source):** Only a single callback can be registered for each `(participant_identity, track_source)` pair. Re-registering with the same key silently replaces the previous callback. To fan-out a stream to multiple consumers, dispatch from within your single callback. -For advanced use cases, use the full `client-sdk-cpp` API directly, or expand the bridge to support your use case. +For advanced use cases, use the full `client-sdk-cpp` API directly, or expand the SessionManager to support your use case. diff --git a/src/session_manager/src/managed_audio_track.cpp b/src/session_manager/managed_audio_track.cpp similarity index 97% rename from src/session_manager/src/managed_audio_track.cpp rename to src/session_manager/managed_audio_track.cpp index 91b8a7ad..a31ada1b 100644 --- a/src/session_manager/src/managed_audio_track.cpp +++ b/src/session_manager/managed_audio_track.cpp @@ -17,7 +17,7 @@ /// @file managed_audio_track.cpp /// @brief Implementation of ManagedAudioTrack. -#include "livekit_bridge/managed_audio_track.h" +#include "session_manager/managed_audio_track.h" #include "livekit/audio_frame.h" #include "livekit/audio_source.h" @@ -29,7 +29,7 @@ #include "lk_log.h" -namespace livekit_bridge { +namespace session_manager { ManagedAudioTrack::ManagedAudioTrack( std::string name, int sample_rate, int num_channels, @@ -128,4 +128,4 @@ void ManagedAudioTrack::release() { participant_ = nullptr; } -} // namespace livekit_bridge +} // namespace session_manager diff --git a/src/session_manager/src/managed_video_track.cpp b/src/session_manager/managed_video_track.cpp similarity index 97% rename from src/session_manager/src/managed_video_track.cpp rename to src/session_manager/managed_video_track.cpp index 5bea1c32..7c91b67a 100644 --- a/src/session_manager/src/managed_video_track.cpp +++ b/src/session_manager/managed_video_track.cpp @@ -17,7 +17,7 @@ /// @file managed_video_track.cpp /// @brief Implementation of ManagedVideoTrack. -#include "livekit_bridge/managed_video_track.h" +#include "session_manager/managed_video_track.h" #include "livekit/local_participant.h" #include "livekit/local_track_publication.h" @@ -29,7 +29,7 @@ #include "lk_log.h" -namespace livekit_bridge { +namespace session_manager { ManagedVideoTrack::ManagedVideoTrack( std::string name, int width, int height, @@ -127,4 +127,4 @@ void ManagedVideoTrack::release() { participant_ = nullptr; } -} // namespace livekit_bridge +} // namespace session_manager diff --git a/src/session_manager/src/rpc_controller.cpp b/src/session_manager/rpc_controller.cpp similarity index 91% rename from src/session_manager/src/rpc_controller.cpp rename to src/session_manager/rpc_controller.cpp index 31514666..ef4482e1 100644 --- a/src/session_manager/src/rpc_controller.cpp +++ b/src/session_manager/rpc_controller.cpp @@ -18,7 +18,7 @@ /// @brief Implementation of RpcController. #include "rpc_controller.h" -#include "livekit_bridge/rpc_constants.h" +#include "session_manager/rpc_constants.h" #include "livekit/local_participant.h" #include "livekit/rpc_error.h" @@ -26,7 +26,7 @@ #include #include -namespace livekit_bridge { +namespace session_manager { RpcController::RpcController(TrackActionFn track_action_fn) : track_action_fn_(std::move(track_action_fn)), lp_(nullptr) {} @@ -50,8 +50,8 @@ void RpcController::disable() { std::string RpcController::performRpc(const std::string &destination_identity, - const std::string &method, const std::string &payload, - const std::optional &response_timeout) { + const std::string &method, const std::string &payload, + const std::optional &response_timeout) { assert(lp_ != nullptr); return lp_->performRpc(destination_identity, method, payload, response_timeout); @@ -77,8 +77,8 @@ void RpcController::unregisterRpcMethod(const std::string &method_name) { // Built-in outgoing convenience (track control) // --------------------------------------------------------------- -void RpcController::requestRemoteTrackMute(const std::string &destination_identity, - const std::string &track_name) { +void RpcController::requestRemoteTrackMute( + const std::string &destination_identity, const std::string &track_name) { namespace tc = rpc::track_control; performRpc(destination_identity, tc::kMethod, tc::formatPayload(tc::kActionMute, track_name), std::nullopt); @@ -141,4 +141,4 @@ RpcController::handleTrackControlRpc(const livekit::RpcInvocationData &data) { return tc::kResponseOk; } -} // namespace livekit_bridge +} // namespace session_manager diff --git a/src/session_manager/src/rpc_controller.h b/src/session_manager/rpc_controller.h similarity index 96% rename from src/session_manager/src/rpc_controller.h rename to src/session_manager/rpc_controller.h index 4abe4011..cd532956 100644 --- a/src/session_manager/src/rpc_controller.h +++ b/src/session_manager/rpc_controller.h @@ -15,12 +15,13 @@ */ /// @file rpc_controller.h -/// @brief Internal RPC controller that owns all RPC concerns for the bridge. +/// @brief Internal RPC controller that owns all RPC concerns for the +/// SessionManager. #pragma once #include "livekit/local_participant.h" -#include "livekit_bridge/rpc_constants.h" +#include "session_manager/rpc_constants.h" #include #include @@ -31,7 +32,7 @@ namespace livekit { struct RpcInvocationData; } // namespace livekit -namespace livekit_bridge { +namespace session_manager { namespace test { class RpcControllerTest; @@ -49,7 +50,7 @@ class RpcControllerTest; * enable() and unregistered on disable(). User-registered handlers are * forwarded directly to the underlying LocalParticipant. * - * Not part of the public API; lives in session_manager/src/. + * Not part of the public API; lives in session_manager/. */ class RpcController { public: @@ -142,4 +143,4 @@ class RpcController { livekit::LocalParticipant *lp_; }; -} // namespace livekit_bridge +} // namespace session_manager diff --git a/src/session_manager/src/session_manager.cpp b/src/session_manager/session_manager.cpp similarity index 94% rename from src/session_manager/src/session_manager.cpp rename to src/session_manager/session_manager.cpp index 7c290ead..dbf538fb 100644 --- a/src/session_manager/src/session_manager.cpp +++ b/src/session_manager/session_manager.cpp @@ -17,10 +17,10 @@ /// @file session_manager.cpp /// @brief Implementation of the SessionManager high-level API. -#include "livekit_bridge/session_manager.h" -#include "bridge_room_delegate.h" -#include "livekit_bridge/rpc_constants.h" +#include "session_manager/session_manager.h" #include "rpc_controller.h" +#include "session_manager/rpc_constants.h" +#include "session_manager_room_delegate.h" #include "livekit/audio_frame.h" #include "livekit/audio_source.h" @@ -41,7 +41,7 @@ #include "lk_log.h" -namespace livekit_bridge { +namespace session_manager { // --------------------------------------------------------------- // CallbackKey @@ -118,7 +118,7 @@ bool SessionManager::connect(const std::string &url, const std::string &token, // onTrackSubscribed events are delivered only after // room_/delegate_/connected_ are all in a consistent state. - auto delegate = std::make_unique(*this); + auto delegate = std::make_unique(*this); assert(delegate != nullptr); room->setDelegate(delegate.get()); livekit::LocalParticipant *lp = nullptr; @@ -152,8 +152,9 @@ void SessionManager::disconnect() { std::lock_guard lock(mutex_); if (!connected_) { - LK_LOG_WARN("Attempting to disconnect an already disconnected bridge. " - "Things may not disconnect properly."); + LK_LOG_WARN( + "Attempting to disconnect an already disconnected SessionManager. " + "Things may not disconnect properly."); } connected_ = false; @@ -225,7 +226,8 @@ bool SessionManager::isConnected() const { std::shared_ptr SessionManager::createAudioTrack(const std::string &name, int sample_rate, - int num_channels, livekit::TrackSource source) { + int num_channels, + livekit::TrackSource source) { std::lock_guard lock(mutex_); if (!connected_ || !room_) { @@ -251,12 +253,12 @@ SessionManager::createAudioTrack(const std::string &name, int sample_rate, auto publication = lp->publishTrack(track, opts); // 4. Wrap in handle and retain a reference - auto bridge_track = + auto managed_audio_track = std::shared_ptr(new ManagedAudioTrack( - name, sample_rate, num_channels, std::move(audio_source), - std::move(track), std::move(publication), lp)); - published_audio_tracks_.emplace_back(bridge_track); - return bridge_track; + name, sample_rate, num_channels, std::move(audio_source), + std::move(track), std::move(publication), lp)); + published_audio_tracks_.emplace_back(managed_audio_track); + return managed_audio_track; } std::shared_ptr @@ -286,11 +288,11 @@ SessionManager::createVideoTrack(const std::string &name, int width, int height, auto publication = lp->publishTrack(track, opts); // 4. Wrap in handle and retain a reference - auto bridge_track = std::shared_ptr( + auto managed_video_track = std::shared_ptr( new ManagedVideoTrack(name, width, height, std::move(video_source), std::move(track), std::move(publication), lp)); - published_video_tracks_.emplace_back(bridge_track); - return bridge_track; + published_video_tracks_.emplace_back(managed_video_track); + return managed_video_track; } // --------------------------------------------------------------- @@ -358,11 +360,9 @@ void SessionManager::clearOnVideoFrameCallback( // RPC (delegates to RpcController) // --------------------------------------------------------------- -std::optional -SessionManager::performRpc(const std::string &destination_identity, - const std::string &method, - const std::string &payload, - const std::optional &response_timeout) { +std::optional SessionManager::performRpc( + const std::string &destination_identity, const std::string &method, + const std::string &payload, const std::optional &response_timeout) { if (!isConnected()) { return std::nullopt; @@ -468,8 +468,8 @@ bool SessionManager::requestRemoteTrackUnmute( // Track action callback for RpcController // --------------------------------------------------------------- -void SessionManager::executeTrackAction(const rpc::track_control::Action &action, - const std::string &track_name) { +void SessionManager::executeTrackAction( + const rpc::track_control::Action &action, const std::string &track_name) { std::lock_guard lock(mutex_); for (auto &track : published_audio_tracks_) { @@ -653,4 +653,4 @@ SessionManager::startVideoReader(const CallbackKey &key, return old_thread; } -} // namespace livekit_bridge +} // namespace session_manager diff --git a/src/session_manager/src/bridge_room_delegate.cpp b/src/session_manager/session_manager_room_delegate.cpp similarity index 72% rename from src/session_manager/src/bridge_room_delegate.cpp rename to src/session_manager/session_manager_room_delegate.cpp index c42ba4e3..3f95d406 100644 --- a/src/session_manager/src/bridge_room_delegate.cpp +++ b/src/session_manager/session_manager_room_delegate.cpp @@ -14,19 +14,19 @@ * limitations under the License. */ -/// @file bridge_room_delegate.cpp -/// @brief Implementation of BridgeRoomDelegate event forwarding. +/// @file session_manager_room_delegate.cpp +/// @brief Implementation of SessionManagerRoomDelegate event forwarding. -#include "bridge_room_delegate.h" +#include "session_manager_room_delegate.h" #include "livekit/remote_participant.h" #include "livekit/remote_track_publication.h" #include "livekit/track.h" -#include "livekit_bridge/session_manager.h" +#include "session_manager/session_manager.h" -namespace livekit_bridge { +namespace session_manager { -void BridgeRoomDelegate::onTrackSubscribed( +void SessionManagerRoomDelegate::onTrackSubscribed( livekit::Room & /*room*/, const livekit::TrackSubscribedEvent &ev) { if (!ev.track || !ev.participant || !ev.publication) { return; @@ -35,10 +35,10 @@ void BridgeRoomDelegate::onTrackSubscribed( const std::string identity = ev.participant->identity(); const livekit::TrackSource source = ev.publication->source(); - bridge_.onTrackSubscribed(identity, source, ev.track); + manager_.onTrackSubscribed(identity, source, ev.track); } -void BridgeRoomDelegate::onTrackUnsubscribed( +void SessionManagerRoomDelegate::onTrackUnsubscribed( livekit::Room & /*room*/, const livekit::TrackUnsubscribedEvent &ev) { if (!ev.participant || !ev.publication) { return; @@ -47,7 +47,7 @@ void BridgeRoomDelegate::onTrackUnsubscribed( const std::string identity = ev.participant->identity(); const livekit::TrackSource source = ev.publication->source(); - bridge_.onTrackUnsubscribed(identity, source); + manager_.onTrackUnsubscribed(identity, source); } -} // namespace livekit_bridge +} // namespace session_manager diff --git a/src/session_manager/src/bridge_room_delegate.h b/src/session_manager/session_manager_room_delegate.h similarity index 83% rename from src/session_manager/src/bridge_room_delegate.h rename to src/session_manager/session_manager_room_delegate.h index 04dcfa8b..2b1d3033 100644 --- a/src/session_manager/src/bridge_room_delegate.h +++ b/src/session_manager/session_manager_room_delegate.h @@ -14,14 +14,14 @@ * limitations under the License. */ -/// @file bridge_room_delegate.h +/// @file session_manager_room_delegate.h /// @brief Internal RoomDelegate forwarding SDK events to SessionManager. #pragma once #include "livekit/room_delegate.h" -namespace livekit_bridge { +namespace session_manager { class SessionManager; @@ -31,9 +31,9 @@ class SessionManager; * Handles track subscribe/unsubscribe lifecycle. Not part of the public API, * so its in src/ instead of include/. */ -class BridgeRoomDelegate : public livekit::RoomDelegate { +class SessionManagerRoomDelegate : public livekit::RoomDelegate { public: - explicit BridgeRoomDelegate(SessionManager &bridge) : bridge_(bridge) {} + explicit SessionManagerRoomDelegate(SessionManager &manager) : manager_(manager) {} /// Forwards a track-subscribed event to SessionManager::onTrackSubscribed(). void onTrackSubscribed(livekit::Room &room, @@ -45,7 +45,7 @@ class BridgeRoomDelegate : public livekit::RoomDelegate { const livekit::TrackUnsubscribedEvent &ev) override; private: - SessionManager &bridge_; + SessionManager &manager_; }; -} // namespace livekit_bridge +} // namespace session_manager diff --git a/src/session_manager/tests/CMakeLists.txt b/src/tests/session_manager/CMakeLists.txt similarity index 55% rename from src/session_manager/tests/CMakeLists.txt rename to src/tests/session_manager/CMakeLists.txt index c42274b8..e78a1ad2 100644 --- a/src/session_manager/tests/CMakeLists.txt +++ b/src/tests/session_manager/CMakeLists.txt @@ -25,75 +25,66 @@ enable_testing() include(GoogleTest) # ============================================================================ -# Bridge Unit Tests +# SessionManager Unit Tests # ============================================================================ -file(GLOB BRIDGE_TEST_SOURCES +file(GLOB SESSION_MANAGER_TEST_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" ) -if(BRIDGE_TEST_SOURCES) - add_executable(livekit_bridge_tests - ${BRIDGE_TEST_SOURCES} +if(SESSION_MANAGER_TEST_SOURCES) + add_executable(session_manager_tests + ${SESSION_MANAGER_TEST_SOURCES} ) - target_include_directories(livekit_bridge_tests + target_include_directories(session_manager_tests PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/../src + ${LIVEKIT_ROOT_DIR}/src/session_manager/ ) - target_link_libraries(livekit_bridge_tests + target_link_libraries(session_manager_tests PRIVATE - livekit_bridge + livekit GTest::gtest_main ) # Copy shared libraries to test executable directory if(WIN32) - add_custom_command(TARGET livekit_bridge_tests POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - $ - $ + add_custom_command(TARGET session_manager_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ - $ + $ COMMAND ${CMAKE_COMMAND} -E copy_if_different "$/livekit_ffi.dll" - $ - COMMENT "Copying DLLs to bridge test directory" + $ + COMMENT "Copying DLLs to session_manager test directory" ) elseif(APPLE) - add_custom_command(TARGET livekit_bridge_tests POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - $ - $ + add_custom_command(TARGET session_manager_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ - $ + $ COMMAND ${CMAKE_COMMAND} -E copy_if_different "$/liblivekit_ffi.dylib" - $ - COMMENT "Copying dylibs to bridge test directory" + $ + COMMENT "Copying dylibs to session_manager test directory" ) else() - add_custom_command(TARGET livekit_bridge_tests POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - $ - $ + add_custom_command(TARGET session_manager_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ - $ + $ COMMAND ${CMAKE_COMMAND} -E copy_if_different "$/liblivekit_ffi.so" - $ - COMMENT "Copying shared libraries to bridge test directory" + $ + COMMENT "Copying shared libraries to session_manager test directory" ) endif() # Register tests with CTest - gtest_discover_tests(livekit_bridge_tests + gtest_discover_tests(session_manager_tests WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} PROPERTIES - LABELS "bridge_unit" + LABELS "session_manager_unit" ) endif() diff --git a/src/session_manager/tests/integration/test_session_manager_rpc_roundtrip.cpp b/src/tests/session_manager/integration/test_session_manager_rpc_roundtrip.cpp similarity index 87% rename from src/session_manager/tests/integration/test_session_manager_rpc_roundtrip.cpp rename to src/tests/session_manager/integration/test_session_manager_rpc_roundtrip.cpp index f3390501..371454e1 100644 --- a/src/session_manager/tests/integration/test_session_manager_rpc_roundtrip.cpp +++ b/src/tests/session_manager/integration/test_session_manager_rpc_roundtrip.cpp @@ -14,24 +14,23 @@ * limitations under the License. */ -#include "../common/bridge_test_common.h" #include -namespace livekit_bridge { +namespace session_manager { namespace test { -class BridgeRpcRoundtripTest : public BridgeTestBase {}; +class SessionManagerRpcRoundtripTest : public SessionManagerTestBase {}; // --------------------------------------------------------------------------- -// Test 1: Basic RPC round-trip through the bridge. +// Test 1: Basic RPC round-trip through the SessionManager. // // Receiver registers an "echo" handler, caller performs an RPC call, and the // response is verified. // --------------------------------------------------------------------------- -TEST_F(BridgeRpcRoundtripTest, BasicRpcRoundTrip) { +TEST_F(SessionManagerRpcRoundtripTest, BasicRpcRoundTrip) { skipIfNotConfigured(); - std::cout << "\n=== Bridge RPC Round-Trip Test ===" << std::endl; + std::cout << "\n=== SessionManager RPC Round-Trip Test ===" << std::endl; SessionManager caller; SessionManager receiver; @@ -56,7 +55,7 @@ TEST_F(BridgeRpcRoundtripTest, BasicRpcRoundTrip) { std::cout << "RPC handler registered, performing call..." << std::endl; - std::string test_payload = "hello from bridge"; + std::string test_payload = "hello from SessionManager"; std::string response = caller.performRpc(receiver_identity, "echo", test_payload, 10.0); @@ -83,10 +82,11 @@ TEST_F(BridgeRpcRoundtripTest, BasicRpcRoundTrip) { // The handler throws an RpcError with a custom code and message. The caller // should catch the same error code, message, and data. // --------------------------------------------------------------------------- -TEST_F(BridgeRpcRoundtripTest, RpcErrorPropagation) { +TEST_F(SessionManagerRpcRoundtripTest, RpcErrorPropagation) { skipIfNotConfigured(); - std::cout << "\n=== Bridge RPC Error Propagation Test ===" << std::endl; + std::cout << "\n=== SessionManager RPC Error Propagation Test ===" + << std::endl; SessionManager caller; SessionManager receiver; @@ -124,10 +124,11 @@ TEST_F(BridgeRpcRoundtripTest, RpcErrorPropagation) { // --------------------------------------------------------------------------- // Test 3: Calling an unregistered method returns UNSUPPORTED_METHOD. // --------------------------------------------------------------------------- -TEST_F(BridgeRpcRoundtripTest, UnregisteredMethod) { +TEST_F(SessionManagerRpcRoundtripTest, UnregisteredMethod) { skipIfNotConfigured(); - std::cout << "\n=== Bridge RPC Unsupported Method Test ===" << std::endl; + std::cout << "\n=== SessionManager RPC Unsupported Method Test ===" + << std::endl; SessionManager caller; SessionManager receiver; @@ -154,7 +155,7 @@ TEST_F(BridgeRpcRoundtripTest, UnregisteredMethod) { // Remote Track Control Tests // =========================================================================== -class BridgeRemoteTrackControlTest : public BridgeTestBase {}; +class SessionManagerRemoteTrackControlTest : public SessionManagerTestBase {}; // --------------------------------------------------------------------------- // Test 4: Remote mute of an audio track. @@ -162,10 +163,11 @@ class BridgeRemoteTrackControlTest : public BridgeTestBase {}; // Publisher creates an audio track, enables remote track control. Controller // requests mute, then unmute. // --------------------------------------------------------------------------- -TEST_F(BridgeRemoteTrackControlTest, RemoteMuteAudioTrack) { +TEST_F(SessionManagerRemoteTrackControlTest, RemoteMuteAudioTrack) { skipIfNotConfigured(); - std::cout << "\n=== Bridge Remote Mute Audio Track Test ===" << std::endl; + std::cout << "\n=== SessionManager Remote Mute Audio Track Test ===" + << std::endl; SessionManager publisher; SessionManager controller; @@ -201,10 +203,11 @@ TEST_F(BridgeRemoteTrackControlTest, RemoteMuteAudioTrack) { // --------------------------------------------------------------------------- // Test 5: Remote mute of a video track. // --------------------------------------------------------------------------- -TEST_F(BridgeRemoteTrackControlTest, RemoteMuteVideoTrack) { +TEST_F(SessionManagerRemoteTrackControlTest, RemoteMuteVideoTrack) { skipIfNotConfigured(); - std::cout << "\n=== Bridge Remote Mute Video Track Test ===" << std::endl; + std::cout << "\n=== SessionManager Remote Mute Video Track Test ===" + << std::endl; SessionManager publisher; SessionManager controller; @@ -237,10 +240,10 @@ TEST_F(BridgeRemoteTrackControlTest, RemoteMuteVideoTrack) { // --------------------------------------------------------------------------- // Test 7: Remote mute on a nonexistent track returns an error. // --------------------------------------------------------------------------- -TEST_F(BridgeRemoteTrackControlTest, RemoteMuteNonexistentTrack) { +TEST_F(SessionManagerRemoteTrackControlTest, RemoteMuteNonexistentTrack) { skipIfNotConfigured(); - std::cout << "\n=== Bridge Remote Mute Nonexistent Track Test ===" + std::cout << "\n=== SessionManager Remote Mute Nonexistent Track Test ===" << std::endl; SessionManager publisher; @@ -268,4 +271,4 @@ TEST_F(BridgeRemoteTrackControlTest, RemoteMuteNonexistentTrack) { } } // namespace test -} // namespace livekit_bridge +} // namespace session_manager diff --git a/src/session_manager/tests/test_callback_key.cpp b/src/tests/session_manager/test_callback_key.cpp similarity index 97% rename from src/session_manager/tests/test_callback_key.cpp rename to src/tests/session_manager/test_callback_key.cpp index 258e2bb0..e8a3b149 100644 --- a/src/session_manager/tests/test_callback_key.cpp +++ b/src/tests/session_manager/test_callback_key.cpp @@ -18,13 +18,13 @@ /// @brief Unit tests for SessionManager::CallbackKey hash and equality. #include -#include +#include #include #include -namespace livekit_bridge { +namespace session_manager { namespace test { class CallbackKeyTest : public ::testing::Test { @@ -121,4 +121,4 @@ TEST_F(CallbackKeyTest, EmptyIdentityWorks) { } } // namespace test -} // namespace livekit_bridge +} // namespace session_manager diff --git a/src/session_manager/tests/test_managed_audio_track.cpp b/src/tests/session_manager/test_managed_audio_track.cpp similarity index 97% rename from src/session_manager/tests/test_managed_audio_track.cpp rename to src/tests/session_manager/test_managed_audio_track.cpp index 3ec31a22..0d25e51d 100644 --- a/src/session_manager/tests/test_managed_audio_track.cpp +++ b/src/tests/session_manager/test_managed_audio_track.cpp @@ -18,13 +18,13 @@ /// @brief Unit tests for ManagedAudioTrack. #include -#include +#include #include #include #include -namespace livekit_bridge { +namespace session_manager { namespace test { class ManagedAudioTrackTest : public ::testing::Test { @@ -115,4 +115,4 @@ TEST_F(ManagedAudioTrackTest, UnmuteOnReleasedTrackDoesNotCrash) { } } // namespace test -} // namespace livekit_bridge +} // namespace session_manager diff --git a/src/session_manager/tests/test_managed_video_track.cpp b/src/tests/session_manager/test_managed_video_track.cpp similarity index 96% rename from src/session_manager/tests/test_managed_video_track.cpp rename to src/tests/session_manager/test_managed_video_track.cpp index 3659f4bc..29a5ed36 100644 --- a/src/session_manager/tests/test_managed_video_track.cpp +++ b/src/tests/session_manager/test_managed_video_track.cpp @@ -18,13 +18,13 @@ /// @brief Unit tests for ManagedVideoTrack. #include -#include +#include #include #include #include -namespace livekit_bridge { +namespace session_manager { namespace test { class ManagedVideoTrackTest : public ::testing::Test { @@ -111,4 +111,4 @@ TEST_F(ManagedVideoTrackTest, UnmuteOnReleasedTrackDoesNotCrash) { } } // namespace test -} // namespace livekit_bridge +} // namespace session_manager diff --git a/src/session_manager/tests/test_rpc_controller.cpp b/src/tests/session_manager/test_rpc_controller.cpp similarity index 96% rename from src/session_manager/tests/test_rpc_controller.cpp rename to src/tests/session_manager/test_rpc_controller.cpp index be2d0355..5080fc1d 100644 --- a/src/session_manager/tests/test_rpc_controller.cpp +++ b/src/tests/session_manager/test_rpc_controller.cpp @@ -19,8 +19,8 @@ #include -#include "livekit_bridge/rpc_constants.h" #include "rpc_controller.h" +#include "session_manager/rpc_constants.h" #include "livekit/local_participant.h" #include "livekit/rpc_error.h" @@ -28,7 +28,7 @@ #include #include -namespace livekit_bridge { +namespace session_manager { namespace test { // Records (action, track_name) pairs passed to the TrackActionFn callback. @@ -45,9 +45,9 @@ class RpcControllerTest : public ::testing::Test { namespace tc = rpc::track_control; return std::make_unique( [this](const tc::Action &action, const std::string &track_name) { - const char *action_str = - (action == tc::Action::kActionMute) ? tc::kActionMute - : tc::kActionUnmute; + const char *action_str = (action == tc::Action::kActionMute) + ? tc::kActionMute + : tc::kActionUnmute; recorded_actions_.push_back({action_str, track_name}); }); } @@ -270,4 +270,4 @@ TEST_F(RpcControllerTest, FormatPayloadRoundTrip) { } } // namespace test -} // namespace livekit_bridge +} // namespace session_manager diff --git a/src/session_manager/tests/test_session_manager.cpp b/src/tests/session_manager/test_session_manager.cpp similarity index 57% rename from src/session_manager/tests/test_session_manager.cpp rename to src/tests/session_manager/test_session_manager.cpp index 3f50b7a8..ff6ac642 100644 --- a/src/session_manager/tests/test_session_manager.cpp +++ b/src/tests/session_manager/test_session_manager.cpp @@ -18,18 +18,18 @@ /// @brief Unit tests for SessionManager. #include -#include +#include #include #include -namespace livekit_bridge { +namespace session_manager { namespace test { class SessionManagerTest : public ::testing::Test { protected: - // No SetUp/TearDown needed -- we test the bridge without initializing + // No SetUp/TearDown needed -- we test the SessionManager without initializing // the LiveKit SDK, since we only exercise pre-connection behaviour. }; @@ -38,36 +38,37 @@ class SessionManagerTest : public ::testing::Test { // ============================================================================ TEST_F(SessionManagerTest, InitiallyNotConnected) { - SessionManager bridge; + SessionManager session_manager; - EXPECT_FALSE(bridge.isConnected()) - << "Bridge should not be connected immediately after construction"; + EXPECT_FALSE(session_manager.isConnected()) + << "SessionManager should not be connected immediately after " + "construction"; } TEST_F(SessionManagerTest, DisconnectBeforeConnectIsNoOp) { - SessionManager bridge; + SessionManager session_manager; - EXPECT_NO_THROW(bridge.disconnect()) - << "disconnect() on an unconnected bridge should be a safe no-op"; + EXPECT_NO_THROW(session_manager.disconnect()) + << "disconnect() on an unconnected SessionManager should be a safe no-op"; - EXPECT_FALSE(bridge.isConnected()); + EXPECT_FALSE(session_manager.isConnected()); } TEST_F(SessionManagerTest, MultipleDisconnectsAreIdempotent) { - SessionManager bridge; + SessionManager session_manager; EXPECT_NO_THROW({ - bridge.disconnect(); - bridge.disconnect(); - bridge.disconnect(); + session_manager.disconnect(); + session_manager.disconnect(); }) << "Multiple disconnect() calls should be safe"; } -TEST_F(SessionManagerTest, DestructorOnUnconnectedBridgeIsSafe) { - // Just verify no crash when the bridge is destroyed without connecting. +TEST_F(SessionManagerTest, DestructorOnUnconnectedSessionManagerIsSafe) { + // Just verify no crash when the SessionManager is destroyed without + // connecting. EXPECT_NO_THROW({ - SessionManager bridge; - // bridge goes out of scope here + SessionManager session_manager; + // SessionManager goes out of scope here }); } @@ -76,19 +77,19 @@ TEST_F(SessionManagerTest, DestructorOnUnconnectedBridgeIsSafe) { // ============================================================================ TEST_F(SessionManagerTest, CreateAudioTrackBeforeConnectThrows) { - SessionManager bridge; + SessionManager session_manager; - EXPECT_THROW(bridge.createAudioTrack("mic", 48000, 2, - livekit::TrackSource::SOURCE_MICROPHONE), + EXPECT_THROW(session_manager.createAudioTrack( + "mic", 48000, 2, livekit::TrackSource::SOURCE_MICROPHONE), std::runtime_error) << "createAudioTrack should throw when not connected"; } TEST_F(SessionManagerTest, CreateVideoTrackBeforeConnectThrows) { - SessionManager bridge; + SessionManager session_manager; - EXPECT_THROW(bridge.createVideoTrack("cam", 1280, 720, - livekit::TrackSource::SOURCE_CAMERA), + EXPECT_THROW(session_manager.createVideoTrack( + "cam", 1280, 720, livekit::TrackSource::SOURCE_CAMERA), std::runtime_error) << "createVideoTrack should throw when not connected"; } @@ -98,88 +99,89 @@ TEST_F(SessionManagerTest, CreateVideoTrackBeforeConnectThrows) { // ============================================================================ TEST_F(SessionManagerTest, RegisterAndUnregisterAudioCallbackDoesNotCrash) { - SessionManager bridge; + SessionManager session_manager; EXPECT_NO_THROW({ - bridge.setOnAudioFrameCallback("remote-participant", - livekit::TrackSource::SOURCE_MICROPHONE, - [](const livekit::AudioFrame &) {}); + session_manager.setOnAudioFrameCallback( + "remote-participant", livekit::TrackSource::SOURCE_MICROPHONE, + [](const livekit::AudioFrame &) {}); - bridge.clearOnAudioFrameCallback("remote-participant", - livekit::TrackSource::SOURCE_MICROPHONE); + session_manager.clearOnAudioFrameCallback( + "remote-participant", livekit::TrackSource::SOURCE_MICROPHONE); }) << "Registering and unregistering an audio callback should be safe " "even without a connection"; } TEST_F(SessionManagerTest, RegisterAndUnregisterVideoCallbackDoesNotCrash) { - SessionManager bridge; + SessionManager session_manager; EXPECT_NO_THROW({ - bridge.setOnVideoFrameCallback( + session_manager.setOnVideoFrameCallback( "remote-participant", livekit::TrackSource::SOURCE_CAMERA, [](const livekit::VideoFrame &, std::int64_t) {}); - bridge.clearOnVideoFrameCallback("remote-participant", - livekit::TrackSource::SOURCE_CAMERA); + session_manager.clearOnVideoFrameCallback( + "remote-participant", livekit::TrackSource::SOURCE_CAMERA); }) << "Registering and unregistering a video callback should be safe " "even without a connection"; } TEST_F(SessionManagerTest, UnregisterNonExistentCallbackIsNoOp) { - SessionManager bridge; + SessionManager session_manager; EXPECT_NO_THROW({ - bridge.clearOnAudioFrameCallback("nonexistent", - livekit::TrackSource::SOURCE_MICROPHONE); - bridge.clearOnVideoFrameCallback("nonexistent", - livekit::TrackSource::SOURCE_CAMERA); + session_manager.clearOnAudioFrameCallback( + "nonexistent", livekit::TrackSource::SOURCE_MICROPHONE); + session_manager.clearOnVideoFrameCallback( + "nonexistent", livekit::TrackSource::SOURCE_CAMERA); }) << "Unregistering a callback that was never registered should be a no-op"; } TEST_F(SessionManagerTest, MultipleRegistrationsSameKeyOverwrites) { - SessionManager bridge; + SessionManager session_manager; int call_count = 0; // Register a first callback - bridge.setOnAudioFrameCallback("alice", - livekit::TrackSource::SOURCE_MICROPHONE, - [](const livekit::AudioFrame &) {}); + session_manager.setOnAudioFrameCallback( + "alice", livekit::TrackSource::SOURCE_MICROPHONE, + [](const livekit::AudioFrame &) {}); // Register a second callback for the same key -- should overwrite - bridge.setOnAudioFrameCallback( + session_manager.setOnAudioFrameCallback( "alice", livekit::TrackSource::SOURCE_MICROPHONE, [&call_count](const livekit::AudioFrame &) { call_count++; }); // Unregister once should be enough (only one entry per key) - EXPECT_NO_THROW(bridge.clearOnAudioFrameCallback( + EXPECT_NO_THROW(session_manager.clearOnAudioFrameCallback( "alice", livekit::TrackSource::SOURCE_MICROPHONE)); } TEST_F(SessionManagerTest, RegisterCallbacksForMultipleParticipants) { - SessionManager bridge; + SessionManager session_manager; EXPECT_NO_THROW({ - bridge.setOnAudioFrameCallback("alice", - livekit::TrackSource::SOURCE_MICROPHONE, - [](const livekit::AudioFrame &) {}); + session_manager.setOnAudioFrameCallback( + "alice", livekit::TrackSource::SOURCE_MICROPHONE, + [](const livekit::AudioFrame &) {}); - bridge.setOnVideoFrameCallback( + session_manager.setOnVideoFrameCallback( "bob", livekit::TrackSource::SOURCE_CAMERA, [](const livekit::VideoFrame &, std::int64_t) {}); - bridge.setOnAudioFrameCallback( + session_manager.setOnAudioFrameCallback( "charlie", livekit::TrackSource::SOURCE_SCREENSHARE_AUDIO, [](const livekit::AudioFrame &) {}); }) << "Should be able to register callbacks for multiple participants"; // Cleanup - bridge.clearOnAudioFrameCallback("alice", - livekit::TrackSource::SOURCE_MICROPHONE); - bridge.clearOnVideoFrameCallback("bob", livekit::TrackSource::SOURCE_CAMERA); - bridge.clearOnAudioFrameCallback( + session_manager.clearOnAudioFrameCallback( + "alice", livekit::TrackSource::SOURCE_MICROPHONE); + session_manager.clearOnVideoFrameCallback( + "bob", livekit::TrackSource::SOURCE_CAMERA); + session_manager.clearOnAudioFrameCallback( "charlie", livekit::TrackSource::SOURCE_SCREENSHARE_AUDIO); } } // namespace test -} // namespace livekit_bridge +} // namespace session_manager From c2d155573c1f6929b5d1a7ae940e6de86910fff9 Mon Sep 17 00:00:00 2001 From: Stephen DeRosa Date: Tue, 10 Mar 2026 13:41:21 -0600 Subject: [PATCH 06/13] SessionManager: examples using it just to use the base SDK --- examples/CMakeLists.txt | 48 +++- .../fallback_capture.cpp | 0 .../simple_room => common}/fallback_capture.h | 0 .../video_producer.cpp | 233 ++++++++++++++++++ .../session_manager_base_sdk/video_viewer.cpp | 154 ++++++++++++ include/session_manager/session_manager.h | 21 ++ src/session_manager/README.md | 38 ++- src/session_manager/session_manager.cpp | 5 + 8 files changed, 491 insertions(+), 8 deletions(-) rename examples/{base_sdk/simple_room => common}/fallback_capture.cpp (100%) rename examples/{base_sdk/simple_room => common}/fallback_capture.h (100%) create mode 100644 examples/session_manager_base_sdk/video_producer.cpp create mode 100644 examples/session_manager_base_sdk/video_viewer.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6e250323..53880163 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -51,6 +51,8 @@ set(EXAMPLES_ALL MuteUnmuteReceiver RpcCaller RpcReceiver + SessionManagerBaseSdkProducer + SessionManagerBaseSdkViewer ) # Examples that use SDL3 (need SDL3 lib copied on Linux; SimpleRoom is handled separately above) @@ -59,12 +61,14 @@ set(EXAMPLES_NEED_SDL3 HumanRobotHuman MuteUnmuteCaller MuteUnmuteReceiver + SessionManagerBaseSdkProducer + SessionManagerBaseSdkViewer ) add_executable(SimpleRoom base_sdk/simple_room/main.cpp - base_sdk/simple_room/fallback_capture.cpp - base_sdk/simple_room/fallback_capture.h + ${EXAMPLES_COMMON_DIR}/fallback_capture.cpp + ${EXAMPLES_COMMON_DIR}/fallback_capture.h ${EXAMPLES_COMMON_DIR}/sdl_media.cpp ${EXAMPLES_COMMON_DIR}/sdl_media.h ${EXAMPLES_COMMON_DIR}/sdl_media_manager.cpp @@ -293,7 +297,45 @@ target_include_directories(MuteUnmuteReceiver PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS} ${EXAMPLES_COMMON_DIR} ) -ßtarget_link_libraries(MuteUnmuteReceiver PRIVATE livekit spdlog::spdlog SDL3::SDL3) +target_link_libraries(MuteUnmuteReceiver PRIVATE livekit spdlog::spdlog SDL3::SDL3) + +# --- session_manager_base_sdk (SessionManager connect + getRoom(); Room for all else) --- +add_executable(SessionManagerBaseSdkProducer + session_manager_base_sdk/video_producer.cpp + ${EXAMPLES_COMMON_DIR}/sdl_media.cpp + ${EXAMPLES_COMMON_DIR}/sdl_media.h +) +target_include_directories(SessionManagerBaseSdkProducer PRIVATE + ${EXAMPLES_PRIVATE_INCLUDE_DIRS} + ${EXAMPLES_COMMON_DIR} +) +target_link_libraries(SessionManagerBaseSdkProducer PRIVATE livekit spdlog::spdlog SDL3::SDL3) + +add_executable(SessionManagerBaseSdkViewer + session_manager_base_sdk/video_viewer.cpp + ${EXAMPLES_COMMON_DIR}/fallback_capture.cpp + ${EXAMPLES_COMMON_DIR}/fallback_capture.h + ${EXAMPLES_COMMON_DIR}/sdl_media.cpp + ${EXAMPLES_COMMON_DIR}/sdl_media.h + ${EXAMPLES_COMMON_DIR}/sdl_media_manager.cpp + ${EXAMPLES_COMMON_DIR}/sdl_media_manager.h + ${EXAMPLES_COMMON_DIR}/sdl_video_renderer.cpp + ${EXAMPLES_COMMON_DIR}/sdl_video_renderer.h + ${EXAMPLES_COMMON_DIR}/wav_audio_source.cpp + ${EXAMPLES_COMMON_DIR}/wav_audio_source.h +) +target_include_directories(SessionManagerBaseSdkViewer PRIVATE + ${EXAMPLES_PRIVATE_INCLUDE_DIRS} + ${EXAMPLES_COMMON_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/base_sdk/simple_room +) +target_link_libraries(SessionManagerBaseSdkViewer PRIVATE livekit spdlog::spdlog SDL3::SDL3) + +add_custom_command(TARGET SessionManagerBaseSdkViewer POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${LIVEKIT_ROOT_DIR}/data + $/data +) # Copy SDL3 shared library to examples output directories if(UNIX AND NOT APPLE) diff --git a/examples/base_sdk/simple_room/fallback_capture.cpp b/examples/common/fallback_capture.cpp similarity index 100% rename from examples/base_sdk/simple_room/fallback_capture.cpp rename to examples/common/fallback_capture.cpp diff --git a/examples/base_sdk/simple_room/fallback_capture.h b/examples/common/fallback_capture.h similarity index 100% rename from examples/base_sdk/simple_room/fallback_capture.h rename to examples/common/fallback_capture.h diff --git a/examples/session_manager_base_sdk/video_producer.cpp b/examples/session_manager_base_sdk/video_producer.cpp new file mode 100644 index 00000000..eb579346 --- /dev/null +++ b/examples/session_manager_base_sdk/video_producer.cpp @@ -0,0 +1,233 @@ +/* + * Copyright 2025 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. + */ + +/* + * SessionManager + base SDK video producer. + * + * SessionManager handles connect/disconnect; getRoom() provides the Room. + * All other work uses the Room directly: room_info(), localParticipant(), + * publishTrack(), etc. + * + * Usage: + * video_producer + * LIVEKIT_URL=... LIVEKIT_TOKEN=... video_producer + * + * Use identity "producer" for the token. Run video_viewer in another terminal. + * + * Requires a webcam. Falls back to solid-color frames if no camera is found. + */ + +#include "livekit/livekit.h" +#include "lk_log.h" +#include "session_manager/session_manager.h" +#include "sdl_media.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace livekit; + +static std::atomic g_running{true}; +static void handleSignal(int) { g_running.store(false); } + +int main(int argc, char *argv[]) { + std::string url, token; + if (argc >= 3) { + url = argv[1]; + token = argv[2]; + } else { + const char *e = std::getenv("LIVEKIT_URL"); + if (e) + url = e; + e = std::getenv("LIVEKIT_TOKEN"); + if (e) + token = e; + } + + if (url.empty() || token.empty()) { + std::cerr << "Usage: video_producer \n" + << " or: LIVEKIT_URL=... LIVEKIT_TOKEN=... video_producer\n"; + return 1; + } + + std::signal(SIGINT, handleSignal); + + if (!SDL_Init(SDL_INIT_CAMERA)) { + LK_LOG_ERROR("[video_producer] SDL_Init failed: {}", SDL_GetError()); + return 1; + } + + session_manager::SessionManager sm; + LK_LOG_INFO("[video_producer] Connecting to {} ...", url); + livekit::RoomOptions options; + options.auto_subscribe = true; + if (!sm.connect(url, token, options)) { + LK_LOG_ERROR("[video_producer] Failed to connect."); + SDL_Quit(); + return 1; + } + + livekit::Room *room = sm.getRoom(); + if (!room) { + LK_LOG_ERROR("[video_producer] getRoom() returned nullptr."); + sm.disconnect(); + SDL_Quit(); + return 1; + } + + LK_LOG_INFO("[video_producer] Connected."); + + auto info = room->room_info(); + std::cout << "[video_producer] Room info:\n" + << " SID: " << (info.sid ? *info.sid : "(none)") << "\n" + << " Name: " << info.name << "\n" + << " Num participants: " << info.num_participants << "\n"; + + constexpr int kWidth = 1280; + constexpr int kHeight = 720; + + auto videoSource = std::make_shared(kWidth, kHeight); + auto videoTrack = + LocalVideoTrack::createLocalVideoTrack("cam", videoSource); + + TrackPublishOptions videoOpts; + videoOpts.source = TrackSource::SOURCE_CAMERA; + videoOpts.dtx = false; + videoOpts.simulcast = true; + + std::shared_ptr videoPub; + try { + videoPub = room->localParticipant()->publishTrack(videoTrack, videoOpts); + LK_LOG_INFO("[video_producer] Published cam track {}x{} (SID: {}).", + kWidth, kHeight, videoPub->sid()); + } catch (const std::exception &e) { + LK_LOG_ERROR("[video_producer] Failed to publish track: {}", e.what()); + sm.disconnect(); + SDL_Quit(); + return 1; + } + + // ---- SDL Camera capture or fallback ---- + bool cam_using_sdl = false; + std::unique_ptr sdl_cam; + std::atomic cam_running{true}; + std::thread cam_thread; + + { + int camCount = 0; + SDL_CameraID *cams = SDL_GetCameras(&camCount); + bool has_cam = cams && camCount > 0; + if (cams) + SDL_free(cams); + + if (has_cam) { + sdl_cam = std::make_unique( + kWidth, kHeight, 30, SDL_PIXELFORMAT_RGBA32, + [videoSource](const uint8_t *pixels, int pitch, int width, int height, + SDL_PixelFormat /*fmt*/, Uint64 timestampNS) { + auto frame = + VideoFrame::create(width, height, VideoBufferType::RGBA); + uint8_t *dst = frame.data(); + const int dstPitch = width * 4; + for (int y = 0; y < height; ++y) { + std::memcpy(dst + y * dstPitch, pixels + y * pitch, dstPitch); + } + try { + videoSource->captureFrame( + frame, static_cast(timestampNS / 1000), + VideoRotation::VIDEO_ROTATION_0); + } catch (const std::exception &e) { + LK_LOG_ERROR("[video_producer] captureFrame error: {}", e.what()); + } + }); + + if (sdl_cam->init()) { + cam_using_sdl = true; + LK_LOG_INFO("[video_producer] Using SDL webcam."); + cam_thread = std::thread([&]() { + while (cam_running.load()) { + sdl_cam->pump(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + }); + } else { + LK_LOG_ERROR("[video_producer] SDL camera init failed."); + sdl_cam.reset(); + } + } + + if (!cam_using_sdl) { + LK_LOG_INFO("[video_producer] No camera found; sending solid blue " + "frames."); + cam_thread = std::thread([videoSource, &cam_running]() { + auto frame = + VideoFrame::create(kWidth, kHeight, VideoBufferType::RGBA); + uint8_t *dst = frame.data(); + for (int i = 0; i < kWidth * kHeight; ++i) { + dst[i * 4 + 0] = 0; + dst[i * 4 + 1] = 0; + dst[i * 4 + 2] = 180; + dst[i * 4 + 3] = 255; + } + std::int64_t ts = 0; + while (cam_running.load()) { + try { + videoSource->captureFrame(frame, ts, + VideoRotation::VIDEO_ROTATION_0); + } catch (...) { + break; + } + ts += 33333; + std::this_thread::sleep_for(std::chrono::milliseconds(33)); + } + }); + } + } + + LK_LOG_INFO("[video_producer] Streaming... press Ctrl-C to stop."); + + while (g_running.load()) { + SDL_Event e; + while (SDL_PollEvent(&e)) { + if (e.type == SDL_EVENT_QUIT) { + g_running.store(false); + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + // ---- Cleanup ---- + LK_LOG_INFO("[video_producer] Shutting down..."); + cam_running.store(false); + if (cam_thread.joinable()) + cam_thread.join(); + sdl_cam.reset(); + + room->localParticipant()->unpublishTrack(videoPub->sid()); + sm.disconnect(); + SDL_Quit(); + LK_LOG_INFO("[video_producer] Done."); + return 0; +} diff --git a/examples/session_manager_base_sdk/video_viewer.cpp b/examples/session_manager_base_sdk/video_viewer.cpp new file mode 100644 index 00000000..decee09a --- /dev/null +++ b/examples/session_manager_base_sdk/video_viewer.cpp @@ -0,0 +1,154 @@ +/* + * Copyright 2025 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. + */ + +/* + * SessionManager + base SDK video viewer. + * + * SessionManager handles connect/disconnect; getRoom() provides the Room. + * All other work uses the Room directly: room_info(), remoteParticipants(), + * track publications, VideoStream::fromTrack(), etc. + * + * Usage: + * video_viewer + * LIVEKIT_URL=... LIVEKIT_TOKEN=... video_viewer + * + * Use identity "viewer" for the token. Run video_producer (identity + * "producer") first. Displays the producer's camera track in an SDL window. + */ + +#include "livekit/livekit.h" +#include "lk_log.h" +#include "session_manager/session_manager.h" +#include "sdl_media_manager.h" + +#include + +#include +#include +#include +#include +#include +#include + +using namespace livekit; + +std::atomic g_running{true}; +static void handleSignal(int) { g_running.store(false); } + +int main(int argc, char *argv[]) { + std::string url, token; + if (argc >= 3) { + url = argv[1]; + token = argv[2]; + } else { + const char *e = std::getenv("LIVEKIT_URL"); + if (e) + url = e; + e = std::getenv("LIVEKIT_TOKEN"); + if (e) + token = e; + } + + if (url.empty() || token.empty()) { + std::cerr << "Usage: video_viewer \n" + << " or: LIVEKIT_URL=... LIVEKIT_TOKEN=... video_viewer\n"; + return 1; + } + + std::signal(SIGINT, handleSignal); + + if (!SDL_Init(SDL_INIT_VIDEO)) { + LK_LOG_ERROR("[video_viewer] SDL_Init failed: {}", SDL_GetError()); + return 1; + } + + SDLMediaManager media; + + session_manager::SessionManager sm; + LK_LOG_INFO("[video_viewer] Connecting to {} ...", url); + livekit::RoomOptions options; + options.auto_subscribe = true; + if (!sm.connect(url, token, options)) { + LK_LOG_ERROR("[video_viewer] Failed to connect."); + SDL_Quit(); + return 1; + } + + livekit::Room *room = sm.getRoom(); + if (!room) { + LK_LOG_ERROR("[video_viewer] getRoom() returned nullptr."); + sm.disconnect(); + SDL_Quit(); + return 1; + } + + LK_LOG_INFO("[video_viewer] Connected. Waiting for producer..."); + + auto info = room->room_info(); + std::cout << "[video_viewer] Room info:\n" + << " SID: " << (info.sid ? *info.sid : "(none)") << "\n" + << " Name: " << info.name << "\n" + << " Num participants: " << info.num_participants << "\n"; + + std::cout << "[video_viewer] Waiting for video... Ctrl-C or close window to " + "stop.\n"; + + bool renderer_initialized = false; + + while (g_running.load()) { + // Poll room for subscribed video tracks (SessionManager owns the delegate; + // we discover tracks via room->remoteParticipants() and their publications) + if (!renderer_initialized && room) { + for (const auto &rp : room->remoteParticipants()) { + for (const auto &[sid, pub] : rp->trackPublications()) { + if (!pub->subscribed() || pub->kind() != TrackKind::KIND_VIDEO) { + continue; + } + if (pub->source() != TrackSource::SOURCE_CAMERA) { + continue; + } + auto track = pub->track(); + if (!track) { + continue; + } + + VideoStream::Options opts; + opts.format = VideoBufferType::RGBA; + auto video_stream = VideoStream::fromTrack(track, opts); + if (video_stream && media.initRenderer(video_stream)) { + std::cout << "[video_viewer] Subscribed to video track from " + << rp->identity() << "\n"; + renderer_initialized = true; + break; + } + } + if (renderer_initialized) { + break; + } + } + } + + media.render(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + LK_LOG_INFO("[video_viewer] Shutting down..."); + media.shutdownRenderer(); + sm.disconnect(); + SDL_Quit(); + LK_LOG_INFO("[video_viewer] Done."); + return 0; +} diff --git a/include/session_manager/session_manager.h b/include/session_manager/session_manager.h index f739c900..2c28b00a 100644 --- a/include/session_manager/session_manager.h +++ b/include/session_manager/session_manager.h @@ -155,6 +155,27 @@ class SessionManager { /// Whether the SessionManager is currently connected to a room. bool isConnected() const; + /** + * Get the underlying Room for direct base SDK access. + * + * Use this to access room_info(), remoteParticipants(), + * registerTextStreamHandler, registerByteStreamHandler, e2eeManager(), etc. + * + * @return Non-null pointer to the Room while connected; nullptr otherwise. + * + * @note The pointer is valid only while isConnected() is true. Do not store + * the pointer across disconnect/reconnect cycles. + * + * @warning Do NOT call setDelegate() on the returned Room — SessionManager + * manages the delegate for room lifecycle and track events. + * + * @note For publishing audio/video tracks, prefer createAudioTrack() and + * createVideoTrack() so SessionManager can manage track lifecycle on + * disconnect. Direct publishTrack() via localParticipant() bypasses + * that management. + */ + livekit::Room *getRoom() const; + // --------------------------------------------------------------- // Track creation (publishing) // --------------------------------------------------------------- diff --git a/src/session_manager/README.md b/src/session_manager/README.md index b75c7692..01693f55 100644 --- a/src/session_manager/README.md +++ b/src/session_manager/README.md @@ -60,6 +60,27 @@ cam.reset(); // unpublishes the video track sm.disconnect(); ``` +### Accessing the Base SDK via getRoom() + +The SessionManager owns the underlying `livekit::Room` internally. Use `getRoom()` to access base SDK features while the SessionManager manages the connection lifecycle: + +```cpp +if (auto* room = sm.getRoom()) { + auto info = room->room_info(); + // ... use info.sid, info.name, info.num_participants, etc. + + for (const auto& rp : room->remoteParticipants()) { + // ... enumerate remote participants + } + + room->registerTextStreamHandler("topic", [](auto reader, auto identity) { + // ... handle incoming text streams + }); +} +``` + +The returned pointer is valid only while `isConnected()` is true. Do not call `setDelegate()` on the returned Room — SessionManager manages the delegate. For publishing audio/video tracks, prefer `createAudioTrack()` and `createVideoTrack()` so SessionManager can manage track lifecycle on disconnect. + ## Architecture ### Data Flow Overview @@ -133,6 +154,7 @@ sm.connect(url, token, options); | `connect(url, token, options)` | Connect to a LiveKit room. Initializes the SDK, creates a Room, and connects with auto-subscribe enabled. | | `disconnect()` | Disconnect and release all resources. Joins all reader threads. Safe to call multiple times. | | `isConnected()` | Returns whether the SessionManager is currently connected. | +| `getRoom()` | Returns a raw pointer to the underlying `livekit::Room` for direct base SDK access (e.g. `room_info()`, `remoteParticipants()`, `registerTextStreamHandler`). Returns `nullptr` when disconnected. Do not call `setDelegate()` on the returned Room. | | `createAudioTrack(name, sample_rate, num_channels, source)` | Create and publish a local audio track with the given `TrackSource` (e.g. `SOURCE_MICROPHONE`, `SOURCE_SCREENSHARE_AUDIO`). Returns an RAII `shared_ptr`. | | `createVideoTrack(name, width, height, source)` | Create and publish a local video track with the given `TrackSource` (e.g. `SOURCE_CAMERA`, `SOURCE_SCREENSHARE`). Returns an RAII `shared_ptr`. | | `setOnAudioFrameCallback(identity, source, callback)` | Register a callback for audio frames from a specific remote participant + track source. | @@ -164,11 +186,17 @@ sm.connect(url, token, options); | `name()` / `width()` / `height()` | Accessors for track configuration. | ## Examples -- examples/robot.cpp: publishes video and audio from a webcam and microphone. This requires a webcam and microphone to be available. -- examples/human.cpp: receives and renders video to the screen, receives and plays audio through the speaker. -### Running the examples: -Note: the following workflow works for both `human` and `robot`. + + +### Running the examples + +``` +export LIVEKIT_URL="wss://your-server.livekit.cloud" +export LIVEKIT_TOKEN= +./build-release/bin/ +``` +(Or pass ` ` as positional arguments.) 1. create a `robo_room` ``` @@ -199,7 +227,7 @@ export LIVEKIT_TOKEN= ``` export LIVEKIT_URL="wss://your-server.livekit.cloud" export LIVEKIT_TOKEN= -./build-release/bin/human +./build-release/bin/HumanRobotHuman ``` The human will print periodic summaries like: diff --git a/src/session_manager/session_manager.cpp b/src/session_manager/session_manager.cpp index dbf538fb..091cd226 100644 --- a/src/session_manager/session_manager.cpp +++ b/src/session_manager/session_manager.cpp @@ -220,6 +220,11 @@ bool SessionManager::isConnected() const { return connected_; } +livekit::Room *SessionManager::getRoom() const { + std::lock_guard lock(mutex_); + return (connected_ && room_) ? room_.get() : nullptr; +} + // --------------------------------------------------------------- // Track creation (publishing) // --------------------------------------------------------------- From 8b31f1184627ca5ae77c70862f88ec67d149edc9 Mon Sep 17 00:00:00 2001 From: Stephen DeRosa Date: Tue, 10 Mar 2026 15:25:00 -0600 Subject: [PATCH 07/13] windows compile --- .../session_manager_base_sdk/video_producer.cpp | 16 +++++++--------- src/session_manager/README.md | 9 ++------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/examples/session_manager_base_sdk/video_producer.cpp b/examples/session_manager_base_sdk/video_producer.cpp index eb579346..892b702f 100644 --- a/examples/session_manager_base_sdk/video_producer.cpp +++ b/examples/session_manager_base_sdk/video_producer.cpp @@ -32,8 +32,8 @@ #include "livekit/livekit.h" #include "lk_log.h" -#include "session_manager/session_manager.h" #include "sdl_media.h" +#include "session_manager/session_manager.h" #include @@ -109,8 +109,7 @@ int main(int argc, char *argv[]) { constexpr int kHeight = 720; auto videoSource = std::make_shared(kWidth, kHeight); - auto videoTrack = - LocalVideoTrack::createLocalVideoTrack("cam", videoSource); + auto videoTrack = LocalVideoTrack::createLocalVideoTrack("cam", videoSource); TrackPublishOptions videoOpts; videoOpts.source = TrackSource::SOURCE_CAMERA; @@ -120,8 +119,8 @@ int main(int argc, char *argv[]) { std::shared_ptr videoPub; try { videoPub = room->localParticipant()->publishTrack(videoTrack, videoOpts); - LK_LOG_INFO("[video_producer] Published cam track {}x{} (SID: {}).", - kWidth, kHeight, videoPub->sid()); + LK_LOG_INFO("[video_producer] Published cam track {}x{} (SID: {}).", kWidth, + kHeight, videoPub->sid()); } catch (const std::exception &e) { LK_LOG_ERROR("[video_producer] Failed to publish track: {}", e.what()); sm.disconnect(); @@ -181,9 +180,8 @@ int main(int argc, char *argv[]) { if (!cam_using_sdl) { LK_LOG_INFO("[video_producer] No camera found; sending solid blue " "frames."); - cam_thread = std::thread([videoSource, &cam_running]() { - auto frame = - VideoFrame::create(kWidth, kHeight, VideoBufferType::RGBA); + cam_thread = std::thread([videoSource, &cam_running, kWidth, kHeight]() { + auto frame = VideoFrame::create(kWidth, kHeight, VideoBufferType::RGBA); uint8_t *dst = frame.data(); for (int i = 0; i < kWidth * kHeight; ++i) { dst[i * 4 + 0] = 0; @@ -195,7 +193,7 @@ int main(int argc, char *argv[]) { while (cam_running.load()) { try { videoSource->captureFrame(frame, ts, - VideoRotation::VIDEO_ROTATION_0); + VideoRotation::VIDEO_ROTATION_0); } catch (...) { break; } diff --git a/src/session_manager/README.md b/src/session_manager/README.md index 01693f55..480ff16d 100644 --- a/src/session_manager/README.md +++ b/src/session_manager/README.md @@ -1,8 +1,7 @@ # LiveKit SessionManager A simplified, high-level C++ wrapper around the [LiveKit C++ SDK](../README.md). -The SessionManager abstracts away room lifecycle management, track creation, publishing, and subscription boilerplate so that external codebases can interface with LiveKit in just a few lines. -It is intended that this library will be used to integrate the LiveKit C++ SDK into other SDKs such as, but not limited to, Foxglove, ROS, and Rerun. +The SessionManager abstracts away room lifecycle management, track creation, publishing, and subscription boilerplate so interfacing with LiveKit is just a few lines of code. ## Usage Overview @@ -185,11 +184,7 @@ sm.connect(url, token, options); | `release()` | Explicitly unpublish and free resources. Called automatically by the destructor. | | `name()` / `width()` / `height()` | Accessors for track configuration. | -## Examples - - - -### Running the examples +## Running the examples ``` export LIVEKIT_URL="wss://your-server.livekit.cloud" From d5b457710c46c11389b3412a9cf99d174b52b55a Mon Sep 17 00:00:00 2001 From: Stephen DeRosa Date: Tue, 10 Mar 2026 15:40:24 -0600 Subject: [PATCH 08/13] move session_manager inside include/livekit/ & clang --- examples/human_robot/human.cpp | 2 +- examples/human_robot/robot.cpp | 2 +- examples/mute_unmute/caller.cpp | 2 +- examples/mute_unmute/receiver.cpp | 2 +- examples/rpc/custom_caller.cpp | 2 +- examples/rpc/custom_receiver.cpp | 2 +- examples/session_manager_base_sdk/video_producer.cpp | 2 +- examples/session_manager_base_sdk/video_viewer.cpp | 2 +- include/{ => livekit}/session_manager/managed_audio_track.h | 0 include/{ => livekit}/session_manager/managed_video_track.h | 0 include/{ => livekit}/session_manager/rpc_constants.h | 0 include/{ => livekit}/session_manager/session_manager.h | 6 +++--- src/session_manager/README.md | 2 +- src/session_manager/managed_audio_track.cpp | 2 +- src/session_manager/managed_video_track.cpp | 2 +- src/session_manager/rpc_controller.cpp | 2 +- src/session_manager/rpc_controller.h | 2 +- src/session_manager/session_manager.cpp | 4 ++-- src/session_manager/session_manager_room_delegate.cpp | 2 +- src/session_manager/session_manager_room_delegate.h | 3 ++- src/tests/session_manager/test_callback_key.cpp | 2 +- src/tests/session_manager/test_managed_audio_track.cpp | 2 +- src/tests/session_manager/test_managed_video_track.cpp | 2 +- src/tests/session_manager/test_rpc_controller.cpp | 2 +- src/tests/session_manager/test_session_manager.cpp | 2 +- 25 files changed, 26 insertions(+), 25 deletions(-) rename include/{ => livekit}/session_manager/managed_audio_track.h (100%) rename include/{ => livekit}/session_manager/managed_video_track.h (100%) rename include/{ => livekit}/session_manager/rpc_constants.h (100%) rename include/{ => livekit}/session_manager/session_manager.h (99%) diff --git a/examples/human_robot/human.cpp b/examples/human_robot/human.cpp index 85618568..a5a6cded 100644 --- a/examples/human_robot/human.cpp +++ b/examples/human_robot/human.cpp @@ -44,11 +44,11 @@ */ #include "livekit/audio_frame.h" +#include "livekit/session_manager/session_manager.h" #include "livekit/track.h" #include "livekit/video_frame.h" #include "lk_log.h" #include "sdl_media.h" -#include "session_manager/session_manager.h" #include diff --git a/examples/human_robot/robot.cpp b/examples/human_robot/robot.cpp index 388dd8e7..f0e6e615 100644 --- a/examples/human_robot/robot.cpp +++ b/examples/human_robot/robot.cpp @@ -34,11 +34,11 @@ */ #include "livekit/audio_frame.h" +#include "livekit/session_manager/session_manager.h" #include "livekit/track.h" #include "livekit/video_frame.h" #include "lk_log.h" #include "sdl_media.h" -#include "session_manager/session_manager.h" #include diff --git a/examples/mute_unmute/caller.cpp b/examples/mute_unmute/caller.cpp index be1bc1ba..936f48c8 100644 --- a/examples/mute_unmute/caller.cpp +++ b/examples/mute_unmute/caller.cpp @@ -33,10 +33,10 @@ #include "livekit/audio_frame.h" #include "livekit/rpc_error.h" +#include "livekit/session_manager/session_manager.h" #include "livekit/track.h" #include "livekit/video_frame.h" #include "sdl_media.h" -#include "session_manager/session_manager.h" #include diff --git a/examples/mute_unmute/receiver.cpp b/examples/mute_unmute/receiver.cpp index 5f4f9090..bd5c3494 100644 --- a/examples/mute_unmute/receiver.cpp +++ b/examples/mute_unmute/receiver.cpp @@ -33,9 +33,9 @@ * --join --room my-room --identity receiver --valid-for 24h */ +#include "livekit/session_manager/session_manager.h" #include "livekit/track.h" #include "sdl_media.h" -#include "session_manager/session_manager.h" #include diff --git a/examples/rpc/custom_caller.cpp b/examples/rpc/custom_caller.cpp index bae614be..3771fa58 100644 --- a/examples/rpc/custom_caller.cpp +++ b/examples/rpc/custom_caller.cpp @@ -31,7 +31,7 @@ */ #include "livekit/rpc_error.h" -#include "session_manager/session_manager.h" +#include "livekit/session_manager/session_manager.h" #include #include diff --git a/examples/rpc/custom_receiver.cpp b/examples/rpc/custom_receiver.cpp index 8c3a1814..5f96c729 100644 --- a/examples/rpc/custom_receiver.cpp +++ b/examples/rpc/custom_receiver.cpp @@ -28,7 +28,7 @@ * lk token create --join --room --identity receiver --valid-for 24h */ -#include "session_manager/session_manager.h" +#include "livekit/session_manager/session_manager.h" #include #include diff --git a/examples/session_manager_base_sdk/video_producer.cpp b/examples/session_manager_base_sdk/video_producer.cpp index 892b702f..90a460c5 100644 --- a/examples/session_manager_base_sdk/video_producer.cpp +++ b/examples/session_manager_base_sdk/video_producer.cpp @@ -31,9 +31,9 @@ */ #include "livekit/livekit.h" +#include "livekit/session_manager/session_manager.h" #include "lk_log.h" #include "sdl_media.h" -#include "session_manager/session_manager.h" #include diff --git a/examples/session_manager_base_sdk/video_viewer.cpp b/examples/session_manager_base_sdk/video_viewer.cpp index decee09a..a8717780 100644 --- a/examples/session_manager_base_sdk/video_viewer.cpp +++ b/examples/session_manager_base_sdk/video_viewer.cpp @@ -30,8 +30,8 @@ */ #include "livekit/livekit.h" +#include "livekit/session_manager/session_manager.h" #include "lk_log.h" -#include "session_manager/session_manager.h" #include "sdl_media_manager.h" #include diff --git a/include/session_manager/managed_audio_track.h b/include/livekit/session_manager/managed_audio_track.h similarity index 100% rename from include/session_manager/managed_audio_track.h rename to include/livekit/session_manager/managed_audio_track.h diff --git a/include/session_manager/managed_video_track.h b/include/livekit/session_manager/managed_video_track.h similarity index 100% rename from include/session_manager/managed_video_track.h rename to include/livekit/session_manager/managed_video_track.h diff --git a/include/session_manager/rpc_constants.h b/include/livekit/session_manager/rpc_constants.h similarity index 100% rename from include/session_manager/rpc_constants.h rename to include/livekit/session_manager/rpc_constants.h diff --git a/include/session_manager/session_manager.h b/include/livekit/session_manager/session_manager.h similarity index 99% rename from include/session_manager/session_manager.h rename to include/livekit/session_manager/session_manager.h index 2c28b00a..6175fcb1 100644 --- a/include/session_manager/session_manager.h +++ b/include/livekit/session_manager/session_manager.h @@ -19,9 +19,9 @@ #pragma once -#include "session_manager/managed_audio_track.h" -#include "session_manager/managed_video_track.h" -#include "session_manager/rpc_constants.h" +#include "livekit/session_manager/managed_audio_track.h" +#include "livekit/session_manager/managed_video_track.h" +#include "livekit/session_manager/rpc_constants.h" #include "livekit/local_participant.h" #include "livekit/room.h" diff --git a/src/session_manager/README.md b/src/session_manager/README.md index 480ff16d..cfa9913b 100644 --- a/src/session_manager/README.md +++ b/src/session_manager/README.md @@ -6,7 +6,7 @@ The SessionManager abstracts away room lifecycle management, track creation, pub ## Usage Overview ```cpp -#include "session_manager/session_manager.h" +#include "livekit/session_manager/session_manager.h" #include "livekit/audio_frame.h" #include "livekit/video_frame.h" #include "livekit/track.h" diff --git a/src/session_manager/managed_audio_track.cpp b/src/session_manager/managed_audio_track.cpp index a31ada1b..eafb9f90 100644 --- a/src/session_manager/managed_audio_track.cpp +++ b/src/session_manager/managed_audio_track.cpp @@ -17,7 +17,7 @@ /// @file managed_audio_track.cpp /// @brief Implementation of ManagedAudioTrack. -#include "session_manager/managed_audio_track.h" +#include "livekit/session_manager/managed_audio_track.h" #include "livekit/audio_frame.h" #include "livekit/audio_source.h" diff --git a/src/session_manager/managed_video_track.cpp b/src/session_manager/managed_video_track.cpp index 7c91b67a..9b276fd5 100644 --- a/src/session_manager/managed_video_track.cpp +++ b/src/session_manager/managed_video_track.cpp @@ -17,7 +17,7 @@ /// @file managed_video_track.cpp /// @brief Implementation of ManagedVideoTrack. -#include "session_manager/managed_video_track.h" +#include "livekit/session_manager/managed_video_track.h" #include "livekit/local_participant.h" #include "livekit/local_track_publication.h" diff --git a/src/session_manager/rpc_controller.cpp b/src/session_manager/rpc_controller.cpp index ef4482e1..96c58cab 100644 --- a/src/session_manager/rpc_controller.cpp +++ b/src/session_manager/rpc_controller.cpp @@ -18,7 +18,7 @@ /// @brief Implementation of RpcController. #include "rpc_controller.h" -#include "session_manager/rpc_constants.h" +#include "livekit/session_manager/rpc_constants.h" #include "livekit/local_participant.h" #include "livekit/rpc_error.h" diff --git a/src/session_manager/rpc_controller.h b/src/session_manager/rpc_controller.h index cd532956..d673d23a 100644 --- a/src/session_manager/rpc_controller.h +++ b/src/session_manager/rpc_controller.h @@ -21,7 +21,7 @@ #pragma once #include "livekit/local_participant.h" -#include "session_manager/rpc_constants.h" +#include "livekit/session_manager/rpc_constants.h" #include #include diff --git a/src/session_manager/session_manager.cpp b/src/session_manager/session_manager.cpp index 091cd226..529bb8bd 100644 --- a/src/session_manager/session_manager.cpp +++ b/src/session_manager/session_manager.cpp @@ -17,9 +17,9 @@ /// @file session_manager.cpp /// @brief Implementation of the SessionManager high-level API. -#include "session_manager/session_manager.h" +#include "livekit/session_manager/session_manager.h" +#include "livekit/session_manager/rpc_constants.h" #include "rpc_controller.h" -#include "session_manager/rpc_constants.h" #include "session_manager_room_delegate.h" #include "livekit/audio_frame.h" diff --git a/src/session_manager/session_manager_room_delegate.cpp b/src/session_manager/session_manager_room_delegate.cpp index 3f95d406..a60f1398 100644 --- a/src/session_manager/session_manager_room_delegate.cpp +++ b/src/session_manager/session_manager_room_delegate.cpp @@ -21,8 +21,8 @@ #include "livekit/remote_participant.h" #include "livekit/remote_track_publication.h" +#include "livekit/session_manager/session_manager.h" #include "livekit/track.h" -#include "session_manager/session_manager.h" namespace session_manager { diff --git a/src/session_manager/session_manager_room_delegate.h b/src/session_manager/session_manager_room_delegate.h index 2b1d3033..0d2513e6 100644 --- a/src/session_manager/session_manager_room_delegate.h +++ b/src/session_manager/session_manager_room_delegate.h @@ -33,7 +33,8 @@ class SessionManager; */ class SessionManagerRoomDelegate : public livekit::RoomDelegate { public: - explicit SessionManagerRoomDelegate(SessionManager &manager) : manager_(manager) {} + explicit SessionManagerRoomDelegate(SessionManager &manager) + : manager_(manager) {} /// Forwards a track-subscribed event to SessionManager::onTrackSubscribed(). void onTrackSubscribed(livekit::Room &room, diff --git a/src/tests/session_manager/test_callback_key.cpp b/src/tests/session_manager/test_callback_key.cpp index e8a3b149..6834e878 100644 --- a/src/tests/session_manager/test_callback_key.cpp +++ b/src/tests/session_manager/test_callback_key.cpp @@ -18,7 +18,7 @@ /// @brief Unit tests for SessionManager::CallbackKey hash and equality. #include -#include +#include #include diff --git a/src/tests/session_manager/test_managed_audio_track.cpp b/src/tests/session_manager/test_managed_audio_track.cpp index 0d25e51d..5667ce7a 100644 --- a/src/tests/session_manager/test_managed_audio_track.cpp +++ b/src/tests/session_manager/test_managed_audio_track.cpp @@ -18,7 +18,7 @@ /// @brief Unit tests for ManagedAudioTrack. #include -#include +#include #include #include diff --git a/src/tests/session_manager/test_managed_video_track.cpp b/src/tests/session_manager/test_managed_video_track.cpp index 29a5ed36..7ce2c1d0 100644 --- a/src/tests/session_manager/test_managed_video_track.cpp +++ b/src/tests/session_manager/test_managed_video_track.cpp @@ -18,7 +18,7 @@ /// @brief Unit tests for ManagedVideoTrack. #include -#include +#include #include #include diff --git a/src/tests/session_manager/test_rpc_controller.cpp b/src/tests/session_manager/test_rpc_controller.cpp index 5080fc1d..a5b72b48 100644 --- a/src/tests/session_manager/test_rpc_controller.cpp +++ b/src/tests/session_manager/test_rpc_controller.cpp @@ -19,8 +19,8 @@ #include +#include "livekit/session_manager/rpc_constants.h" #include "rpc_controller.h" -#include "session_manager/rpc_constants.h" #include "livekit/local_participant.h" #include "livekit/rpc_error.h" diff --git a/src/tests/session_manager/test_session_manager.cpp b/src/tests/session_manager/test_session_manager.cpp index ff6ac642..9fbe0ebd 100644 --- a/src/tests/session_manager/test_session_manager.cpp +++ b/src/tests/session_manager/test_session_manager.cpp @@ -18,7 +18,7 @@ /// @brief Unit tests for SessionManager. #include -#include +#include #include From 7d8980f4bb759653fc1313a545a60ab35b80d6c7 Mon Sep 17 00:00:00 2001 From: Stephen DeRosa Date: Wed, 11 Mar 2026 10:29:36 -0600 Subject: [PATCH 09/13] remove session_manager namespace, use livekit --- examples/human_robot/human.cpp | 2 +- examples/human_robot/robot.cpp | 4 ++-- examples/mute_unmute/caller.cpp | 2 +- examples/mute_unmute/receiver.cpp | 2 +- examples/rpc/custom_caller.cpp | 2 +- examples/rpc/custom_receiver.cpp | 2 +- examples/session_manager_base_sdk/video_producer.cpp | 2 +- examples/session_manager_base_sdk/video_viewer.cpp | 2 +- include/livekit/session_manager/managed_audio_track.h | 4 ++-- include/livekit/session_manager/managed_video_track.h | 4 ++-- include/livekit/session_manager/rpc_constants.h | 4 ++-- include/livekit/session_manager/session_manager.h | 6 ++---- src/session_manager/README.md | 2 +- src/session_manager/managed_audio_track.cpp | 4 ++-- src/session_manager/managed_video_track.cpp | 4 ++-- src/session_manager/rpc_controller.cpp | 4 ++-- src/session_manager/rpc_controller.h | 6 ++---- src/session_manager/session_manager.cpp | 4 ++-- src/session_manager/session_manager_room_delegate.cpp | 4 ++-- src/session_manager/session_manager_room_delegate.h | 4 ++-- .../integration/test_session_manager_rpc_roundtrip.cpp | 4 ++-- src/tests/session_manager/test_callback_key.cpp | 4 ++-- src/tests/session_manager/test_managed_audio_track.cpp | 4 ++-- src/tests/session_manager/test_managed_video_track.cpp | 4 ++-- src/tests/session_manager/test_rpc_controller.cpp | 4 ++-- src/tests/session_manager/test_session_manager.cpp | 4 ++-- 26 files changed, 44 insertions(+), 48 deletions(-) diff --git a/examples/human_robot/human.cpp b/examples/human_robot/human.cpp index a5a6cded..cb546654 100644 --- a/examples/human_robot/human.cpp +++ b/examples/human_robot/human.cpp @@ -192,7 +192,7 @@ int main(int argc, char *argv[]) { std::mutex speaker_mutex; // ----- Connect to LiveKit ----- - session_manager::SessionManager sm; + livekit::SessionManager sm; std::cout << "[human] Connecting to " << url << " ...\n"; livekit::RoomOptions options; options.auto_subscribe = true; diff --git a/examples/human_robot/robot.cpp b/examples/human_robot/robot.cpp index f0e6e615..6b637647 100644 --- a/examples/human_robot/robot.cpp +++ b/examples/human_robot/robot.cpp @@ -357,7 +357,7 @@ int main(int argc, char *argv[]) { } // ----- Connect to LiveKit ----- - session_manager::SessionManager sm; + livekit::SessionManager sm; LK_LOG_INFO("[robot] Connecting to {} ...", url); livekit::RoomOptions options; options.auto_subscribe = true; @@ -377,7 +377,7 @@ int main(int argc, char *argv[]) { constexpr int kSimWidth = 480; constexpr int kSimHeight = 320; - std::shared_ptr mic; + std::shared_ptr mic; if (use_mic) { mic = sm.createAudioTrack("robot-mic", kSampleRate, kChannels, livekit::TrackSource::SOURCE_MICROPHONE); diff --git a/examples/mute_unmute/caller.cpp b/examples/mute_unmute/caller.cpp index 936f48c8..6119b7f5 100644 --- a/examples/mute_unmute/caller.cpp +++ b/examples/mute_unmute/caller.cpp @@ -146,7 +146,7 @@ int main(int argc, char *argv[]) { std::mutex speaker_mutex; // ----- Connect to LiveKit ----- - session_manager::SessionManager sm; + livekit::SessionManager sm; std::cout << "[caller] Connecting to " << url << " ...\n"; livekit::RoomOptions options; diff --git a/examples/mute_unmute/receiver.cpp b/examples/mute_unmute/receiver.cpp index bd5c3494..dce5874d 100644 --- a/examples/mute_unmute/receiver.cpp +++ b/examples/mute_unmute/receiver.cpp @@ -83,7 +83,7 @@ int main(int argc, char *argv[]) { } // ----- Connect to LiveKit ----- - session_manager::SessionManager sm; + livekit::SessionManager sm; std::cout << "[receiver] Connecting to " << url << " ...\n"; livekit::RoomOptions options; diff --git a/examples/rpc/custom_caller.cpp b/examples/rpc/custom_caller.cpp index 3771fa58..22e2c16a 100644 --- a/examples/rpc/custom_caller.cpp +++ b/examples/rpc/custom_caller.cpp @@ -65,7 +65,7 @@ int main(int argc, char *argv[]) { std::signal(SIGINT, handleSignal); - session_manager::SessionManager sm; + livekit::SessionManager sm; std::cout << "[caller] Connecting to " << url << " ...\n"; livekit::RoomOptions options; diff --git a/examples/rpc/custom_receiver.cpp b/examples/rpc/custom_receiver.cpp index 5f96c729..e06c45e5 100644 --- a/examples/rpc/custom_receiver.cpp +++ b/examples/rpc/custom_receiver.cpp @@ -62,7 +62,7 @@ int main(int argc, char *argv[]) { std::signal(SIGINT, handleSignal); - session_manager::SessionManager sm; + livekit::SessionManager sm; std::cout << "[receiver] Connecting to " << url << " ...\n"; livekit::RoomOptions options; diff --git a/examples/session_manager_base_sdk/video_producer.cpp b/examples/session_manager_base_sdk/video_producer.cpp index 90a460c5..6e8897a1 100644 --- a/examples/session_manager_base_sdk/video_producer.cpp +++ b/examples/session_manager_base_sdk/video_producer.cpp @@ -79,7 +79,7 @@ int main(int argc, char *argv[]) { return 1; } - session_manager::SessionManager sm; + livekit::SessionManager sm; LK_LOG_INFO("[video_producer] Connecting to {} ...", url); livekit::RoomOptions options; options.auto_subscribe = true; diff --git a/examples/session_manager_base_sdk/video_viewer.cpp b/examples/session_manager_base_sdk/video_viewer.cpp index a8717780..bfc63b2d 100644 --- a/examples/session_manager_base_sdk/video_viewer.cpp +++ b/examples/session_manager_base_sdk/video_viewer.cpp @@ -77,7 +77,7 @@ int main(int argc, char *argv[]) { SDLMediaManager media; - session_manager::SessionManager sm; + livekit::SessionManager sm; LK_LOG_INFO("[video_viewer] Connecting to {} ...", url); livekit::RoomOptions options; options.auto_subscribe = true; diff --git a/include/livekit/session_manager/managed_audio_track.h b/include/livekit/session_manager/managed_audio_track.h index 2432f426..44fc1500 100644 --- a/include/livekit/session_manager/managed_audio_track.h +++ b/include/livekit/session_manager/managed_audio_track.h @@ -32,7 +32,7 @@ class LocalTrackPublication; class LocalParticipant; } // namespace livekit -namespace session_manager { +namespace livekit { namespace test { class ManagedAudioTrackTest; @@ -144,4 +144,4 @@ class ManagedAudioTrack { livekit::LocalParticipant *participant_ = nullptr; // not owned }; -} // namespace session_manager +} // namespace livekit diff --git a/include/livekit/session_manager/managed_video_track.h b/include/livekit/session_manager/managed_video_track.h index 4a28b606..2bac4919 100644 --- a/include/livekit/session_manager/managed_video_track.h +++ b/include/livekit/session_manager/managed_video_track.h @@ -32,7 +32,7 @@ class LocalTrackPublication; class LocalParticipant; } // namespace livekit -namespace session_manager { +namespace livekit { namespace test { class ManagedVideoTrackTest; @@ -142,4 +142,4 @@ class ManagedVideoTrack { livekit::LocalParticipant *participant_ = nullptr; // not owned }; -} // namespace session_manager +} // namespace livekit diff --git a/include/livekit/session_manager/rpc_constants.h b/include/livekit/session_manager/rpc_constants.h index 6b5deaab..4614e267 100644 --- a/include/livekit/session_manager/rpc_constants.h +++ b/include/livekit/session_manager/rpc_constants.h @@ -21,7 +21,7 @@ #include -namespace session_manager { +namespace livekit { namespace rpc { /// Built-in RPC method name used by remote track control. @@ -60,4 +60,4 @@ inline std::string formatPayload(const char *action, } // namespace track_control } // namespace rpc -} // namespace session_manager +} // namespace livekit diff --git a/include/livekit/session_manager/session_manager.h b/include/livekit/session_manager/session_manager.h index 6175fcb1..ce36c77a 100644 --- a/include/livekit/session_manager/session_manager.h +++ b/include/livekit/session_manager/session_manager.h @@ -37,6 +37,7 @@ #include namespace livekit { + class Room; class AudioFrame; class VideoFrame; @@ -44,9 +45,6 @@ class AudioStream; class VideoStream; class Track; enum class TrackSource; -} // namespace livekit - -namespace session_manager { class SessionManagerRoomDelegate; class RpcController; @@ -460,4 +458,4 @@ class SessionManager { std::vector> published_video_tracks_; }; -} // namespace session_manager +} // namespace livekit diff --git a/src/session_manager/README.md b/src/session_manager/README.md index cfa9913b..0f039629 100644 --- a/src/session_manager/README.md +++ b/src/session_manager/README.md @@ -12,7 +12,7 @@ The SessionManager abstracts away room lifecycle management, track creation, pub #include "livekit/track.h" // 1. Connect -session_manager::SessionManager sm; +livekit::SessionManager sm; livekit::RoomOptions options; options.auto_subscribe = true; // automatically subscribe to all remote tracks options.dynacast = false; diff --git a/src/session_manager/managed_audio_track.cpp b/src/session_manager/managed_audio_track.cpp index eafb9f90..a48009f9 100644 --- a/src/session_manager/managed_audio_track.cpp +++ b/src/session_manager/managed_audio_track.cpp @@ -29,7 +29,7 @@ #include "lk_log.h" -namespace session_manager { +namespace livekit { ManagedAudioTrack::ManagedAudioTrack( std::string name, int sample_rate, int num_channels, @@ -128,4 +128,4 @@ void ManagedAudioTrack::release() { participant_ = nullptr; } -} // namespace session_manager +} // namespace livekit diff --git a/src/session_manager/managed_video_track.cpp b/src/session_manager/managed_video_track.cpp index 9b276fd5..508fb86b 100644 --- a/src/session_manager/managed_video_track.cpp +++ b/src/session_manager/managed_video_track.cpp @@ -29,7 +29,7 @@ #include "lk_log.h" -namespace session_manager { +namespace livekit { ManagedVideoTrack::ManagedVideoTrack( std::string name, int width, int height, @@ -127,4 +127,4 @@ void ManagedVideoTrack::release() { participant_ = nullptr; } -} // namespace session_manager +} // namespace livekit diff --git a/src/session_manager/rpc_controller.cpp b/src/session_manager/rpc_controller.cpp index 96c58cab..fc9ac075 100644 --- a/src/session_manager/rpc_controller.cpp +++ b/src/session_manager/rpc_controller.cpp @@ -26,7 +26,7 @@ #include #include -namespace session_manager { +namespace livekit { RpcController::RpcController(TrackActionFn track_action_fn) : track_action_fn_(std::move(track_action_fn)), lp_(nullptr) {} @@ -141,4 +141,4 @@ RpcController::handleTrackControlRpc(const livekit::RpcInvocationData &data) { return tc::kResponseOk; } -} // namespace session_manager +} // namespace livekit diff --git a/src/session_manager/rpc_controller.h b/src/session_manager/rpc_controller.h index d673d23a..cd656b5e 100644 --- a/src/session_manager/rpc_controller.h +++ b/src/session_manager/rpc_controller.h @@ -29,10 +29,8 @@ #include namespace livekit { -struct RpcInvocationData; -} // namespace livekit -namespace session_manager { +struct RpcInvocationData; namespace test { class RpcControllerTest; @@ -143,4 +141,4 @@ class RpcController { livekit::LocalParticipant *lp_; }; -} // namespace session_manager +} // namespace livekit diff --git a/src/session_manager/session_manager.cpp b/src/session_manager/session_manager.cpp index 529bb8bd..7915fabf 100644 --- a/src/session_manager/session_manager.cpp +++ b/src/session_manager/session_manager.cpp @@ -41,7 +41,7 @@ #include "lk_log.h" -namespace session_manager { +namespace livekit { // --------------------------------------------------------------- // CallbackKey @@ -658,4 +658,4 @@ SessionManager::startVideoReader(const CallbackKey &key, return old_thread; } -} // namespace session_manager +} // namespace livekit diff --git a/src/session_manager/session_manager_room_delegate.cpp b/src/session_manager/session_manager_room_delegate.cpp index a60f1398..e82743cb 100644 --- a/src/session_manager/session_manager_room_delegate.cpp +++ b/src/session_manager/session_manager_room_delegate.cpp @@ -24,7 +24,7 @@ #include "livekit/session_manager/session_manager.h" #include "livekit/track.h" -namespace session_manager { +namespace livekit { void SessionManagerRoomDelegate::onTrackSubscribed( livekit::Room & /*room*/, const livekit::TrackSubscribedEvent &ev) { @@ -50,4 +50,4 @@ void SessionManagerRoomDelegate::onTrackUnsubscribed( manager_.onTrackUnsubscribed(identity, source); } -} // namespace session_manager +} // namespace livekit diff --git a/src/session_manager/session_manager_room_delegate.h b/src/session_manager/session_manager_room_delegate.h index 0d2513e6..d4c68519 100644 --- a/src/session_manager/session_manager_room_delegate.h +++ b/src/session_manager/session_manager_room_delegate.h @@ -21,7 +21,7 @@ #include "livekit/room_delegate.h" -namespace session_manager { +namespace livekit { class SessionManager; @@ -49,4 +49,4 @@ class SessionManagerRoomDelegate : public livekit::RoomDelegate { SessionManager &manager_; }; -} // namespace session_manager +} // namespace livekit diff --git a/src/tests/session_manager/integration/test_session_manager_rpc_roundtrip.cpp b/src/tests/session_manager/integration/test_session_manager_rpc_roundtrip.cpp index 371454e1..9b3718e7 100644 --- a/src/tests/session_manager/integration/test_session_manager_rpc_roundtrip.cpp +++ b/src/tests/session_manager/integration/test_session_manager_rpc_roundtrip.cpp @@ -16,7 +16,7 @@ #include -namespace session_manager { +namespace livekit { namespace test { class SessionManagerRpcRoundtripTest : public SessionManagerTestBase {}; @@ -271,4 +271,4 @@ TEST_F(SessionManagerRemoteTrackControlTest, RemoteMuteNonexistentTrack) { } } // namespace test -} // namespace session_manager +} // namespace livekit diff --git a/src/tests/session_manager/test_callback_key.cpp b/src/tests/session_manager/test_callback_key.cpp index 6834e878..b3d75708 100644 --- a/src/tests/session_manager/test_callback_key.cpp +++ b/src/tests/session_manager/test_callback_key.cpp @@ -24,7 +24,7 @@ #include -namespace session_manager { +namespace livekit { namespace test { class CallbackKeyTest : public ::testing::Test { @@ -121,4 +121,4 @@ TEST_F(CallbackKeyTest, EmptyIdentityWorks) { } } // namespace test -} // namespace session_manager +} // namespace livekit diff --git a/src/tests/session_manager/test_managed_audio_track.cpp b/src/tests/session_manager/test_managed_audio_track.cpp index 5667ce7a..2a14bb11 100644 --- a/src/tests/session_manager/test_managed_audio_track.cpp +++ b/src/tests/session_manager/test_managed_audio_track.cpp @@ -24,7 +24,7 @@ #include #include -namespace session_manager { +namespace livekit { namespace test { class ManagedAudioTrackTest : public ::testing::Test { @@ -115,4 +115,4 @@ TEST_F(ManagedAudioTrackTest, UnmuteOnReleasedTrackDoesNotCrash) { } } // namespace test -} // namespace session_manager +} // namespace livekit diff --git a/src/tests/session_manager/test_managed_video_track.cpp b/src/tests/session_manager/test_managed_video_track.cpp index 7ce2c1d0..8d35d920 100644 --- a/src/tests/session_manager/test_managed_video_track.cpp +++ b/src/tests/session_manager/test_managed_video_track.cpp @@ -24,7 +24,7 @@ #include #include -namespace session_manager { +namespace livekit { namespace test { class ManagedVideoTrackTest : public ::testing::Test { @@ -111,4 +111,4 @@ TEST_F(ManagedVideoTrackTest, UnmuteOnReleasedTrackDoesNotCrash) { } } // namespace test -} // namespace session_manager +} // namespace livekit diff --git a/src/tests/session_manager/test_rpc_controller.cpp b/src/tests/session_manager/test_rpc_controller.cpp index a5b72b48..a2f0a6f9 100644 --- a/src/tests/session_manager/test_rpc_controller.cpp +++ b/src/tests/session_manager/test_rpc_controller.cpp @@ -28,7 +28,7 @@ #include #include -namespace session_manager { +namespace livekit { namespace test { // Records (action, track_name) pairs passed to the TrackActionFn callback. @@ -270,4 +270,4 @@ TEST_F(RpcControllerTest, FormatPayloadRoundTrip) { } } // namespace test -} // namespace session_manager +} // namespace livekit diff --git a/src/tests/session_manager/test_session_manager.cpp b/src/tests/session_manager/test_session_manager.cpp index 9fbe0ebd..4cbafb62 100644 --- a/src/tests/session_manager/test_session_manager.cpp +++ b/src/tests/session_manager/test_session_manager.cpp @@ -24,7 +24,7 @@ #include -namespace session_manager { +namespace livekit { namespace test { class SessionManagerTest : public ::testing::Test { @@ -184,4 +184,4 @@ TEST_F(SessionManagerTest, RegisterCallbacksForMultipleParticipants) { } } // namespace test -} // namespace session_manager +} // namespace livekit From 65a63dffa7f0d4ed2653ee8d82580d20b58b9f1b Mon Sep 17 00:00:00 2001 From: Stephen DeRosa Date: Wed, 11 Mar 2026 10:42:29 -0600 Subject: [PATCH 10/13] ManagedAudio/ManagedVideoTrack -> ManagedLocalAudio/ManagedLocaleVideoTrack --- .gitignore | 1 + CMakeLists.txt | 4 +-- examples/human_robot/robot.cpp | 2 +- ...io_track.h => managed_local_audio_track.h} | 27 +++++++++-------- ...eo_track.h => managed_local_video_track.h} | 16 +++++----- .../livekit/session_manager/session_manager.h | 12 ++++---- src/session_manager/README.md | 18 +++++------ ...rack.cpp => managed_local_audio_track.cpp} | 28 ++++++++--------- ...rack.cpp => managed_local_video_track.cpp} | 28 ++++++++--------- src/session_manager/session_manager.cpp | 22 +++++++------- ...cpp => test_managed_local_audio_track.cpp} | 30 +++++++++---------- ...cpp => test_managed_local_video_track.cpp} | 30 +++++++++---------- 12 files changed, 110 insertions(+), 108 deletions(-) rename include/livekit/session_manager/{managed_audio_track.h => managed_local_audio_track.h} (87%) rename include/livekit/session_manager/{managed_video_track.h => managed_local_video_track.h} (91%) rename src/session_manager/{managed_audio_track.cpp => managed_local_audio_track.cpp} (78%) rename src/session_manager/{managed_video_track.cpp => managed_local_video_track.cpp} (78%) rename src/tests/session_manager/{test_managed_audio_track.cpp => test_managed_local_audio_track.cpp} (73%) rename src/tests/session_manager/{test_managed_video_track.cpp => test_managed_local_video_track.cpp} (72%) diff --git a/.gitignore b/.gitignore index d98ce162..447173b3 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ received_green.avif docs/*.bak docs/html/ docs/latex/ +web/ .vs/ .vscode/ .cursor/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 3de39910..52d7a019 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -354,8 +354,8 @@ add_library(livekit SHARED src/video_utils.cpp src/video_utils.h src/session_manager/session_manager.cpp - src/session_manager/managed_audio_track.cpp - src/session_manager/managed_video_track.cpp + src/session_manager/managed_local_audio_track.cpp + src/session_manager/managed_local_video_track.cpp src/session_manager/session_manager_room_delegate.cpp src/session_manager/session_manager_room_delegate.h src/session_manager/rpc_controller.cpp diff --git a/examples/human_robot/robot.cpp b/examples/human_robot/robot.cpp index 6b637647..5d3a5424 100644 --- a/examples/human_robot/robot.cpp +++ b/examples/human_robot/robot.cpp @@ -377,7 +377,7 @@ int main(int argc, char *argv[]) { constexpr int kSimWidth = 480; constexpr int kSimHeight = 320; - std::shared_ptr mic; + std::shared_ptr mic; if (use_mic) { mic = sm.createAudioTrack("robot-mic", kSampleRate, kChannels, livekit::TrackSource::SOURCE_MICROPHONE); diff --git a/include/livekit/session_manager/managed_audio_track.h b/include/livekit/session_manager/managed_local_audio_track.h similarity index 87% rename from include/livekit/session_manager/managed_audio_track.h rename to include/livekit/session_manager/managed_local_audio_track.h index 44fc1500..fd6a08b1 100644 --- a/include/livekit/session_manager/managed_audio_track.h +++ b/include/livekit/session_manager/managed_local_audio_track.h @@ -14,7 +14,7 @@ * limitations under the License. */ -/// @file managed_audio_track.h +/// @file managed_local_audio_track.h /// @brief Handle for a published local audio track. #pragma once @@ -35,7 +35,7 @@ class LocalParticipant; namespace livekit { namespace test { -class ManagedAudioTrackTest; +class ManagedLocalAudioTrackTest; } // namespace test /** @@ -62,13 +62,13 @@ class ManagedAudioTrackTest; * mic->mute(); * mic->release(); // unpublishes the track mid-session */ -class ManagedAudioTrack { +class ManagedLocalAudioTrack { public: - ~ManagedAudioTrack(); + ~ManagedLocalAudioTrack(); // Non-copyable - ManagedAudioTrack(const ManagedAudioTrack &) = delete; - ManagedAudioTrack &operator=(const ManagedAudioTrack &) = delete; + ManagedLocalAudioTrack(const ManagedLocalAudioTrack &) = delete; + ManagedLocalAudioTrack &operator=(const ManagedLocalAudioTrack &) = delete; /** * Push a PCM audio frame to the track. @@ -124,13 +124,14 @@ class ManagedAudioTrack { private: friend class SessionManager; - friend class test::ManagedAudioTrackTest; - - ManagedAudioTrack(std::string name, int sample_rate, int num_channels, - std::shared_ptr source, - std::shared_ptr track, - std::shared_ptr publication, - livekit::LocalParticipant *participant); + friend class test::ManagedLocalAudioTrackTest; + + ManagedLocalAudioTrack( + std::string name, int sample_rate, int num_channels, + std::shared_ptr source, + std::shared_ptr track, + std::shared_ptr publication, + livekit::LocalParticipant *participant); mutable std::mutex mutex_; std::string name_; diff --git a/include/livekit/session_manager/managed_video_track.h b/include/livekit/session_manager/managed_local_video_track.h similarity index 91% rename from include/livekit/session_manager/managed_video_track.h rename to include/livekit/session_manager/managed_local_video_track.h index 2bac4919..cdd6ddb1 100644 --- a/include/livekit/session_manager/managed_video_track.h +++ b/include/livekit/session_manager/managed_local_video_track.h @@ -14,7 +14,7 @@ * limitations under the License. */ -/// @file managed_video_track.h +/// @file managed_local_video_track.h /// @brief Handle for a published local video track. #pragma once @@ -35,7 +35,7 @@ class LocalParticipant; namespace livekit { namespace test { -class ManagedVideoTrackTest; +class ManagedLocalVideoTrackTest; } // namespace test /** @@ -62,13 +62,13 @@ class ManagedVideoTrackTest; * cam->mute(); * cam->release(); // unpublishes the track mid-session */ -class ManagedVideoTrack { +class ManagedLocalVideoTrack { public: - ~ManagedVideoTrack(); + ~ManagedLocalVideoTrack(); // Non-copyable - ManagedVideoTrack(const ManagedVideoTrack &) = delete; - ManagedVideoTrack &operator=(const ManagedVideoTrack &) = delete; + ManagedLocalVideoTrack(const ManagedLocalVideoTrack &) = delete; + ManagedLocalVideoTrack &operator=(const ManagedLocalVideoTrack &) = delete; /** * Push an RGBA video frame to the track. @@ -122,9 +122,9 @@ class ManagedVideoTrack { private: friend class SessionManager; - friend class test::ManagedVideoTrackTest; + friend class test::ManagedLocalVideoTrackTest; - ManagedVideoTrack(std::string name, int width, int height, + ManagedLocalVideoTrack(std::string name, int width, int height, std::shared_ptr source, std::shared_ptr track, std::shared_ptr publication, diff --git a/include/livekit/session_manager/session_manager.h b/include/livekit/session_manager/session_manager.h index ce36c77a..1f44a7dd 100644 --- a/include/livekit/session_manager/session_manager.h +++ b/include/livekit/session_manager/session_manager.h @@ -19,8 +19,8 @@ #pragma once -#include "livekit/session_manager/managed_audio_track.h" -#include "livekit/session_manager/managed_video_track.h" +#include "livekit/session_manager/managed_local_audio_track.h" +#include "livekit/session_manager/managed_local_video_track.h" #include "livekit/session_manager/rpc_constants.h" #include "livekit/local_participant.h" @@ -198,7 +198,7 @@ class SessionManager { * @return Shared pointer to the published audio track handle (never null). * @throws std::runtime_error if the SessionManager is not connected. */ - std::shared_ptr + std::shared_ptr createAudioTrack(const std::string &name, int sample_rate, int num_channels, livekit::TrackSource source); @@ -222,7 +222,7 @@ class SessionManager { * @return Shared pointer to the published video track handle (never null). * @throws std::runtime_error if the SessionManager is not connected. */ - std::shared_ptr + std::shared_ptr createVideoTrack(const std::string &name, int width, int height, livekit::TrackSource source); @@ -453,9 +453,9 @@ class SessionManager { /// All tracks created by this SessionManager. The SessionManager retains a /// shared_ptr so it can force-release every track on disconnect() before the /// room is destroyed, preventing dangling @c participant_ pointers. - std::vector> published_audio_tracks_; + std::vector> published_audio_tracks_; /// @copydoc published_audio_tracks_ - std::vector> published_video_tracks_; + std::vector> published_video_tracks_; }; } // namespace livekit diff --git a/src/session_manager/README.md b/src/session_manager/README.md index 0f039629..2caf1b40 100644 --- a/src/session_manager/README.md +++ b/src/session_manager/README.md @@ -87,8 +87,8 @@ The returned pointer is valid only while `isConnected()` is true. Do not call `s ``` Your Application | | - | pushFrame() -----> ManagedAudioTrack | (sending to remote participants) - | pushFrame() -----> ManagedVideoTrack | + | pushFrame() -----> ManagedLocalAudioTrack | (sending to remote participants) + | pushFrame() -----> ManagedLocalVideoTrack | | | | callback() <------ Reader Thread | (receiving from remote participants) | | @@ -103,7 +103,7 @@ Your Application **`SessionManager`** -- The main entry point. Owns the full room lifecycle: SDK initialization, room connection, track publishing, and frame callback management. -**`ManagedAudioTrack` / `ManagedVideoTrack`** -- RAII handles for published local tracks. Created via `createAudioTrack()` / `createVideoTrack()`. When the `shared_ptr` is dropped, the track is automatically unpublished and all underlying SDK resources are freed. Call `pushFrame()` to send audio/video data to remote participants. +**`ManagedLocalAudioTrack` / `ManagedLocalVideoTrack`** -- RAII handles for published local tracks. Created via `createAudioTrack()` / `createVideoTrack()`. When the `shared_ptr` is dropped, the track is automatically unpublished and all underlying SDK resources are freed. Call `pushFrame()` to send audio/video data to remote participants. **`SessionManagerRoomDelegate`** -- Internal (not part of the public API; lives in `src/`). Listens for `onTrackSubscribed` / `onTrackUnsubscribed` events from the LiveKit SDK and wires up reader threads automatically. @@ -115,7 +115,7 @@ When a remote participant publishes an audio or video track and the SessionManag In short: -- **Sending** (you -> remote): `ManagedAudioTrack::pushFrame()` / `ManagedVideoTrack::pushFrame()` +- **Sending** (you -> remote): `ManagedLocalAudioTrack::pushFrame()` / `ManagedLocalVideoTrack::pushFrame()` - **Receiving** (remote -> you): reader threads invoke your registered callbacks Reader threads are managed entirely by the SessionManager. They are created when a matching remote track is subscribed, and torn down (stream closed, thread joined) when the track is unsubscribed, the callback is unregistered, or `disconnect()` is called. @@ -154,8 +154,8 @@ sm.connect(url, token, options); | `disconnect()` | Disconnect and release all resources. Joins all reader threads. Safe to call multiple times. | | `isConnected()` | Returns whether the SessionManager is currently connected. | | `getRoom()` | Returns a raw pointer to the underlying `livekit::Room` for direct base SDK access (e.g. `room_info()`, `remoteParticipants()`, `registerTextStreamHandler`). Returns `nullptr` when disconnected. Do not call `setDelegate()` on the returned Room. | -| `createAudioTrack(name, sample_rate, num_channels, source)` | Create and publish a local audio track with the given `TrackSource` (e.g. `SOURCE_MICROPHONE`, `SOURCE_SCREENSHARE_AUDIO`). Returns an RAII `shared_ptr`. | -| `createVideoTrack(name, width, height, source)` | Create and publish a local video track with the given `TrackSource` (e.g. `SOURCE_CAMERA`, `SOURCE_SCREENSHARE`). Returns an RAII `shared_ptr`. | +| `createAudioTrack(name, sample_rate, num_channels, source)` | Create and publish a local audio track with the given `TrackSource` (e.g. `SOURCE_MICROPHONE`, `SOURCE_SCREENSHARE_AUDIO`). Returns an RAII `shared_ptr`. | +| `createVideoTrack(name, width, height, source)` | Create and publish a local video track with the given `TrackSource` (e.g. `SOURCE_CAMERA`, `SOURCE_SCREENSHARE`). Returns an RAII `shared_ptr`. | | `setOnAudioFrameCallback(identity, source, callback)` | Register a callback for audio frames from a specific remote participant + track source. | | `setOnVideoFrameCallback(identity, source, callback)` | Register a callback for video frames from a specific remote participant + track source. | | `clearOnAudioFrameCallback(identity, source)` | Clear the audio callback for a specific remote participant + track source. Stops and joins the reader thread if active. | @@ -166,7 +166,7 @@ sm.connect(url, token, options); | `requestRemoteTrackMute(identity, track_name)` | Ask a remote participant to mute a track by name. Throws `livekit::RpcError` on failure. | | `requestRemoteTrackUnmute(identity, track_name)` | Ask a remote participant to unmute a track by name. Throws `livekit::RpcError` on failure. | -### `ManagedAudioTrack` +### `ManagedLocalAudioTrack` | Method | Description | |---|---| @@ -175,7 +175,7 @@ sm.connect(url, token, options); | `release()` | Explicitly unpublish and free resources. Called automatically by the destructor. | | `name()` / `sampleRate()` / `numChannels()` | Accessors for track configuration. | -### `ManagedVideoTrack` +### `ManagedLocalVideoTrack` | Method | Description | |---|---| @@ -237,7 +237,7 @@ The human will print periodic summaries like: The SessionManager includes a unit test suite built with [Google Test](https://github.com/google/googletest). Tests cover 1. `CallbackKey` hashing/equality, -2. `ManagedAudioTrack`/`ManagedVideoTrack` state management, and +2. `ManagedLocalAudioTrack`/`ManagedLocalVideoTrack` state management, and 3. `SessionManager` pre-connection behaviour (callback registration, error handling). ### Building and running tests diff --git a/src/session_manager/managed_audio_track.cpp b/src/session_manager/managed_local_audio_track.cpp similarity index 78% rename from src/session_manager/managed_audio_track.cpp rename to src/session_manager/managed_local_audio_track.cpp index a48009f9..cf463aa4 100644 --- a/src/session_manager/managed_audio_track.cpp +++ b/src/session_manager/managed_local_audio_track.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -/// @file managed_audio_track.cpp -/// @brief Implementation of ManagedAudioTrack. +/// @file managed_local_audio_track.cpp +/// @brief Implementation of ManagedLocalAudioTrack. -#include "livekit/session_manager/managed_audio_track.h" +#include "livekit/session_manager/managed_local_audio_track.h" #include "livekit/audio_frame.h" #include "livekit/audio_source.h" @@ -31,7 +31,7 @@ namespace livekit { -ManagedAudioTrack::ManagedAudioTrack( +ManagedLocalAudioTrack::ManagedLocalAudioTrack( std::string name, int sample_rate, int num_channels, std::shared_ptr source, std::shared_ptr track, @@ -42,9 +42,9 @@ ManagedAudioTrack::ManagedAudioTrack( track_(std::move(track)), publication_(std::move(publication)), participant_(participant) {} -ManagedAudioTrack::~ManagedAudioTrack() { release(); } +ManagedLocalAudioTrack::~ManagedLocalAudioTrack() { release(); } -bool ManagedAudioTrack::pushFrame(const std::vector &data, +bool ManagedLocalAudioTrack::pushFrame(const std::vector &data, int samples_per_channel, int timeout_ms) { livekit::AudioFrame frame(std::vector(data.begin(), data.end()), sample_rate_, num_channels_, samples_per_channel); @@ -57,13 +57,13 @@ bool ManagedAudioTrack::pushFrame(const std::vector &data, try { source_->captureFrame(frame, timeout_ms); } catch (const std::exception &e) { - LK_LOG_ERROR("ManagedAudioTrack captureFrame error: {}", e.what()); + LK_LOG_ERROR("ManagedLocalAudioTrack captureFrame error: {}", e.what()); return false; } return true; } -bool ManagedAudioTrack::pushFrame(const std::int16_t *data, +bool ManagedLocalAudioTrack::pushFrame(const std::int16_t *data, int samples_per_channel, int timeout_ms) { const int total_samples = samples_per_channel * num_channels_; livekit::AudioFrame frame( @@ -78,32 +78,32 @@ bool ManagedAudioTrack::pushFrame(const std::int16_t *data, try { source_->captureFrame(frame, timeout_ms); } catch (const std::exception &e) { - LK_LOG_ERROR("ManagedAudioTrack captureFrame error: {}", e.what()); + LK_LOG_ERROR("ManagedLocalAudioTrack captureFrame error: {}", e.what()); return false; } return true; } -void ManagedAudioTrack::mute() { +void ManagedLocalAudioTrack::mute() { std::lock_guard lock(mutex_); if (!released_ && track_) { track_->mute(); } } -void ManagedAudioTrack::unmute() { +void ManagedLocalAudioTrack::unmute() { std::lock_guard lock(mutex_); if (!released_ && track_) { track_->unmute(); } } -bool ManagedAudioTrack::isReleased() const noexcept { +bool ManagedLocalAudioTrack::isReleased() const noexcept { std::lock_guard lock(mutex_); return released_; } -void ManagedAudioTrack::release() { +void ManagedLocalAudioTrack::release() { std::lock_guard lock(mutex_); if (released_) { return; @@ -116,7 +116,7 @@ void ManagedAudioTrack::release() { participant_->unpublishTrack(publication_->sid()); } catch (...) { // Best-effort cleanup; ignore errors during teardown - LK_LOG_WARN("ManagedAudioTrack unpublishTrack error, continuing with " + LK_LOG_WARN("ManagedLocalAudioTrack unpublishTrack error, continuing with " "cleanup"); } } diff --git a/src/session_manager/managed_video_track.cpp b/src/session_manager/managed_local_video_track.cpp similarity index 78% rename from src/session_manager/managed_video_track.cpp rename to src/session_manager/managed_local_video_track.cpp index 508fb86b..e22d8815 100644 --- a/src/session_manager/managed_video_track.cpp +++ b/src/session_manager/managed_local_video_track.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -/// @file managed_video_track.cpp -/// @brief Implementation of ManagedVideoTrack. +/// @file managed_local_video_track.cpp +/// @brief Implementation of ManagedLocalVideoTrack. -#include "livekit/session_manager/managed_video_track.h" +#include "livekit/session_manager/managed_local_video_track.h" #include "livekit/local_participant.h" #include "livekit/local_track_publication.h" @@ -31,7 +31,7 @@ namespace livekit { -ManagedVideoTrack::ManagedVideoTrack( +ManagedLocalVideoTrack::ManagedLocalVideoTrack( std::string name, int width, int height, std::shared_ptr source, std::shared_ptr track, @@ -41,9 +41,9 @@ ManagedVideoTrack::ManagedVideoTrack( source_(std::move(source)), track_(std::move(track)), publication_(std::move(publication)), participant_(participant) {} -ManagedVideoTrack::~ManagedVideoTrack() { release(); } +ManagedLocalVideoTrack::~ManagedLocalVideoTrack() { release(); } -bool ManagedVideoTrack::pushFrame(const std::vector &rgba, +bool ManagedLocalVideoTrack::pushFrame(const std::vector &rgba, std::int64_t timestamp_us) { livekit::VideoFrame frame( width_, height_, livekit::VideoBufferType::RGBA, @@ -57,13 +57,13 @@ bool ManagedVideoTrack::pushFrame(const std::vector &rgba, try { source_->captureFrame(frame, timestamp_us); } catch (const std::exception &e) { - LK_LOG_ERROR("ManagedVideoTrack captureFrame error: {}", e.what()); + LK_LOG_ERROR("ManagedLocalVideoTrack captureFrame error: {}", e.what()); return false; } return true; } -bool ManagedVideoTrack::pushFrame(const std::uint8_t *rgba, +bool ManagedLocalVideoTrack::pushFrame(const std::uint8_t *rgba, std::size_t rgba_size, std::int64_t timestamp_us) { livekit::VideoFrame frame(width_, height_, livekit::VideoBufferType::RGBA, @@ -77,32 +77,32 @@ bool ManagedVideoTrack::pushFrame(const std::uint8_t *rgba, try { source_->captureFrame(frame, timestamp_us); } catch (const std::exception &e) { - LK_LOG_ERROR("ManagedVideoTrack captureFrame error: {}", e.what()); + LK_LOG_ERROR("ManagedLocalVideoTrack captureFrame error: {}", e.what()); return false; } return true; } -void ManagedVideoTrack::mute() { +void ManagedLocalVideoTrack::mute() { std::lock_guard lock(mutex_); if (!released_ && track_) { track_->mute(); } } -void ManagedVideoTrack::unmute() { +void ManagedLocalVideoTrack::unmute() { std::lock_guard lock(mutex_); if (!released_ && track_) { track_->unmute(); } } -bool ManagedVideoTrack::isReleased() const noexcept { +bool ManagedLocalVideoTrack::isReleased() const noexcept { std::lock_guard lock(mutex_); return released_; } -void ManagedVideoTrack::release() { +void ManagedLocalVideoTrack::release() { std::lock_guard lock(mutex_); if (released_) { return; @@ -115,7 +115,7 @@ void ManagedVideoTrack::release() { participant_->unpublishTrack(publication_->sid()); } catch (...) { // Best-effort cleanup; ignore errors during teardown - LK_LOG_WARN("ManagedVideoTrack unpublishTrack error, continuing with " + LK_LOG_WARN("ManagedLocalVideoTrack unpublishTrack error, continuing with " "cleanup"); } } diff --git a/src/session_manager/session_manager.cpp b/src/session_manager/session_manager.cpp index 7915fabf..bbb93b04 100644 --- a/src/session_manager/session_manager.cpp +++ b/src/session_manager/session_manager.cpp @@ -229,7 +229,7 @@ livekit::Room *SessionManager::getRoom() const { // Track creation (publishing) // --------------------------------------------------------------- -std::shared_ptr +std::shared_ptr SessionManager::createAudioTrack(const std::string &name, int sample_rate, int num_channels, livekit::TrackSource source) { @@ -258,15 +258,15 @@ SessionManager::createAudioTrack(const std::string &name, int sample_rate, auto publication = lp->publishTrack(track, opts); // 4. Wrap in handle and retain a reference - auto managed_audio_track = - std::shared_ptr(new ManagedAudioTrack( + auto managed = + std::shared_ptr(new ManagedLocalAudioTrack( name, sample_rate, num_channels, std::move(audio_source), std::move(track), std::move(publication), lp)); - published_audio_tracks_.emplace_back(managed_audio_track); - return managed_audio_track; + published_audio_tracks_.emplace_back(managed); + return managed; } -std::shared_ptr +std::shared_ptr SessionManager::createVideoTrack(const std::string &name, int width, int height, livekit::TrackSource source) { std::lock_guard lock(mutex_); @@ -293,11 +293,11 @@ SessionManager::createVideoTrack(const std::string &name, int width, int height, auto publication = lp->publishTrack(track, opts); // 4. Wrap in handle and retain a reference - auto managed_video_track = std::shared_ptr( - new ManagedVideoTrack(name, width, height, std::move(video_source), - std::move(track), std::move(publication), lp)); - published_video_tracks_.emplace_back(managed_video_track); - return managed_video_track; + auto managed = std::shared_ptr( + new ManagedLocalVideoTrack(name, width, height, std::move(video_source), + std::move(track), std::move(publication), lp)); + published_video_tracks_.emplace_back(managed); + return managed; } // --------------------------------------------------------------- diff --git a/src/tests/session_manager/test_managed_audio_track.cpp b/src/tests/session_manager/test_managed_local_audio_track.cpp similarity index 73% rename from src/tests/session_manager/test_managed_audio_track.cpp rename to src/tests/session_manager/test_managed_local_audio_track.cpp index 2a14bb11..58bb9f19 100644 --- a/src/tests/session_manager/test_managed_audio_track.cpp +++ b/src/tests/session_manager/test_managed_local_audio_track.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -/// @file test_managed_audio_track.cpp -/// @brief Unit tests for ManagedAudioTrack. +/// @file test_managed_local_audio_track.cpp +/// @brief Unit tests for ManagedLocalAudioTrack. #include -#include +#include #include #include @@ -27,16 +27,16 @@ namespace livekit { namespace test { -class ManagedAudioTrackTest : public ::testing::Test { +class ManagedLocalAudioTrackTest : public ::testing::Test { protected: - /// Create a ManagedAudioTrack with null SDK objects for pure-logic testing. + /// Create a ManagedLocalAudioTrack with null SDK objects for pure-logic testing. /// The track is usable for accessor and state management tests but will /// crash if pushFrame / mute / unmute try to dereference SDK pointers /// on a non-released track. - static ManagedAudioTrack createNullTrack(const std::string &name = "mic", + static ManagedLocalAudioTrack createNullTrack(const std::string &name = "mic", int sample_rate = 48000, int num_channels = 2) { - return ManagedAudioTrack(name, sample_rate, num_channels, + return ManagedLocalAudioTrack(name, sample_rate, num_channels, nullptr, // source nullptr, // track nullptr, // publication @@ -45,7 +45,7 @@ class ManagedAudioTrackTest : public ::testing::Test { } }; -TEST_F(ManagedAudioTrackTest, AccessorsReturnConstructionValues) { +TEST_F(ManagedLocalAudioTrackTest, AccessorsReturnConstructionValues) { auto track = createNullTrack("test-mic", 16000, 1); EXPECT_EQ(track.name(), "test-mic") << "Name should match construction value"; @@ -53,14 +53,14 @@ TEST_F(ManagedAudioTrackTest, AccessorsReturnConstructionValues) { EXPECT_EQ(track.numChannels(), 1) << "Channel count should match"; } -TEST_F(ManagedAudioTrackTest, InitiallyNotReleased) { +TEST_F(ManagedLocalAudioTrackTest, InitiallyNotReleased) { auto track = createNullTrack(); EXPECT_FALSE(track.isReleased()) << "Track should not be released immediately after construction"; } -TEST_F(ManagedAudioTrackTest, ReleaseMarksTrackAsReleased) { +TEST_F(ManagedLocalAudioTrackTest, ReleaseMarksTrackAsReleased) { auto track = createNullTrack(); track.release(); @@ -69,7 +69,7 @@ TEST_F(ManagedAudioTrackTest, ReleaseMarksTrackAsReleased) { << "Track should be released after calling release()"; } -TEST_F(ManagedAudioTrackTest, DoubleReleaseIsIdempotent) { +TEST_F(ManagedLocalAudioTrackTest, DoubleReleaseIsIdempotent) { auto track = createNullTrack(); track.release(); @@ -78,7 +78,7 @@ TEST_F(ManagedAudioTrackTest, DoubleReleaseIsIdempotent) { EXPECT_TRUE(track.isReleased()); } -TEST_F(ManagedAudioTrackTest, PushFrameAfterReleaseReturnsFalse) { +TEST_F(ManagedLocalAudioTrackTest, PushFrameAfterReleaseReturnsFalse) { auto track = createNullTrack(); track.release(); @@ -88,7 +88,7 @@ TEST_F(ManagedAudioTrackTest, PushFrameAfterReleaseReturnsFalse) { << "pushFrame (vector) on a released track should return false"; } -TEST_F(ManagedAudioTrackTest, PushFrameRawPointerAfterReleaseReturnsFalse) { +TEST_F(ManagedLocalAudioTrackTest, PushFrameRawPointerAfterReleaseReturnsFalse) { auto track = createNullTrack(); track.release(); @@ -98,7 +98,7 @@ TEST_F(ManagedAudioTrackTest, PushFrameRawPointerAfterReleaseReturnsFalse) { << "pushFrame (raw pointer) on a released track should return false"; } -TEST_F(ManagedAudioTrackTest, MuteOnReleasedTrackDoesNotCrash) { +TEST_F(ManagedLocalAudioTrackTest, MuteOnReleasedTrackDoesNotCrash) { auto track = createNullTrack(); track.release(); @@ -106,7 +106,7 @@ TEST_F(ManagedAudioTrackTest, MuteOnReleasedTrackDoesNotCrash) { << "mute() on a released track should be a no-op"; } -TEST_F(ManagedAudioTrackTest, UnmuteOnReleasedTrackDoesNotCrash) { +TEST_F(ManagedLocalAudioTrackTest, UnmuteOnReleasedTrackDoesNotCrash) { auto track = createNullTrack(); track.release(); diff --git a/src/tests/session_manager/test_managed_video_track.cpp b/src/tests/session_manager/test_managed_local_video_track.cpp similarity index 72% rename from src/tests/session_manager/test_managed_video_track.cpp rename to src/tests/session_manager/test_managed_local_video_track.cpp index 8d35d920..39a9a1ea 100644 --- a/src/tests/session_manager/test_managed_video_track.cpp +++ b/src/tests/session_manager/test_managed_local_video_track.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -/// @file test_managed_video_track.cpp -/// @brief Unit tests for ManagedVideoTrack. +/// @file test_managed_local_video_track.cpp +/// @brief Unit tests for ManagedLocalVideoTrack. #include -#include +#include #include #include @@ -27,12 +27,12 @@ namespace livekit { namespace test { -class ManagedVideoTrackTest : public ::testing::Test { +class ManagedLocalVideoTrackTest : public ::testing::Test { protected: - /// Create a ManagedVideoTrack with null SDK objects for pure-logic testing. - static ManagedVideoTrack createNullTrack(const std::string &name = "cam", + /// Create a ManagedLocalVideoTrack with null SDK objects for pure-logic testing. + static ManagedLocalVideoTrack createNullTrack(const std::string &name = "cam", int width = 1280, int height = 720) { - return ManagedVideoTrack(name, width, height, + return ManagedLocalVideoTrack(name, width, height, nullptr, // source nullptr, // track nullptr, // publication @@ -41,7 +41,7 @@ class ManagedVideoTrackTest : public ::testing::Test { } }; -TEST_F(ManagedVideoTrackTest, AccessorsReturnConstructionValues) { +TEST_F(ManagedLocalVideoTrackTest, AccessorsReturnConstructionValues) { auto track = createNullTrack("test-cam", 640, 480); EXPECT_EQ(track.name(), "test-cam") << "Name should match construction value"; @@ -49,14 +49,14 @@ TEST_F(ManagedVideoTrackTest, AccessorsReturnConstructionValues) { EXPECT_EQ(track.height(), 480) << "Height should match"; } -TEST_F(ManagedVideoTrackTest, InitiallyNotReleased) { +TEST_F(ManagedLocalVideoTrackTest, InitiallyNotReleased) { auto track = createNullTrack(); EXPECT_FALSE(track.isReleased()) << "Track should not be released immediately after construction"; } -TEST_F(ManagedVideoTrackTest, ReleaseMarksTrackAsReleased) { +TEST_F(ManagedLocalVideoTrackTest, ReleaseMarksTrackAsReleased) { auto track = createNullTrack(); track.release(); @@ -65,7 +65,7 @@ TEST_F(ManagedVideoTrackTest, ReleaseMarksTrackAsReleased) { << "Track should be released after calling release()"; } -TEST_F(ManagedVideoTrackTest, DoubleReleaseIsIdempotent) { +TEST_F(ManagedLocalVideoTrackTest, DoubleReleaseIsIdempotent) { auto track = createNullTrack(); track.release(); @@ -74,7 +74,7 @@ TEST_F(ManagedVideoTrackTest, DoubleReleaseIsIdempotent) { EXPECT_TRUE(track.isReleased()); } -TEST_F(ManagedVideoTrackTest, PushFrameAfterReleaseReturnsFalse) { +TEST_F(ManagedLocalVideoTrackTest, PushFrameAfterReleaseReturnsFalse) { auto track = createNullTrack(); track.release(); @@ -84,7 +84,7 @@ TEST_F(ManagedVideoTrackTest, PushFrameAfterReleaseReturnsFalse) { << "pushFrame (vector) on a released track should return false"; } -TEST_F(ManagedVideoTrackTest, PushFrameRawPointerAfterReleaseReturnsFalse) { +TEST_F(ManagedLocalVideoTrackTest, PushFrameRawPointerAfterReleaseReturnsFalse) { auto track = createNullTrack(); track.release(); @@ -94,7 +94,7 @@ TEST_F(ManagedVideoTrackTest, PushFrameRawPointerAfterReleaseReturnsFalse) { << "pushFrame (raw pointer) on a released track should return false"; } -TEST_F(ManagedVideoTrackTest, MuteOnReleasedTrackDoesNotCrash) { +TEST_F(ManagedLocalVideoTrackTest, MuteOnReleasedTrackDoesNotCrash) { auto track = createNullTrack(); track.release(); @@ -102,7 +102,7 @@ TEST_F(ManagedVideoTrackTest, MuteOnReleasedTrackDoesNotCrash) { << "mute() on a released track should be a no-op"; } -TEST_F(ManagedVideoTrackTest, UnmuteOnReleasedTrackDoesNotCrash) { +TEST_F(ManagedLocalVideoTrackTest, UnmuteOnReleasedTrackDoesNotCrash) { auto track = createNullTrack(); track.release(); From 3cdbb606f996dde0c1d32a487c4007ae2c61f588 Mon Sep 17 00:00:00 2001 From: Stephen DeRosa Date: Wed, 11 Mar 2026 10:53:51 -0600 Subject: [PATCH 11/13] rpc_constants.h/.cpp --- CMakeLists.txt | 1 + .../livekit/session_manager/rpc_constants.h | 21 +++------- src/session_manager/rpc_constants.cpp | 41 +++++++++++++++++++ 3 files changed, 48 insertions(+), 15 deletions(-) create mode 100644 src/session_manager/rpc_constants.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 52d7a019..c355fbb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -358,6 +358,7 @@ add_library(livekit SHARED src/session_manager/managed_local_video_track.cpp src/session_manager/session_manager_room_delegate.cpp src/session_manager/session_manager_room_delegate.h + src/session_manager/rpc_constants.cpp src/session_manager/rpc_controller.cpp src/session_manager/rpc_controller.h ) diff --git a/include/livekit/session_manager/rpc_constants.h b/include/livekit/session_manager/rpc_constants.h index 4614e267..d81b1471 100644 --- a/include/livekit/session_manager/rpc_constants.h +++ b/include/livekit/session_manager/rpc_constants.h @@ -34,29 +34,20 @@ namespace track_control { enum class Action { kActionMute, kActionUnmute }; /// RPC method name registered by the SessionManager for remote track control. -constexpr const char *kMethod = "lk.session_manager.track-control"; +extern const char *kMethod; /// Payload action strings. -constexpr const char *kActionMute = "mute"; -constexpr const char *kActionUnmute = "unmute"; +extern const char *kActionMute; +extern const char *kActionUnmute; /// Delimiter between action and track name in the payload (e.g. "mute:cam"). -constexpr char kDelimiter = ':'; +extern const char kDelimiter; /// Response payload returned on success. -constexpr const char *kResponseOk = "ok"; +extern const char *kResponseOk; /// Build a track-control RPC payload: ":". -inline std::string formatPayload(const char *action, - const std::string &track_name) { - std::string payload; - payload.reserve(std::char_traits::length(action) + 1 + - track_name.size()); - payload += action; - payload += kDelimiter; - payload += track_name; - return payload; -} +std::string formatPayload(const char *action, const std::string &track_name); } // namespace track_control } // namespace rpc diff --git a/src/session_manager/rpc_constants.cpp b/src/session_manager/rpc_constants.cpp new file mode 100644 index 00000000..6ca10109 --- /dev/null +++ b/src/session_manager/rpc_constants.cpp @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#include "livekit/session_manager/rpc_constants.h" + +namespace livekit { +namespace rpc { +namespace track_control { + +const char *kMethod = "lk.session_manager.track-control"; +const char *kActionMute = "mute"; +const char *kActionUnmute = "unmute"; +const char kDelimiter = ':'; +const char *kResponseOk = "ok"; + +std::string formatPayload(const char *action, const std::string &track_name) { + std::string payload; + payload.reserve(std::char_traits::length(action) + 1 + + track_name.size()); + payload += action; + payload += kDelimiter; + payload += track_name; + return payload; +} + +} // namespace track_control +} // namespace rpc +} // namespace livekit From 85de0a1f9631eae5ae88066c722424895c273452 Mon Sep 17 00:00:00 2001 From: Stephen DeRosa Date: Wed, 11 Mar 2026 11:09:53 -0600 Subject: [PATCH 12/13] spdlog public dependecy, included in livekit.h --- CMakeLists.txt | 3 ++- cmake/LiveKitConfig.cmake.in | 3 +++ examples/CMakeLists.txt | 18 ++++++------------ .../base_sdk/logging_levels/basic_usage.cpp | 1 - examples/base_sdk/simple_room/main.cpp | 1 - examples/common/fallback_capture.cpp | 1 - examples/common/sdl_media.cpp | 1 - examples/common/sdl_media_manager.cpp | 1 - examples/common/sdl_video_renderer.cpp | 1 - examples/human_robot/human.cpp | 1 - examples/human_robot/robot.cpp | 1 - .../video_producer.cpp | 1 - .../session_manager_base_sdk/video_viewer.cpp | 1 - include/livekit/livekit.h | 1 + {src => include/livekit}/lk_log.h | 0 .../livekit/session_manager/session_manager.h | 1 + src/audio_source.cpp | 2 +- src/data_stream.cpp | 2 +- src/ffi_client.cpp | 2 +- src/livekit.cpp | 2 +- src/room.cpp | 2 +- .../managed_local_audio_track.cpp | 2 +- .../managed_local_video_track.cpp | 2 +- src/session_manager/session_manager.cpp | 2 +- src/tests/CMakeLists.txt | 1 - src/tests/integration/test_logging.cpp | 2 +- src/video_frame.cpp | 2 +- src/video_stream.cpp | 2 +- 28 files changed, 24 insertions(+), 35 deletions(-) rename {src => include/livekit}/lk_log.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index c355fbb5..9efd17dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -381,10 +381,11 @@ target_include_directories(livekit ) target_link_libraries(livekit + PUBLIC + $ PRIVATE livekit_ffi ${LIVEKIT_PROTOBUF_TARGET} - spdlog::spdlog ) target_compile_definitions(livekit diff --git a/cmake/LiveKitConfig.cmake.in b/cmake/LiveKitConfig.cmake.in index b5805e4d..90b1a7e1 100644 --- a/cmake/LiveKitConfig.cmake.in +++ b/cmake/LiveKitConfig.cmake.in @@ -1,4 +1,7 @@ @PACKAGE_INIT@ +include(CMakeFindDependencyMacro) +find_dependency(spdlog) + include("${CMAKE_CURRENT_LIST_DIR}/LiveKitTargets.cmake") diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 53880163..3f4c7c3e 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -88,7 +88,6 @@ target_include_directories(SimpleRoom PRIVATE target_link_libraries(SimpleRoom PRIVATE livekit - spdlog::spdlog SDL3::SDL3 ) @@ -143,7 +142,6 @@ target_link_libraries(SimpleRpc PRIVATE nlohmann_json::nlohmann_json livekit - spdlog::spdlog ) # --- SimpleJoystick example (sender + receiver executables with shared json_utils) --- @@ -174,7 +172,6 @@ target_link_libraries(SimpleJoystickReceiver PRIVATE simple_joystick_json_utils livekit - spdlog::spdlog ) add_executable(SimpleJoystickSender @@ -187,7 +184,6 @@ target_link_libraries(SimpleJoystickSender PRIVATE simple_joystick_json_utils livekit - spdlog::spdlog ) # --- LoggingLevelsBasicUsage example --- @@ -201,7 +197,6 @@ target_include_directories(LoggingLevelsBasicUsage PRIVATE ${EXAMPLES_PRIVATE_IN target_link_libraries(LoggingLevelsBasicUsage PRIVATE livekit - spdlog::spdlog ) # --- LoggingLevelsCustomSinks example --- @@ -226,7 +221,6 @@ target_include_directories(SimpleDataStream PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_D target_link_libraries(SimpleDataStream PRIVATE livekit - spdlog::spdlog ) add_custom_command( @@ -248,7 +242,7 @@ target_include_directories(HumanRobotRobot PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS} ${EXAMPLES_COMMON_DIR} ) -target_link_libraries(HumanRobotRobot PRIVATE livekit spdlog::spdlog SDL3::SDL3) +target_link_libraries(HumanRobotRobot PRIVATE livekit SDL3::SDL3) add_executable(HumanRobotHuman human_robot/human.cpp @@ -259,7 +253,7 @@ target_include_directories(HumanRobotHuman PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS} ${EXAMPLES_COMMON_DIR} ) -target_link_libraries(HumanRobotHuman PRIVATE livekit spdlog::spdlog SDL3::SDL3) +target_link_libraries(HumanRobotHuman PRIVATE livekit SDL3::SDL3) # --- rpc examples (headless custom RPC caller + receiver) --- @@ -286,7 +280,7 @@ target_include_directories(MuteUnmuteCaller PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS} ${EXAMPLES_COMMON_DIR} ) -target_link_libraries(MuteUnmuteCaller PRIVATE livekit spdlog::spdlog SDL3::SDL3) +target_link_libraries(MuteUnmuteCaller PRIVATE livekit SDL3::SDL3) add_executable(MuteUnmuteReceiver mute_unmute/receiver.cpp @@ -297,7 +291,7 @@ target_include_directories(MuteUnmuteReceiver PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS} ${EXAMPLES_COMMON_DIR} ) -target_link_libraries(MuteUnmuteReceiver PRIVATE livekit spdlog::spdlog SDL3::SDL3) +target_link_libraries(MuteUnmuteReceiver PRIVATE livekit SDL3::SDL3) # --- session_manager_base_sdk (SessionManager connect + getRoom(); Room for all else) --- add_executable(SessionManagerBaseSdkProducer @@ -309,7 +303,7 @@ target_include_directories(SessionManagerBaseSdkProducer PRIVATE ${EXAMPLES_PRIVATE_INCLUDE_DIRS} ${EXAMPLES_COMMON_DIR} ) -target_link_libraries(SessionManagerBaseSdkProducer PRIVATE livekit spdlog::spdlog SDL3::SDL3) +target_link_libraries(SessionManagerBaseSdkProducer PRIVATE livekit SDL3::SDL3) add_executable(SessionManagerBaseSdkViewer session_manager_base_sdk/video_viewer.cpp @@ -329,7 +323,7 @@ target_include_directories(SessionManagerBaseSdkViewer PRIVATE ${EXAMPLES_COMMON_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/base_sdk/simple_room ) -target_link_libraries(SessionManagerBaseSdkViewer PRIVATE livekit spdlog::spdlog SDL3::SDL3) +target_link_libraries(SessionManagerBaseSdkViewer PRIVATE livekit SDL3::SDL3) add_custom_command(TARGET SessionManagerBaseSdkViewer POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory diff --git a/examples/base_sdk/logging_levels/basic_usage.cpp b/examples/base_sdk/logging_levels/basic_usage.cpp index 657f6c3d..e77a5c5f 100644 --- a/examples/base_sdk/logging_levels/basic_usage.cpp +++ b/examples/base_sdk/logging_levels/basic_usage.cpp @@ -35,7 +35,6 @@ /// see which messages are filtered at each setting. #include "livekit/livekit.h" -#include "lk_log.h" #include #include diff --git a/examples/base_sdk/simple_room/main.cpp b/examples/base_sdk/simple_room/main.cpp index 90761857..4b2e2e16 100644 --- a/examples/base_sdk/simple_room/main.cpp +++ b/examples/base_sdk/simple_room/main.cpp @@ -27,7 +27,6 @@ #include #include "livekit/livekit.h" -#include "lk_log.h" #include "sdl_media_manager.h" #include "wav_audio_source.h" diff --git a/examples/common/fallback_capture.cpp b/examples/common/fallback_capture.cpp index 29508e34..26269886 100644 --- a/examples/common/fallback_capture.cpp +++ b/examples/common/fallback_capture.cpp @@ -24,7 +24,6 @@ #include #include "livekit/livekit.h" -#include "lk_log.h" #include "wav_audio_source.h" using namespace livekit; diff --git a/examples/common/sdl_media.cpp b/examples/common/sdl_media.cpp index 6975aca0..f14f4734 100644 --- a/examples/common/sdl_media.cpp +++ b/examples/common/sdl_media.cpp @@ -16,7 +16,6 @@ #include "sdl_media.h" -#include "lk_log.h" // ---------------------- SDLMicSource ----------------------------- diff --git a/examples/common/sdl_media_manager.cpp b/examples/common/sdl_media_manager.cpp index 93dc0d45..fcb4b533 100644 --- a/examples/common/sdl_media_manager.cpp +++ b/examples/common/sdl_media_manager.cpp @@ -18,7 +18,6 @@ #include "fallback_capture.h" #include "livekit/livekit.h" -#include "lk_log.h" #include "sdl_media.h" #include "sdl_video_renderer.h" #include diff --git a/examples/common/sdl_video_renderer.cpp b/examples/common/sdl_video_renderer.cpp index b820f539..fb7e4d66 100644 --- a/examples/common/sdl_video_renderer.cpp +++ b/examples/common/sdl_video_renderer.cpp @@ -17,7 +17,6 @@ #include "sdl_video_renderer.h" #include "livekit/livekit.h" -#include "lk_log.h" #include using namespace livekit; diff --git a/examples/human_robot/human.cpp b/examples/human_robot/human.cpp index cb546654..3aa4d419 100644 --- a/examples/human_robot/human.cpp +++ b/examples/human_robot/human.cpp @@ -47,7 +47,6 @@ #include "livekit/session_manager/session_manager.h" #include "livekit/track.h" #include "livekit/video_frame.h" -#include "lk_log.h" #include "sdl_media.h" #include diff --git a/examples/human_robot/robot.cpp b/examples/human_robot/robot.cpp index 5d3a5424..9b422198 100644 --- a/examples/human_robot/robot.cpp +++ b/examples/human_robot/robot.cpp @@ -37,7 +37,6 @@ #include "livekit/session_manager/session_manager.h" #include "livekit/track.h" #include "livekit/video_frame.h" -#include "lk_log.h" #include "sdl_media.h" #include diff --git a/examples/session_manager_base_sdk/video_producer.cpp b/examples/session_manager_base_sdk/video_producer.cpp index 6e8897a1..adb44307 100644 --- a/examples/session_manager_base_sdk/video_producer.cpp +++ b/examples/session_manager_base_sdk/video_producer.cpp @@ -32,7 +32,6 @@ #include "livekit/livekit.h" #include "livekit/session_manager/session_manager.h" -#include "lk_log.h" #include "sdl_media.h" #include diff --git a/examples/session_manager_base_sdk/video_viewer.cpp b/examples/session_manager_base_sdk/video_viewer.cpp index bfc63b2d..57e5ca69 100644 --- a/examples/session_manager_base_sdk/video_viewer.cpp +++ b/examples/session_manager_base_sdk/video_viewer.cpp @@ -31,7 +31,6 @@ #include "livekit/livekit.h" #include "livekit/session_manager/session_manager.h" -#include "lk_log.h" #include "sdl_media_manager.h" #include diff --git a/include/livekit/livekit.h b/include/livekit/livekit.h index d6f3ec01..a676282b 100644 --- a/include/livekit/livekit.h +++ b/include/livekit/livekit.h @@ -26,6 +26,7 @@ #include "local_participant.h" #include "local_track_publication.h" #include "local_video_track.h" +#include "lk_log.h" #include "logging.h" #include "participant.h" #include "remote_participant.h" diff --git a/src/lk_log.h b/include/livekit/lk_log.h similarity index 100% rename from src/lk_log.h rename to include/livekit/lk_log.h diff --git a/include/livekit/session_manager/session_manager.h b/include/livekit/session_manager/session_manager.h index 1f44a7dd..428c019f 100644 --- a/include/livekit/session_manager/session_manager.h +++ b/include/livekit/session_manager/session_manager.h @@ -19,6 +19,7 @@ #pragma once +#include "livekit/lk_log.h" #include "livekit/session_manager/managed_local_audio_track.h" #include "livekit/session_manager/managed_local_video_track.h" #include "livekit/session_manager/rpc_constants.h" diff --git a/src/audio_source.cpp b/src/audio_source.cpp index 4e1a48a1..bc648e50 100644 --- a/src/audio_source.cpp +++ b/src/audio_source.cpp @@ -24,7 +24,7 @@ #include "ffi.pb.h" #include "ffi_client.h" #include "livekit/audio_frame.h" -#include "lk_log.h" +#include "livekit/lk_log.h" namespace livekit { diff --git a/src/data_stream.cpp b/src/data_stream.cpp index 3376466a..edd03d0c 100644 --- a/src/data_stream.cpp +++ b/src/data_stream.cpp @@ -7,7 +7,7 @@ #include "ffi_client.h" #include "livekit/local_participant.h" -#include "lk_log.h" +#include "livekit/lk_log.h" #include "room.pb.h" namespace livekit { diff --git a/src/ffi_client.cpp b/src/ffi_client.cpp index 916ab88a..e8443d0c 100644 --- a/src/ffi_client.cpp +++ b/src/ffi_client.cpp @@ -26,7 +26,7 @@ #include "livekit/rpc_error.h" #include "livekit/track.h" #include "livekit_ffi.h" -#include "lk_log.h" +#include "livekit/lk_log.h" #include "room.pb.h" #include "room_proto_converter.h" diff --git a/src/livekit.cpp b/src/livekit.cpp index b9132efc..86b9ebd6 100644 --- a/src/livekit.cpp +++ b/src/livekit.cpp @@ -16,7 +16,7 @@ #include "livekit/livekit.h" #include "ffi_client.h" -#include "lk_log.h" +#include "livekit/lk_log.h" namespace livekit { diff --git a/src/room.cpp b/src/room.cpp index 7eed7ed9..9d0249e4 100644 --- a/src/room.cpp +++ b/src/room.cpp @@ -31,7 +31,7 @@ #include "ffi.pb.h" #include "ffi_client.h" #include "livekit_ffi.h" -#include "lk_log.h" +#include "livekit/lk_log.h" #include "room.pb.h" #include "room_proto_converter.h" #include "track.pb.h" diff --git a/src/session_manager/managed_local_audio_track.cpp b/src/session_manager/managed_local_audio_track.cpp index cf463aa4..5fe2c8f1 100644 --- a/src/session_manager/managed_local_audio_track.cpp +++ b/src/session_manager/managed_local_audio_track.cpp @@ -27,7 +27,7 @@ #include -#include "lk_log.h" +#include "livekit/lk_log.h" namespace livekit { diff --git a/src/session_manager/managed_local_video_track.cpp b/src/session_manager/managed_local_video_track.cpp index e22d8815..27a97f5f 100644 --- a/src/session_manager/managed_local_video_track.cpp +++ b/src/session_manager/managed_local_video_track.cpp @@ -27,7 +27,7 @@ #include -#include "lk_log.h" +#include "livekit/lk_log.h" namespace livekit { diff --git a/src/session_manager/session_manager.cpp b/src/session_manager/session_manager.cpp index bbb93b04..7389564f 100644 --- a/src/session_manager/session_manager.cpp +++ b/src/session_manager/session_manager.cpp @@ -39,7 +39,7 @@ #include #include -#include "lk_log.h" +#include "livekit/lk_log.h" namespace livekit { diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index af6aed18..6ff68ef9 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -41,7 +41,6 @@ if(INTEGRATION_TEST_SOURCES) target_link_libraries(livekit_integration_tests PRIVATE livekit - spdlog::spdlog GTest::gtest_main GTest::gmock ) diff --git a/src/tests/integration/test_logging.cpp b/src/tests/integration/test_logging.cpp index 88e16166..5ccb0fa1 100644 --- a/src/tests/integration/test_logging.cpp +++ b/src/tests/integration/test_logging.cpp @@ -17,7 +17,7 @@ #include #include -#include "lk_log.h" +#include "livekit/lk_log.h" #include #include diff --git a/src/video_frame.cpp b/src/video_frame.cpp index 5fdc83e2..271c5ce5 100644 --- a/src/video_frame.cpp +++ b/src/video_frame.cpp @@ -6,7 +6,7 @@ #include #include "livekit/ffi_handle.h" -#include "lk_log.h" +#include "livekit/lk_log.h" #include "video_utils.h" namespace livekit { diff --git a/src/video_stream.cpp b/src/video_stream.cpp index 182617e3..20038567 100644 --- a/src/video_stream.cpp +++ b/src/video_stream.cpp @@ -5,7 +5,7 @@ #include "ffi.pb.h" #include "ffi_client.h" #include "livekit/track.h" -#include "lk_log.h" +#include "livekit/lk_log.h" #include "video_frame.pb.h" #include "video_utils.h" From 19961547a54b01dc284f93b0ddfd961d7d45303e Mon Sep 17 00:00:00 2001 From: Stephen DeRosa Date: Wed, 11 Mar 2026 14:37:08 -0600 Subject: [PATCH 13/13] fix build error --- build.sh | 22 ++++++++++++++++++++++ examples/common/sdl_media.cpp | 1 + 2 files changed, 23 insertions(+) diff --git a/build.sh b/build.sh index 278cea55..e3b02d6a 100755 --- a/build.sh +++ b/build.sh @@ -37,6 +37,7 @@ Commands: release Configure + build Release version (build-release/) release-examples Configure + build Release version with examples release-tests Configure + build Release version with tests + build-all Configure + build all of the above (debug/release + examples + tests) clean Clean both Debug and Release build directories clean-all Full clean (build dirs + Rust targets) help Show this help message @@ -60,6 +61,7 @@ Examples: ./build.sh debug ./build.sh debug-tests ./build.sh release-tests + ./build.sh build-all ./build.sh clean ./build.sh clean-all EOF @@ -401,6 +403,26 @@ case "${cmd}" in fi fi ;; + build-all) + echo "==> Build-all: debug, debug-examples, debug-tests, release, release-examples, release-tests" + BUILD_TYPE="Debug" + BUILD_DIR="${PROJECT_ROOT}/build-debug" + PRESET="${OS_TYPE}-debug" + configure && build + PRESET="${OS_TYPE}-debug-examples" + configure && build + PRESET="${OS_TYPE}-debug-tests" + configure && build + BUILD_TYPE="Release" + BUILD_DIR="${PROJECT_ROOT}/build-release" + PRESET="${OS_TYPE}-release" + configure && build + PRESET="${OS_TYPE}-release-examples" + configure && build + PRESET="${OS_TYPE}-release-tests" + configure && build + echo "==> Build-all complete." + ;; clean) clean ;; diff --git a/examples/common/sdl_media.cpp b/examples/common/sdl_media.cpp index f14f4734..d4a44e63 100644 --- a/examples/common/sdl_media.cpp +++ b/examples/common/sdl_media.cpp @@ -16,6 +16,7 @@ #include "sdl_media.h" +#include "livekit/lk_log.h" // ---------------------- SDLMicSource -----------------------------