-
Notifications
You must be signed in to change notification settings - Fork 33
Expand file tree
/
Copy pathroom.h
More file actions
362 lines (313 loc) · 15 KB
/
Copy pathroom.h
File metadata and controls
362 lines (313 loc) · 15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
/*
* 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.
*/
#pragma once
#include <chrono>
#include <cstdint>
#include <future>
#include <memory>
#include <mutex>
#include "livekit/data_stream.h"
#include "livekit/e2ee.h"
#include "livekit/ffi_handle.h"
#include "livekit/room_event_types.h"
#include "livekit/stats.h"
#include "livekit/subscription_thread_dispatcher.h"
#include "livekit/visibility.h"
namespace livekit {
class RoomDelegate;
struct RoomInfoData;
namespace proto {
class FfiEvent;
}
struct E2EEOptions;
class E2EEManager;
class LocalParticipant;
class RemoteParticipant;
/// Represents a single ICE server configuration.
struct IceServer {
/// TURN/STUN server URL (e.g. "stun:stun.l.google.com:19302").
std::string url;
/// Optional username for TURN authentication.
std::string username;
/// Optional credential (password) for TURN authentication.
std::string credential;
};
/// WebRTC configuration (ICE, transport, etc.).
struct RtcConfig {
/// ICE transport type (e.g., ALL, RELAY). Maps to proto::IceTransportType.
int ice_transport_type = 0;
/// Continuous or single ICE gathering. Maps to
/// proto::ContinualGatheringPolicy.
int continual_gathering_policy = 0;
/// List of STUN/TURN servers for ICE candidate generation.
std::vector<IceServer> ice_servers;
};
/// Top-level room connection options.
struct RoomOptions {
/// If true (default), automatically subscribe to all remote tracks.
/// This is CRITICAL. Without auto_subscribe, you will never receive:
/// - `track_subscribed` events
/// - remote audio/video frames
///
/// @see https://docs.livekit.io/transport/media/subscribe/#selective-subscription
bool auto_subscribe = true;
/// Enable adaptive stream for subscribed video tracks.
///
/// When enabled, the SDK tells the server it may adjust the video layers sent
/// to this client based on what the application is currently rendering. This
/// lets the server pause or downscale subscribed video that is off-screen,
/// hidden, or only needed at a smaller size, reducing downstream bandwidth and
/// decode work. This affects media received by this room; use @ref dynacast
/// to control how this client publishes layers to others.
///
/// @see https://docs.livekit.io/transport/media/subscribe/#adaptive-stream
///
/// If unset, the Rust SDK default is used.
std::optional<bool> adaptive_stream;
/// Enable dynacast (server sends optimal layers depending on subscribers).
///
/// @see https://docs.livekit.io/transport/media/publish/#dynacast
bool dynacast = false;
/// Optional end-to-end encryption settings.
///
/// @see https://docs.livekit.io/transport/encryption/
std::optional<E2EEOptions> encryption;
/// Optional WebRTC configuration (ICE policy, servers, etc.)
///
/// @see https://docs.livekit.io/intro/basics/connect/#connection-reliability
std::optional<RtcConfig> rtc_config;
/// Number of retries for the initial room join after the first attempt.
///
/// If unset, the Rust SDK default is used.
std::optional<std::uint32_t> join_retries;
/// Enable single peer connection mode. When true, uses one RTCPeerConnection
/// for both publishing and subscribing instead of two separate connections.
/// Falls back to dual peer connection if the server doesn't support single PC.
bool single_peer_connection = true;
/// Timeout for each individual signal connection attempt.
///
/// If unset, the Rust SDK default is used.
std::optional<std::chrono::milliseconds> connect_timeout;
};
/// Represents a LiveKit room session.
/// A Room manages:
/// - the connection to the LiveKit server
/// - participant list (local + remote)
/// - track publications
/// - server events forwarded to a RoomDelegate
class LIVEKIT_API Room {
public:
Room();
~Room();
/// Assign a RoomDelegate that receives room lifecycle callbacks.
///
/// The delegate must remain valid for the lifetime of the Room or until a
/// different delegate is assigned. The Room does not take ownership.
/// Typical usage:
/// class MyDelegate : public RoomDelegate { ... };
/// MyDelegate del;
/// Room room;
/// room.setDelegate(&del);
void setDelegate(RoomDelegate* delegate);
/// Connect to a LiveKit room using the given URL and token, applying the
/// supplied connection options.
///
/// @param url WebSocket URL of the LiveKit server.
/// @param token Access token for authentication.
/// @param options Connection options controlling auto-subscribe,
/// dynacast, E2EE, and WebRTC configuration.
/// Behavior:
/// - Registers an FFI event listener *before* sending the connect request.
/// - Sends a proto::FfiRequest::Connect with the URL, token,
/// and the provided RoomOptions.
/// - Blocks until the FFI connect response arrives.
/// - Initializes local participant and remote participants.
/// - Emits room/participant/track events to the delegate.
/// IMPORTANT:
/// RoomOptions defaults auto_subscribe = true.
/// Without auto_subscribe enabled, remote tracks will NOT be subscribed
/// automatically, and no remote audio/video will ever arrive.
bool connect(const std::string& url, const std::string& token, const RoomOptions& options);
/// Disconnect from the room.
///
/// This method attempts a best-effort graceful disconnect of the room. If the room was connected prior, after @ref
/// disconnect() is called the room object is considered in a terminal state and should no longer be used. If @ref
/// disconnect() was called before @ref connect(), no operations are performed and the room object is still valid.
///
/// @note `~Room()` invokes `disconnect()` automatically if the room is
/// still connected, so explicit calls are optional.
/// @warning Safe to call from any thread, but **must not** be called from inside a
/// `RoomDelegate` callback — doing so will deadlock the event listener.
///
/// @param reason Reason reported to the server (default: ClientInitiated).
/// @returns true if the graceful disconnect succeeds; false if the
/// room was already disconnected (no-op) or the graceful disconnect fails.
bool disconnect(DisconnectReason reason = DisconnectReason::ClientInitiated);
// Accessors
/// Retrieve static metadata about the room.
/// This contains fields such as:
/// - SID
/// - room name
/// - metadata
/// - participant counts
/// - creation timestamp
RoomInfoData roomInfo() const;
/// Get the local participant.
///
/// This object represents the current user, including:
/// - published tracks (audio/video/screen)
/// - identity, SID, metadata
/// - publishing/unpublishing operations
///
/// The returned handle is non-owning. Call @c lock() to obtain a usable
/// @c weak_ptr; the result is empty (`lock() == nullptr`) before connect,
/// after room end-of-stream teardown, or once the room is destroyed. This
/// lets callers that cache the handle detect object lifetime instead of holding a
/// dangling pointer.
///
/// @return Weak handle to the local participant.
std::weak_ptr<LocalParticipant> localParticipant() const;
/// Look up a remote participant by identity.
///
/// @param identity The participant’s identity string (not SID).
/// @return Weak handle to the RemoteParticipant if present, otherwise an
/// empty handle (`lock() == nullptr`). The handle also becomes empty once
/// the participant disconnects, the room is torn down, or the room is
/// destroyed. RemoteParticipant contains:
/// - identity/name/metadata
/// - track publications
/// - callbacks for track subscribed/unsubscribed, muted/unmuted
std::weak_ptr<RemoteParticipant> remoteParticipant(const std::string& identity) const;
/// Returns a snapshot of all current remote participants.
///
/// @return Vector of weak handles to the current remote participants. Each
/// handle can be promoted with @c lock(); a handle becomes empty once the
/// corresponding participant disconnects or the room is torn down.
std::vector<std::weak_ptr<RemoteParticipant>> remoteParticipants() const;
/// Returns the current connection state of the room.
ConnectionState connectionState() const;
/// Retrieve aggregated WebRTC stats for this room session.
///
/// Dispatches an async request to the server and returns a future that
/// resolves with the stats.
///
/// @return Future of the room session stats.
///
/// @throws std::runtime_error Synchronously, if the room is not currently
/// connected, or if the FFI request fails to
/// dispatch.
/// @throws std::runtime_error On `future.get()`, if the async request fails.
std::future<SessionStats> getStats() const;
/// Register a handler for incoming text streams on a specific topic.
///
/// When a remote participant opens a text stream with the given topic,
/// the handler is invoked with:
/// - a shared_ptr<TextStreamReader> for consuming the stream
/// - the identity of the participant who sent the stream
///
/// Notes:
/// - Only one handler may be registered per topic.
/// - If no handler is registered for a topic, incoming streams with that
/// topic are ignored.
/// - The handler is invoked on the Room event thread. The handler must
/// not block; spawn a background thread if synchronous reading is
/// required.
///
/// @throws std::runtime_error if a handler is already registered for the topic.
void registerTextStreamHandler(const std::string& topic, TextStreamHandler handler);
/// Unregister the text stream handler for the given topic.
///
/// If no handler exists for the topic, this function is a no-op.
void unregisterTextStreamHandler(const std::string& topic);
/// Register a handler for incoming byte streams on a specific topic.
///
/// When a remote participant opens a byte stream with the given topic,
/// the handler is invoked with:
/// - a shared_ptr<ByteStreamReader> for consuming the stream
/// - the identity of the participant who sent the stream
///
/// Notes:
/// - Only one handler may be registered per topic.
/// - If no handler is registered for a topic, incoming streams with that
/// topic are ignored.
/// - The ByteStreamReader remains valid as long as the shared_ptr is held,
/// preventing lifetime-related crashes when reading asynchronously.
///
/// @throws std::runtime_error if a handler is already registered for the topic.
void registerByteStreamHandler(const std::string& topic, ByteStreamHandler handler);
/// Unregister the byte stream handler for the given topic.
///
/// If no handler exists for the topic, this function is a no-op.
void unregisterByteStreamHandler(const std::string& topic);
/// Returns the room's E2EE manager as a weak handle, or an empty handle if
/// E2EE was not enabled at connect time.
///
/// Notes:
/// - The manager is created after a successful connect().
/// - If E2EE was not configured in RoomOptions, @c lock() returns nullptr.
/// - The handle also becomes empty once the room is torn down or destroyed.
///
/// @return Weak handle to the E2EE manager.
std::weak_ptr<E2EEManager> e2eeManager() const;
// ---------------------------------------------------------------
// Frame callbacks
// ---------------------------------------------------------------
/// @brief Sets the audio frame callback via SubscriptionThreadDispatcher.
void setOnAudioFrameCallback(const std::string& participant_identity, const std::string& track_name,
AudioFrameCallback callback, const AudioStream::Options& opts = {});
/// @brief Sets the video frame callback via SubscriptionThreadDispatcher.
void setOnVideoFrameCallback(const std::string& participant_identity, const std::string& track_name,
VideoFrameCallback callback, const VideoStream::Options& opts = {});
/// @brief Sets the video frame event callback via
/// SubscriptionThreadDispatcher.
void setOnVideoFrameEventCallback(const std::string& participant_identity, const std::string& track_name,
VideoFrameEventCallback callback, const VideoStream::Options& opts = {});
/// @brief Clears the audio frame callback via SubscriptionThreadDispatcher.
void clearOnAudioFrameCallback(const std::string& participant_identity, const std::string& track_name);
/// @brief Clears the video frame callback via SubscriptionThreadDispatcher.
void clearOnVideoFrameCallback(const std::string& participant_identity, const std::string& track_name);
/// @brief Adds a data frame callback via SubscriptionThreadDispatcher.
DataFrameCallbackId addOnDataFrameCallback(const std::string& participant_identity, const std::string& track_name,
DataFrameCallback callback);
/// @brief Removes the data frame callback via SubscriptionThreadDispatcher.
void removeOnDataFrameCallback(DataFrameCallbackId id);
private:
friend struct RoomTestAccess;
mutable std::mutex lock_;
ConnectionState connection_state_ = ConnectionState::Disconnected;
RoomDelegate* delegate_ = nullptr; // Not owned
RoomInfoData room_info_;
std::shared_ptr<FfiHandle> room_handle_;
/// The local participant is owned by the room and is not shared with other objects.
/// It is a shared_ptr just to utilize the weak_ptr interface for the localParticipant() accessor.
std::shared_ptr<LocalParticipant> local_participant_;
std::unordered_map<std::string, std::shared_ptr<RemoteParticipant>> remote_participants_;
// Data stream
std::unordered_map<std::string, TextStreamHandler> text_stream_handlers_;
std::unordered_map<std::string, ByteStreamHandler> byte_stream_handlers_;
std::unordered_map<std::string, std::shared_ptr<TextStreamReader>> text_stream_readers_;
std::unordered_map<std::string, std::shared_ptr<ByteStreamReader>> byte_stream_readers_;
// The E2EE manager is owned by the room and is not shared with other objects.
// It is a shared_ptr just to utilize the weak_ptr interface for the e2eeManager() accessor.
std::shared_ptr<E2EEManager> e2ee_manager_;
std::shared_ptr<SubscriptionThreadDispatcher> subscription_thread_dispatcher_;
// FfiClient listener ID (0 means no listener registered)
int listener_id_{0};
void onEvent(const proto::FfiEvent& event);
// Shared shutdown path for explicit disconnect, server disconnect, EOS, and destruction.
bool shutdown(bool disconnect_ffi, DisconnectReason reason, bool notify_delegate);
};
} // namespace livekit