From 77230827953f4dea0dd7c8d43186c0a011ce08af Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Sun, 4 Oct 2026 18:00:28 -0700 Subject: [PATCH] feat(session): subscribe only to the slot's own camera and mic Every plugin source connected with auto_subscribe on, so each one pulled every camera in the room and discarded all but one. In the 2026-10-04 old-gods-of-appalachia show (8 sources, 7 cameras) each source carried 4-7 Mbps and all 8 hit congestion in the same instant: they share the director's single downlink, so the SFU starved the cameras on screen. The session now connects with auto_subscribe off and calls setSubscribed(true) on exactly the wanted publications: at connect, on onTrackPublished (re-sweeping the participant, since SDK 1.10.1 delivers that event's publication null), and after a reconnect. Any subscription it did not ask for is reported and handed back. Audio-only sources no longer subscribe-then-disable video; they never subscribe to it. The integration test gains a bystander camera that must never be subscribed (fails with 3 unwanted subscriptions before this change) and a full publisher leave/rejoin as a new participant session. Co-Authored-By: Claude Opus 5.5 (1M context) --- core/include/stplugin/session.h | 8 +- core/include/stplugin/session_types.h | 8 ++ core/src/session.cpp | 145 ++++++++++++++++-------- core/src/session_types.cpp | 8 ++ core/tests/test_integration_livekit.cpp | 82 +++++++++++++- core/tests/test_session.cpp | 29 +++++ scripts/livekit-dev-room.py | 9 +- 7 files changed, 234 insertions(+), 55 deletions(-) diff --git a/core/include/stplugin/session.h b/core/include/stplugin/session.h index 95e48ef..3a467b6 100644 --- a/core/include/stplugin/session.h +++ b/core/include/stplugin/session.h @@ -44,11 +44,9 @@ struct SessionConfig { bool subscribe_audio = true; - /// False for an audio-only source (the soundboard, say): the wanted - /// video track is never attached (no AttachVideo command posted), and - /// its publication is explicitly disabled server-side (RemoteTrack- - /// Publication::setEnabled(false)) so the SFU stops sending it at all -- - /// not just "decoded and discarded here", genuinely not delivered. + /// False for an audio-only source (the soundboard, say): the slot's + /// video track is never subscribed to (see shouldSubscribe), so the SFU + /// never sends it at all -- not just "decoded and discarded here". bool subscribe_video = true; /// How long connect() waits for the room to come up before giving up. diff --git a/core/include/stplugin/session_types.h b/core/include/stplugin/session_types.h index e7c544d..970725d 100644 --- a/core/include/stplugin/session_types.h +++ b/core/include/stplugin/session_types.h @@ -70,6 +70,14 @@ bool isWantedVideoTrack(const std::string &wanted_identity, const std::string &t bool isWantedAudioTrack(const std::string &wanted_identity, const std::string &track_identity, MediaKind kind, MediaSource source); +/// Should this session subscribe to this publication at all? The session +/// connects with auto_subscribe off and asks for exactly these, so the SFU +/// never sends this source anything it would only throw away. Without it, +/// every plugin source pulled every camera in the room: eight sources in one +/// OBS meant the director's downlink carried each camera eight times. +bool shouldSubscribe(const std::string &wanted_identity, const std::string &track_identity, MediaKind kind, + MediaSource source, bool subscribe_video, bool subscribe_audio); + // --------------------------------------------------------------------------- // Session state // --------------------------------------------------------------------------- diff --git a/core/src/session.cpp b/core/src/session.cpp index 6d5fa43..3e45b57 100644 --- a/core/src/session.cpp +++ b/core/src/session.cpp @@ -269,39 +269,21 @@ struct LiveKitSession::Impl : public livekit::RoomDelegate { // Handles the wanted video track once matched, shared by onTrackSubscribed // (a fresh subscription) and attachExistingTracks (one already up when - // this session started watching). Two responsibilities that only make - // sense together, both keyed off the SAME publication: + // this session started watching). An audio-only source (the soundboard) + // never gets here for video: shouldSubscribe() never asks for it. // - // - subscribe_video: an audio-only source (the soundboard) never wants - // this video at all. Rather than attach it and let the OBS adapter - // discard every decoded frame, disable the publication itself - // (RemoteTrackPublication::setEnabled(false)) so the SFU stops - // sending it -- real bandwidth saved, not just wasted decode. - // - Fixed video quality: LiveKit's default subscriber behaviour lets - // the SFU switch simulcast layers per its own adaptive/bandwidth - // logic, which for a source with no rendered-size hint (this is a - // native C++ subscriber, not a sized