요약
- 압축 코덱은 출력 크기를 지정받아 그 크기에 맞추므로, 입력 샘플링 레이트를 낮춰도 파일이 거의 그대로다. Opus 16 kbps에서 입력을 16 kHz → 8 kHz로 낮췄더니 크기는 6%만 줄었다.
- 샘플링 레이트와 파일 크기가 비례하는 건 압축하지 않는 PCM에서만이다.
- 대신 libopus는 입력 레이트를 코딩 대역의 천장으로 삼는다. 낮추면 아낀 건 없이 고역만 영구히 잃는다.
본문
두 값은 다른 단계에 있다
- 샘플링 레이트: 입력 해상도. 1초에 파형을 몇 번 측정하는가. 담을 수 있는 최고 주파수를 정한다(나이퀴스트)
- 비트레이트: 출력 예산. 1초 분량을 몇 비트로 적을 것인가
-b:a 16k는 "1초를 16,000비트로 요약하라"는 지시다. 원본을 48 kHz로 떴든 8 kHz로 떴든 인코더는 시킨 대로 16,000비트를 쓴다. 파일 크기를 정하는 건 지시값이지 입력이 아니다.
PCM에서만 비례한다
48,000회/초 × 16비트 = 768,000 bps
16,000회/초 × 16비트 = 256,000 bps
압축이 없으면 측정값을 그대로 적으므로 샘플링을 1/3로 줄이면 파일도 1/3이 된다. "샘플링 낮추면 용량 준다"는 이 경우에만 맞는 말인데, 목표 비트레이트를 받는 코덱에 그대로 적용하는 오해가 흔하다.
실측 (1:1 음성통화 녹음 8건, 44분)
| 설정 | 파일의 유효 비트레이트 |
|---|---|
| Opus 16 kbps / 입력 16 kHz | 8.9 kbps |
| Opus 16 kbps / 입력 8 kHz | 8.4 kbps |
6% 차이는 VBR이 담을 소리가 줄어 예산을 덜 쓴 잔돈이다. 절감이라 부를 수준이 아니다.
입력 레이트는 코딩 대역의 천장으로 작동한다
libopus src/opus_encoder.c:
/* Prevents Opus from wasting bits on frequencies that are above
the Nyquist rate of the input signal */
if (st->Fs <= 8000 && st->bandwidth > OPUS_BANDWIDTH_NARROWBAND)
st->bandwidth = OPUS_BANDWIDTH_NARROWBAND;
입력이 8 kHz면 비트레이트를 64 kbps로 올려도 narrowband(4 kHz)에 묶인다. 패킷 TOC 바이트를 직접 파싱해 확인한 결과다.
| 입력 | 비트레이트 | 실제 코딩된 대역 |
|---|---|---|
| 48 kHz | 8 kbps | narrowband 4 kHz |
| 48 kHz | 64 kbps | fullband 20 kHz |
| 8 kHz | 64 kbps | narrowband 4 kHz |
즉 인코더의 대역 자동 선택은 좁히는 방향으로만 작동한다. 48 kHz로 넣어두면 예산이 적을 때 알아서 좁혀주지만, 8 kHz로 넣으면 넓힐 방법이 없다. 그래서 입력 레이트는 "코덱이 고를 수 있는 상한"을 정하는 값이지 크기 손잡이가 아니다.
음성 구간의 대역별 에너지를 원본과 비교했을 때, 8 kHz 입력 변형만 3.4 kHz 위가 원본의 13~22%, 6 kHz 위는 1%로 무너졌다. 위 코드가 강제한 결과다.
컨테이너 표기에 속지 말 것
Ogg Opus는 granule position을 48 kHz 단위로 고정하므로(RFC 7845 §4) ffprobe는 무엇을 넣든 sample_rate=48000을 보고한다. 입력 레이트 지정이 무시된 게 아니라 표기가 고정된 것이다. 지정값은 OpusHead의 Input Sample Rate 필드에 참고용 메타데이터로 남고(재생 레이트가 아니라고 RFC가 명시), 실제 효력은 위의 천장으로 나타난다.
부연
- 대역 단계는 NB 4 kHz / MB 6 kHz / WB 8 kHz / SWB 12 kHz / FB 20 kHz의 5단계지만(RFC 6716 Table 1), libopus는 자동 선택에서 mediumband를 쓰지 않아 실제로 고르는 건 네 가지다
- 자동 선택의 판단 기준은 지정 비트레이트 그 자체가 아니라 채널 수·프레임 길이·VBR 여부·패킷 손실률을 반영한 등가 비트레이트이며, 음성/음악 추정과 히스테리시스가 함께 걸린다
- 이 동작은 RFC의 규범 조항이 아니라 참조 인코더의 기본값이고,
OPUS_SET_BANDWIDTH로 명시하면 자동 선택이 멈춘다
결론
용량을 줄이려면 -b:a를 조절한다. -ar은 대역 상한 설정이다. 음성이라면 -ar 16000으로 wideband까지 허용해두고 -b:a로 품질과 크기를 정하는 게 맞다.
관련 노트
- Opus VBR은 오디오 복잡도에 따라 bitrate를 실시간으로 조절한다
- 나이퀴스트 정리는 샘플링 주파수를 정하는 좋은 기준이 된다
- 오디오 파일은 컨테이너, 스트림, 패킷으로 구성된다.
- tcpdump 패킷 크기 분포로 오디오 코덱을 추정할 수 있다
참고
- RFC 6716 §2 Table 1 (대역폭 5단계), §2.1.3 (인코더의 대역 자동 선택): https://www.rfc-editor.org/rfc/rfc6716.html#section-2.1.3
- RFC 7845 §4 (granule position 48 kHz 고정), §5.1 (Input Sample Rate 필드): https://www.rfc-editor.org/rfc/rfc7845.html#section-5.1
- libopus v1.5.2
src/opus_encoder.c대역 자동 선택과 Nyquist 상한: https://gitlab.xiph.org/xiph/opus/-/blob/v1.5.2/src/opus_encoder.c#L1440 - Opus API
OPUS_SET_BANDWIDTH/OPUS_SET_MAX_BANDWIDTH: https://opus-codec.org/docs/opus_api-1.5/group__opus__encoderctls.html