Skip to content

Commit 4b75ba8

Browse files
setOn* replaces the callback in place
1 parent 2aa6d4c commit 4b75ba8

12 files changed

Lines changed: 1282 additions & 416 deletions

README.md

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,23 @@ room->addOnDataFrameCallback(sender_identity, "app-data",
184184
});
185185
```
186186

187+
Calling `setOnAudioFrameCallback` / `setOnVideoFrameCallback` /
188+
`setOnVideoFrameEventCallback` again for the same
189+
`(participant_identity, track_name)` **replaces** the callback in place. The
190+
previous reader is stopped and its thread joined before the call returns, then a
191+
fresh reader is started bound to the new callback — there is no need to call
192+
`clearOn*FrameCallback` first. Two consequences worth knowing:
193+
194+
- **These calls block** until any in-flight invocation of the previous callback
195+
returns. When the call returns, the old callback is guaranteed to have
196+
finished and been destroyed. A callback that blocks forever blocks
197+
registration forever.
198+
- **Do not register or clear from inside a frame callback.** Doing so would make
199+
the join a self-join. The SDK detects this, logs an error, and detaches the
200+
reader (media) or leaves it in place to be reaped at teardown (data), but the
201+
registration does not behave as intended. Drive callback changes from another
202+
thread.
203+
187204
For end-to-end samples and a fuller set of demos, see the [cpp-example-collection repo](https://github.com/livekit-examples/cpp-example-collection).
188205

189206
### Generating tokens
@@ -260,13 +277,6 @@ The following features are deprecated and will be removed in the next major rele
260277

261278
- `PacketTrailerFeatures` is deprecated. Use `FrameMetadataFeatures` via
262279
`TrackPublishOptions::frame_metadata_features` instead.
263-
- `Room::setOnAudioFrameCallback`, `Room::setOnVideoFrameCallback`, and
264-
`Room::setOnVideoFrameEventCallback` are deprecated. Use the `[[nodiscard]]`
265-
variants `trySetOnAudioFrameCallback`, `trySetOnVideoFrameCallback`, and
266-
`trySetOnVideoFrameEventCallback` instead, which return `false` when a reader
267-
is already active for the key (instead of silently replacing a running callback).
268-
To replace an active callback, call `clearOn*FrameCallback` first. (The same rename
269-
applies to the corresponding `SubscriptionThreadDispatcher` methods.)
270280

271281
### `v1.0.0`
272282

include/livekit/room.h

Lines changed: 32 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -303,92 +303,76 @@ class LIVEKIT_API Room {
303303
// Frame callbacks
304304
// ---------------------------------------------------------------
305305

306-
/// Register an audio frame callback for a remote subscription.
306+
/// Register or replace an audio frame callback for a remote subscription.
307307
///
308308
/// The callback is keyed by @p participant_identity and @p track_name. If the
309309
/// matching remote audio track is already subscribed, a reader is started
310310
/// immediately; otherwise the reader starts when the track is subscribed.
311311
///
312-
/// To replace a callback whose reader is already running, call
313-
/// @ref clearOnAudioFrameCallback first, then register again:
312+
/// Registering again for the same key replaces the callback in place. The
313+
/// previous reader is stopped and joined, then a fresh reader is started bound
314+
/// to the new callback -- no need to clear first:
314315
/// @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-
/// }
316+
/// room.setOnAudioFrameCallback(identity, track_name, new_handler);
319317
/// @endcode
318+
/// When this call returns, the previous callback has finished executing and
319+
/// its copy has been destroyed.
320+
///
321+
/// @warning This call blocks until any in-flight invocation of the previous
322+
/// callback returns. A slow callback makes registration slow; a
323+
/// callback that never returns blocks this call indefinitely.
324+
///
325+
/// @warning Calling this from inside a frame callback for the same key is not
326+
/// supported. The re-entrant call is detected and logged, and the
327+
/// reader is detached rather than self-joined.
320328
///
321329
/// @param participant_identity Identity of the remote participant.
322330
/// @param track_name Track name to match.
323331
/// @param callback Function invoked for each decoded audio frame.
324332
/// @param opts Options used when creating the backing
325333
/// @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 = {});
334+
void setOnAudioFrameCallback(const std::string& participant_identity, const std::string& track_name,
335+
AudioFrameCallback callback, const AudioStream::Options& opts = {});
331336

332-
/// Register a video frame callback for a remote subscription.
337+
/// Register or replace a video frame callback for a remote subscription.
333338
///
334339
/// The callback is keyed by @p participant_identity and @p track_name. If the
335340
/// matching remote video track is already subscribed, a reader is started
336341
/// immediately; otherwise the reader starts when the track is subscribed.
337342
///
338-
/// To replace a callback whose reader is already running, call
339-
/// @ref clearOnVideoFrameCallback first, then register again.
343+
/// Registering again for the same key replaces the callback in place; see
344+
/// @ref setOnAudioFrameCallback for the full replacement semantics, blocking
345+
/// behavior, and re-entrancy caveat. This shares its registration slot with
346+
/// @ref setOnVideoFrameEventCallback -- registering either one replaces the
347+
/// other for the same key.
340348
///
341349
/// @param participant_identity Identity of the remote participant.
342350
/// @param track_name Track name to match.
343351
/// @param callback Function invoked for each decoded video frame.
344352
/// @param opts Options used when creating the backing
345353
/// @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 = {});
354+
void setOnVideoFrameCallback(const std::string& participant_identity, const std::string& track_name,
355+
VideoFrameCallback callback, const VideoStream::Options& opts = {});
351356

352-
/// Register a rich video frame event callback for a remote subscription.
357+
/// Register or replace a rich video frame event callback for a remote
358+
/// subscription.
353359
///
354360
/// The callback is keyed by @p participant_identity and @p track_name. If the
355361
/// matching remote video track is already subscribed, a reader is started
356362
/// immediately; otherwise the reader starts when the track is subscribed.
357363
///
358-
/// To replace a callback whose reader is already running, call
359-
/// @ref clearOnVideoFrameCallback first, then register again.
364+
/// Registering again for the same key replaces the callback in place; see
365+
/// @ref setOnAudioFrameCallback for the full replacement semantics, blocking
366+
/// behavior, and re-entrancy caveat. This shares its registration slot with
367+
/// @ref setOnVideoFrameCallback -- registering either one replaces the other
368+
/// for the same key.
360369
///
361370
/// @param participant_identity Identity of the remote participant.
362371
/// @param track_name Track name to match.
363372
/// @param callback Function invoked for each decoded video frame
364373
/// event, including optional metadata.
365374
/// @param opts Options used when creating the backing
366375
/// @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-
[[deprecated("Room::setOnAudioFrameCallback is deprecated; use trySetOnAudioFrameCallback instead")]]
378-
void setOnAudioFrameCallback(const std::string& participant_identity, const std::string& track_name,
379-
AudioFrameCallback callback, const AudioStream::Options& opts = {});
380-
381-
/// @deprecated Use trySetOnVideoFrameCallback() instead.
382-
///
383-
/// Forwards to @ref trySetOnVideoFrameCallback and discards the result.
384-
[[deprecated("Room::setOnVideoFrameCallback is deprecated; use trySetOnVideoFrameCallback instead")]]
385-
void setOnVideoFrameCallback(const std::string& participant_identity, const std::string& track_name,
386-
VideoFrameCallback callback, const VideoStream::Options& opts = {});
387-
388-
/// @deprecated Use trySetOnVideoFrameEventCallback() instead.
389-
///
390-
/// Forwards to @ref trySetOnVideoFrameEventCallback and discards the result.
391-
[[deprecated("Room::setOnVideoFrameEventCallback is deprecated; use trySetOnVideoFrameEventCallback instead")]]
392376
void setOnVideoFrameEventCallback(const std::string& participant_identity, const std::string& track_name,
393377
VideoFrameEventCallback callback, const VideoStream::Options& opts = {});
394378

0 commit comments

Comments
 (0)