Zettelkasten

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)]

시그니처 확인 방법 (우선순위)

  1. C 헤더 파일 — 가장 정확. Agora의 경우 GitHub 오픈소스 repos에서 agora_base.h, agora_media_base.h 등 확인
  2. 다른 언어 SDK — Go SDK(AgoraIO-Extensions/Agora-Golang-Server-SDK)의 CGo 바인딩이 C 시그니처를 그대로 반영
  3. 기존 SDK 코드의 유사 패턴 — 비디오 encoded frame observer 등 이미 구현된 유사 기능에서 유추
  4. 시행착오 — 가비지 값이 나오면 파라미터를 하나씩 조정. 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의 EncodedAudioFrameInfo vs encoded_audio_frame_rev_info)
  • ctypes.Structure의 _fields_ 정의 전에 인스턴스를 만들면 안 됨 — 클래스 레벨에서 _fields_가 확정되어야 함
  • 콜백 함수 참조를 Python 객체로 유지해야 함 — GC가 수거하면 segfault

참고

Agora Server Gateway SDK의 encoded frame observer는 원본이 아닌 재인코딩된 Opus를 전달한다