Bug analysis · PPI-1215

게스트 재생 지연으로 영상 종료 체인이 도달하지 못한 자동전환 실패

마지막 업데이트 2026-09-12

쉬운 설명 한 장 보기 · 사전지식 없이 읽는 요약

2026-08-18 모니터·게스트 병합 로그에서 video_action_balancegame_02 재생을 추적했다. 게스트는 전체 파일을 버퍼링한 상태에서도 반복적인 DECODE_OVERLOAD waiting과 높은 dropped frame을 보였고, 모니터 종료 시점에 약 29초 뒤처져 있었다. 이벤트 구독 해제 증거는 없으며, 이후 videoSrc-changed는 수동 스텝 이동의 결과다.

로컬 구현 · 리뷰 대기 분석일 2026-08-20 환경 Guest Chrome 151 / Windows 10 대상 video_action_balancegame_02

결론

직접 원인은 게스트 플레이어가 종료 이벤트를 놓친 것이 아니라, 게스트 미디어 시간이 영상 끝까지 도달하지 못한 것이다. 모니터 로컬 영상은 76.649초까지 재생되어 자체 watchdog과 native ended가 발생했지만, 자동전환 권한은 게스트 세션의 onEnd → notifyVideoEnd() → goToNextStep() 체인에 있었다. 당시 게스트는 약 47.686초에 머물러 native ended 조건을 충족하지 못했고, 기존 watchdog도 반복적인 짧은 진전을 누적 지연으로 계산하지 못했다.

76.649s영상 duration
47.686s모니터 종료 구간의 게스트 checkpoint
28.963s확인된 재생 위치 차이
57%게스트 dropped frames: 807 / 1404

증거 타임라인

시각 (UTC)주체관측해석
11:35:48.122Guest프로그램 재생 시도video_action_balancegame_02 재생 시작
11:35:48.126~141Guestpause, play, SEEK_REBUFFER, seeking, canplay초기 seek 복구 구간
재생 3.356sGuestDECODE_OVERLOAD waiting 후 5,356ms 만에 canplay전체 buffered 범위가 있음에도 미디어 진행 정지
재생 6.966s 이후Guest1~2초대 waiting/canplay 반복짧은 재개가 stall 타이머를 계속 초기화
재생 47.631sGuestdropped 807 / total 1404, buffered [0, 76.649]다운로드 부족보다 decode/render/present 경로 지연에 부합
11:37:04.768Monitorwatchdog onEnd, current 76.596 / duration 76.649모니터 로컬 재생은 정상 종료 범위 도달
11:37:04.819~820Monitornear-end, pause, native ended모니터 플레이어 종료 확인
11:37:15.076Monitor방향 jump의 step change진행자의 수동 스텝 전환
11:37:15.184Guestwatchdog silenced: videoSrc-changed, current 54.86 / duration 76.649원인이 아니라 수동 전환 이후 소스 교체 결과
게스트 waiting 구간에서 네트워크 버퍼는 영상 끝까지 채워져 있었고 연결 정보도 4g/downlink 10이었다. 따라서 이 로그만 보면 네트워크 다운로드 starvation보다 브라우저의 decode/render/present 진행 정지가 더 강한 설명이다.

왜 모니터가 끝났는데 자동전환되지 않았나

정상 종료 체인

Guest 미디어가 끝에 도달
native ended 또는 guest watchdog
MeetVideo.onEnd
notifyVideoEnd() + goToNextStep()

장애 당시 체인

Monitor 로컬 영상 종료
Guest는 약 29초 뒤처짐
Guest native ended 미발생
기존 watchdog 누적 지연 미감지
Guest onEnd와 자동전환 미발생

로그에는 게스트가 종료 이벤트를 emit했지만 구독자가 받지 못한 흔적이 없다. 더 앞 단계인 게스트 종료 조건 자체가 성립하지 않았다. 모니터의 로컬 onEnd는 모니터 화면 정리에 사용되며 게스트 스텝 전환 콜백을 소유하지 않는다.

가설 판정

