Skip to content

Commit 709a8ab

Browse files
SubscriptionThreadDispatcher: proper replacing of audio/video callbacks. Deprecate a setOn*Callback(), replace with trySetOn*Callback()
1 parent 338f266 commit 709a8ab

8 files changed

Lines changed: 460 additions & 102 deletions

include/livekit/room.h

Lines changed: 112 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -303,16 +303,118 @@ class LIVEKIT_API Room {
303303
// Frame callbacks
304304
// ---------------------------------------------------------------
305305

306-
/// @brief Sets the audio frame callback via SubscriptionThreadDispatcher.
306+
/// Register an audio frame callback for a remote subscription.
307+
///
308+
/// The callback is keyed by @p participant_identity and @p track_name. If the
309+
/// matching remote audio track is already subscribed, a reader is started
310+
/// immediately; otherwise the reader starts when the track is subscribed.
311+
///
312+
/// To replace a callback whose reader is already running, call
313+
/// @ref clearOnAudioFrameCallback first, then register again:
314+
/// @code
315+
/// room.clearOnAudioFrameCallback(identity, track_name);
316+
/// if (!room.trySetOnAudioFrameCallback(identity, track_name, new_handler)) {
317+
/// // registration was rejected (a reader is still active)
318+
/// }
319+
/// @endcode
320+
///
321+
/// @param participant_identity Identity of the remote participant.
322+
/// @param track_name Track name to match.
323+
/// @param callback Function invoked for each decoded audio frame.
324+
/// @param opts Options used when creating the backing
325+
/// @ref AudioStream.
326+
/// @return @c true if the callback was registered; @c false if a reader is
327+
/// already active for the key (call @ref clearOnAudioFrameCallback
328+
/// first) or the room has no dispatcher.
329+
[[nodiscard]] bool trySetOnAudioFrameCallback(const std::string& participant_identity, const std::string& track_name,
330+
AudioFrameCallback callback, const AudioStream::Options& opts = {});
331+
332+
/// Register a video frame callback for a remote subscription.
333+
///
334+
/// The callback is keyed by @p participant_identity and @p track_name. If the
335+
/// matching remote video track is already subscribed, a reader is started
336+
/// immediately; otherwise the reader starts when the track is subscribed.
337+
///
338+
/// To replace a callback whose reader is already running, call
339+
/// @ref clearOnVideoFrameCallback first, then register again.
340+
///
341+
/// @param participant_identity Identity of the remote participant.
342+
/// @param track_name Track name to match.
343+
/// @param callback Function invoked for each decoded video frame.
344+
/// @param opts Options used when creating the backing
345+
/// @ref VideoStream.
346+
/// @return @c true if the callback was registered; @c false if a reader is
347+
/// already active for the key (call @ref clearOnVideoFrameCallback
348+
/// first) or the room has no dispatcher.
349+
[[nodiscard]] bool trySetOnVideoFrameCallback(const std::string& participant_identity, const std::string& track_name,
350+
VideoFrameCallback callback, const VideoStream::Options& opts = {});
351+
352+
/// Register a rich video frame event callback for a remote subscription.
353+
///
354+
/// The callback is keyed by @p participant_identity and @p track_name. If the
355+
/// matching remote video track is already subscribed, a reader is started
356+
/// immediately; otherwise the reader starts when the track is subscribed.
357+
///
358+
/// To replace a callback whose reader is already running, call
359+
/// @ref clearOnVideoFrameCallback first, then register again.
360+
///
361+
/// @param participant_identity Identity of the remote participant.
362+
/// @param track_name Track name to match.
363+
/// @param callback Function invoked for each decoded video frame
364+
/// event, including optional metadata.
365+
/// @param opts Options used when creating the backing
366+
/// @ref VideoStream.
367+
/// @return @c true if the callback was registered; @c false if a reader is
368+
/// already active for the key (call @ref clearOnVideoFrameCallback
369+
/// first) or the room has no dispatcher.
370+
[[nodiscard]] bool trySetOnVideoFrameEventCallback(const std::string& participant_identity,
371+
const std::string& track_name, VideoFrameEventCallback callback,
372+
const VideoStream::Options& opts = {});
373+
374+
/// @deprecated Use trySetOnAudioFrameCallback() instead.
375+
///
376+
/// Forwards to @ref trySetOnAudioFrameCallback and discards the result.
377+
/// Replacing an active callback is not supported through this overload; call
378+
/// @ref clearOnAudioFrameCallback first, then @ref trySetOnAudioFrameCallback.
379+
///
380+
/// @param participant_identity Identity of the remote participant.
381+
/// @param track_name Track name to match.
382+
/// @param callback Function invoked for each decoded audio frame.
383+
/// @param opts Options used when creating the backing
384+
/// @ref AudioStream.
385+
[[deprecated("Room::setOnAudioFrameCallback is deprecated; use trySetOnAudioFrameCallback instead")]]
307386
void setOnAudioFrameCallback(const std::string& participant_identity, const std::string& track_name,
308387
AudioFrameCallback callback, const AudioStream::Options& opts = {});
309388

310-
/// @brief Sets the video frame callback via SubscriptionThreadDispatcher.
389+
/// @deprecated Use trySetOnVideoFrameCallback() instead.
390+
///
391+
/// Forwards to @ref trySetOnVideoFrameCallback and discards the result.
392+
/// Replacing an active callback is not supported through this overload; call
393+
/// @ref clearOnVideoFrameCallback first, then @ref trySetOnVideoFrameCallback.
394+
///
395+
/// @param participant_identity Identity of the remote participant.
396+
/// @param track_name Track name to match.
397+
/// @param callback Function invoked for each decoded video frame.
398+
/// @param opts Options used when creating the backing
399+
/// @ref VideoStream.
400+
[[deprecated("Room::setOnVideoFrameCallback is deprecated; use trySetOnVideoFrameCallback instead")]]
311401
void setOnVideoFrameCallback(const std::string& participant_identity, const std::string& track_name,
312402
VideoFrameCallback callback, const VideoStream::Options& opts = {});
313403

314-
/// @brief Sets the video frame event callback via
315-
/// SubscriptionThreadDispatcher.
404+
/// @deprecated Use trySetOnVideoFrameEventCallback() instead.
405+
///
406+
/// Forwards to @ref trySetOnVideoFrameEventCallback and discards the result.
407+
/// Replacing an active callback is not supported through this overload; call
408+
/// @ref clearOnVideoFrameCallback first, then
409+
/// @ref trySetOnVideoFrameEventCallback.
410+
///
411+
/// @param participant_identity Identity of the remote participant.
412+
/// @param track_name Track name to match.
413+
/// @param callback Function invoked for each decoded video frame
414+
/// event, including optional metadata.
415+
/// @param opts Options used when creating the backing
416+
/// @ref VideoStream.
417+
[[deprecated("Room::setOnVideoFrameEventCallback is deprecated; use trySetOnVideoFrameEventCallback instead")]]
316418
void setOnVideoFrameEventCallback(const std::string& participant_identity, const std::string& track_name,
317419
VideoFrameEventCallback callback, const VideoStream::Options& opts = {});
318420

@@ -354,6 +456,12 @@ class LIVEKIT_API Room {
354456
// FfiClient listener ID (0 means no listener registered)
355457
int listener_id_{0};
356458

459+
/// Find a currently subscribed remote track matching the given participant
460+
/// identity and track name. Returns nullptr if no such subscribed track
461+
/// exists. Acquires @ref lock_.
462+
std::shared_ptr<Track> findSubscribedRemoteTrack(const std::string& participant_identity,
463+
const std::string& track_name) const;
464+
357465
void onEvent(const proto::FfiEvent& event);
358466
};
359467
} // namespace livekit

