3:1 진행자 카드뷰
영상·소리 끊김 분석
마지막 업데이트 2026-09-28
진행자 콘텐츠 영상의 재생 처리 병목이 유력하다. 아동은 같은 영상을 길이에 가까운 시간에 종료 직전까지 재생했지만, 진행자는 버퍼가 남은 상태에서 정지와 프레임 누락을 반복했다. CPU·GPU 과부하와 3명 동시 처리의 인과관계는 아직 미확정이다.
1. 범위와 증거
진행자 1명이 아동 3명을 카드 모드로 모니터링하던 중 영상·소리가 끊겨 반복 새로고침했다. 목표는 진행자 로컬 부하인지, 아동 또는 공통 경로 문제인지 구분하는 것이다. 아동 이름·생년월일·전체 식별자와 원본 로그는 문서에 공개하지 않는다. 아동 번호는 사용자가 첨부한 순서다.
| 자료 | 시간 범위 (한국시간) | 건수 / 형식 |
|---|---|---|
| 진행자 …2b63 | 20:47:27–21:27:11 | 50,955 / JSON logEntries |
| 아동 ① · 룸 …a21a_5 | 21:02:25–21:27:10 | 16,061 / JSON Lines |
| 아동 ② · 룸 …0308_8 | 21:00:09–21:24:33 | 16,189 / JSON Lines |
| 아동 ③ · 룸 …501c_8 | 21:00:05–21:25:14 | 10,681 / JSON Lines |
총 4개 파일 93,886건을 파싱했다. 진행자 time과 아동 ts의 epoch millisecond를 UTC+9로 정렬했다. 단말 간 시계 오차는 별도 측정하지 않았다. 새로고침은 동일 URL navigation과 socket 재연결로 추정하며, 사용자 클릭 자체를 증명하는 기록은 아니다.
2. 같은 콘텐츠의 재생 결과 비교
| 콘텐츠 / 아동 | 아동의 진행 | 진행자의 정지 관찰 |
|---|---|---|
video_tiki08_movie_02_low아동 ② · 65.944초 | 21:13:04 시작 → 21:14:10 65.7초에서 종료 보조 처리 | 21:13:55 · 40.533초 위치 갱신 7.280초 중단 434/1,175 프레임 누락 (37%) |
video_newpp08_step_05아동 ③ · 59.967초 | 21:11:39 시작 → 21:12:39 58.94초에서 종료 보조 처리 | 21:12:32 · 27.993초 위치 갱신 5.260초 중단 371/761 누락 (49%) |
video_newpp08_step_06아동 ③ · 87.367초 | 21:13:47 시작 → 21:15:14 86.392초에서 종료 보조 처리 | 21:14:41 · 30.192초 위치 갱신 7.131초 중단 448/860 누락 (52%) 버퍼 57.189초 남음 |
종료 보조 처리는 Video onEnd triggered via watchdog backstop 기록이다. 아동이 중간 끊김 없이 재생했다는 완전한 증명은 아니지만, 영상 길이에 가까운 실제 시간에 끝 부근까지 도달했다는 양성 근거다. 누락률은 해당 스냅샷의 누적값이며, 사건 전체 평균이 아니다.
- 진행자:
DECODE_OVERLOAD72회,SEEK_REBUFFER25회, 5초 이상 timeupdate gap 15회. - 동일 페이지 재진입: 최초 진입 이후 8회. 21:12:36 재진입 후 21:13:55 재발, 21:14:44 재진입 후 21:15:01 재발.
- 아동별 Low power probe 48회: ①·② 59.9fps, ③ 47.6–58.8fps / 중앙값 58.8fps. 화면 프레임 간격 샘플이며 영상 FPS·연속 성능 기록이 아니다.
- 아동 오디오 출력 프로브 21회 모두 시계 전진. 진행자는 26회 중 20회 전진하지 않음. 별도 AudioContext 프로브이며 실제 스피커 출력과 동일시하지 않는다.
3. 판정과 병목 후보
유력: 진행자 내부 콘텐츠 재생 처리 병목. 미확정: CPU 포화, GPU/드라이버 문제, 메인 스레드 작업, 3명 동시 처리의 직접 인과 및 소리 끊김과의 동일 원인 여부.
| 검증 우선순위 | 근거 | 한계 |
|---|---|---|
| 1. 동시 콘텐츠 재생 | 21:13:48–21:13:59 서로 다른 두 영상에서 재생 대기. 음소거 영상도 재생 진행. | 음소거만으로 불필요한 영상이라고 단정할 수 없음. CPU/GPU 프로파일 없음. |
| 2. 자막 이벤트·로그 처리 | 수신 16,706 · room mismatch 무시 11,060 · 적용 5,646. 관련 로그 33,412건(전체 66%). 전체 로그 최대 초당 500건. | 다중 핸들러 분배와 부합하나 정지와의 인과 미확정. 로그 수는 CPU 시간 아님. |
| 추가 측정: 카메라 수신 | 3명 영상 처리 부하는 후보로 남음. | layer 2 요청 로그만으로 고화질 수신을 증명하지 못함. 단일 레이어 조건은 아래 정정 참조. |
DECODE_OVERLOAD는 현재 코드에서 충분한 버퍼 등을 기준으로 붙이는 분류다. CPU 사용률 측정값이 아니다. 아동 영상에 같은 경고가 없다는 사실만으로 정상 판정하지 않고, 위 재생 시작·종료 근처 기록을 함께 사용했다.
4. 별도로 남는 오류
- 진행자 21:03:55·21:06:56·21:09:28에 consumer resume
unauthorized3회. 일부 실시간 수신 문제의 별도 후보이며 CPU 원인으로 묶지 않는다. - 아동 초반 21:00–21:03에 단계 갱신 ACK timeout. 아동 ②·③은 서버 도달 실패 진단과 commit 재시도 소진도 기록했다. 이 진단만으로 서버 자체 장애를 확정하지 않는다.
- 아동 ③
setSinkId의 user gesture 필요 오류 15회. 특정 출력 경로의 실패이며 모든 오디오 무음의 증거는 아니다. 21:25:14에는 로컬 카메라·마이크 track 종료와 loopback 연결 상실. - 아동 ② 21:24:17 자동 전환 timeout. 수업 종료 근처 문제를 앞선 재생 정지와 분리한다.
- 진행자 오디오 Blob 보관 로그 최대 9개·1,304,005바이트. 이 값은 전체 메모리가 아니며 메모리 누수·GC 병목을 판정할 근거가 아니다.
5. 카드뷰 MeetVideo 480p 적용 검토
완화 가능성이 있어 첫 비교 실험으로 적합하다. 실제 저해상도 파일을 재생해야 하며 CSS 크기 축소만으로 대체할 수 없다. 현재 경로는 MonitorMediaDisplay → GuestLayoutContent → MeetVideo → useResourceCache → /api/resources/urls이다.
GuestLayoutContent는 기기 자동 판정값을isLowPerformanceDevice로 전달한다. 카드 3개 동시 부하 자체를 반영한 강제 설정은 아니다.- 서버는 flag가 true이고
s3KeyProcessed480p가 있을 때만 480p를 선택한다. 없으면 기존 processed/raw로 폴백한다. - 캐시 주의: 메모리는 fileName, 디스크는 fileName+updatedAt 기반으로 화질 구분이 없다. flag만 변경하면 기존 화질 캐시가 재사용될 수 있다. 화질별 캐시 구분 또는 실험용 격리와 실제
videoWidth/videoHeight확인이 필요하다. - 기대 범위는 콘텐츠 영상 처리 부하 완화다. 카메라 수신·자막 처리·오디오 권한 오류를 직접 해결하지 않는다.
6. 카메라 layer 2 해석 정정
현재 use-mediasoup-producer.ts의 camera-video 송신은 encodings: [{ maxBitrate: 300000, scaleResolutionDownBy: 2 }]인 중화질 단일 레이어다. 이는 최대 비트레이트 설정 300kbps와 원본 가로·세로 각 1/2을 뜻하며, 실제 수신 해상도·비트레이트는 측정하지 않았다.
진행자의 use-monitor-session.ts는 여전히 setConsumerLayer(peerId, 2)와 “high quality” 문구를 사용한다. 다중 레이어라면 하향 선택이 부하를 줄일 수 있지만, 단일 레이어에는 추가로 선택할 저화질 스트림이 없다. 별도 use-mediasoup-device.ts에는 다중 레이어 설정도 있으므로 모든 송신 경로를 일반화하면 안 된다.
사건 당시 배포 코드와 현재 코드의 동일성은 미검증. 현재 코드만으로 사건 당시 단일 레이어였다고 확정하지 않는다. 해당 세션의 consumer type·RTP encodings·실제 inbound 해상도/FPS를 먼저 확인한다.
7. 다음 검증과 변경 상태
- 동일 PC·동일 콘텐츠·3명 세션으로 기준 측정: CPU/GPU Video Decode, 긴 JS 작업, 프레임 누락 델타, 정지 시간, 실제 출력 음성.
- 연결 3명은 유지하고 콘텐츠 재생만 하나로 제한하여 동시 재생 영향을 구분한다.
- 기준 조건으로 복귀한 후 콘텐츠만 480p로 변경한다. 캐시와 실제 해상도를 확인하고 다른 변수는 유지한다.
- 실제 카메라 송신 레이어를 확인한 뒤 별도 실험으로 수신 또는 송신 해상도/FPS를 줄인다. 자막 DEBUG 로그 축소 실험도 분리한다.
- 필요하면 1명→2명→3명 비교로 동시 세션 수에 따른 부하 증가를 확인한다.
수행한 검증: Python JSON/JSON Lines 파싱·카테고리 집계·epoch 시간 정렬·동일 videoSrc 교차 비교. CodeGraph와 범위를 제한한 소스 조회로 재생·리소스 선택·카메라 송신/수신 요청 경로 확인.
미수행: 런타임 코드 변경, 단위/통합 테스트, CPU 프로파일 수집, 480p 또는 layer 변경 A/B, 배포. 따라서 before/after 개선 수치 없음. 문서 추가만 수행하며 커밋·푸시·배포는 포함하지 않는다.
후속 백로그 제안: “3:1 진행자 카드뷰 영상·소리 끊김 — 재생 병목 원인 및 저해상도 적용 검토”. 근거가 충분한 조사 항목이며 PC 교체나 특정 수정의 성공을 전제로 하지 않는다.
8. 코드 및 관련 문서
아래 경로는 PPI 저장소 기준, 참조 revision d5f63ac3이며 사건 배포 revision을 뜻하지 않는다.
apps/web/shared/ui/monitor-media-display.tsxapps/web/shared/ui/guest-layout-content.tsxapps/web/shared/ui/meet-video.tsxapps/web/shared/lib/audio-output-probe.tsapps/web/hooks/use-resource-cache.tsapps/web/app/api/resources/urls/route.tsapps/web/lib/utils.tsapps/web/entities/monitor-session/model/use-monitor-session.tsapps/web/hooks/mediasoup/use-mediasoup-producer.tsapps/web/hooks/mediasoup/use-mediasoup-consumer.tsapps/web/hooks/mediasoup/use-mediasoup-device.tsapps/socket/src/mediasoup/consumerManager.ts
모니터 카드뷰·포커스뷰 실행 경로 · WebRTC 통계 관측 경로
LLM용 분석 요약
진행자 50,955건과 아동 3명 42,931건 비교. 같은 콘텐츠가 아동에서는 길이에 가까운 시간에 종료 직전까지 진행하지만 진행자는 버퍼를 확보한 채 5초 이상 timeupdate gap 15회, DECODE_OVERLOAD 72회. 진행자 재생 처리 병목 유력, CPU/GPU/JS와 3명 부하의 인과 미확정. 480p 콘텐츠 실험은 화질별 캐시 및 실제 해상도 확인 필요. layer 2 로그는 요청값일 뿐이며 현재 camera-video 송신은 단일 중화질 레이어. 당시 배포본 동일성 미확인. 소리·실시간 카메라 전체 원인은 별도. 코드 수정과 성능 재현 테스트는 수행하지 않음.