-
Notifications
You must be signed in to change notification settings - Fork 20
Bridge: RpcController #67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,8 +21,11 @@ | |
|
|
||
| #include "livekit_bridge/bridge_audio_track.h" | ||
| #include "livekit_bridge/bridge_video_track.h" | ||
| #include "livekit_bridge/rpc_constants.h" | ||
|
|
||
| #include "livekit/local_participant.h" | ||
| #include "livekit/room.h" | ||
| #include "livekit/rpc_error.h" | ||
|
|
||
| #include <cstdint> | ||
| #include <functional> | ||
|
|
@@ -46,6 +49,7 @@ enum class TrackSource; | |
| namespace livekit_bridge { | ||
|
|
||
| class BridgeRoomDelegate; | ||
| class RpcController; | ||
|
|
||
| namespace test { | ||
| class CallbackKeyTest; | ||
|
|
@@ -264,6 +268,90 @@ class LiveKitBridge { | |
| void clearOnVideoFrameCallback(const std::string &participant_identity, | ||
| livekit::TrackSource source); | ||
|
|
||
| // --------------------------------------------------------------- | ||
| // RPC (Remote Procedure Call) | ||
| // --------------------------------------------------------------- | ||
|
|
||
| /** | ||
| * Initiate a blocking RPC call to a remote participant. | ||
| * | ||
| * Sends a request to the participant identified by | ||
| * @p destination_identity and blocks until a response is received | ||
| * or the call times out. | ||
| * | ||
| * @param destination_identity Identity of the remote participant. | ||
| * @param method Name of the RPC method to invoke. | ||
| * @param payload Request payload string. | ||
| * @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. | ||
| */ | ||
| std::optional<std::string> | ||
| performRpc(const std::string &destination_identity, const std::string &method, | ||
| const std::string &payload, | ||
| const std::optional<double> &response_timeout = std::nullopt); | ||
|
|
||
| /** | ||
| * 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 | ||
| * an optional response payload or throw a @c livekit::RpcError to | ||
| * signal failure to the caller. | ||
| * | ||
| * If a handler is already registered for @p method_name, it is | ||
| * silently replaced. | ||
| * | ||
| * @param method_name Name of the RPC method to handle. | ||
| * @param handler Callback invoked on each incoming invocation. | ||
| * @return true if the RPC method was registered successfully. | ||
| */ | ||
| bool registerRpcMethod(const std::string &method_name, | ||
| livekit::LocalParticipant::RpcHandler handler); | ||
|
|
||
| /** | ||
| * Unregister a previously registered RPC method handler. | ||
| * | ||
| * After this call, invocations for @p method_name result in an | ||
| * "unsupported method" error being returned to the remote caller. | ||
| * If no handler is registered for this name, the call is a no-op. | ||
| * | ||
| * @param method_name Name of the RPC method to unregister. | ||
| * @return true if the RPC method was unregistered successfully. | ||
| */ | ||
| bool unregisterRpcMethod(const std::string &method_name); | ||
stephen-derosa marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // --------------------------------------------------------------- | ||
| // Remote Track Control (via RPC) | ||
| // --------------------------------------------------------------- | ||
|
|
||
| /** | ||
| * Request a remote participant to mute a published track. | ||
| * | ||
| * The remote participant must be a LiveKitBridge instance (which | ||
| * automatically registers the built-in track-control RPC handler). | ||
| * | ||
| * @param destination_identity Identity of the remote participant. | ||
| * @param track_name Name of the track to mute. | ||
| * @return true if the track was muted successfully. | ||
| */ | ||
| bool requestRemoteTrackMute(const std::string &destination_identity, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These APIs are very concerning and can be very confusing. What if the remote participant is not a livektiBridge (or SessionManager after the rename )? How do you make sure these actions are backward compatitable ?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These built in RPC calls only work from session manager to session manager, since the RpcController belongs to the session manager. Of course users can register their own remote track mute/unmute RPC methods! |
||
| const std::string &track_name); | ||
|
|
||
| /** | ||
| * Request a remote participant to unmute a published track. | ||
| * | ||
| * The remote participant must be a LiveKitBridge instance (which | ||
| * automatically registers the built-in track-control RPC handler). | ||
| * | ||
| * @param destination_identity Identity of the remote participant. | ||
| * @param track_name Name of the track to unmute. | ||
| * @return true if the track was unmuted successfully. | ||
| */ | ||
| bool requestRemoteTrackUnmute(const std::string &destination_identity, | ||
| const std::string &track_name); | ||
|
|
||
| private: | ||
| friend class BridgeRoomDelegate; | ||
| friend class test::CallbackKeyTest; | ||
|
|
@@ -314,6 +402,13 @@ class LiveKitBridge { | |
| const std::shared_ptr<livekit::Track> &track, | ||
| VideoFrameCallback cb); | ||
|
|
||
| /// Execute a track action (mute/unmute) by track name. | ||
| /// Used as the TrackActionFn callback for RpcController. | ||
| /// Throws livekit::RpcError if the track is not found. | ||
| /// @pre Caller does NOT hold mutex_ (acquires it internally). | ||
| void executeTrackAction(const rpc::track_control::Action &action, | ||
| const std::string &track_name); | ||
|
|
||
| mutable std::mutex mutex_; | ||
| bool connected_; | ||
| bool connecting_; // guards against concurrent connect() calls | ||
|
|
@@ -323,6 +418,7 @@ class LiveKitBridge { | |
|
|
||
| std::unique_ptr<livekit::Room> room_; | ||
| std::unique_ptr<BridgeRoomDelegate> delegate_; | ||
| std::unique_ptr<RpcController> rpc_controller_; | ||
|
|
||
| /// Registered callbacks (may be registered before tracks are subscribed). | ||
| std::unordered_map<CallbackKey, AudioFrameCallback, CallbackKeyHash> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| /* | ||
| * 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_constants.h | ||
| /// @brief Constants for built-in bridge RPC methods. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <string> | ||
|
|
||
| namespace livekit_bridge { | ||
| namespace rpc { | ||
|
|
||
| /// Built-in RPC method name used by remote track control. | ||
| /// Allows remote participants to mute or unmute tracks | ||
| /// published by this bridge. Must be called after connect(). | ||
| /// Audio/video tracks support mute and unmute. Data tracks | ||
| /// only support mute and unmute. | ||
| 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"; | ||
|
|
||
| /// Payload action strings. | ||
| constexpr const char *kActionMute = "mute"; | ||
| constexpr const char *kActionUnmute = "unmute"; | ||
|
|
||
| /// Delimiter between action and track name in the payload (e.g. "mute:cam"). | ||
| constexpr char kDelimiter = ':'; | ||
|
|
||
| /// Response payload returned on success. | ||
| constexpr const char *kResponseOk = "ok"; | ||
|
|
||
| /// Build a track-control RPC payload: "<action>:<track_name>". | ||
| inline std::string formatPayload(const char *action, | ||
| const std::string &track_name) { | ||
| std::string payload; | ||
| payload.reserve(std::char_traits<char>::length(action) + 1 + | ||
| track_name.size()); | ||
| payload += action; | ||
| payload += kDelimiter; | ||
| payload += track_name; | ||
| return payload; | ||
| } | ||
|
|
||
| } // namespace track_control | ||
| } // namespace rpc | ||
| } // namespace livekit_bridge |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RpcMethod is pretty low level, I wonder if we should make a remote controller, and we will setup a remoteControlInternal method as the communication channel for the remote control
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
im not sure i understand the need to break this into another layer. My intention here was to have a single object (currently RPCManager) which does all the interfacing with the local participant required to register/make/receive RPC calls. I wanted to keep the function of
registerRpcMethodhere the same as it is in the local participant for clarity.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
who is going to call this registerRpcMethod() ? like developers ? or it will be called by your RpcController / RpcManager ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a developer in their application would call:
livekit_bridge->registerRpcMethod("my-awesome-rpc", awesome_rpc_handle)which simply wraps around theRpcController