251 lines
12 KiB
Markdown
251 lines
12 KiB
Markdown
# 서버 방향성
|
|
|
|
이 문서는 macOS, Ubuntu, Rocky Linux 9, Windows WSL2에서 실행할 수신기/믹서 서버의 초기 방향을 정리합니다.
|
|
|
|
현재 단계의 목표는 복잡한 믹서 기능을 만드는 것이 아니라, Orange Pi Zero 2W 클라이언트에서 보낸 오디오가 서버 실행 장비에서 실제로 들리는 최소 경로를 확인하고, 이후 2대에서 16대까지 확장할 수 있는 기반을 잡는 것입니다.
|
|
|
|
```text
|
|
Orange Pi Zero 2W 클라이언트 -> UDP/Wi-Fi -> 서버 -> 기본 오디오 출력
|
|
```
|
|
|
|
## 역할
|
|
|
|
서버는 수신 장비에서 실행되며 다음 역할을 맡습니다.
|
|
|
|
- 고정 UDP 포트에서 클라이언트의 오디오 패킷을 수신합니다.
|
|
- 수신 패킷의 순서와 기본 메타데이터를 확인합니다.
|
|
- PCM 오디오 payload를 실행 OS의 오디오 출력 장치로 재생합니다.
|
|
- 첫 bring-up에서는 송신기 1대로 오디오 경로를 확인합니다.
|
|
- 실제 목표 규모는 최소 2대, 최대 16대 송신기입니다.
|
|
- 이후 단계에서 2대 이상의 다중 송신기 믹싱, 동기화, UI를 확장 후보로 검토합니다.
|
|
|
|
## 초기 범위
|
|
|
|
초기 서버의 성공 기준은 다음과 같습니다.
|
|
|
|
- 지원 OS에서 서버 프로그램을 실행할 수 있습니다.
|
|
- 서버가 지정된 UDP 포트에 바인딩됩니다.
|
|
- Orange Pi Zero 2W 클라이언트가 보낸 UDP 패킷을 수신합니다.
|
|
- 수신한 PCM 프레임을 실행 OS의 기본 오디오 출력으로 재생합니다.
|
|
- 패킷 수신 상태, sequence 변화, 간단한 손실 통계를 확인할 수 있습니다.
|
|
|
|
다중 송신기 믹싱은 1대 오디오 경로 확인 이후의 핵심 확장 목표입니다. 송신기별 음량 조절, 자동 검색, 압축 코덱, 지터 버퍼 고도화, GUI는 초기 성공 이후에 다룹니다.
|
|
|
|
## 목표 송신기 규모
|
|
|
|
현재 목표 규모는 다음과 같습니다.
|
|
|
|
```text
|
|
minimum_senders = 2
|
|
maximum_senders = 16
|
|
```
|
|
|
|
첫 구현과 첫 실기 검증은 문제 원인을 줄이기 위해 송신기 1대로 시작합니다. 이후 검증은 2대, 4대, 8대, 16대 순서로 늘리는 방향을 우선합니다.
|
|
|
|
현재 무압축 `PCM_S16LE / mono / 48 kHz / 10 ms` 기준으로 송신기 1대는 UDP/IP 포함 대략 0.8 Mbps 이상을 사용합니다. 16대는 단순 계산으로 13 Mbps 이상입니다. 권장 네트워크는 2.4 GHz Wi-Fi로 두되, 실제 2.4 GHz 환경에서는 MAC 오버헤드, 주변 간섭, 재전송, airtime 경쟁, 공유기 성능 때문에 단순 대역폭 계산보다 훨씬 보수적으로 검증해야 합니다.
|
|
|
|
따라서 16대 목표를 달성하려면 단순 대역폭보다 다음 항목을 중요하게 봅니다.
|
|
|
|
- 2.4 GHz Wi-Fi 기준 검증
|
|
- 송신기별 sequence 손실과 순서 역전 통계 확인
|
|
- 송신기 수 증가에 따른 지터 버퍼 underflow와 overflow 관찰
|
|
- 10 ms 프레임 유지가 어려울 경우 20 ms 프레임 후보 검토
|
|
- 16대 검증 전 압축 코덱 도입 여부 재검토
|
|
|
|
## 네트워크 수신 방향
|
|
|
|
초기 수신은 UDP 기반으로 진행합니다.
|
|
|
|
포트는 다음 값을 초기 후보로 둡니다.
|
|
|
|
```text
|
|
listen_port = 4860
|
|
```
|
|
|
|
서버는 먼저 단일 클라이언트의 패킷만 받아 오디오 경로를 확인합니다. 이후 같은 포트로 들어오는 여러 클라이언트를 `sender_id`, `session_id`, `sequence`, `capture_sample_index`로 구분해 최소 2대, 최대 16대까지 단계적으로 검증합니다.
|
|
|
|
## 포트 운영 방향
|
|
|
|
초기 기본값은 서버 UDP 수신 포트 1개입니다.
|
|
|
|
마이크가 여러 대가 되더라도 먼저 하나의 UDP 포트로 받고, 패킷 헤더의 송신기 식별자와 sequence, timestamp를 이용해 서버 내부에서 스트림을 나누는 방향을 우선합니다.
|
|
|
|
포트를 하나만 쓰는 이유는 다음과 같습니다.
|
|
|
|
- OS 방화벽과 공유기 설정이 단순합니다.
|
|
- 송신기 수가 늘어나도 서버 실행 옵션이 복잡해지지 않습니다.
|
|
- 자동 검색을 추가할 때 안내할 수신 포트가 하나라 단순합니다.
|
|
- 서버가 송신기별 지터 버퍼와 믹싱 상태를 한 곳에서 관리하기 쉽습니다.
|
|
|
|
마이크 수만큼 포트를 나누는 방식은 다음 상황에서만 후보로 둡니다.
|
|
|
|
- 송신기마다 별도 서버 프로세스를 띄워 완전히 분리해 테스트해야 할 때
|
|
- 특정 네트워크 도구로 송신기별 트래픽을 강하게 분리해서 봐야 할 때
|
|
- 나중에 운영상 포트 분리가 더 명확하다고 판단될 때
|
|
|
|
따라서 현재 방향은 `listen_port = 4860` 하나를 사용하고, 다중 송신기는 패킷 메타데이터로 구분하는 것입니다.
|
|
|
|
## 개발 도구와 의존성 방향
|
|
|
|
서버는 장기적으로 macOS, Ubuntu, Rocky Linux 9, Windows WSL2에서 실행할 수 있게 설계합니다.
|
|
|
|
Homebrew를 개발/실행 준비 기준으로 둡니다. macOS와 Linux 개발 환경에서 같은 방식으로 CMake와 miniaudio 같은 의존성을 설치할 수 있고, Windows는 네이티브 Windows가 아니라 WSL2 Linux 환경을 사용합니다.
|
|
|
|
서버 구현 기준은 다음 방향을 우선합니다.
|
|
|
|
- 빌드: CMake
|
|
- 의존성 관리: Homebrew
|
|
- Windows 지원: WSL2 Linux 환경
|
|
- 네이티브 Windows 빌드: 초기 범위에서 제외
|
|
|
|
WSL2에서 실제 오디오를 출력하려면 WSLg 또는 Linux 오디오 출력 경로가 동작해야 합니다. Windows 네이티브 오디오 API 지원은 초기 범위에 포함하지 않습니다.
|
|
|
|
공식 참고 링크:
|
|
|
|
- https://docs.brew.sh/Homebrew-on-Linux
|
|
- https://formulae.brew.sh/formula/miniaudio
|
|
|
|
## 오디오 출력 방향
|
|
|
|
초기 오디오 출력은 여러 OS에서 C++로 접근 가능한 라이브러리를 후보로 검토합니다.
|
|
|
|
후보는 다음과 같습니다.
|
|
|
|
- miniaudio: 단일 파일에 가까운 경량 오디오 라이브러리입니다. macOS와 Linux의 기본 오디오 백엔드를 지원하고 Homebrew로 설치할 수 있어 초기 우선 후보로 둡니다.
|
|
- Core Audio: macOS 기본 오디오 API입니다. 추가 런타임 의존성이 적지만 초기 구현이 다소 길어질 수 있습니다.
|
|
- PortAudio: 크로스 플랫폼 오디오 입출력 라이브러리입니다. 예제가 많고 초기 재생 경로를 빠르게 만들 수 있습니다.
|
|
- RtAudio: C++ 인터페이스가 단순한 오디오 입출력 라이브러리입니다.
|
|
|
|
첫 구현에서는 안정적인 최소 재생 경로를 우선합니다. 현재 우선 후보는 `miniaudio`입니다.
|
|
|
|
## 초기 오디오 포맷 후보
|
|
|
|
클라이언트 방향성과 맞춰 초기 포맷 후보는 다음과 같습니다.
|
|
|
|
```text
|
|
sample_rate = 48000
|
|
sample_format = signed 16-bit little endian
|
|
channels = 1
|
|
frame_ms = 10
|
|
```
|
|
|
|
`48 kHz / 16-bit / mono / 10 ms` 기준 payload는 960바이트입니다. UDP/IP 헤더를 더해도 일반적인 MTU 1500 안에 들어가므로, 20 ms 프레임보다 UDP fragmentation 위험이 낮습니다.
|
|
|
|
이 값은 시작점입니다. 실제 USB 마이크나 출력 경로에서 다른 포맷이 더 자연스러우면 구현 전에 조정합니다.
|
|
|
|
## 패킷 포맷 후보
|
|
|
|
초기 패킷은 서버가 지정하는 고정 길이 헤더와 PCM payload로 구성하는 방향을 검토합니다.
|
|
|
|
헤더 v1 후보:
|
|
|
|
```text
|
|
magic u32 "MIC1"
|
|
version u8 1
|
|
header_len u8 56
|
|
packet_type u8 1 = audio
|
|
flags u8 reserved
|
|
sender_id u16 0 reserved, 1..65535
|
|
stream_id u16 initial 0
|
|
session_id u32 random value after sender restart
|
|
sequence u32 increments per sender/session
|
|
capture_sample_index u64 first sample index in this packet
|
|
sender_monotonic_us u64 sender monotonic timestamp
|
|
sample_rate u32 initial 48000
|
|
frame_samples u16 initial 480
|
|
payload_bytes u16 initial 960
|
|
codec_id u8 1 = PCM_S16LE
|
|
sample_format u8 1 = S16LE
|
|
channels u8 initial 1
|
|
channel_layout u8 0 = mono/default
|
|
header_crc32 u32 0 allowed in initial implementation
|
|
reserved u32 future use
|
|
```
|
|
|
|
헤더는 네트워크 바이트 오더를 사용합니다. PCM payload는 초기 후보인 `PCM_S16LE`에 맞춰 little endian으로 둡니다.
|
|
|
|
서버는 `sender_id`, `session_id`, `sequence`, `capture_sample_index`를 이용해 송신기 구분, 재시작 감지, 패킷 손실, 순서 역전, 재생 위치를 확인할 수 있어야 합니다. 초기 단계에서는 복잡한 재전송이나 복구를 넣지 않고, 끊김 여부를 관찰하는 데 집중합니다.
|
|
|
|
## 지터와 버퍼링 방향
|
|
|
|
Wi-Fi UDP 오디오에서는 패킷 도착 간격이 흔들릴 수 있습니다.
|
|
|
|
초기 버전은 아주 단순한 짧은 재생 버퍼를 후보로 둡니다.
|
|
|
|
- 너무 짧으면 끊김이 늘어납니다.
|
|
- 너무 길면 지연이 커집니다.
|
|
- 첫 목표는 지연 시간 최적화가 아니라 소리 경로 확인입니다.
|
|
|
|
구체적인 버퍼 길이는 구현 전 테스트 환경과 오디오 라이브러리 선택에 맞춰 정합니다.
|
|
|
|
## 설정 방향
|
|
|
|
초기 서버 설정 후보는 다음과 같습니다.
|
|
|
|
```text
|
|
listen_port = 4860
|
|
web_port = 4861
|
|
sample_rate = 48000
|
|
channels = 1
|
|
frame_ms = 10
|
|
audio_output = default
|
|
```
|
|
|
|
송신기별 음량 조절 설정은 현재 범위에서 제외합니다. 초기 목표는 수신한 단일 송신기 오디오를 노트북 스피커로 출력하는 것이고, 다음 목표는 2대 이상 송신기의 동시 수신과 믹싱입니다.
|
|
|
|
설정 파일 형식은 단순 key-value 텍스트 형식을 후보로 둡니다. 자동 검색을 추가하더라도 고정 포트와 고정 설정 방식은 유지합니다.
|
|
|
|
## Web UI 방향
|
|
|
|
서버 제어와 상태 확인은 GUI 앱보다 Web UI를 우선합니다.
|
|
|
|
현재 기준은 다음과 같습니다.
|
|
|
|
```text
|
|
web_host = 127.0.0.1
|
|
web_port = 4861
|
|
```
|
|
|
|
Web UI는 같은 장비의 브라우저에서만 접근하는 localhost 전용으로 둡니다. 같은 Wi-Fi의 다른 장비에서 접속하는 기능은 현재 범위에 포함하지 않습니다. 별도 비밀번호나 인증은 두지 않고, `127.0.0.1` 바인딩으로 외부 노출을 막습니다.
|
|
|
|
초기 Web UI는 다음 기능을 제공합니다.
|
|
|
|
- UDP 수신 상태와 오류 표시
|
|
- 오디오 출력 상태와 오류 표시
|
|
- sender별 packet, dropped, out_of_order, last seen 표시
|
|
- 전체 output mute
|
|
- sender별 mute
|
|
- sender별 gain
|
|
- sender별 display name
|
|
- 통계 reset
|
|
|
|
UDP 바인딩 실패나 수신 오류가 발생하면 Web UI의 `udp_state`, `udp_error`에 표시하는 방향을 우선합니다.
|
|
|
|
## 검증 순서
|
|
|
|
초기 검증은 다음 순서를 따릅니다.
|
|
|
|
1. 지원 OS에서 서버가 UDP 포트를 열 수 있는지 확인합니다.
|
|
2. 같은 장비 또는 다른 장비에서 테스트 UDP 패킷이 수신되는지 확인합니다.
|
|
3. Orange Pi Zero 2W와 서버 실행 장비 사이의 Wi-Fi 통신을 확인합니다.
|
|
4. 클라이언트 1대의 오디오 패킷이 서버에 도착하는지 확인합니다.
|
|
5. 서버가 수신한 단일 송신기 PCM 프레임을 실행 OS의 오디오 출력으로 재생합니다.
|
|
6. sequence 로그로 패킷 손실과 순서 역전을 관찰합니다.
|
|
7. 송신기를 2대, 4대, 8대, 16대 순서로 늘리며 손실, 순서 역전, 지터 버퍼 underflow와 overflow를 기록합니다.
|
|
8. 실제 소리 끊김과 지연을 기록합니다.
|
|
|
|
## 현재 구현 단계
|
|
|
|
서버 구현은 Homebrew, CMake, miniaudio 기준으로 시작합니다.
|
|
|
|
현재 서버 구현 범위는 다음과 같습니다.
|
|
|
|
- UDP `4860` 바인딩
|
|
- localhost Web UI `127.0.0.1:4861`
|
|
- 서버 지정 56바이트 패킷 헤더 v1 파싱
|
|
- 단일 활성 `sender_id`의 `PCM_S16LE / mono / 48 kHz / 10 ms` payload 재생
|
|
- sequence 기반 손실/순서 역전 로그 출력
|
|
- Web UI를 통한 output mute, sender mute, sender gain, sender display name, 통계 reset
|
|
|
|
다음 작업은 클라이언트 송신 패킷을 서버 지정 헤더 v1에 맞추고, 실제 송신기에서 들어온 오디오가 실행 OS의 기본 출력 장치로 재생되는지 확인하는 것입니다. 그 다음 서버 작업은 단일 활성 송신기 제한을 풀고, 2대에서 16대까지 송신기별 버퍼와 믹싱을 검증하는 방향으로 진행합니다.
|