Skip to content

Commit

Permalink
Build uStreamer Debian package with Janus plugin (#6)
Browse files Browse the repository at this point in the history
Resolves #3

This PR builds a uStreamer Debian package (with Janus plugin) in CI. The
reason why we couldn't just use the [official(?) uStreamer Debian
package](https://salsa.debian.org/reedy/ustreamer/) is because it
doesn't compile the Janus plugin (which we need for WebRTC support).

Now we can avoid building uStreamer from source every time TinyPilot is
installed or updated.

You can test the uStreamer Debian package on a device via this [scratch
TinyPilot Pro build
bundle](https://app.circleci.com/pipelines/github/tiny-pilot/tinypilot-pro/2801/workflows/ba68bf47-f01c-4240-a008-6456f956dca4/jobs/20659/artifacts).

Notes:

* We compile the uStreamer Debian package for both [`armhf` and `amd64`
architectures](https://github.com/tiny-pilot/ustreamer-debian/pull/6/files#diff-78a8a19706dbd2a4425dd72bdab0502ed7a2cef16365ab7030a5a0588927bf47R33-R38)
because the `amd64` version will be used when testing the uStreamer
Ansible role in CI (using molecule) and the `armhf` version will be used
when installing TinyPilot on a device.

* FYI, compiling for multiple architectures produces Debian packages in
the following [directory
structure](https://github.com/tiny-pilot/ustreamer-debian/pull/6/files#diff-78a8a19706dbd2a4425dd72bdab0502ed7a2cef16365ab7030a5a0588927bf47R46-R49):
  * `/build/linux_arm_v7/*.deb`
  * `/build/linux_amd64/*.deb`
  
As opposed to just `/build/*.deb` when compiling for a single
architecture.

* Most of the [lintian
code](https://github.com/tiny-pilot/ustreamer-debian/pull/6/files#diff-78a8a19706dbd2a4425dd72bdab0502ed7a2cef16365ab7030a5a0588927bf47R52-R80)
is copy/pasted from [TinyPilot Community
repo](https://github.com/tiny-pilot/tinypilot/blob/b984ab93a58533220ad6358831ce8e405810db8f/.circleci/continue_config.yml#L97-L122),
besides the [loop to check multiple Debian
packages](https://github.com/tiny-pilot/ustreamer-debian/pull/6/files#diff-78a8a19706dbd2a4425dd72bdab0502ed7a2cef16365ab7030a5a0588927bf47R71-R80).

* Seeing as [uStreamer makes use of a simple `v${MAJOR}.${MINOR}`
versioning schema](https://github.com/tiny-pilot/ustreamer/tags), I used
that (without the `v` prefix) as the Debian package version with an
added a timestamp revision number to allow for the Debian package to be
updated even when the uStreamer version has stayed the same. The
resulting uStreamer Debian package versioning schema being
[`MAJOR.MINOR-YYYYMMDDhhmmss`](https://github.com/tiny-pilot/ustreamer-debian/pull/6/files#diff-dd2c0eb6ea5cfc6c4bd4eac30934e2d5746747af48fef6da689e85b752f39557R63)

* Seeing as Docker doesn't support dynamic `WORKDIR` values based on a
command's output, I stole these
([1](https://github.com/tiny-pilot/ustreamer-debian/pull/6/files#diff-dd2c0eb6ea5cfc6c4bd4eac30934e2d5746747af48fef6da689e85b752f39557R46-R70),
[2](https://github.com/tiny-pilot/ustreamer-debian/pull/6/files#diff-dd2c0eb6ea5cfc6c4bd4eac30934e2d5746747af48fef6da689e85b752f39557R140-R146))
clever workarounds from Michael's
[PR](https://github.com/tiny-pilot/tinypilot/pull/1352/files#diff-d3aed37eb2a4156ced425c7bcab79741d1234d58d182d91099b780c3c3136ce4R31-R53).

* The [copyright notice I've used
here](https://github.com/tiny-pilot/ustreamer-debian/pull/6/files#diff-eb1289be1c2cdfc905cecdba0c6810aa4d987d52e34f9dee681c1ff9cd69c34fR1-R2)
was given to me my Michael via email

* When we built uStreamer on the device, we technically [only ever ran
`make`](https://github.com/tiny-pilot/ansible-role-ustreamer/blob/b1017f8f4436071b5d8dcf812ee351fc846e1fa6/tasks/main.yml#L153-L161)
and never `make install`. This Debian package builds using `make && make
install` which results in the binary being moved to `bin/ustreamer`. I
thought running the `make install` is a good thing, so I just [added a
symlink (`/opt/ustreamer/ustreamer ->
bin/ustreamer`)](https://github.com/tiny-pilot/ustreamer-debian/pull/6/files#diff-fe915a3611d3df024aa33709affc37ff7a049adc629d69f2f0fe0dd063ad155fR1-R2)
to maintain TinyPilot's path to uStreamer.

Helpful resources on creating Debian packages:
* https://vincent.bernat.ch/en/blog/2019-pragmatic-debian-packaging
* https://github.com/vincentbernat/pragmatic-debian-packages/
* https://salsa.debian.org/reedy/ustreamer/
* https://github.com/tiny-pilot/tinypilot/pull/1352/files

<a data-ca-tag
href="https://codeapprove.com/pr/tiny-pilot/ustreamer-debian/6"><img
src="https://codeapprove.com/external/github-tag-allbg.png" alt="Review
on CodeApprove" /></a>
  • Loading branch information
jdeanwallace authored May 24, 2023
1 parent 249e368 commit 258e1e4
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 3 deletions.
60 changes: 60 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,68 @@ jobs:
- run:
name: Run static analysis on bash scripts
command: ./dev-scripts/check-bash
build_debian_package:
docker:
- image: cimg/base:stable
steps:
- checkout
- setup_remote_docker:
version: 20.10.11
docker_layer_caching: true
- run:
name: Enable multiarch builds with QEMU
command: ./dev-scripts/enable-multiarch-docker
- run:
name: Build Debian package
command: ./dev-scripts/build-debian-pkg "linux/arm/v7,linux/amd64"
- run:
name: Print Debian package contents
command: |
set -exu
while read -r file; do
dpkg --contents "${file}"
done < <(find . -name '*.deb')
- persist_to_workspace:
root: build
paths:
- ./*/*.deb
- store_artifacts:
path: build
lint_debian_package:
docker:
- image: cimg/base:2022.11-22.04
steps:
- checkout
- attach_workspace:
at: ./
- run:
name: Update apt packages
command: sudo apt-get update
- run:
name: Install lintian
command: sudo apt-get install -y lintian=2.114.0ubuntu1
- run:
name: Print lintian version
command: lintian --version
- run:
name: Run lintian
command: |
set -exu
while read -r file; do
lintian \
--check \
--no-tag-display-limit \
--suppress-tags-from-file .lintianignore \
--no-cfg \
--fail-on warning,error \
"${file}"
done < <(find . -name '*.deb')
workflows:
test:
jobs:
- check_whitespace
- check_bash
- build_debian_package
- lint_debian_package:
requires:
- build_debian_package
4 changes: 4 additions & 0 deletions .lintianignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Debian doesn't want packages to install to /opt, but it also doesn't give
# clear guidance on where they *should* go. It's too much churn at this point to
# change, so we're going to ignore this.
dir-or-file-in-opt
148 changes: 148 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# syntax=docker/dockerfile:1.4
# Enable here-documents:
# https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md#here-documents

FROM debian:bullseye-20220328-slim AS build

RUN set -x && \
apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
debhelper \
dpkg-dev \
devscripts \
git \
build-essential \
wget \
gnupg

# Add bullseye-backports apt suite to later install janus dependency.
RUN cat | bash <<'EOF'
set -ex
# Add keyring.
wget \
--output-document - \
https://ftp-master.debian.org/keys/archive-key-11.asc | \
gpg \
--dearmor > \
/usr/share/keyrings/bullseye-archive-keyring.gpg
# Add repository.
echo 'deb [signed-by=/usr/share/keyrings/bullseye-archive-keyring.gpg] http://deb.debian.org/debian bullseye-backports main' > \
/etc/apt/sources.list.d/bullseye-backports.list
# Update package index.
apt-get update
EOF

# Docker populates this value from the --platform argument. See
# https://docs.docker.com/build/building/multi-platform/
ARG TARGETPLATFORM

ARG PKG_NAME='ustreamer'
ARG PKG_VERSION='5.38'

# This should be a timestamp, formatted `YYYYMMDDhhmmss`. That way the package
# manager always installs the most recently built package.
ARG PKG_BUILD_NUMBER

# Docker's platform names don't match Debian's platform names, so we translate
# the platform name from the Docker version to the Debian version and save the
# result to a file so we can re-use it in later stages.
RUN cat | bash <<'EOF'
set -exu
case "${TARGETPLATFORM}" in
'linux/amd64')
PKG_ARCH='amd64'
;;
'linux/arm/v7')
PKG_ARCH='armhf'
;;
*)
echo "Unrecognized target platform: ${TARGETPLATFORM}" >&2
exit 1
esac
echo "${PKG_ARCH}" > /tmp/pkg-arch
echo "${PKG_NAME}-${PKG_VERSION}-${PKG_BUILD_NUMBER}-${PKG_ARCH}" > /tmp/pkg-id
EOF

# We ultimately need the directory name to be the package ID, but there's no
# way to specify a dynamic value in Docker's WORKDIR command, so we use a
# placeholder directory name to assemble the Debian package and then rename the
# directory to its package ID name in the final stages of packaging.
WORKDIR /build/placeholder-pkg-id

RUN git \
clone \
--branch "v${PKG_VERSION}" \
--depth 1 \
https://github.com/tiny-pilot/ustreamer.git \
.

COPY debian debian

WORKDIR debian

RUN set -x && \
PKG_ARCH="$(cat /tmp/pkg-arch)" && \
set -u && \
cat >control <<EOF
Source: ${PKG_NAME}
Section: video
Priority: optional
Maintainer: TinyPilot Support <[email protected]>
Build-Depends: debhelper (>= 11),
dh-exec,
libevent-dev,
libjpeg-dev,
uuid-dev,
libbsd-dev,
janus-dev,
libasound2-dev,
libspeex-dev,
libspeexdsp-dev,
libopus-dev

Package: ${PKG_NAME}
Architecture: ${PKG_ARCH}
Depends: \${shlibs:Depends}, \${misc:Depends}
Homepage: https://github.com/tiny-pilot/ustreamer
Description: Lightweight and fast MJPEG-HTTP streamer
µStreamer is a lightweight and very quick server to stream MJPEG video
from any V4L2 device to the net. All new browsers have native
support of this video format, as well as most video players such as
mplayer, VLC etc. µStreamer is a part of the PiKVM project designed to
stream VGA and HDMI screencast hardware data with the highest resolution
and FPS possible.
EOF

RUN cat >changelog <<EOF
${PKG_NAME} (${PKG_VERSION}-${PKG_BUILD_NUMBER}) bullseye; urgency=medium

* Latest µStreamer release.

-- TinyPilot Support <[email protected]> $(date '+%a, %d %b %Y %H:%M:%S %z')
EOF

# Install build dependencies based on Debian control file.
RUN mk-build-deps \
--tool 'apt-get --option Debug::pkgProblemResolver=yes --no-install-recommends -qqy' \
--install \
--remove \
control

# Allow Janus C header files to be included when compiling third-party plugins.
# https://github.com/tiny-pilot/ansible-role-tinypilot/issues/192
RUN sed \
--in-place \
's/^#include "refcount\.h"$/#include "\.\.\/refcount\.h"/g' \
/usr/include/janus/plugins/plugin.h

# Rename the placeholder build directory to the final package ID.
WORKDIR /build
RUN set -x && \
PKG_ID="$(cat /tmp/pkg-id)" && \
mv placeholder-pkg-id "${PKG_ID}" && \
cd "${PKG_ID}" && \
DH_VERBOSE=1 dpkg-buildpackage --build=binary

FROM scratch as artifact

COPY --from=build "/build/*.deb" ./
1 change: 1 addition & 0 deletions debian/compat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
11
17 changes: 17 additions & 0 deletions debian/rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/make -f

# Set all dpkg-architecture variables (i.e., ${DEB_HOST_MULTIARCH}, etc.).
include /usr/share/dpkg/architecture.mk

# Prevent debhelper from generating an extra package with debug symbols.
export DEB_BUILD_OPTIONS=noddebs

export PREFIX=/opt/ustreamer

%:
dh $@

override_dh_usrlocal:

override_dh_auto_build:
dh_auto_build -- WITH_JANUS=1
2 changes: 2 additions & 0 deletions debian/ustreamer.copyright
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Copyright (C) 2018-2022 by Maxim Devaev [email protected]
Copyright (c) 2023 TinyPilot, LLC <[email protected]>
3 changes: 3 additions & 0 deletions debian/ustreamer.install
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/dh-exec
janus/libjanus_ustreamer.so usr/lib/${DEB_HOST_MULTIARCH}/janus/plugins/
LICENSE opt/ustreamer/
2 changes: 2 additions & 0 deletions debian/ustreamer.links
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
opt/ustreamer/bin/ustreamer opt/ustreamer/ustreamer
opt/ustreamer/bin/ustreamer-dump opt/ustreamer/ustreamer-dump
10 changes: 10 additions & 0 deletions debian/ustreamer.preinstall
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash

# Exit script on first failure.
set -e

# If a .git directory exists, the previous version was installed with the legacy
# installer, so wipe the install location.
if [[ -d /opt/ustreamer/.git ]]; then
rm -rf /opt/ustreamer
fi
6 changes: 3 additions & 3 deletions dev-scripts/build-debian-pkg
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ set -u

readonly BUILD_TARGETS="${1:-linux/arm/v7,linux/amd64}"

PKG_VERSION="$(date '+%Y%m%d%H%M%S')"
readonly PKG_VERSION
PKG_BUILD_NUMBER="$(date '+%Y%m%d%H%M%S')"
readonly PKG_BUILD_NUMBER

# Use plain Docker build progress output when we're running in CI.
DOCKER_PROGRESS='auto'
Expand All @@ -38,7 +38,7 @@ readonly DOCKER_PROGRESS
DOCKER_BUILDKIT=1 docker buildx build \
--file Dockerfile \
--platform "${BUILD_TARGETS}" \
--build-arg PKG_VERSION="${PKG_VERSION}" \
--build-arg PKG_BUILD_NUMBER="${PKG_BUILD_NUMBER}" \
--target=artifact \
--progress="${DOCKER_PROGRESS}" \
--output "type=local,dest=$(pwd)/build/" \
Expand Down

0 comments on commit 258e1e4

Please sign in to comment.