마지막 업데이트 2026-07-29
setSinkId() 호출 시 시스템 볼륨 슬라이더 미동작 분석의 핵심을 수사반장식으로 먼저 안내합니다. 기술적 결론과 원문 근거는 아래 본문에 보존되어 있습니다.
setSinkId() 자체다. 특정 deviceId로 오디오 출력을 직접 바인딩하는 순간 OS 기본 오디오 세션(믹서)을 우회하므로,
시스템 볼륨 슬라이더는 해당 스트림에 대한 제어권을 잃는다. 버그가 아니라 W3C Audio Output Devices API 스펙대로의 동작이며,
SW로 볼륨을 제어할 유일한 수단은 HTMLMediaElement.volume이다 — 앱 내 볼륨 슬라이더 제공(커밋 id-016)이 그 배경이다.
게스트(아동) 페이지에서 USB 헤드셋을 연결하면 setSinkId()로 오디오 출력이 헤드셋으로 강제 라우팅됩니다.
이 시점부터 macOS / Windows 시스템 볼륨 슬라이더를 조작해도 앱 내 오디오(핑퐁이 음성, 활동 영상)의 볼륨이 변하지 않습니다.
"슬라이더가 안 먹는다"는 증상은 앱의 볼륨 코드 버그처럼 보인다. 하지만 실제로는 해당 스트림에 대한 제어권 자체가 없는 상태다. 그래서 볼륨 코드를 뒤지는 대신, 소리가 장치까지 흘러가는 경로부터 두 경우를 나란히 놓고 비교했다.
일반 재생과 setSinkId() 사용 시의 경로 차이입니다.
setSinkId()는 W3C Audio Output Devices API 스펙에 따라 HTMLMediaElement의
오디오 출력을 특정 deviceId로 직접 바인딩합니다.
이 경로는 OS의 기본 오디오 세션(macOS Core Audio, Windows WASAPI)이 노출하는
볼륨 컨트롤 레이어를 거치지 않습니다.
OS 시스템 볼륨 슬라이더는 "기본 출력 장치(default output device)"의 세션 볼륨을 조절합니다.
setSinkId()로 지정된 스트림은 OS 입장에서 별개의 장치 스트림이기 때문에
기본 출력 슬라이더의 제어 범위 밖에 있습니다.
navigator.mediaSession은 재생/정지/트랙 이동만 지원하며 볼륨 이벤트를 노출하지 않습니다.
원인이 setSinkId()로 확정됐으니 다음 질문은 하나다 — 이 호출을 빼면 되지 않는가. 그래서 프로젝트가 이 API를 쓰는 이유와 적용 범위를 확인했다.
게스트 입장 시 findPreferredDevice()로 USB 헤드셋을 자동 감지하고,
감지된 deviceId를 각 오디오 엘리먼트에 적용합니다.
OS 설정과 무관하게 핑퐁이 음성과 활동 영상을 확실히 헤드셋으로 출력하기 위한 목적입니다.
| 파일 | 라우팅 대상 |
|---|---|
| use-ai-session.ts | 핑퐁이 AI 음성 |
| meet-video.tsx | 활동 영상 오디오 |
| use-host-session-relay.ts | 호스트→게스트 relay 오디오 |
| use-host-media-manager.ts | 원격 비디오 오디오 |
| 볼륨 제어 방법 | setSinkId 미사용 | setSinkId 사용 후 |
|---|---|---|
| macOS 시스템 볼륨 슬라이더 | 동작 | 미동작 |
| Windows 시스템 볼륨 슬라이더 | 동작 | 미동작 |
| 헤드셋 하드웨어 볼륨 버튼 | 동작 | 동작 (장치 자체 제어) |
| HTMLMediaElement.volume 프로퍼티 | 동작 | 동작 — 유일한 SW 제어 수단 |
| navigator.mediaSession 볼륨 | 미지원 (API 없음) | 미지원 (API 없음) |
USB 헤드셋 강제 라우팅이라는 목적상 setSinkId()는 뺄 수 없고, 볼륨 제어 수단을 전수 점검(위 표)한 결과 남은 SW 경로는 하나뿐이었다.
setSinkId()로 라우팅된 스트림의 볼륨을 조절하는 유일한 SW 수단은
HTMLMediaElement.volume 프로퍼티 직접 조작입니다.
따라서 앱 내 볼륨 슬라이더 제공이 필수이며, 이것이 커밋 id-016의 배경입니다.
// setSinkId() 라우팅 후 볼륨 제어
audioElement.setSinkId(deviceId);
audioElement.volume = 0.8; // 0.0 ~ 1.0, 이 방법만 유효
헤드셋 하드웨어 볼륨 버튼이 있는 경우 동작하지만 모든 기기에 해당하지 않으므로, 앱 내 슬라이더를 함께 제공하는 것이 정석입니다.
setSinkId()와 앱 내 볼륨 제어 이슈와 별개로, iPad Pro 내장 스피커+내장 마이크 조합에서는
Safari/WebKit 마이크 캡처 세션과 echo cancellation이 영상 오디오를 시스템 레벨에서 감쇠시킬 수 있다.
자세한 분석은 iPad Pro 내장 스피커/마이크 영상 오디오 감쇠 원인 분석을 참고한다.