Bonjour à tous, je participe pour la première fois, désolé si le formatage de ce message n’est pas idéal.
Je mets en place un forum Discourse pour un groupe passionné d’automobile et ils souhaitaient utiliser les discussions vidéo en direct via LiveKit pour les grandes salles/présentations.
Lors des tests, nous avons systématiquement rencontré des vignettes vidéo noires, mais uniquement lors de l’utilisation de l’intégration LiveKit. J’ai utilisé Claude Code pour enquêter et j’ai trouvé ce qui ressemble à la cause du problème ainsi qu’un correctif ; le rapport généré par Claude suit ci-dessous.
Je peux volontiers soumettre une PR avec une proposition de correctif si cela peut être utile.
Résumé
Dans une salle LiveKit (SFU), passer de la page de la salle au widget d’appel flottant laisse le spectateur avec des vignettes noires pour tous les autres. Les autres participants voient toujours correctement le spectateur. Revenir à la page de la salle, ou quitter et rejoindre à nouveau tout en utilisant le widget, restaure la vidéo. Cela se produit à chaque fois, car le démontage de la page de la salle envoie watching=false après que le widget a envoyé watching=true.
Version
- Discourse au commit
1f09a33bffd908d6e38ef4b51697e8390e2a4c7f(la branchelatest, octobre 2026), plugin Live inclus dans le bundle - LiveKit Cloud comme serveur média (
live_livekit_room_policy = per_room) - Firefox 159 sur Linux (également observé sur d’autres navigateurs et téléphones)
Étapes
- Deux comptes (l’un dans une fenêtre privée) rejoignent la même salle activée pour LiveKit avec les caméras allumées.
- Dans une fenêtre, cliquez sur un lien qui quitte la page de la salle, ce qui déplace l’appel dans le widget.
- Dans cette fenêtre, la vignette de l’autre participant est noire. L’autre fenêtre voit les deux correctement.
Ce que j’ai observé
- Dans la fenêtre avec les vignettes noires, la balise
<video>de l’autre personne possède un flux, n’est pas en pause et a unreadyStatede 4. Mais sa piste est àreadyState: "live",muted: true, taille8x8, etgetVideoPlaybackQuality().totalVideoFramesest à 0. Aucun média n’est jamais arrivé sur cette piste. - Dans l’onglet réseau, lors d’un passage d’une page au widget : trois appels
POST /live/rooms/<id>/state, avecwatching= true, true, false dans cet ordre.
Cause (issue de la lecture du code au commit mentionné ci-dessus)
room-page.gjs,willDestroy:next(() => liveWebrtc.setWatching(roomId, false, { keepVideo }))call-widget.gjs: l’élémentdidInsertdu widget appellewatchWidgetRoom, qui exécutenext(() => setWatching(roomId, true))live-webrtc.js,setWatching: pour les salles non mesh, il appellesetVideoSubscriptionsEnabled(watching), ce qui désabonne les pistes vidéo caméra, partage d’écran et audio du partage d’écran du spectateur (livekit-session.js,#applyDesiredSubscription)
Le false différé de la page arrive après le true du widget, donc le spectateur se retrouve dans l’état “ne regarde pas” alors que le widget est à l’écran. Le passage du widget vers la page de la salle fonctionne car le stopWatchingWidgetRoom du widget s’exécute de manière synchrone lors de la destruction, et le true de la page est différé, donc le true l’emporte. Une nouvelle connexion via le widget fonctionne car il n’y a pas de page précédente.
Les salles peer-to-peer ne désabonnent personne (la branche SFU est ignorée), ce qui correspondrait à ce problème étant spécifique aux salles LiveKit.
Contournement confirmé, dans la console du navigateur
- Avec la vignette noire,
Discourse.__container__.lookup("service:live-webrtc").setWatching(1, true)a restauré la vidéo. - Ce wrapper, installé avant le changement, a maintenu la vignette active lors de chaque passage page → widget :
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);
};
Correctif suggéré
Le démontage de la page de la salle ne devrait pas désactiver le suivi “watching” lorsque le widget est sur le point d’afficher la même salle. Par exemple, déduisez l’état de suivi en fonction du montage de la page ou du widget, au lieu de laisser chacun envoyer ses propres commandes d’activation/désactivation. Ainsi, l’ordre n’a plus d’importance.
Related, peut-être séparé
Un POST .../state dans la même session a reçu une réponse 429. Les échecs de ces appels sont ignorés (.catch(() => {})), donc une mise à jour “watching” refusée échoue silencieusement.