Rust bindings for GStreamerGL: Memoirs
Rust is a great programming language but the community around it’s just amazing. Those are the ingredients for the craft of useful software tools, just like Servo, an experimental browser engine designed for tasks isolation and high parallelization.Both projects, Rust and Servo, are funded by Mozilla.
Thanks to Mozilla and Igalia I have the opportunity to work on Servo, adding it HTML5 multimedia features.
First, with the help of Fernando Jiménez, we
finished what my colleague Philippe Normand and
Sebastian Dröge (one of my programming heroes)
started: a media player in Rust designed to be integrated in Servo. This media
player lives in its own crate: servo/media
along with the WebAudio engine. A crate, in Rust jargon, is like a library.
This crate is (very ad-hocly) designed to be multimedia framework agnostic,
but the only backend right now is for
GStreamer. Later we integrated it into
Servo adding an initial support
for audio
and video
tags.
Currently, servo/media
passes, through a IPC channel, the array with the whole
frame to render in Servo. This implies, at least, one copy of the frame in
memory, and we would like to avoid it.
For painting and compositing the web content, Servo uses WebRender, a crate designed to use the GPU intensively. Thus, if instead of raw frame data we pass OpenGL textures to WebRender the performance could be enhanced notoriously.
Luckily, GStreamer already supports the uploading, downloading, painting and composition of video frames as OpenGL textures with the OpenGL plugin and its OpenGL Integration library. Even more, with plugins such as GStreamer-VAAPI, Gst-OMX (OpenMAX), and others, it’s possible to process video without using the main CPU or its mapped memory in different platforms.
But from what’s available in GStreamer to what it’s available in Rust there’s a distance. Nonetheless, Sebastian has putting a lot of effort in the Rust bindings for GStreamer, either for applications and plugins, sadly, GStreamer’s OpenGL Integration library (GstGL for short) wasn’t available at that time. So I rolled up my sleeves and got to work on the bindings.
These are the stories of that work.
As GStreamer shares with GTK+ the
GObject
framework and its introspection
mechanism, both projects have collaborated on the required infrastructure to
support Rust bindings. Thanks to all the GNOME folks who are working on the
intercommunication between Rust and GObject. The quest has been long and
complex, since Rust doesn’t map all the object oriented concepts, and GObject,
being a set of practices and software helpers to do object oriented programming
with C
, its usage is not homogeneous.
The Rubicon that ease the generation of Rust bindings for GObject-based projects
is GIR, a tool, written in Rust, that reads
gir
files, along with metadata in toml
, and outputs two types of bindings:
sys and api.
Rust can call external functions through
FFI (foreign function
interface), which is just a declaration of a C
function with Rust types. But
these functions are considered unsafe. The sys bindings, are just the
exporting of the C function for the library organized by the library’s
namespace.
The next step is to create a safe and rustified API. This is the api bindings.
As we said, GObject libraries are quite homogeneous, and even following the introspection annotations, there will be cases where GIR won’t be able to generate the correct bindings. For that reason GIR is constantly evolving, looking for a common way to solve the corner cases that exist in every GObject project. For example, these are my patches in order to generate the GstGL bindings.
The done tasks were:
For this document we assume that the reader has a functional Rust setup and they know the basic concepts.
Clone and build gir
cd ~/ws
git clone https://github.com/gtk-rs/gir.git
cd gir
cargo build --release
the reason to build gir
in release mode is because, otherwise would be very
slow.
For sys bindings.
These kind of bindings are normally straight forward (and unsafe) since they
only map the C
API to Rust via FFI mechanism.
cd ~/ws
git clone https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys.git
cd gstreamer-rs-sys
cp /usr/share/gir-1.0/GstGL-1.0.gir gir-files/
- Verify if the gir file is more o less correct
- If there something strange, we should fix the code that generated it.
- If that is not possible, the last resource is to fix the gir file directly,
which is just XML, not manually but through a script using
xmlstartlet. See
fix.sh
in
gtk-rs
as example.
- Create the toml file with the metadata required to create the bindings. In
other words, this file contains the exceptions, rules and options used by the
tool to generated the bindings. See
Gir_GstGL.toml
in
gstreamer-rs-sys
as example. The documentation of the toml file is in the gir’s README.md file.
~/ws/gir/target/release/gir -c Gir_GstGL.toml
This command will generate, as specified in the toml file (target_path
), a
crate in the directory named gstreamer-gl-sys
.
Api bindings.
These type of bindings may require more manual work since their purpose is to offer a rustified API of the library, with all its syntactic sugar, semantics, and so on. But in general terms, the process is similar:
cd ~/ws
git clone https://gitlab.freedesktop.org/gstreamer/gstreamer-sys.git
cd gstreamer-sys
cp /usr/share/gir-1.0/GstGL-1.0.gir gir-files/
Again, it would be possible to end up applying fixes to the gir file through a
fix.sh
script using xmlstartlet
.
And again, the confection of the toml file might take a lot of time, by trial
and error, by cleaning and tidying up the API. See
Gir_GstGL.toml
in gstreamer-rs
as example.
~/ws/gir/target/release/gir -c Gir_GstGL.toml
A good way to test your bindings is by crafting a test application, which shows
how to use the API. Personally I devoted a ton of time in the test application
for
GstGL,
but worth it. It made me aware of a missing part in the crate used for GL
applications in Rust, named Glutin, which
was a way to get the used EGLDisplay
. So also worked on that and sent a pull
request that was recently merged.
The sweets of the free software development.
Nowadays I’m integrating GstGL API in servo/media
and later, Servo!