include/livekit/subscription_thread_dispatcher.h

Lines changed: 82 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,54 +93,124 @@ class LIVEKIT_API SubscriptionThreadDispatcher {
9393
/// Stops all active readers and clears all registered callbacks.
9494
~SubscriptionThreadDispatcher();
9595

96-
/// Register or replace an audio frame callback for a remote subscription.
96+
/// Register an audio frame callback for a remote subscription.
9797
///
9898
/// The callback is keyed by remote participant identity plus @p track_name.
9999
/// If the matching remote audio track is already subscribed, @ref Room may
100100
/// immediately call @ref handleTrackSubscribed to start a reader.
101101
///
102+
/// Registration only succeeds when no reader is currently active for the
103+
/// key. To replace a callback whose reader is already running, call
104+
/// @ref clearOnAudioFrameCallback first, then register again.
105+
///
102106
/// @param participant_identity Identity of the remote participant.
103107
/// @param track_name Track name to match.
104108
/// @param callback Function invoked for each decoded audio frame.
105109
/// @param opts Options used when creating the backing
106110
/// @ref AudioStream.
107-
void setOnAudioFrameCallback(const std::string& participant_identity, const std::string& track_name,
108-
AudioFrameCallback callback, const AudioStream::Options& opts = {});
111+
/// @return @c true if the callback was registered; @c false if a reader is
112+
/// already active for the key (the registration is left unchanged).
113+
[[nodiscard]] bool trySetOnAudioFrameCallback(const std::string& participant_identity, const std::string& track_name,
114+
AudioFrameCallback callback, const AudioStream::Options& opts = {});
109115

110-
/// Register or replace a video frame callback for a remote subscription.
116+
/// Register a video frame callback for a remote subscription.
111117
///
112118
/// The callback is keyed by remote participant identity plus @p track_name.
113119
/// If the matching remote video track is already subscribed, @ref Room may
114120
/// immediately call @ref handleTrackSubscribed to start a reader.
115121
///
122+
/// Registration only succeeds when no reader is currently active for the
123+
/// key. To replace a callback whose reader is already running, call
124+
/// @ref clearOnVideoFrameCallback first, then register again.
125+
///
116126
/// @param participant_identity Identity of the remote participant.
117127
/// @param track_name Track name to match.
118128
/// @param callback Function invoked for each decoded video frame.
119129
/// @param opts Options used when creating the backing
120130
/// @ref VideoStream.
121-
void setOnVideoFrameCallback(const std::string& participant_identity, const std::string& track_name,
122-
VideoFrameCallback callback, const VideoStream::Options& opts = {});
131+
/// @return @c true if the callback was registered; @c false if a reader is
132+
/// already active for the key (the registration is left unchanged).
133+
[[nodiscard]] bool trySetOnVideoFrameCallback(const std::string& participant_identity, const std::string& track_name,
134+
VideoFrameCallback callback, const VideoStream::Options& opts = {});
123135

124-
/// Register or replace a rich video frame event callback for a remote
125-
/// subscription.
136+
/// Register a rich video frame event callback for a remote subscription.
126137
///
127138
/// The callback is keyed by remote participant identity plus @p track_name.
128139
/// If the matching remote video track is already subscribed, @ref Room may
129140
/// immediately call @ref handleTrackSubscribed to start a reader.
130141
///
142+
/// Registration only succeeds when no reader is currently active for the
143+
/// key. To replace a callback whose reader is already running, call
144+
/// @ref clearOnVideoFrameCallback first, then register again.
145+
///
146+
/// @param participant_identity Identity of the remote participant.
147+
/// @param track_name Track name to match.
148+
/// @param callback Function invoked for each decoded video frame
149+
/// event, including optional metadata.
150+
/// @param opts Options used when creating the backing
151+
/// @ref VideoStream.
152+
/// @return @c true if the callback was registered; @c false if a reader is
153+
/// already active for the key (the registration is left unchanged).
154+
[[nodiscard]] bool trySetOnVideoFrameEventCallback(const std::string& participant_identity,
155+
const std::string& track_name, VideoFrameEventCallback callback,
156+
const VideoStream::Options& opts = {});
157+
158+
/// @deprecated Use trySetOnAudioFrameCallback() instead.
159+
///
160+
/// Forwards to @ref trySetOnAudioFrameCallback and discards the result.
161+
/// Replacing an active callback is not supported through this overload; call
162+
/// @ref clearOnAudioFrameCallback first, then @ref trySetOnAudioFrameCallback.
163+
///
164+
/// @param participant_identity Identity of the remote participant.
165+
/// @param track_name Track name to match.
166+
/// @param callback Function invoked for each decoded audio frame.
167+
/// @param opts Options used when creating the backing
168+
/// @ref AudioStream.
169+
[[deprecated(
170+
"SubscriptionThreadDispatcher::setOnAudioFrameCallback is deprecated; use trySetOnAudioFrameCallback instead")]]
171+
void setOnAudioFrameCallback(const std::string& participant_identity, const std::string& track_name,
172+
AudioFrameCallback callback, const AudioStream::Options& opts = {});
173+
174+
/// @deprecated Use trySetOnVideoFrameCallback() instead.
175+
///
176+
/// Forwards to @ref trySetOnVideoFrameCallback and discards the result.
177+
/// Replacing an active callback is not supported through this overload; call
178+
/// @ref clearOnVideoFrameCallback first, then @ref trySetOnVideoFrameCallback.
179+
///
180+
/// @param participant_identity Identity of the remote participant.
181+
/// @param track_name Track name to match.
182+
/// @param callback Function invoked for each decoded video frame.
183+
/// @param opts Options used when creating the backing
184+
/// @ref VideoStream.
185+
[[deprecated(
186+
"SubscriptionThreadDispatcher::setOnVideoFrameCallback is deprecated; use trySetOnVideoFrameCallback instead")]]
187+
void setOnVideoFrameCallback(const std::string& participant_identity, const std::string& track_name,
188+
VideoFrameCallback callback, const VideoStream::Options& opts = {});
189+
190+
/// @deprecated Use trySetOnVideoFrameEventCallback() instead.
191+
///
192+
/// Forwards to @ref trySetOnVideoFrameEventCallback and discards the result.
193+
/// Replacing an active callback is not supported through this overload; call
194+
/// @ref clearOnVideoFrameCallback first, then
195+
/// @ref trySetOnVideoFrameEventCallback.
196+
///
131197
/// @param participant_identity Identity of the remote participant.
132198
/// @param track_name Track name to match.
133199
/// @param callback Function invoked for each decoded video frame
134200
/// event, including optional metadata.
135201
/// @param opts Options used when creating the backing
136202
/// @ref VideoStream.
203+
[[deprecated(
204+
"SubscriptionThreadDispatcher::setOnVideoFrameEventCallback is deprecated; use "
205+
"trySetOnVideoFrameEventCallback instead")]]
137206
void setOnVideoFrameEventCallback(const std::string& participant_identity, const std::string& track_name,
138207
VideoFrameEventCallback callback, const VideoStream::Options& opts = {});
139208

140209
/// Remove an audio callback registration and stop any active reader.
141210
///
142211
/// If an audio reader thread is active for the given key, its stream is
143-
/// closed and the thread is joined before this call returns.
212+
/// closed and the thread is joined before this call returns. Call this
213+
/// before @ref trySetOnAudioFrameCallback to replace an active callback.
144214
///
145215
/// @param participant_identity Identity of the remote participant.
146216
/// @param track_name Track name to clear.
@@ -149,7 +219,9 @@ class LIVEKIT_API SubscriptionThreadDispatcher {
149219
/// Remove a video callback registration and stop any active reader.
150220
///
151221
/// If a video reader thread is active for the given key, its stream is
152-
/// closed and the thread is joined before this call returns.
222+
/// closed and the thread is joined before this call returns. Call this
223+
/// before @ref trySetOnVideoFrameCallback (or
224+
/// @ref trySetOnVideoFrameEventCallback) to replace an active callback.
153225
///
154226
/// @param participant_identity Identity of the remote participant.
155227
/// @param track_name Track name to clear.

0 commit comments

Comments
 (0)