Cross-Compile Packages for Raspbian from x86_64
by SirnI have been building a DVB streaming server with Raspberry Pi 4 in the past couple of weeks.1 The setup turns out to work pretty well as Raspberry Pi 4 can render 720p in realtime with hardware encoding. Streaming MPEG2TS is also possible with Ethernet. However, FFmpeg version shipped with Raspbian do not retain a metadata required for maintaining aspect ratio when using OpenMAX hardware encoder/decoder (h264_omx
). This can be fixed by patching FFmpeg and recompile from source.
This article is my notes on cross-building .deb
for armhf
Raspbian from x86_64
Debian machine.2 Instructions are taken from the following blog entries:
Setting up Pbuilder on Debian
pbuilder is an automatic Debian package builder, which build packages inside a clean-room environment based on debootstrap. To get started, install pbuilder and its dependencies:
$ sudo apt install pbuilder qemu-user-static curl
$ sudo apt install ubuntu-keyring debian-archive-keyring
$ curl -O http://archive.raspbian.org/raspbian/pool/main/r/raspbian-archive-keyring/raspbian-archive-keyring_20120528.2_all.deb
$ dpkg -i raspbian-archive-keyring_20120528.2_all.deb
pbuilder is using .pbuilderrc
to bootstrap the environment and require root to run in its default operation mode. Create /root/.pbuilderrc
with the following content. This is taken from Stein Magnus Jodal's article (see link above), but with changes such as PBUILDERSATISFYDEPENDSCMD
so statisfydepends
works under armhf
:
#!/bin/bash
set -e
if [ "$OS" == "debian" ]; then
MIRRORSITE="http://ftp.jp.debian.org/debian/"
COMPONENTS="main contrib non-free"
DEBOOTSTRAPOPTS=("${DEBOOTSTRAPOPTS[@]}" "--keyring=/usr/share/keyrings/debian-archive-keyring.gpg")
: ${DIST:="buster"}
: ${ARCH:="amd64"}
elif [ "$OS" == "raspbian" ]; then
MIRRORSITE="https://archive.raspbian.org/raspbian/"
COMPONENTS="main contrib non-free rpi"
DEBOOTSTRAPOPTS=("${DEBOOTSTRAPOPTS[@]}" "--keyring=/usr/share/keyrings/raspbian-archive-keyring.gpg")
PBUILDERSATISFYDEPENDSCMD="/usr/lib/pbuilder/pbuilder-satisfydepends-apt"
: ${DIST:="buster"}
: ${ARCH:="armhf"}
elif [ "$OS" == "ubuntu" ]; then
MIRRORSITE="http://jp.archive.ubuntu.com/ubuntu/"
COMPONENTS="main restricted universe multiverse"
DEBOOTSTRAPOPTS=("${DEBOOTSTRAPOPTS[@]}" "--keyring=/usr/share/keyrings/ubuntu-archive-keyring.gpg")
else
echo "Unknown OS: $OS"
exit 1
fi
if [ "$DIST" == "" ]; then
echo "DIST is not set"
exit 1
fi
if [ "$ARCH" == "" ]; then
echo "ARCH is not set"
exit 1
fi
NAME="$OS-$DIST-$ARCH"
if [ "$ARCH" == "armel" ] && [ "$(dpkg --print-architecture)" != "armel" ]; then
DEBOOTSTRAP="qemu-debootstrap"
fi
if [ "$ARCH" == "armhf" ] && [ "$(dpkg --print-architecture)" != "armhf" ]; then
DEBOOTSTRAP="qemu-debootstrap"
fi
DEBOOTSTRAPOPTS=("${DEBOOTSTRAPOPTS[@]}" "--arch=$ARCH")
BASETGZ="/var/cache/pbuilder/$NAME-base.tgz"
DISTRIBUTION="$DIST"
BUILDRESULT="/var/cache/pbuilder/$NAME/result/"
APTCACHE="/var/cache/pbuilder/$NAME/aptcache/"
BUILDPLACE="/var/cache/pbuilder/build"
HOOKDIR="/var/cache/pbuilder/hook.d/"
Create pbuilder base image for Raspbian:
$ sudo mkdir -p /var/cache/pbuilder/raspbian-buster-armhf/aptcache/
$ sudo OS=raspbian DIST=buster ARCH=armhf pbuilder --create
The repository we specified in pbuilderrc
does not include some Raspberry Pi's override packages. We must enter the chroot environment to add them:
$ sudo OS=raspbian DIST=buster ARCH=armhf pbuilder login --save-after-login
Inside chroot:
# apt update
# apt install curl
# curl http://archive.raspberrypi.org/debian/raspberrypi.gpg.key | apt-key add -
# cat <<EOF > /etc/apt/sources.list.d/raspi.list
deb http://archive.raspberrypi.org/debian/ buster main
EOF
# apt update
Check if repository were added:
# apt-cache policy
Package files:
100 /var/lib/dpkg/status
release a=now
500 http://archive.raspberrypi.org/debian buster/main armhf Packages
release o=Raspberry Pi Foundation,a=testing,n=buster,l=Raspberry Pi Foundation,c=main,b=armhf
origin archive.raspberrypi.org
500 http://raspbian.raspberrypi.org/raspbian buster/rpi armhf Packages
release o=Raspbian,a=stable,n=buster,l=Raspbian,c=rpi,b=armhf
origin raspbian.raspberrypi.org
500 http://raspbian.raspberrypi.org/raspbian buster/non-free armhf Packages
release o=Raspbian,a=stable,n=buster,l=Raspbian,c=non-free,b=armhf
origin raspbian.raspberrypi.org
500 http://raspbian.raspberrypi.org/raspbian buster/contrib armhf Packages
release o=Raspbian,a=stable,n=buster,l=Raspbian,c=contrib,b=armhf
origin raspbian.raspberrypi.org
500 http://raspbian.raspberrypi.org/raspbian buster/main armhf Packages
release o=Raspbian,a=stable,n=buster,l=Raspbian,c=main,b=armhf
origin raspbian.raspberrypi.org
Pinned packages:
We're done with the base image. Exit chroot with exit
.
Fetching Source & Patching
The easiest way to fetch Debian source is to use dget
which is part of devscripts
. get
expects dsc
file as an input which can be found in repository archive alongside with .deb
files:
$ sudo apt install devscripts
$ mkdir src/ && cd src/
$ dget -u http://archive.raspberrypi.org/debian/pool/main/f/ffmpeg/ffmpeg_4.1.6-1~deb10u1+rpt1.dsc
Now we have source code extracted, we can start applying patches. In Debian it is recommened to use quilt
to manage patches:
$ sudo apt install quilt
Setup quilt for project:
$ cd ffmpeg-4.1.6
$ export QUILT_PATCHES=debian/patches
Make sure the patch is latest:
$ quilt push -a
Create a new patch and tell quilt to track the file we want to change (e.g. in this case libavcodec/omx.c
). quilt add
need to be done before making any changes to source code, once for every file:
$ quilt new fix-omx-aspect-ratio.patch
$ quilt add libavcodec/omx.c
Apply and save patch:
$ curl -sSL https://github.com/gameboym/FFmpeg/commit/7e728faef468cc7330fec1ee259d53147602c8af.patch | patch -n1
$ quilt refresh
Building
Now we have pbuilder setup and source code patched, we can start building the package. Stay in the source directory (same as debian/
directory) and run:
$ sudo OS=raspbian DIST=buster ARCH=armhf DEB_BUILD_OPTIONS="parallel=$(nproc)" pdebuild
# Alternatively, without running `make check` afterward:
$ sudo OS=raspbian DIST=buster ARCH=armhf DEB_BUILD_OPTIONS="parallel=$(nproc) nocheck" pdebuild
After a while pdebuild
should build a .deb
for us in /var/cache/pbuilder/raspbian-buster-armhf/result/
.
Creating Local Repo & Installation
Copy the files in /var/cache/pbuilder/raspbian-buster-armhf/result
to Raspberry Pi 4 (for example, using rsync
) and run the following command to create package index (Package.gz
):
$ cd /opt/apt
$ sudo sh -c 'dpkg-scanpackages -m . | gzip -c > Packages.gz'
Add the repository to apt sources list:
$ cat <<EOF | sudo tee /etc/apt/sources.list.d/local.list
deb [trusted=yes] file:///opt/apt ./
EOF
Optionally, make local repo highest priority:
$ cat <<EOF | sudo tee /etc/apt/preferences.d/local
Package: *
Pin: origin ""
Pin-Priority: 1001
EOF
Update the repository and install our package:
$ sudo apt update
$ sudo apt install ffmpeg
And that's it!
Changes
- Rev 1.2 (May 30, 2021)
- Code formatting
- Rev 1.1 (Mar 25, 2021)
- Fix typo
- Rev 1 (Oct 10, 2020)
- Publish
Footnote
- So I can watch TV on my computer/phone rather than in front of a TV; also doubling as a DIY recorder. My setup mostly followed the article in Chinachu's Medium (in Japanese). ↩
- Although I prefer Void Linux or Alpine Linux,
h264_omx
is only available on 32-bit armhf Raspbian. Usingh264_omx
is crucial in getting a realtime encoding on Raspberry Pi 4. ↩