Hallo zusammen, ich bin zum ersten Mal hier als Mitwirkender und entschuldige mich, falls die Formatierung nicht korrekt ist.
Ich richte ein Discourse-Forum für eine Gruppe von Automobilliebhabern ein, die Live-Videochats über LiveKit für größere Räume/Präsentationen nutzen wollte.
Beim Testen traten bei Video-Streams konsistent schwarze Kacheln auf, aber nur bei Verwendung der LiveKit-Integration. Ich habe Claude Code zur Untersuchung eingesetzt und einen möglichen Fehler und eine Lösung gefunden. Der von Claude erstellte Bericht folgt unten.
Gerne kann ich einen PR mit einer vorgeschlagenen Lösung erstellen, wenn das hilfreich wäre.
Zusammenfassung
In einem LiveKit-(SFU)-Raum bleiben für alle anderen Teilnehmer schwarze Kacheln zurück, wenn man von der Raumseite zum schwebenden Call-Widget wechselt. Die anderen Teilnehmer sehen den Betrachter weiterhin korrekt. Wenn man zur Raumseite zurückkehrt oder während des Widgets den Raum verlässt und erneut betritt, wird das Video wiederhergestellt. Dies passiert jedes Mal, weil das Herunterfahren der Raumseite watching=false sendet, nachdem das Widget watching=true gesendet hat.
Version
- Discourse im Commit
1f09a33bffd908d6e38ef4b51697e8390e2a4c7f(der Branchlatest, Oktober 2026), Live-Plugin wie mitgeliefert - LiveKit Cloud als Media-Server (
live_livekit_room_policy = per_room) - Firefox 159 auf Linux (auch in anderen Browsern und auf Smartphones beobachtet)
Schritte
- Zwei Konten (eines in einem privaten Fenster) betreten denselben LiveKit-Raum mit aktivierten Kameras.
- In einem Fenster auf einen Link klicken, der die Raumseite verlässt, sodass der Call zum Widget wechselt.
- In diesem Fenster ist die Kachel des anderen Teilnehmers schwarz. Das andere Fenster sieht beide Teilnehmer korrekt.
Beobachtungen
- Im Fenster mit den schwarzen Kacheln hat das
<video>-Element des anderen Teilnehmers einen Stream, ist nicht pausiert und hatreadyState4. Aber sein Track hatreadyState: "live",muted: true,8×8, undgetVideoPlaybackQuality().totalVideoFramesist 0. Es sind nie Medien darüber empfangen worden. - Netzwerk-Tab, Wechsel von einer Seite → Widget: drei
POST /live/rooms/<id>/state-Aufrufe, mitwatching= true, true, false in dieser Reihenfolge.
Ursache (aus der Analyse des Codes im oben genannten Commit)
room-page.gjs,willDestroy:next(() => liveWebrtc.setWatching(roomId, false, { keepVideo }))call-widget.gjs:didInsertdes Widgets ruftwatchWidgetRoomauf, welchesnext(() => setWatching(roomId, true))ausführtlive-webrtc.js,setWatching: Bei Nicht-Mesh-Räumen wirdsetVideoSubscriptionsEnabled(watching)aufgerufen, was die Kamera-, Bildschirm- und Bildschirmton-Tracks des Betrachters deaktiviert (livekit-session.js,#applyDesiredSubscription)
Das aufgeschobene false der Seite landet nach dem true des Widgets, sodass sich der Betrachter im “nicht beobachtenden” Zustand befindet, während das Widget sichtbar ist. Widget → Raumseite funktioniert, weil stopWatchingWidgetRoom beim Destroy synchron ausgeführt wird und das true der Seite aufgeschoben ist, sodass true gewinnt. Ein neuer Beitritt im Widget funktioniert, weil keine alte Seite vorhanden ist.
Peer-to-Peer-Räume deaktivieren keine Abonnements (der SFU-Zweig wird übersprungen), was damit übereinstimmt, dass dieses Problem spezifisch für LiveKit-Räume ist.
Bestätigter Workaround, in der Browser-Konsole
- Mit schwarzer Kachel hat
Discourse.__container__.lookup("service:live-webrtc").setWatching(1, true)das Video wiederhergestellt. - Dieses, vor dem Wechsel installierte Wrapper-Skript, hielt die Kachel bei jedem Seiten → Widget-Wechsel aktiv:
const svc = Discourse.__container__.lookup("service:live-webrtc");
const orig = svc.setWatching.bind(svc);
svc.setWatching = (roomId, watching, options = {}) => {
if (watching) return orig(roomId, true, options);
setTimeout(() => {
const shown = document.querySelector(".live-call-widget, .live-room-page");
orig(roomId, !!shown, options);
}, 500);
};
Vorgeschlagene Lösung
Beim Herunterfahren der Raumseite sollte “watching” nicht deaktiviert werden, wenn das Widget dabei ist, denselben Raum anzuzeigen. Leiten Sie den watching-Zustand beispielsweise daraus ab, ob die Seite oder das Widget gemountet ist, anstatt jedes Element sein eigenes Set/Clear senden zu lassen. Auf diese Weise spielt die Reihenfolge keine Rolle mehr.
Verwandt, möglicherweise getrennt
Ein POST .../state-Aufruf in derselben Sitzung wurde mit 429 beantwortet. Fehler bei diesen Aufrufen werden ignoriert (.catch(() => {})), sodass ein abgelehntes “watching”-Update lautlos fehlschlägt.