← PPI Docs
LIVEKIT / VOICEPIPELINE · iOS / WEBKIT

아동 기기의 오디오 출력
렌더 밀림을 줄이기 위한 경로 분석

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

LiveKit / VoicePipeline 모드에서 AudioContext 처리와 생성·재개·폐기가 AI 음성의 지지직·드르륵·끊김에 미치는 영향을 확인하고 줄이기 위한 문서입니다.

작성 · 2026-09-09코드 기준 · develop / 2a8afc34현재 구현 분석 + 단일 Context 통합 제안 · 미구현
2026-09-10 결정: 공유 AudioContext 개선안 철회. 덕킹 문제에 따른 사용자 결정이다. 이 문서의 구조·관측 분석은 참고 자료로 유지하지만 공유 ctx 통합 제안은 현재 권장안이 아니다. 후속 설계는 PPI-1290 · iPad AI 음성 밀림 — 최소 변경 완화 설계에서 확인한다. OFF 처리 노드 우회를 우선하며 코드 미반영·효과 미검증 상태다.

01입출력 흐름 핵심

개선 목표: 아동 기기에서 안정적으로 AI 음성을 듣게 합니다. AudioContext를 사용하는 단계를 찾아 처리 우회와 생명주기 개선 효과를 비교합니다. 현재 로그는 입력 Context의 진행 지연을 보여주며, 이것이 스피커 출력 렌더 밀림을 일으켰는지는 아직 검증 전입니다.
AI → 아동

수신 음성은 직접 출력

LiveKit이 받은 원본 오디오 트랙을 <audio>에 붙여 재생합니다. 앱의 마이크 보정 그래프를 통과하지 않습니다.

현재 Room은 webAudioMix를 켜지 않습니다. SDK 내부 Context가 존재하는 것과 수신 음성이 Web Audio로 출력되는 것은 별개입니다.

아동 → AI / 진행자

마이크는 보정 후 분기

원본 마이크를 Web Audio로 보정한 뒤 AI 입력용과 진행자·녹음용 스트림으로 나눕니다. AI 듣기 OFF는 AI 분기만 차단합니다.

Context는 페이지 수명으로 재사용하지만 그래프는 세션마다 구성합니다. 진행자 음소거 등 별도 전달 정책은 이후 경로에서 적용됩니다.

범위는 LiveKit / VoicePipeline 모드의 게스트 AI 음성 출력과 아동 마이크 입력입니다. 아래 흐름도와 개선 제안은 이 모드의 브라우저 입출력 경로에 한정합니다.

02한눈에 보는 현재 파이프라인

직접 재생·원본 신호Web Audio 처리전달 목적지점선 상자 = 관측용 분기
출력 · DOWNLINK

AI 음성 → 아동 스피커

LiveKit AI 음성WebRTC 수신 트랙
<audio> 직접 재생audioTrack.attach(audioElement)
mute · play · 출력 장치 제어
아동 스피커 / 헤드셋Web Audio 보정 그래프를 경유하지 않음
↳ 수신 트랙에서 별도 분기
원본 스트림 → 진행자 전달 콜백
진단 활성 구간: 원본 스트림 → Context → Analyser
관측 노드는 스피커 출력의 중간 단계가 아닙니다.

SDK용 Context의 생성·폐기는 별도 수명 문제입니다. 이를 공유하려고 webAudioMix를 켜면 출력 경로도 바뀌므로 기존 mute 제어와 함께 검토해야 합니다.

입력 · UPLINK

아동 마이크 → AI / 진행자

iPad 마이크getUserMedia → 원본 MediaStreamTrack
브라우저 에코 제거 등 캡처 정책
공유 AudioContextGate → EQ → Compressor → Limiter → Gain
설정에 따라 보정 적용 · 20ms 레벨/게인 갱신
AI 전용 입력 Gate듣기 OFF면 이 분기만 0
LiveKit → AIAI용 destination track
진행자용 출력 분기AI 입력 Gate를 거치지 않음
Mediasoup → 진행자·녹음relay destination track
↳ 원본 source → Analyser
음량 측정 / 보정 제어
Audio chain health는 이 입력 Context의 시계를 관측합니다.

