mic/docs/03-server-direction.md

11 KiB

서버 방향성

이 문서는 macOS, Ubuntu, Rocky Linux 9, Windows WSL2에서 실행할 수신기/믹서 서버의 초기 방향을 정리합니다.

현재 단계의 목표는 복잡한 믹서 기능을 만드는 것이 아니라, Orange Pi Zero 2W 클라이언트에서 보낸 오디오가 서버 실행 장비에서 실제로 들리는 최소 경로를 확인하고, 이후 2대에서 16대까지 확장할 수 있는 기반을 잡는 것입니다.

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는 초기 성공 이후에 다룹니다.

목표 송신기 규모

현재 목표 규모는 다음과 같습니다.

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 기반으로 진행합니다.

포트는 다음 값을 초기 후보로 둡니다.

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 지원은 초기 범위에 포함하지 않습니다.

공식 참고 링크:

오디오 출력 방향

초기 오디오 출력은 여러 OS에서 C++로 접근 가능한 라이브러리를 후보로 검토합니다.

후보는 다음과 같습니다.

  • miniaudio: 단일 파일에 가까운 경량 오디오 라이브러리입니다. macOS와 Linux의 기본 오디오 백엔드를 지원하고 Homebrew로 설치할 수 있어 초기 우선 후보로 둡니다.
  • Core Audio: macOS 기본 오디오 API입니다. 추가 런타임 의존성이 적지만 초기 구현이 다소 길어질 수 있습니다.
  • PortAudio: 크로스 플랫폼 오디오 입출력 라이브러리입니다. 예제가 많고 초기 재생 경로를 빠르게 만들 수 있습니다.
  • RtAudio: C++ 인터페이스가 단순한 오디오 입출력 라이브러리입니다.

첫 구현에서는 안정적인 최소 재생 경로를 우선합니다. 현재 우선 후보는 miniaudio입니다.

초기 오디오 포맷 후보

클라이언트 방향성과 맞춰 초기 포맷 후보는 다음과 같습니다.

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 후보:

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 오디오에서는 패킷 도착 간격이 흔들릴 수 있습니다.

초기 버전은 아주 단순한 짧은 재생 버퍼를 후보로 둡니다.

  • 너무 짧으면 끊김이 늘어납니다.
  • 너무 길면 지연이 커집니다.
  • 첫 목표는 지연 시간 최적화가 아니라 소리 경로 확인입니다.

구체적인 버퍼 길이는 구현 전 테스트 환경과 오디오 라이브러리 선택에 맞춰 정합니다.

설정 방향

초기 서버 설정 후보는 다음과 같습니다.

listen_port = 4860
sample_rate = 48000
channels = 1
frame_ms = 10
audio_output = default

송신기별 음량 조절 설정은 현재 범위에서 제외합니다. 초기 목표는 수신한 단일 송신기 오디오를 노트북 스피커로 출력하는 것이고, 다음 목표는 2대 이상 송신기의 동시 수신과 믹싱입니다.

설정 파일 형식은 단순 key-value 텍스트 형식을 후보로 둡니다. 자동 검색을 추가하더라도 고정 포트와 고정 설정 방식은 유지합니다.

검증 순서

초기 검증은 다음 순서를 따릅니다.

  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 바인딩
  • 서버 지정 56바이트 패킷 헤더 v1 파싱
  • 단일 활성 sender_idPCM_S16LE / mono / 48 kHz / 10 ms payload 재생
  • sequence 기반 손실/순서 역전 로그 출력

다음 작업은 클라이언트 송신 패킷을 서버 지정 헤더 v1에 맞추고, 실제 송신기에서 들어온 오디오가 실행 OS의 기본 출력 장치로 재생되는지 확인하는 것입니다. 그 다음 서버 작업은 단일 활성 송신기 제한을 풀고, 2대에서 16대까지 송신기별 버퍼와 믹싱을 검증하는 방향으로 진행합니다.