Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

Ruby FFI 모듈 완벽 가이드: VLC 라이브러리로 MP3 음악 재생하기

Ruby 개발자라면 한 번쯤 이런 궁금증이 생기곤 합니다.

Ruby에서 FFI란 무엇일까요?

FFI는 "Foreign Function Interface(외부 함수 인터페이스)"의 약자입니다.

쉽게 말해 다른 프로그래밍 언어로 작성된 함수를 사용할 수 있게 해주는 통로입니다.

Ruby의 FFI 모듈을 활용하면 평소에는 접근하기 어려운 외부 라이브러리와 코드를 자유롭게 사용할 수 있습니다.

특히 C 코드와 함께 작업할 때 널리 활용됩니다.

FFI를 사용하는 대표적인 젬(gem)은 다음과 같습니다:

  • rb-inotify — 파일 시스템 이벤트 감지
  • ruby-vips — 고속 이미지 처리
  • ruby-nfc — NFC 태그 읽기

물론 FFI 모듈의 도움을 받아 직접 FFI 기반 프로그램을 만들 수도 있습니다.

어떻게 할 수 있을까요?

지금부터 몇 가지 코드 예제를 통해 차근차근 살펴보겠습니다!

FFI 기본기: 라이브러리 로딩과 함수 임포트

FFI는 직접 생성한 모듈 안에 FFI의 기능들을 포함시키는 방식으로 사용합니다.

다음과 같이 말이죠:

require 'ffi'

module A
  extend FFI::Library
end

그다음, 모듈 내부에서 ffi_lib 메서드를 사용해 라이브러리를 불러옵니다.

C 라이브러리는 Windows의 "DLL"이나 Linux의 "SO" 파일처럼, 프로그램이 외부에 공개하는 함수 정의 목록, 즉 "심볼(symbol)"을 내보냅니다.

여기서는 C의 표준 라이브러리인 "libc"를 불러와 보겠습니다.

방법은 다음과 같습니다:

module A
  extend FFI::Library
  ffi_lib 'c'
end

이제:

attach_function 메서드를 사용하면 이 라이브러리의 특정 함수를 Ruby 메서드처럼 가져올 수 있습니다.

예제를 보겠습니다:

module A
  extend FFI::Library
  ffi_lib 'c'

  attach_function :strlen, [:string], :int
end

A.strlen("abc")
# 3

이 코드는 어떻게 동작할까요?

먼저, C에는 strlen이라는 함수가 있고, 이 함수는 char * 타입의 인자를 받습니다.

char *는 Ruby에서 String에 해당합니다.

그리고…

strlen 함수는 문자열의 길이를 반환값으로 돌려줍니다.

정리하면, attach_function은 3개의 매개변수를 받습니다:

  1. C 함수의 이름
  2. 인자 타입들의 배열
  3. 반환 타입

각 함수가 어떤 타입을 사용하는지는 해당 라이브러리의 문서를 참고해서 확인해야 합니다.

이제 다른 예제로 넘어가 볼까요?

나만의 미디어 플레이어 만들기

FFI로 활용할 수 있는 흥미로운 라이브러리는 무궁무진합니다.

Linux에서는 대부분 /usr/lib/ 디렉터리 아래에 있으며, 이름이 "lib"으로 시작하고 확장자가 "so"로 끝납니다.

다음 명령어로 목록을 확인할 수 있습니다:

ls /usr/lib/lib*.so

이번 예제에서는 VLC의 미디어 플레이어 라이브러리를 사용해 보겠습니다:

/usr/lib/libvlc.so

목표는 MP3 파일을 불러와서 재생하는 것입니다!

먼저 여기서부터 시작해 봅시다:

require 'ffi'

module VLC
  extend FFI::Library
  ffi_lib 'vlc'

  attach_function :libvlc_get_version, [], :string
end

이렇게 하면 라이브러리가 제대로 로드되었는지 테스트하고 버전을 출력해 볼 수 있습니다.

실행 결과:

VLC.libvlc_get_version
# "3.0.6 Vetinari"

네 번째 매개변수를 추가하면 메서드에 원하는 이름을 붙일 수도 있습니다.

예를 들면:

attach_function :get_version, :libvlc_get_version, [], :string

이제 이렇게 호출할 수 있습니다:

VLC.get_version

그럼 MP3를 재생하려면 무엇이 필요할까요?

문서를 살펴보면 libvlc_media_player_play 함수가 바로 그 역할을 하는 것 같습니다.

이 함수는 미디어 플레이어 "객체"가 필요합니다.

미디어 플레이어는 미디어를 필요로 하고, 미디어는 다시 VLC 인스턴스를 필요로 합니다.

즉, 전체 과정은 다음과 같습니다:

VLC instance -> media -> media_player -> play

이 구조는 libVLC 공식 문서를 읽으며 파악한 것입니다.

Ruby에서 메모리 포인터를 다룬다고?

C 언어에는 객체라는 개념이 없습니다. 모든 것은 메모리 포인터로 처리됩니다.

메모리 포인터란 데이터가 담겨 있는 메모리 주소를 의미합니다.

코드 예제를 통해 확인해 보겠습니다:

module VLC
  extend FFI::Library
  ffi_lib 'vlc'

  attach_function :get_version, :libvlc_get_version, [], :string
  attach_function :new, :libvlc_new, [:int, :int], :pointer
end

VLC.new(0, 0)
# FFI::Pointer address=0x000055c6f04ae7b0

VLC.new를 호출하면 포인터가 반환됩니다.

이 포인터는 이후 포인터를 필요로 하는 다른 C 함수들에 계속 전달해야 합니다.

자, 이제:

전체 예제와 실제 사용 방법을 살펴보겠습니다.

module VLC
  extend FFI::Library
  ffi_lib 'vlc'

  attach_function :version, :libvlc_get_version, [], :string

  attach_function :new, :libvlc_new, [:int, :int], :pointer
  attach_function :libvlc_media_new_path, [:pointer, :string], :pointer
  attach_function :libvlc_media_player_new_from_media, [:pointer], :pointer

  attach_function :play, :libvlc_media_player_play, [:pointer], :int
  attach_function :stop, :libvlc_media_player_stop, [:pointer], :int
  attach_function :pause, :libvlc_media_player_pause, [:pointer], :int
end

이 정도면 음악을 재생할 준비가 끝났습니다:

vlc    = VLC.new(0, 0)
media  = VLC.libvlc_media_new_path(vlc, "/home/jesus/Downloads/meditation.mp3")
player = VLC.libvlc_media_player_new_from_media(media)

VLC.play(player)

이 코드는 어떤 시각적 인터페이스도 열지 않고 음악을 재생합니다.

여기에 웹 인터페이스를 얹으면 나만의 음악 플레이어 서비스를 만들 수도 있습니다.

직접 한번 도전해 보세요!

마치며

지금까지 FFI의 놀라운 힘에 대해 알아보았습니다! 마치 Ruby 언어 자체의 일부인 것처럼 외부 라이브러리를 자연스럽게 사용할 수 있게 해주는 강력한 모듈입니다.

더 많은 사람들이 이 글을 즐길 수 있도록 공유 부탁드립니다 🙂

읽어주셔서 감사합니다!