Agora Server Gateway SDK의 encoded frame observer는 원본이 아닌 재인코딩된 Opus를 전달한다
·수정 2026.04.27·수정 1회
요약
audio_recv_encoded_frame=1+ encoded frame observer를 등록하면 Opus 프레임을 콜백으로 받을 수 있지만, 이것은 네트워크 원본이 아닌 SDK 내부에서 디코딩 후 재인코딩된 Opus다- 바이트 단위 비교, 암호화 확인, 프레임 듀레이션 불일치(10ms→20ms)로 확정
- Opus 패스스루가 필요하면 Agora Cloud Recording의 비트랜스코딩 모드(
streamMode: "original")가 유일한 경로
본문
검증 과정
1단계: ctypes 바인딩으로 encoded frame 수신 성공
Python SDK의 register_audio_encoded_frame_observer가 미구현(#todo)이어서, C 네이티브 함수를 직접 ctypes로 바인딩했다.
agora_audio_encoded_frame_rev_observer_create
agora_remote_audio_track_register_encoded_frame_rev_observer
등록 성공(ret=0), 콜백 호출 확인. 수신 프레임은 유효한 Opus로 ffmpeg 디코딩 가능.
2단계: 패스스루 vs 재인코딩 검증
| 검증 | 결과 | 의미 |
|---|---|---|
| 바이트 비교 (콜백 프레임 vs 네트워크 패킷) | 6바이트 서브스트링 매치 0건 | 동일 데이터 아님 |
| 네트워크 payload 엔트로피 | ~6.5 bits/byte | 암호화됨 — 원본 추출 불가 |
| 프레임 듀레이션 | 네트워크 10ms, 콜백 20ms | SDK가 2개를 합쳐서 재인코딩 |
| 프레임 크기 | 네트워크 평균 262B, 콜백 평균 70B | 크기 불일치 |
3단계: C++ 헤더로 API 동작 확인
NGIAgoraAudioTrack.h의 AudioEncFrameRecvParams에 ORIGINAL 모드가 있지만, C API wrapper는 이 파라미터를 받지 않는다. 또한 SDK 내부에 AudioPktConverter 파이프라인이 항상 작동하여 복호화→디코딩→재인코딩을 수행한다.
SDK 내부 파이프라인
Network(Opus, 암호화, 10ms)
↓ SDK 복호화
↓ 지터 버퍼 (NetEq) + FEC 복구
↓ Opus 디코딩 → PCM
↓ 오디오 프로세싱 (AEC, 노이즈 제거 등)
↓ Opus 재인코딩 (20ms)
↓ 콜백 전달
콜백 시그니처 (C 헤더 기준 정확한 정의)
// receive용 struct (send용과 다름!)
typedef struct _encoded_audio_frame_rev_info {
uint64_t sendTs; // 송신 타임스탬프
uint8_t codec; // 코덱 (1=Opus)
} encoded_audio_frame_rev_info;
// observer struct (콜백 1개)
typedef struct _audio_encoded_frame_rev_observer {
int (*on_encoded_audio_frame_received)(
AGORA_HANDLE observer_handle,
const uint8_t* packet,
size_t length,
const encoded_audio_frame_rev_info* info
);
} audio_encoded_frame_rev_observer;
대안
| 솔루션 | Opus 원본 보존 |
|---|---|
| Server Gateway SDK (우리) | 불가 — 재인코딩 |
Cloud Recording (비트랜스코딩, streamMode: "original") |
가능성 높음 |
| On-Premise Recording SDK | 불가 — AAC/PCM만 |