안녕하세요, 모든 분! 첫 기여자라 제대로 형식을 맞추지 못했다면 죄송합니다.
자동차 애호가 그룹을 위해 Discourse 포럼을 설정하고 있으며, 대형 룸/발표 시 LiveKit을 통한 실시간 화상 채팅을 사용하고 싶어 합니다.
테스트 중 LiveKit 통합을 사용할 때만 비디오 스트림에 검은색 타일이 표시되는 문제가 지속적으로 발생했습니다. Claude Code를 사용하여 조사한 결과, 문제점과 수정 방법을 발견한 것 같습니다. 아래는 Claude가 생성한 보고서입니다.
유용하다면 제안된 수정 사항으로 PR을 만들 수 있습니다.
요약
LiveKit(SFU) 룸에서 룸 페이지를 부동 콜 위젯으로 전환하면 시청자에게 다른 모든 사용자의 타일이 검은색으로 표시됩니다. 다른 참여자들은 시청자를 정상적으로 볼 수 있습니다. 룸 페이지로 돌아가거나, 위젯 상태인 채로 나갔다 다시 입장하면 비디오가 다시 표시됩니다. 이는 위젯이 watching=true를 보낸 후에 룸 페이지의 종료 처리가 watching=false를 보내기 때문에 매번 발생합니다.
버전
- Discourse 커밋
1f09a33bffd908d6e38ef4b51697e8390e2a4c7f(latest브랜치, 2026-10), 번들된 대로 Live 플러그인 - 미디어 서버로 LiveKit Cloud (
live_livekit_room_policy = per_room) - 리눅스용 Firefox 159 (다른 브라우저 및 휴대폰에서도 관찰됨)
단계
- 두 개의 계정(하나는 시크릿 창)이 카메라가 켜진 동일한 LiveKit 지원 룸에 참여합니다.
- 한 창에서 룸 페이지를 떠나는 링크를 클릭하면 콜이 위젯으로 이동합니다.
- 해당 창에서 다른 사용자의 타일이 검은색으로 표시됩니다. 다른 창에서는 둘 다 정상적으로 보입니다.
관찰된 현상
- 검은색 창에서 다른 사용자의
<video>요소에는 스트림이 있고 일시 정지되지 않았으며readyState는 4입니다. 그러나 해당 트랙은readyState: "live",muted: true,8×8이며getVideoPlaybackQuality().totalVideoFrames는 0입니다. 미디어가 전혀 도착하지 않았습니다. - 네트워크 탭, 페이지 → 위젯 전환 시: 세 번의
POST /live/rooms/<id>/state호출이 있었으며,watching값은 순서대로 true, true, false였습니다.
원인 (위 커밋의 코드 읽기 결과)
room-page.gjs,willDestroy:next(() => liveWebrtc.setWatching(roomId, false, { keepVideo }))call-widget.gjs: 위젯의didInsert는watchWidgetRoom을 호출하며, 이는next(() => setWatching(roomId, true))를 실행합니다.live-webrtc.js,setWatching: 메시가 아닌 룸의 경우setVideoSubscriptionsEnabled(watching)를 호출하여 시청자의 카메라, 화면 및 화면 오디오 트랙을 구독 취소합니다 (livekit-session.js,#applyDesiredSubscription).
페이지의 지연된 false가 위젯의 true보다 나중에 도착하므로, 위젯이 화면에 있는 동안 시청자는 not-watching 상태가 됩니다. 위젯 → 룸 페이지 전환이 작동하는 이유는 위젯의 stopWatchingWidgetRoom이 파괴 시 동기적으로 실행되고 페이지의 true는 지연되므로 true가 우선하기 때문입니다. 위젯에서 새로 입장할 경우 작동하는 이유는 이전 페이지가 없기 때문입니다.
피어 투 피어(P2P) 룸은 누구도 구독 취소하지 않으므로(SFU 분기가 건너뜀됨), 이 문제가 LiveKit 룸에만 특정된다는 사실과 일치합니다.
확인된 우회책, 브라우저 콘솔
- 타일이 검은색일 때,
Discourse.__container__.lookup("service:live-webrtc").setWatching(1, true)를 실행하면 비디오가 다시 돌아왔습니다. - 이 래퍼는 전환 전에 설치하면 모든 페이지 → 위젯 전환 시 타일이 유지되었습니다.
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);
};
제안된 수정 사항
위젯이 같은 룸을 표시하려고 할 때 룸 페이지의 종료 처리가 watching을 끄면 안 됩니다. 예를 들어, 페이지 또는 위젯 중 어느 것이 마운트되어 있는지를 기준으로 watching 상태를 파생시켜, 각각이 자체 set/clear를 보내는 것 대신 그렇게 하면 순서가 문제 되지 않습니다.
관련 사항, 별도의 문제일 수 있음
같은 세션에서 한 번의 POST .../state 호출에 대해 429가 반환되었습니다. 이러한 호출의 실패는 무시되므로(.catch(() => {})), 거부된 “watching” 업데이트는 조용히 실패합니다.