가설판정근거
onEnd 구독 해제증거 없음이벤트가 발생했으나 listener가 누락된 로그가 없다. 게스트 currentTime이 종료점에 도달하지 않았다.
videoSrc 변경이 최초 원인배제videoSrc-changed는 모니터 종료 약 10초 뒤 수동 jump 이후 기록됐다.
네트워크 버퍼링가능성 낮음waiting 시점에 buffered range가 [0, 76.649]로 전체 영상을 포함했다.
실제 decoder exception확정 불가MEDIA_ERR_DECODE가 없고 corruptedVideoFrames=0이다. DECODE_OVERLOAD는 앱의 휴리스틱 분류다.
저사양 기기확정 불가8 logical cores, 16GB, lowPerf=false만으로 GPU decode 능력, 하드웨어 가속, 드라이버 상태를 판단할 수 없다.
Audio suspected silent인과 증거 없음AudioContext는 running이었고 이후 audio health에서 currentTime과 decoded byte 진행이 확인됐다.

로컬 보강 구현

작업 위치는 PPI-1215 worktree이며 아직 커밋·배포되지 않았다. 기존 모니터 경로를 바꾸지 않고 실제 게스트 세션에만 catch-up을 활성화했다.

  1. DECODE_OVERLOAD 복구 seek: guest에서 waiting이 DECODE_OVERLOAD로 분류된 뒤 playing이 오면, 최초 재생 wall clock 기준 기대 위치를 계산한다.
  2. 보수적 실행 조건: 기대 위치가 현재보다 0.5초 이상 앞서고 그 위치가 이미 buffered일 때만 seek한다.
  3. 끝점 보호: seek target은 duration - 0.1s로 제한해 native ended를 건너뛰지 않는다.
  4. 재귀 방지: catch-up seek가 유발한 waiting은 SEEK_REBUFFER로 분류해 catch-up 반복 루프를 막는다.
  5. watchdog fallback: 영상의 명목 종료 시간이 지났고 media 진행 대비 누적 wall-clock 지연이 기존 maxStallMs 기본값 30초 이상이면 onEnd를 실행한다.
  6. 중복 종료 방지: 기존 exactly-once latch를 유지해 native ended와 watchdog이 겹쳐도 종료 처리는 한 번만 실행한다.
파일변경 역할
apps/web/shared/ui/meet-video.tsxDECODE_OVERLOAD 이후 buffered wall-clock catch-up seek
apps/web/shared/ui/guest-layout-content.tsx실제 guest session에만 옵션 활성화
apps/web/shared/lib/video-end-watchdog.ts누적 지연 기반 종료 fallback과 catch-up target helper
apps/web/shared/lib/video-end-watchdog.test.ts누적 지연, wall-clock target, near-end clamp 회귀 테스트

검증 결과와 남은 한계

리뷰 시 확인할 한계
  • catch-up은 지연된 시간만큼 콘텐츠를 건너뛰는 UX trade-off가 있다.
  • buffered 위치라도 seek가 추가 rebuffer를 유발할 수 있다.
  • 현재 catch-up 실행은 브라우저의 playing 이벤트에 의존한다. 첫 timeupdate fallback은 아직 구현하지 않았다.
  • 이 보강은 자동전환 실패를 방어하지만 GPU, 드라이버, 하드웨어 가속 등 stall의 기기별 근본 원인을 확정하지 않는다.
LLM용 복사
PPI-1215 결론: video_action_balancegame_02 자동전환 실패는 guest onEnd listener 해제나 선행 videoSrc 변경이 아니라 guest media currentTime이 반복 DECODE_OVERLOAD waiting으로 끝에 도달하지 못한 케이스다. Monitor는 76.649초에 종료했지만 guest는 같은 종료 구간에서 약 47.686초로 28.963초 뒤처졌다. Guest 종료 체인이 자동전환을 소유하므로 monitor local ended만으로는 스텝이 전환되지 않는다. 이후 videoSrc-changed는 수동 jump의 결과다. 로컬 보강은 guest-only wall-clock catch-up seek와 누적 지연 watchdog fallback이며 아직 커밋·배포되지 않았다. 타깃 테스트 9개와 git diff --check는 통과했고 브라우저 runtime 검증은 미수행이다.