아동 기기의 AI 출력에 AudioContext가 사용되나요?

실제 스피커 재생 경로에는 사용하지 않습니다. 현재 LiveKit / VoicePipeline 게스트의 흐름은 WebRTC 수신 트랙 → <audio> → 스피커입니다. 앱은 webAudioMix를 켜지 않고 audioTrack.attach(audioElement)로 수신 트랙을 연결합니다.

① SDK 내부 Context는 존재합니다. LiveKit SDK는 연결·재생 시작 시 Context를 확보하고 재개합니다. 그러나 수신 트랙에 Context를 전달하는 것은 webAudioMix가 활성화된 경우입니다. 현재 설정에서는 AI 음성을 Web Audio 출력 그래프로 보내지 않습니다.

② AI 수신 음성의 진단 분기에는 사용합니다. 진단 창이 열리면 1.5초 동안 수신 트랙 → 별도 AudioContext → Analyser로 음량을 측정합니다. 이 분기는 context.destination에 연결하지 않으므로 스피커 출력의 중간 단계가 아닙니다. 정리 시 진단 Context도 닫습니다.

③ 아동 마이크 보정에는 사용합니다. 오른쪽 입력 그래프의 공유 Context는 아동 음성을 AI·진행자에게 보내는 경로입니다. AI 수신 음성의 직접 출력과 구분해야 합니다.

따라서 “AI 출력이 AudioContext를 통과하지 않는다”는 맞지만, “아동 기기가 AudioContext를 전혀 사용하지 않는다”는 아닙니다. SDK의 재생 가능 상태는 Context 상태도 참조합니다. 직접 재생만으로 Context의 생성·재개·폐기 또는 기기 오디오 상태가 무관해진다고 단정할 수 없습니다.

확인 근거: 앱의 connectLiveKitBrowserSession·startRemoteAudioProbe·createAudioLevelProbe, LiveKit client SDK 2.19.2의 Room.acquireAudioContextRemoteAudioTrack.attach.

LiveKit SDK 내부 Context에서도 시간 진행 저하가 가능한가요?

가능합니다. SDK 내부 Context도 브라우저의 AudioContext입니다. 다만 시간 진행 저하와 실제 스피커 출력 렌더 밀림은 같은 측정값이 아닙니다. 예를 들어 suspended 상태에서 currentTime이 멈추는 것은 정상 동작이므로, 상태 전환을 구분해야 합니다.

  • 가능성: SDK Context의 시간 진행 문제도 조사 대상입니다.
  • 이번 로그의 관측: LIVEKIT_AUDIO_CHAIN / Audio chain health는 아동 마이크 처리 Context를 측정합니다. SDK 내부 Context의 진행 저하를 확인한 로그가 아닙니다.
  • AI 출력과의 관계: 현재 AI 음성은 SDK Context 그래프를 통과하지 않습니다. SDK Context가 느려졌다는 사실이 확인되더라도 AI 음성 출력도 반드시 느려졌다는 결론은 낼 수 없습니다.
SDK는 Context 상태를 재생 가능 상태 판단에 사용하므로 재생 시작·복귀와의 관련성은 확인할 가치가 있습니다. 그러나 그 사실만으로 재생 중 지지직·드르륵의 원인이라고 볼 수는 없습니다.

확인 방법: SDK Context에 별도 식별자를 부여하고 state, currentTime, performance.now(), 생성·재개 요청/완료·폐기 시점을 기록합니다. 같은 Context의 연속 구간에서 진행률을 계산하고, 마이크 Context·진단 Context와 분리해 실제 AI 출력 이상 시점과 대조해야 합니다. 마이크 Context 로그로 SDK Context의 상태를 대신 판단하지 않습니다.

근거: Web Audio currentTime 사양 · LiveKit SDK Context 확보·재개·재생 상태 판단. 위 기록 추가는 검증 제안이며 이번 문서 작업에서 구현하지 않았습니다.

