I 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 real time with hardware encoding. Streaming MPEG2TS is also possible with Ethernet. However, FFmpeg version shipped with Raspbian does not retain metadata required for maintaining aspect ratio when using OpenMAX hardware encoder/decoder (h264_omx
). This can be fixed by patching FFmpeg and recompiling from source.
This article is my notes on cross-building .deb
for armhf
Raspbian from an 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 builds 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 requires 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 recommended 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 the 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
needs 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 a package index (Package.gz
):
$ cd /opt/apt
$ sudo sh -c 'dpkg-scanpackages -m . | gzip -c > Packages.gz'
Add the repository to the apt sources list:
$ cat <<EOF | sudo tee /etc/apt/sources.list.d/local.list
deb [trusted=yes] file:///opt/apt ./
EOF
Optionally, make the local repo the 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.3 (Dec 14, 2024)
- Typo and grammar fixes
- Rev 1.2 (May 30, 2021)
- Code formatting
- Rev 1.1 (Mar 25, 2021)
- Fix typo
- Rev 1 (Oct 10, 2020)
- Publish
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 real-time encoding on Raspberry Pi 4.↩︎