
윈도우 소리를 맥북 스피커로 보내기: FFmpeg와 VB-CABLE로 만든 개인용 네트워크 스피커
데스크톱 PC에는 스피커가 없고, 옆에 있는 맥북 스피커는 멀쩡히 남아 있는 상황이 있다. 이럴 때 가장 먼저 떠오르는 생각은 단순하다. 윈도우에서 재생되는 소리를 네트워크로 맥북에 보내고, 맥북이 그것을 스피커처럼 재생하면 되지 않을까?
결론부터 말하면 가능하다. 다만 윈도우에 진짜 스피커 장치처럼 보이게 만드는 것과, 개인용으로 빨리 쓸 수 있게 만드는 것은 난이도가 크게 다르다. 이 글은 직접 실험하면서 정리한 가장 현실적인 구성, 즉 VB-CABLE + FFmpeg + MPEG-TS/Opus over UDP 방식의 개인용 네트워크 스피커 구조를 정리한다.
처음 생각한 구조
이상적인 모습은 다음과 같다.
- 윈도우에
Network Speaker같은 출력 장치가 나타난다. - 사용자는 그 장치를 기본 스피커로 선택한다.
- 윈도우 앱이 오디오를 받아 네트워크로 전송한다.
- 맥 앱이 수신한 오디오를 맥북 스피커로 재생한다.
사용자 경험만 보면 간단해 보이지만, 첫 번째 단계가 가장 어렵다. 윈도우 사운드 설정에 새로운 출력 장치를 만들려면 일반 앱이 아니라 가상 오디오 드라이버가 필요하다. Microsoft의 SysVAD 샘플처럼 가상 오디오 장치를 구현하는 예제가 있기는 하지만, 실제로 쓰려면 Windows Driver Kit, 드라이버 설치, 테스트 서명 또는 배포용 서명 절차까지 고려해야 한다.
개인용 도구를 만들기 위해 처음부터 드라이버를 붙잡는 것은 배보다 배꼽이 커지기 쉽다. 그래서 이 실험에서는 드라이버는 이미 검증된 VB-CABLE에 맡기고, 전송과 재생만 직접 구성했다.
최종 구성
최종적으로 잘 동작한 구성은 다음과 같다.
Windows
기본 출력 장치: CABLE Input(VB-Audio Virtual Cable)
캡처 장치: CABLE Output(VB-Audio Virtual Cable)
송신: FFmpeg
포맷: Opus inside MPEG-TS
전송: UDP
Mac
수신/재생: ffplay
출력: MacBook speaker
윈도우는 실제 스피커 대신 VB-CABLE의 입력 장치로 소리를 보낸다. 그러면 FFmpeg가 VB-CABLE의 출력 장치를 캡처해서 맥으로 전송한다. 맥에서는 ffplay가 UDP 패킷을 받아 재생한다.
처음에는 raw PCM over UDP와 RTP/Opus도 시도했다. raw PCM은 단순하지만 Wi-Fi 상태에 따라 소리가 늘어지거나 지연이 쌓일 수 있었다. RTP/Opus는 구조적으로는 맞지만 SDP와 payload type 설정이 생각보다 예민했다. 결과적으로 개인용 스크립트에서는 MPEG-TS 컨테이너에 Opus를 넣어 UDP로 보내는 방식이 가장 무난했다.
필요한 것
윈도우 쪽에는 두 가지가 필요하다.
- VB-CABLE: 윈도우에 가상 오디오 케이블 장치를 만들어준다.
- FFmpeg: VB-CABLE 오디오를 캡처하고 네트워크로 전송한다.
맥 쪽에는 FFmpeg에 포함된 ffplay가 필요하다.
brew install ffmpeg
윈도우에서 FFmpeg를 설치하는 가장 쉬운 방법은 winget을 사용하는 것이다.
winget install Gyan.FFmpeg
VB-CABLE은 드라이버 성격의 프로그램이므로 별도로 설치해야 한다. 단순 실행파일을 앱 폴더에 같이 넣는 것과는 다르게, 관리자 권한과 장치 설치 과정이 필요할 수 있다.
맥 수신 스크립트
맥에서는 UDP 5004 포트로 들어오는 MPEG-TS 스트림을 ffplay로 재생한다. 아래 예시는 로컬 경로를 포함하지 않는 일반화된 스크립트다.
#!/usr/bin/env bash
set -euo pipefail
PORT="${1:-5004}"
ffplay \
-hide_banner \
-loglevel warning \
-nodisp \
-fflags nobuffer \
-flags low_delay \
-max_delay 50000 \
-probesize 2048 \
-analyzeduration 0 \
-f mpegts \
"udp://0.0.0.0:${PORT}?listen=1&buffer_size=65536&fifo_size=512&overrun_nonfatal=1"
실행 순서는 맥 수신기를 먼저 켜고, 그 다음 윈도우 송신기를 실행하는 방식이 좋다.
윈도우 송신 스크립트
윈도우에서는 DirectShow 입력 장치로 VB-CABLE의 출력 장치를 지정한다. VB-CABLE 장치 이름은 설치 환경에 따라 공백이 조금 다를 수 있으므로, 실제 장치 목록에서 확인한 이름을 넣어야 한다.
param(
[Parameter(Mandatory = $true)]
[string]$MacIp,
[int]$Port = 5004,
[string]$Device = "CABLE Output(VB-Audio Virtual Cable)"
)
$target = "udp://$($MacIp):$($Port)?pkt_size=1316"
ffmpeg `
-hide_banner `
-loglevel info `
-fflags nobuffer `
-flags low_delay `
-f dshow `
-audio_buffer_size 30 `
-i "audio=$Device" `
-vn `
-ac 2 `
-ar 48000 `
-c:a libopus `
-b:a 128k `
-vbr off `
-application lowdelay `
-frame_duration 10 `
-compression_level 0 `
-flush_packets 1 `
-f mpegts `
$target
맥의 IP 주소는 맥에서 다음 명령으로 확인할 수 있다.
ipconfig getifaddr en0
윈도우에서는 다음처럼 실행한다.
powershell -ExecutionPolicy Bypass -File .\send-ts.ps1 -MacIp 192.168.0.10
192.168.0.10은 예시 IP다. 실제로는 자신의 맥북 IP 주소로 바꿔야 한다.
테스트톤으로 먼저 확인하기
문제가 생겼을 때는 실제 윈도우 소리를 바로 보내기보다 테스트톤부터 보내는 편이 좋다. 이렇게 하면 네트워크 문제인지, VB-CABLE 캡처 문제인지 분리해서 볼 수 있다.
param(
[Parameter(Mandatory = $true)]
[string]$MacIp,
[int]$Port = 5004
)
$target = "udp://$($MacIp):$($Port)?pkt_size=1316"
ffmpeg `
-hide_banner `
-loglevel info `
-f lavfi `
-i "sine=frequency=440:sample_rate=48000" `
-ac 2 `
-ar 48000 `
-c:a libopus `
-b:a 128k `
-vbr off `
-application lowdelay `
-frame_duration 10 `
-compression_level 0 `
-flush_packets 1 `
-f mpegts `
$target
테스트톤은 들리는데 일반 오디오가 들리지 않는다면, 윈도우 기본 출력 장치가 CABLE Input(VB-Audio Virtual Cable)으로 설정되어 있는지 확인한다. 테스트톤도 들리지 않는다면 두 장비가 같은 네트워크에 있는지, 맥 방화벽이 수신을 막고 있지 않은지, IP 주소를 잘못 입력하지 않았는지부터 확인한다.
왜 TCP는 피했나?
처음 디버깅할 때는 TCP도 시도했다. TCP는 연결 실패가 비교적 명확하게 보이기 때문에 네트워크 확인용으로는 편하다. 하지만 오디오 스트리밍에서는 버퍼가 커지거나 재전송이 발생하면서 지연이 크게 늘어날 수 있다. 실제 체감 지연이 1~2초 이상으로 느껴질 수 있으므로, 스피커처럼 쓰기에는 UDP가 더 적합했다.
UDP는 패킷 손실에 민감하지만, 같은 공유기 안의 개인용 네트워크에서는 충분히 쓸 만하다. 특히 raw PCM보다 Opus를 사용하면 대역폭 부담이 줄어들고, MPEG-TS 컨테이너를 쓰면 수신 쪽에서 스트림을 인식하기도 쉽다.
FFmpeg를 앱에 포함할 수 있을까?
FFmpeg 실행파일은 앱 폴더에 포함해서 배포하는 형태로 만들 수 있다. 예를 들어 윈도우 폴더 안에 bin/ffmpeg.exe를 넣고, PowerShell 스크립트가 PATH보다 그 파일을 먼저 찾게 만들면 사용자는 FFmpeg를 별도로 설치하지 않아도 된다.
다만 배포를 생각한다면 라이선스를 확인해야 한다. FFmpeg는 기본적으로 LGPL 계열 라이선스이고, 빌드 옵션에 따라 GPL 또는 재배포가 어려운 nonfree 구성이 될 수 있다. 개인용으로 쓰는 것과 공개 배포는 다르므로, 공개 배포를 할 때는 FFmpeg 공식 legal 문서와 사용한 바이너리의 빌드 옵션을 확인해야 한다.
VB-CABLE까지 포함할 수 있을까?
FFmpeg와 달리 VB-CABLE은 단순한 보조 실행파일이 아니라 윈도우 오디오 드라이버에 가깝다. 그래서 앱 폴더에 같이 넣고 실행하는 방식으로 자연스럽게 포함하기 어렵다. 설치에는 관리자 권한과 드라이버 설치 과정이 필요하고, 재배포 조건도 별도로 확인해야 한다.
VB-CABLE 없이 완전히 처음부터 만들려면 자체 가상 오디오 드라이버를 만들어야 한다. Microsoft의 SysVAD 샘플이 출발점이 될 수 있지만, 이 순간부터는 작은 유틸리티가 아니라 Windows 드라이버 프로젝트가 된다. 개인용 네트워크 스피커를 만들기 위한 목적이라면, 드라이버는 VB-CABLE에 맡기고 나머지 송수신 흐름만 직접 다루는 편이 현실적이다.
정리
윈도우 소리를 맥북 스피커로 보내는 것은 가능하다. 하지만 완전히 자연스러운 제품처럼 만들려면 가상 오디오 드라이버라는 큰 벽을 만나게 된다. 개인용으로 빠르게 쓰려면 다음 조합이 가장 단순했다.
VB-CABLE
+ FFmpeg
+ Opus inside MPEG-TS over UDP
+ ffplay on Mac
이 방식은 완벽한 제품은 아니지만, 같은 네트워크 안에서 데스크톱 PC의 소리를 맥북 스피커로 보내는 개인용 도구로는 충분히 실용적이다. 중요한 포인트는 처음부터 드라이버를 만들려고 하지 않는 것이다. 먼저 검증된 가상 오디오 케이블을 사용해 흐름을 완성하고, 정말 필요할 때만 드라이버 제작을 별도 프로젝트로 분리하는 편이 낫다.
참고 자료
'프로그래밍 > C, C++, Java, Python' 카테고리의 다른 글
| Python uuid7은 언제 써야 할까? 정렬 가능한 ID 선택 기준 정리 (0) | 2026.05.12 |
|---|---|
| Python free-threaded 빌드는 무엇이 달라졌을까? 3.13~3.14 기준 설치·확인·제약 정리 (1) | 2026.05.07 |
| Discord에서 Codex를 이어 쓰는 로컬 브리지 만들기 (0) | 2026.05.05 |
| 삼성 SW 역량테스트 B형/Pro 대비 - 최적화 기법 (0) | 2021.03.30 |
| Modbus TCP 통신을 위한 프로토콜 파헤치기 & 예제 코드 (0) | 2021.02.02 |