로그에서 발견한 이상은 입력 쪽입니다. 9월 8일 20:16:01.161~20:16:46.265(KST)에 실제 45.104초 동안 입력 Context는 35.701초 진행했습니다. 이는 평균 약 79.2%의 진행률이며, 스피커 음성이 9.403초 늦었다는 뜻은 아닙니다. 실제 잡음·화면 끊김과의 인과는 미확정입니다.

녹음의 신호 공백은 렌더 밀림인가요?

신호 공백 자체를 렌더 밀림으로 확정할 수 없습니다. 제공된 9월 8일 MP3의 14:30~14:45를 PCM 파형·스펙트럼으로 분석했습니다. 특히 14:39~14:45에서 약 1초마다 0.2초 안팎으로 신호가 거의 무음이 되는 패턴이 확인됩니다.

녹음 위치저신호 구간 길이
14:39.385~14:39.585200ms
14:40.390~14:40.585195ms
14:41.410~14:41.610200ms
14:42.435~14:42.645210ms
14:43.470~14:43.635165ms
14:44.470~14:44.660190ms

측정: 48kHz 모노 MP3를 16비트 PCM으로 디코딩. 5ms 단위 RMS가 −85dBFS 미만으로 60ms 이상 이어진 구간을 탐지했습니다. 위 구간 평균 레벨은 약 −93~−94dBFS, 시작 간격은 1.00~1.035초입니다. 경계는 5ms 분석 해상도 기준의 근삿값입니다.

확인: 신호가 중간에 빔녹음에 주기적인 거의 무음 구간이 존재
가청 출력 지연을 직접 측정한 것은 아님
미확정: 왜 비었는가?렌더 지연·중단 / 입력 게이트·음소거
전송 손실 / 녹음 과정의 무음 삽입 등 구분 필요
“늦게 재생됨”과 “중간에 신호가 빔”은 구분해야 합니다. 렌더 처리 이상은 공백을 만들 수 있는 원인 후보지만, 이번 파형만으로 확정할 수 없습니다. 위 후보들도 이 사건에서 확인된 원인은 아닙니다.

입력 Context 진행률 약 79.2%와 신호 공백 비율이 수치상 비슷해 보여도, 녹음 위치와 로그 시각을 정렬하지 않았으므로 같은 현상으로 연결할 수 없습니다. 모노 녹음만으로 아동 마이크·AI 음성 중 어느 원본 경로에서 공백이 생겼는지, 아동 스피커에서도 동일하게 들렸는지 역시 확정하지 않았습니다.

다음 확인: 녹음 시간과 로그의 실제 시각을 먼저 맞춘 뒤, 가능하면 아동·AI 개별 녹음 트랙과 수신 통계·Context 상태를 비교해 공백이 처음 생기는 지점을 찾습니다.

시간 진행 저하가 음성에 영향을 주는 인과 경로

AudioContext를 통과한다는 사실 자체가 지연의 원인은 아닙니다. 아래 그림은 확인된 처리 경로와 시계 관측에, 실제 렌더 처리가 원활하지 않을 때 가능한 영향을 연결한 검증용 그림입니다.

━━ 실선: 코드로 확인한 경로 / 로그 관측┄┄ 점선: 원인·가청 영향이 아직 미확정
마이크 입력 · 관측 대상
아동 마이크 원본getUserMedia 수집 음성
마이크 보정 AudioContextGate · EQ · 압축 · 증폭
이 Context의 currentTime 진행 부족 관측
AI용 / 진행자용 트랙LiveKit → AI
Mediasoup SFU → 진행자·녹음
아동 음성의 끊김·왜곡 가능성AI가 받는 입력 / 진행자가 듣는 아동 음성
해당 로그만으로 실제 발생을 확인할 수 없음
원인 가설 · 출력과의 연결 검증
Context 상태 전환 / 기기 오디오 처리 문제?정지·재개, 브라우저 렌더 처리 이상 등 확인 필요
이번 진행 부족의 원인은 미확정
입력 currentTime 진행 부족관측 사실: 실제 45.104초 / Context 35.701초
중간 상태 전환·시계 조건을 함께 검증해야 함
AI 출력은 별도 경로
수신 AI 음성 → <audio> → 아동 스피커
위 마이크 Context를 경유하지 않습니다.
아동이 듣는 AI 출력 잡음·끊김?입력 Context 진행 부족에서 바로 도출할 수 없음
SDK Context·수신 통계·실제 재생을 별도로 대조
로그가 직접 보여준 것은 ‘시간 진행량 차이’입니다.
실제 시간 경과 · 45.104초 (100%)
입력 Context currentTime 증가 · 35.701초 (약 79.2%)

