I want to answer one simple question…
What is FFI in Ruby?
FFI stands for “Foreign Function Interface”.
It’s a way to use functions defined in other programming languages.
Ruby’s FFI module gives you access to external libraries & code that you wouldn’t have otherwise.
We often use this to work with C code.
Examples include:
You can write your own programs that use FFI with the help of the FFI module.
How?
Let’s see a few code examples!
FFI Basics: Loading & Importing Functions
You can work with FFI by including its functions into a module you create.
Like this:
require 'ffi' module A extend FFI::Library end
Then, inside the module, we are going to load a library using the ffi_lib
method.
A C library, like a “DLL” in Windows, or “SO” in Linux, exports a list of “symbols”, or function definitions that a program makes available for others to use.
Let’s require “libc“, C’s standard library.
Here’s how:
module A extend FFI::Library ffi_lib 'c' end
Now:
You can import specific functions from this library as Ruby methods.
With the attach_function
method.
Here’s an example:
module A extend FFI::Library ffi_lib 'c' attach_function :strlen, [:string], :int end A.strlen("abc") # 3
How does this work?
First, we know that there is a function in C called strlen
, this function takes chars *
as an argument.
chars *
translates to String
in Ruby.
Then…
This strlen
function returns a value, which is the length of the string.
In other words, attach_function
takes 3 parameters:
- Name of the C function
- An array of argument types
- A return type
You’ll have to read the documentation to find out which types a particular function works with.
Time for another example!
Build Your Own Media Player
You can find many interesting libraries to play with.
In Linux, most are found under the /usr/lib/
directory, starting with “lib” in their name & ending with the “so” extension.
List them like this:
ls /usr/lib/lib*.so
For this example, we’re going to use VLC’s media player library:
/usr/lib/libvlc.so
Our goal is to load an mp3 file & play it!
Let’s start with this:
require 'ffi' module VLC extend FFI::Library ffi_lib 'vlc' attach_function :libvlc_get_version, [], :string end
This allows you to test if the library is loading correctly & then print its version.
Like this:
VLC.libvlc_get_version # "3.0.6 Vetinari"
You can give these methods a custom name by adding a 4th parameter.
Example:
attach_function :get_version, :libvlc_get_version, [], :string
Then you can do:
VLC.get_version
What do we need to play an mp3 now?
If we look at the documentation, the libvlc_media_player_play
function looks like the way to go.
This function needs a media player “object”.
A media player needs a media, and a media needs a VLC instance.
Here’s the process:
VLC instance -> media -> media_player -> play
I pieced this together by reading the documentation for libVLC
.
Memory Pointers in Ruby???
In C you’ll not find any objects, everything is handled with memory pointers.
A memory pointer is a memory address with data.
Let’s see a code example:
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
As a result of calling VLC.new
we get a pointer.
We have to pass it along to other C functions that need it.
Now:
The full example & how to use it.
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
That’s enough to play some music:
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)
This will play the music, without opening any kind of visual interface.
You could build your own web interface on top of this.
Give it a try!
Summary
You’ve learned about the amazing power of FFI
! A Ruby module that allows you to use external libraries as if they were part of the language itself.
Please share this article so more people can enjoy it 🙂
Thanks for reading!
Great article, Thanks!
Thanks for reading 🙂
Thanks for sharing, i couldnt find libvlc.so on my lib folder btw
Thanks for reading! You need the VLC media player installed on your computer 🙂
If you installed vlc via apt-get you can use ‘libvlc.so.5’. Probably there is no more ‘libvlc.so’
Thanks for great article!
Thanks for reading! 🙂
Is it possible to do it with Mac OS?
Yes, give it a try 🙂