Python ctypes로 C 네이티브 라이브러리의 미구현 함수를 바인딩할 수 있다
·수정 2026.04.27·수정 1회
요약
- Python SDK wrapper가 미구현(
#todo)인 C 함수도, 네이티브 라이브러리(.dylib/.so)에 심볼이 export되어 있으면 ctypes로 직접 바인딩하여 호출할 수 있다 - 콜백 함수는
ctypes.CFUNCTYPE으로 정의하고ctypes.Structure에 함수 포인터로 등록한다 - 시그니처(파라미터 타입/순서)를 틀리면 segfault 또는 가비지 값이 나오므로, C 헤더나 다른 언어 SDK(Go 등)에서 정확한 시그니처를 확인해야 한다
본문
패턴
SDK wrapper가 이런 상태일 때:
def register_audio_encoded_frame_observer(self, observer):
ret = -1000
#todo: need to implement
return ret
C 라이브러리에 심볼이 있다면 직접 바인딩 가능:
from agora.rtc._ctypes_handle._ctypes_data import agora_lib, AGORA_HANDLE
# 1. C 함수 바인딩
fn = agora_lib.agora_audio_encoded_frame_rev_observer_create
fn.restype = ctypes.c_void_p
fn.argtypes = [ctypes.POINTER(ObserverStruct)]
# 2. 콜백 타입 정의
CALLBACK = ctypes.CFUNCTYPE(ctypes.c_int, AGORA_HANDLE, ...)
# 3. 구조체에 콜백 등록
class ObserverStruct(ctypes.Structure):
_fields_ = [("on_callback", CALLBACK)]
시그니처 확인 방법 (우선순위)
- C 헤더 파일 — 가장 정확. Agora의 경우 GitHub 오픈소스 repos에서
agora_base.h,agora_media_base.h등 확인 - 다른 언어 SDK — Go SDK(
AgoraIO-Extensions/Agora-Golang-Server-SDK)의 CGo 바인딩이 C 시그니처를 그대로 반영 - 기존 SDK 코드의 유사 패턴 — 비디오 encoded frame observer 등 이미 구현된 유사 기능에서 유추
- 시행착오 — 가비지 값이 나오면 파라미터를 하나씩 조정. segfault 위험 있음
실제 사례: 콜백 시그니처 디버깅
처음 시도한 시그니처 (비디오 analog에서 유추):
CFUNCTYPE(c_int, AGORA_HANDLE, user_id_t, POINTER(c_uint8), c_size_t, POINTER(InfoStruct))
→ length=6164251312 가비지. 파라미터가 밀림.
AGORA_HANDLE 제거:
CFUNCTYPE(c_int, user_id_t, POINTER(c_uint8), c_size_t, POINTER(InfoStruct))
→ length=70 정상값. 하지만 user_id에 바이너리 가비지.
C 헤더에서 확인한 정확한 시그니처:
CFUNCTYPE(c_int, c_void_p, POINTER(c_uint8), c_size_t, POINTER(RevInfoStruct))
→ 첫 파라미터는 user_id가 아닌 observer handle(void*). user_id는 콜백에 안 옴.
주의사항
- SEND용 struct ≠ RECEIVE용 struct — 같은 이름처럼 보여도 필드 구성이 완전히 다를 수 있음 (Agora의
EncodedAudioFrameInfovsencoded_audio_frame_rev_info) - ctypes.Structure의
_fields_정의 전에 인스턴스를 만들면 안 됨 — 클래스 레벨에서_fields_가 확정되어야 함 - 콜백 함수 참조를 Python 객체로 유지해야 함 — GC가 수거하면 segfault
참고
Agora Server Gateway SDK의 encoded frame observer는 원본이 아닌 재인코딩된 Opus를 전달한다