9.403초의 차이는 누적 관측 차이입니다. 음성 버퍼에 그만큼 쌓였거나 스피커에서 그만큼 늦게 재생됐다는 뜻은 아닙니다. 실제 음성 손실량으로 환산할 수도 없습니다.

검증할 인과: Context 상태·렌더 진행 변화 → 처리된 음성의 이상 → 수신측 가청 증상을 같은 시간축에서 확인합니다. SDK Context는 별도로 측정하며, AI 출력 원인은 네트워크 수신·버퍼링·기기 재생 경로까지 구분해서 확인합니다.

03개선 방향의 핵심

작업 방향 · 미구현 / 효과 미검증

지지직 음성 개선을 위한 게스트·LiveKit AudioContext 통합

게스트 마이크 처리와 LiveKit SDK가 별도 Context를 사용하고 SDK Context는 세션 연결·종료에 따라 생성·폐기됩니다. 게스트의 공유 Context를 Room에 전달해 주요 입출력 처리를 통합하고, 생성·폐기 비용 감소와 실제 잡음 개선 여부를 검증합니다. 다중 Context가 이번 증상의 원인이라고 확정한 것은 아닙니다.

현재
PPI 공유 Context아동 마이크 → 보정 → AI / 진행자 전송
별도 SDK Context
Room 연결 시 확보·재개 / 종료 시 폐기
핑퐁이 수신 → <audio> → 스피커SDK Context 출력 그래프는 거치지 않음
통합 제안
하나의 공유 AudioContextRoom A → Room B → Room C에 같은 Context 전달
Room은 교체 · Context는 페이지에서 관리
아동 마이크 분기보정 → 전송용 destination
AI / 진행자·녹음
핑퐁이 수신 분기SDK Gain → context.destination
아동 스피커

두 신호는 자동으로 섞이지 않습니다. 마이크의 MediaStreamAudioDestinationNode는 전송용 트랙을 만들고, context.destination은 스피커로 출력합니다. 마이크를 스피커 destination에 연결하지 않으면 Context 공유만으로 자기 목소리가 재생되지 않습니다.

// 구현 예정 예시 — 현재 PPI에 적용된 코드가 아닙니다.
const room = new Room({
  adaptiveStream: false,
  dynacast: false,
  webAudioMix: { audioContext: sharedContext },
});

SDK 2.19.2는 외부 Context가 있으면 새 내부 Context 대신 이를 사용하고, Room 종료 시 직접 닫지 않습니다. 이 옵션은 Web Audio 수신 출력도 활성화하므로, 기존 <audio> 직접 출력이 유지되는 변경은 아닙니다.

옵션 추가만으로 완료되지 않습니다. SDK의 attach는 <audio>를 음소거·볼륨 0으로 만들어 이중 출력을 막습니다. PPI는 이후 volume=1과 muted를 설정하므로, 그대로 두면 핑퐁이 음성이 두 경로로 재생될 수 있습니다. 출력 제어를 새 경로에 맞춰 함께 변경해야 합니다.
공유 Context의 수명과 사용 주체를 통합

마이크 프로세서와 Room 모두 사용 주체로 관리합니다. Room 종료·연결 실패·재접속에는 해당 노드와 참조만 정리하고 다른 사용자가 있는 Context를 닫지 않습니다. 페이지 종료·회복 불가능한 closed 상태·재개 실패의 교체 정책도 정의합니다.

