AudioContext·오디오 graph 재활용 — iOS 렌더 밀림 완화 설계 공유 ctx 개선안 철회
마지막 업데이트 2026-09-11
공유 AudioContext 개선안은 덕킹 문제로 2026-09-10 철회했다. 아래는 철회 전 Context·graph 재활용 제안과 로그 분석 근거를 보존한 기록이며, 현재 권장안은 OFF 처리 노드 우회를 우선하는 최소 변경 완화 설계다.
한 줄 결론
이 문서는 홍윤우 1회기 “지지직” 분석에서 제안된 재활용 방법을 독립적으로 정리한 설계·검증 가이드다. 현재 구현이나 배포 결과를 기록한 문서가 아니며, 먼저 계측과 동일 기기 A/B 검증을 거쳐야 한다.
2026-09-10 실기기 실험 반영. 게스트 디버그 하네스로 AudioContext를 255개까지 누적해도, 렌더 콜백을 40% 누락시켜도 정상 전원에서는 AI 음성이 밀리지 않았다. 밀림은 저전력 모드 + WebAudio 렌더 부하가 함께 있을 때만 재현됐다(저전력 단독은 정상). 따라서 이 문서의 재활용 제안은 “Context 개수·재생성 자체가 지연을 만든다”가 아니라 저전력 단말에서 WebAudio 총 렌더 부하를 임계 아래로 낮추는 수단으로 읽어야 하며, 우선순위는 상시 렌더 연산(loopback 믹싱·입력 체인·analyser)이 중복되지 않게 하는 쪽이다. 실험 상세는 iPad AI 음성 밀림 재현 — 저전력 모드 × WebAudio 렌더 부하.
1. 왜 재활용을 검토하는가
| 시각(KST) | 관찰 | 해석 범위 |
|---|---|---|
| 20:33:22~20:37:27 | 첫 graph health의 벽시계 대비 currentTime 진행률 약 1.000 | 같은 세션 앞부분은 정상 진행 |
| 20:37:32 이후 | LiveKit disconnect 직후 graph 재생성. 이후 진행률 약 0.913 | 재접속·재생성과 drift가 시간상 인접 |
| 20:43:45 이후 | 새 graph 진행률 약 0.816 | 동일 현상이 재생성 뒤 반복 |
| 20:46:50 이후 | 새 graph 진행률 약 0.813 | lifecycle 관여 가능성 보강 |
게스트 로그의 Audio chain health 194건은 모두 state=running, currentTimeAdvanced=true, processed/relay track live였다. 그러나 현재 health 판정은 “조금이라도 전진했는가”만 보므로, 5초에 4.1초 진행하는 drift를 정상 health로 기록한다. 위 진행률은 로그의 벽시계 차분을 별도로 계산한 값이다.
2. 현재 구조와 바꿀 경계
관련 코드의 LiveKitAudioChainProcessor.restart()는 graph를 destroy한 뒤 다시 build하는 구현이고, useAudioProcessingChain.processMediaStream()은 기존 Context를 close하고 새 Context를 만든다. 이는 재활용 불가능의 증거가 아니라 현재 lifetime 경계가 Context와 graph에 함께 묶여 있다는 뜻이다.
또한 이 처리 graph의 출력은 스피커용 audioContext.destination이 아니라 MediaStreamAudioDestinationNode 두 개다. 따라서 이 문서의 “렌더”는 우선 브라우저의 Web Audio 처리 graph가 실시간으로 진행되는지를 뜻하며, AI audio element의 실제 가청 출력과 동일시하지 않는다.
3. 제안 동작
- 생성: 사용자 제스처가 가능한 시점에 Context와 정적 처리 노드·destination을 한 번 만든다. Context 상태가 suspended면 같은 제스처에서 resume한다.
- 활동/스텝 전환: 새 Context나 graph를 만들지 않고 gate, gain, AI input enable 등 parameter와 정책 상태만 갱신한다.
- 재접속: 기존 Context·처리 노드는 유지하고 이전 source를 disconnect한 뒤 새 microphone track source만 연결한다. WebRTC가 새 publication을 요구하면 destination track의 publication만 갱신한다.
- 정리: 이전 source와 track을 중복 연결하지 않고 monitor timer를 하나만 유지한다. 페이지 종료·영구 세션 종료에서만 destination/track을 정리하고 Context를 close한다.
4. 함께 줄일 수 있는 비효율
| 항목 | 제안 | 주의점 |
|---|---|---|
| 20ms main-thread monitor | RMS/VAD를 AudioWorklet로 옮기거나 50~100ms로 완화하고, 값이 변할 때만 setTargetAtTime 호출 | 오디오 품질·gate 반응속도 회귀를 별도 측정 |
| 중복 DSP | WebRTC AGC/NS/AEC와 Web Audio auto gain/EQ/comp/limiter의 중복을 확인하고 필요한 노드만 유지 | 마이크 VAD·에코·음성 레벨에 직접 영향 |
| 재접속 media churn | MediaSoup transport·camera/mic/AI producer 전량 재생성이 실제로 필요한지 effect dependency와 publication 계약을 확인 | 서버 recording/relay가 새 track을 요구할 수 있음 |
| 진단 probe | throwaway probe Context와 live processing Context를 구분하고, active Context의 drift를 직접 기록 | 새 probe가 정상이어도 주 graph 정상의 증거는 아님 |
5. 검증 설계와 수용 기준
동일 iPhone·동일 iOS·동일 브라우저·동일 네트워크에서 다음 두 조건을 비교한다. 아래 기준은 제안된 판정 기준이며 아직 실기기에서 검증된 수치가 아니다.
| 조건 | Context/graph | 비교 지표 |
|---|---|---|
| A | 현재 방식: 재접속 시 Context/graph 전체 재구성 | 기준선 |
| B | 개선 방식: Context·정적 노드 재활용, source/publication 교체 | 완화 효과 |
- 매 1~5초마다
wallDeltaMs,contextDeltaMs,contextRate, 누적 drift, context/graph generation을 기록한다. AudioContext.state, sampleRate, base/output latency, visibility, audio session 상태, active Context 수를 함께 기록한다.- 가능한 환경에서는
getOutputTimestamp()를 사용해 performance time과 context time을 연결하고, 지원하지 않으면 지원 여부를 기록한다. - 보조 결과로 WebRTC outbound audio energy/packet delta, 서버 독립 child·AI stem, MP3 gap률을 같은 시간축에 비교한다.
- 제안 수용 기준: B에서
contextRate가 지속적으로 0.98 이상이고 누적 drift가 줄며, 같은 조건의 MP3 gap률도 함께 감소할 때 단계적 확대를 검토한다. clock만 좋아지고 gap이 그대로면 재생/녹음 경계의 별도 문제로 판정한다.
6. 위험과 롤아웃 순서
- 계측만 먼저: Context identity, generation, delta/rate, statechange, source/destination lifecycle을 추가한다. 동작 변경 없이 정상 세션 기준선을 확보한다.
- feature flag A/B: iOS 한정 또는 내부 계정에서 B를 켜고 15분 이상 세션, 활동 전환, LiveKit 재접속, 출력 장치 전환을 반복한다.
- 단계적 확대: drift·gap·track publication 오류가 악화되지 않을 때만 범위를 넓힌다. source stale, duplicate audio, destination ended가 발생하면 즉시 A로 되돌린다.
관련 문서 · 코드
- 홍윤우 1회기 녹음 지지직 — 단말 오디오 렌더 결손 원인 분석 — 사건 근거와 4라운드 검증 결과
- iPad AI 음성 밀림 재현 — 저전력 모드 × WebAudio 렌더 부하 (디버그 하네스 실험) — 개수 누적·렌더 기아 단독·메인스레드 정지 기각, 저전력 × 렌더 부하에서만 재현
- 원준호 45·46·47회기 지지직 — 지터·오디오 클럭 언더런 계측 판별 — currentTime과 벽시계 차분 판별법
- 오디오 처리 체인 — Gate→EQ→Comp→Limiter — 기존 처리 노드와 lifetime
- apps/web/lib/voice-agent/livekit-audio-chain-processor.ts — LiveKit 입력 graph, restart/destroyGraph, health logging
- apps/web/hooks/use-audio-processing-chain.ts — 기존 AudioContext 생성·close 기반 입력 처리 경로