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
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
$ 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
- 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-sysas 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
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
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!