이중 재생 방지와 입력·출력 독립 제어 보존

AI 듣기 OFF는 마이크의 AI 분기만 차단하고 진행자·녹음 분기는 유지합니다. AI 출력 OFF는 핑퐁이 수신 분기에 적용합니다. SDK 출력 Gain과 <audio> mute/volume 제어를 정리하고, 종료 후 잔류 음성·중복 attach가 없는지 확인합니다.

기타 Context까지 조사하고 단일화 범위를 명시

수신 음량 진단·출력 프로브 등은 추가 Context를 생성할 수 있습니다. 주요 두 Context를 합치는 것과 페이지 전체를 정확히 하나로 만드는 것은 다릅니다. LiveKit / VoicePipeline 모드에서 살아 있는 Context를 계수하고, 진단 분기의 공유·정리 정책까지 정해야 ‘단일 Context’라고 검증할 수 있습니다.

같은 iPad에서 실제 음질과 회귀를 비교

잡음·신호 공백, 에코·음량·아동 음성 인식, 진행자 청취·녹음, AI 듣기/출력 OFF, 세션 전환·재접속·백그라운드 복귀를 확인합니다. Context 수 감소만으로 성공을 판정하지 않습니다. 공유 Context가 멈추면 입력과 출력이 함께 영향을 받는 점도 검증합니다.

추가 분석: 확인한 사실과 아직 확인하지 못한 원인
  • 현장 보고: 사용자는 아동 스피커와 진행자 실시간 청취에서도 이상이 있었다고 설명했습니다. 이 설명에 따르면 최종 MP3 생성만의 문제라는 가설은 우선순위가 낮아집니다. 다만 진행자가 들은 음성 종류와 두 기기의 정확한 동시 발생 여부는 분리 확인이 필요합니다.
  • 샘플레이트: 로그의 입력 Context 8건과 출력 진단 Context 17건은 모두 48kHz입니다. 원본 마이크와 실제 스피커의 하드웨어 레이트를 확인한 것은 아닙니다. 레이트 차이는 정상적으로 리샘플링되므로 차이 자체를 원인으로 볼 수 없습니다.
  • latencyHint: PPI는 생성 옵션을 생략하고 SDK는 interactive를 명시합니다. 생략 시 기본값도 interactive이므로 옵션 누락이 지연 원인이라는 근거는 없습니다. 실제 브라우저 반영은 별도입니다.
  • 버퍼: analyser.fftSize=2048은 분석 창 크기이며 재생 버퍼 크기가 아닙니다. 약 200ms 공백을 버퍼 설정 오류로 확정하지 않았습니다. WebRTC 지터 버퍼와 Context의 렌더·장치 버퍼도 구분합니다.
  • 자원 제한: 여러 Context의 자원 사용과 반복 생성·폐기는 조사 대상이지만, 두 개가 존재한다는 사실은 고갈의 증거가 아닙니다. Context별 식별자·생성·close 완료·state·진행률을 기록해 누적과 실제 렌더 이상을 구분합니다.
  • 성공 조건: 실제 지지직·끊김 감소와 기능 보존을 확인해야 합니다. 시계 보간, Context 수 감소, 입력 시계 진행률 개선만으로 AI 가청 출력 문제가 해결됐다고 판단하지 않습니다.
공유 Context 권고와 외부 보고의 근거·한계

현재 조사한 외부 자료에서 ‘동일 페이지의 두 AudioContext가 PPI와 같은 약 200ms 주기적 공백을 유발한다’는 직접 근거는 찾지 못했습니다. 공유 통합은 개선 가설을 검증하는 변경입니다.

코드 근거 · 이력 · 검증 범위

이 문서 추가에 따른 앱 런타임 변경은 없습니다. 현재 코드 기준과 실제 9월 8일 배포본 전체의 일치 여부는 별도 확인 대상입니다.