2026-08-24 16:02:47 +02:00
# microduck-gst-plugins
Prebuilt aarch64 GStreamer plugins for the micro duck robot, built in CI from pinned upstream
sources and attached to a release.
Two plugins, for two unrelated reasons. Neither is packaged anywhere we can install from.
| plugin | provides | why it is here |
|---|---|---|
2026-08-25 11:29:02 +02:00
| `libgstrockchipmpp.so` | `mpph264enc` , `mpph265enc` , `mppjpegenc` , `mppvp8enc` , `mppvideodec` , `mppjpegdec` | Debian ships no Rockchip encoder in any suite. Radxa's own `gstreamer1.0-rockchip1_1.14-4` does contain them, so this build is about the pin, dropping `libx11-6` , and riding along with the plugin below — see [below ](#the-permission-trap-that-hid-all-of-this ). Patched; see [`patches/` ](patches/ ). |
2026-08-25 10:43:05 +02:00
| `libgstrswebrtc.so` , `libgstrsrtp.so` | `webrtcsink` , `webrtcsrc` , `rsrtp*` | `gstreamer1.0-plugins-rs` does not exist in **any** Debian suite — not trixie, backports, sid or experimental. Patched; see [`patches/` ](patches/ ). |
2026-08-24 16:02:47 +02:00
`webrtcbin` is **not** here: it comes from `gstreamer1.0-plugins-bad` in Debian and needs no
build.
## Why a repository of its own
The robot's daemon is cross-compiled from a developer's machine with `cargo-zigbuild` , and its one
C dependency is already the documented cost of doing that. GStreamer would be a much larger second
one — a cross sysroot or x86 multiarch, either of which links against an approximation of the
target.
So these are built **natively on an arm64 runner, in a `debian:trixie` container** , which is the
robot's own userland. Nothing is cross-compiled and nothing is approximated. arm64 runners are
free on public repositories, which is one reason this repository is public.
The other reason matters more: a release asset here is fetched by a robot during provisioning and
by the updater's `preinstall` hook, and that hook runs with a cleared environment and no token. A
private repository would break it. This is the same arrangement the daemon already relies on for
ONNX Runtime, which comes from a public `microsoft/onnxruntime` release.
Building rather than taking a third-party binary also buys one concrete thing beyond provenance:
`rkximage` and `kmssrc` , the X11 and KMS *sinks* in the same source tree, are **disabled** . A
headless robot has no use for either, and they are why the prebuilt Radxa deb depends on
`libx11-6` .
2026-08-24 16:51:22 +02:00
## The permission trap that hid all of this
`/dev/mpp_service` arrives as `0600 root:root` , and **an MPP GStreamer plugin registers its
decoders unconditionally but probes MPP before registering its encoders.** With the node
unreadable the probe fails and the encoders are silently omitted — no error, no log line.
That one cause produced four separate misleading results while this was being worked out:
- `mpi_enc_test` wrote an empty file and **exited 0** .
- Radxa's `1.14-4` looked decode-only. It is not; `strings` on its `.so` lists every encoder.
- A third-party `1.14-8` deb installed cleanly and still showed no `mpph264enc` .
- This repository's own CI build shows only `mppjpegdec` and `mppvideodec` , because a container
has no `/dev/mpp_service` either. **That is expected, not a failed build.**
So: a plugin that lists only decoders is evidence about the *device node* , not about the plugin. A
non-root process needs a udev rule giving the node a group — mode `0660` , group `video` — and only
then does `gst-inspect-1.0 mpph264enc` mean anything.
2026-08-24 16:02:47 +02:00
## Consuming a release
```
tar -xzf microduck-gst-plugins-<version>-aarch64.tar.gz
```
Put the `.so` files anywhere and point `GST_PLUGIN_PATH` at it — `/usr/local/lib/gstreamer-1.0`
on a robot, which is deliberately **not** the distro's plugin directory, so an `apt` operation can
never quietly replace or remove them.
```
GST_PLUGIN_PATH=/usr/local/lib/gstreamer-1.0 gst-inspect-1.0 mpph264enc
```
Verify the tarball against the `.sha256` beside it before unpacking. **Pin a version** ; do not
follow "latest". Two provisioning runs a day apart that produce different plugins, with nothing
recording which, is an unreproducible media bug waiting to happen.
### Runtime dependencies
The plugins link against libraries a robot needs installed:
- `librockchip-mpp1` and `librga2` — from Radxa's pool, at the versions in
[`pins.env` ](pins.env ). Not in Debian.
- `libgstreamer1.0-0` , `libgstreamer-plugins-base1.0-0` , `libglib2.0-0` , `libdrm2` — Debian.
2026-08-24 16:51:22 +02:00
`mpph264enc` also needs **read/write access to `/dev/mpp_service`** — see
[the permission trap ](#the-permission-trap-that-hid-all-of-this ), which is the single most
misleading thing about this stack.
2026-08-24 16:02:47 +02:00
## Bumping a pin
Edit [`pins.env` ](pins.env ), commit, tag `vN` , push the tag. The release workflow builds and
attaches the tarball, with the manifest as the release notes so a release always says which
upstream commits it came from.
`workflow_dispatch` builds without cutting a release — worth using, because a workflow that only
ever runs on a tag is one you discover is broken at the moment you need it.
## Licences and source
These are binaries built from other people's source, so where that source is matters:
- **`gstreamer-rockchip` ** is LGPL. Built from
[`JeffyCN/mirrors` ](https://github.com/JeffyCN/mirrors ) on the `gstreamer-rockchip` branch, at
the commit in `pins.env` and recorded in every release's `MANIFEST` .
`rockchip-linux/gstreamer-rockchip` , which every published deb names as its homepage, is a 404;
`JeffyCN/mirrors` is the live mirror under the same maintainer.
- **`gst-plugins-rs` ** is MPL-2.0. Built from
[the upstream repository ](https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs ) at the tag in
`pins.env` .
2026-08-25 11:29:02 +02:00
**Both upstreams are patched, and that matters for more than tidiness.** MPL-2.0 asks that
modifications be identifiable, so they are stated here, listed in [`patches/` ](patches/ ) with what
each one buys and how it ends, and recorded in every release's `MANIFEST` — a release names each
upstream ref *and* every patch applied over it.
2026-08-25 10:43:05 +02:00
2026-08-25 11:29:02 +02:00
Two patches today, both about getting hardware H.264 to a browser:
- **`gst-plugins-rs` ** — `webrtcsink` inserts a software `videoconvert ! videoscale` in front of
any encoder it does not recognise, and `mpph264enc` converts on the SoC's 2D accelerator
instead, so a CPU pass over every frame is added to work the hardware was going to do anyway,
on cores the robot's control loop shares.
- **`gstreamer-rockchip` ** — `mpph264enc` 's pad template omitted `constrained-baseline` from its
profile list, which is the one profile WebRTC asks for. Without this, `webrtcsink` cannot offer
H.264 on an RK3566 at all, and the only trace is a `videorate` complaining about NV12.
[`patches/README.md` ](patches/README.md ) has the reasoning for each, the trade-off it accepts, and
the route upstream that would delete it.
2026-08-25 10:43:05 +02:00
Together the ref and the patch list are both the licence answer and the reason a media bug found on
2026-08-24 16:02:47 +02:00
a robot can be traced to a specific build.
This repository's own build scripts are Apache-2.0, matching the daemon.