How to cross-compile GStreamer for embedded systems
Written by:
Diego NietoAug. 20, 2024
Cross-compiling GStreamer can be worthwhile when working on embedded systems, especially if you need to compile it multiple times. That was the case of our work with NVIDIA Jetson TX2 for an internal project, and that's why we decided to share our findings with everyone interested in this process.
In this guide, we'll walk you through the process of cross-compiling GStreamer, highlighting key considerations such as choosing the right toolchain and configuring the meson build system.
When should we cross-compile GStreamer?
Cross-compiling can be time-consuming to set up, but it offers significant time savings when compiling large systems like GStreamer.
Here, we will focus on cross-compiling GStreamer using meson build statements, though other solutions like Yocto can also be used to cross-compile entire systems to generate a full production image.
Choosing the right toolchain
Selecting the correct toolchain is crucial, since an incorrect toolchain can lead to numerous issues and wasted time. In our case, we used the official NVIDIA Jetson Jetpack image. You can verify the version of Jetpack installed on your system with the following command:
fluendo@jetsonTX2:~/dnieto$ sudo apt-cache show nvidia-jetpack
Package: nvidia-jetpack
Version: 4.6.5-b29
Architecture: arm64
Maintainer: NVIDIA Corporation
Installed-Size: 194
Depends: nvidia-l4t-jetson-multimedia-api (>> 32.7-0), nvidia-l4t-jetson-multimedia-api (<< 32.8-0), nvidia-cuda (= 4.6.5-b29), nvidia-tensorrt (= 4.6.5-b29), nvidia-nsight-sys (= 4.6.5-b29), nvidia-cudnn8 (= 4.6.5-b29), nvidia-opencv (= 4.6.5-b29), nvidia-container ( = 4.6.5-b29), nvidia-visionworks (= 4.6.5-b29), nvidia-vpi (= 4.6.5-b29)
Homepage: http://developer.nvidia.com/jetson
Priority: standard
Section: metapackages
Filename: pool/main/n/nvidia-jetpack/nvidia-jetpack_4.6.5-b29_arm64.deb
Size: 29382
SHA256: 3347532325b9e216c38274bbbc076d6f368fdd0423bad4de8860a7c2ac7da7c2
SHA1: ad441acc5493fbcbae4f3243193bf6e47088da54
MD5sum: 1d79a14bc5b01c0254b0fc45ec29e5db
Description: NVIDIA Jetpack Meta Package
Description-md5: ad1462289bdbc54909ae109d1d32c0a8
Or just showing the release file:
fluendo@jetsonTX2:~/dnieto$ cat /etc/nv_tegra_release
# R32 (release), REVISION: 7.4, GCID: 33514132, BOARD: t186ref, EABI: aarch64, DATE: Fri Jun 9 04:18:38 UTC 2023
With this data, we are ready to find our toolchain in the corresponding NVIDIA release version, in our case, the right one is this one.
In the section “Tools,” you will find a link to the toolchain, in our case, “GCC 7.3.1 for 64-bit BSP and Kernel.” After extracting the package contents, we are ready to set up our build system.
Set up the build system with Meson
Meson build simplifies the process of creating a clean, reusable build system for cross-compiling projects. This is based on the definition of cross-compilation files, which provides the necessary data for the compiler to cross-compile the solution properly.
The following is the cross-compile file which defines the basic elements needed to build the solution. Meson build defines several sections, among them:
- Constants: Some attributes that will be used later are defined here. This section is always read first.
- Host_machine: We can often ignore the target machine definition because the host and target are the same. So, if our case is the same, we can define our host architecture by adding a few attributes: system, cpu_family, CPU, and endianness.
- Binaries: Specifies the location of the binaries needed to cross-compile for the host machine, such as the compiler itself and the other different compile tools.
[constants]
toolchain_dir = '@PATH@'
toolchain_bin = toolchain_dir / 'bin'
[host_machine]
system = 'linux'
cpu_family = 'aarch64'
cpu = 'arm64'
endian = 'little'
[binaries]
c = toolchain_bin / 'aarch64-linux-gnu-gcc'
cpp = toolchain_bin / 'aarch64-linux-gnu-g++'
ar = toolchain_bin / 'aarch64-linux-gnu-ar'
strip = toolchain_bin / 'aarch64-linux-gnu-strip'
rust_ld = toolchain_bin / 'aarch64-linux-gnu-gcc'
rust = ['rustc', '--target', 'aarch64-unknown-linux-gnu']
Above, we are pointing the binaries path to the correct location of the previously downloaded toolchain. Here you can check the full meson build guide, where it is also possible to define the sys_root path if your projects require it. Another interesting feature is that meson provides introspection, allowing you to behave depending on whether you are cross-compiling or not. The following functions could help us to enable or disable some subprojects dynamically when doing the setup process.
meson.is_cross_build() # returns true when cross compiling
meson.can_run_host_binaries() # returns true if the host binaries can be run, either with a wrapper or natively
We might have some dependencies we have to fulfill to cross-compile, e.g., OpenSSL. For those cases, generating dynamic pkg-config configuration through meson build is possible. This meson feature enables us to make this package visible by either loading a dependency or declaring it.
pkgconfig = import('pkgconfig')
openssl_dep = dependency('openssl', version: '1.1.1l')
openssl_lib = shared_library('openssl', dependencies: openssl_dep)
pkgconfig.generate(openssl_lib)
Once we have our cross-compile file, e.g., “nvidia_jetsontx2.cross”, we can run a meson setup statement to build our GStreamer solution:
builddir=builddir
prefix=${PWD}/prefix
buildtype=debug
meson setup ${builddir} \
--prefix ${prefix} \
--buildtype ${buildtype} \
--cross-file nvidia_jetsontx2.cross \
-Dgst-plugins-bad:va=disabled \
-Dugly=disabled \
-Dges=disabled \
-Drtsp_server=disabled \
-Dlibav=disabled \
-Ddevtools=disabled
Notice that the only difference is in the argument “--cross-file,” which points to the path to the file we defined earlier.
After this step, we can launch ninja build as usual to build GStreamer:
ninja -C builddir
Build time comparison
Coming back to the reason we are cross-compiling, we must showcase why it’s worth it. A simple comparison measuring the wall time clock when compiling in both systems gives us the answer: We get a speed up of around three times.
Jetson TX2 compilation
[6423/6423] Linking target subprojects/glib-networking-2.74.0/tls/tests/connection-openssl
real 12m2.542s
user 39m17.896s
sys 6m26.024s
Cross-compilation in an Intel i7-1165G7
[6652/6652] Linking target subprojects/glib-networking-2.74.0/tls/tests/connection-openssl
1213.57user 200.94system 3:40.09elapsed 642%CPU (0avgtext+0avgdata 826476maxresident)k
500632inputs+4273632outputs (1002major+54532611minor)pagefaults 0swaps
So, based on the same setup, we generate the same plugins around three times faster. If you compile the whole GStreamer solution frequently, this is clearly a reason to do it this way.
Do you want to take your business to the next level? Contact our experts here.