Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Accelerated Jellyfin

turing-rk1/jellyfin-forky and turing-rk1/jellyfin-trixie build a Turing RK1 image running the Jellyfin media server, transcoding video on the board’s hardware encoder.

boot2deb build turing-rk1/jellyfin-forky

Flash it the way you would any other RK1 image — see Turing RK1. Jellyfin comes up on port 8096 with the transcode settings already filled in. There is nothing to configure to get hardware encoding. There is one setting to leave alone, described below.

What is accelerated, and what is not

Encoding runs on the VEPU580. Decoding and scaling run on the CPU.

That is the whole shape of it, and it is not the shape Jellyfin’s dashboard implies, so it is worth being plain about. Jellyfin offers an “Enable hardware decoding for” list alongside the hardware-encoding switch. On this image that list is deliberately empty.

The reason is that the two halves of Rockchip’s stack are in different states on a mainline kernel. The *_rkmpp encoders talk to mpp_service and work. The *_rkmpp decoders expect an MPP decode client, and mainline does not provide one — rkvdec is a V4L2 stateless driver instead. The decoders are still compiled in, so they appear in ffmpeg -hwaccels, and Jellyfin’s capability probe reads exactly that list and concludes hardware decoding is available. It is not.

Turning a codec on in that list makes Jellyfin emit -hwaccel rkmpp. That hwaccel finds no MPP decoder, and FFmpeg decodes in software without saying so, at the same CPU cost as no hwaccel at all. The list buys nothing and hides what is actually running.

So: leave Playback → Transcoding → Enable hardware decoding for empty. Everything else in that page is yours to tune.

On this board that is also the faster arrangement rather than a concession. RGA scaling only pays for itself on frames already held in an MPP context, which on a software-decode path they never are. The round trip to and from the 2D engine costs more than swscale saves. Eight Cortex cores decode and scale, and the encoder — the part that actually would not keep up in software — is in hardware.

FFmpeg on this image can decode in hardware, with -hwaccel v4l2request. The stock Jellyfin server cannot ask for it, because its acceleration type is a fixed list with no v4l2request in it. These two recipes install the stock server. Hardware decode as well builds one that can.

What the image sets up for you

SettingValueWhy
FFmpeg path/opt/ffmpeg-rk/bin/ffmpegThe only build here that can reach the VEPU580
Hardware accelerationrkmppThe only type that reaches h264_rkmpp / hevc_rkmpp
Hardware encodingon
Hardware decoding(empty)See above — leave it empty
Tone mappingoffThis FFmpeg is built without OpenCL

These are written to /etc/jellyfin/encoding.xml before first boot. They are starting values: Jellyfin rewrites that file on every start. From first boot onward the dashboard governs every row but the FFmpeg path, which it only displays (see below). To change the defaults for the next image, edit features/jellyfin-rockchip/overlay-pre/etc/jellyfin/encoding.xml in your config tree.

HEVC output is left off, as it is in stock Jellyfin. Whether your clients can play HEVC is a fact about your household, not about the board. hevc_rkmpp is there and works if you turn it on.

Jellyfin’s bundled FFmpeg is not installed

The image installs jellyfin-server and jellyfin-web, not the jellyfin metapackage, so jellyfin-ffmpeg never lands. It would be a second complete FFmpeg that cannot reach the hardware, and it pulls its own pocket’s library versions in behind it.

The consequence is worth knowing: there is no fallback encoder, and on this application no encoder means no server. Jellyfin validates the FFmpeg path during startup and exits if the binary does not run — it does not start with transcoding switched off. So if you point it at a path that does not exist, the service dies at boot. Check with journalctl -u jellyfin. The giveaway is Failed to find valid ffmpeg. If you want the bundled build available as a safety net, add jellyfin-ffmpeg8 to a copy of the jellyfin feature’s package list.

Change the path in encoding.xml, not on the command line. The image ships a jellyfin.service drop-in that clears the --ffmpeg= argument Debian normally passes, so Jellyfin reads the path from <EncoderAppPath> in /etc/jellyfin/encoding.xml. The dashboard’s FFmpeg path field shows that path but cannot edit it, because Jellyfin rejects a change to it made through its API. Edit the file with the server stopped:

sudo systemctl stop jellyfin
sudoedit /etc/jellyfin/encoding.xml
sudo systemctl start jellyfin

A path put back on the command line, by editing /etc/default/jellyfin-encoder, outranks the file without saying so.

Keeping it updated

Jellyfin’s own apt repository stays configured on the running system, since the image writes its sources.list.d entry and keyring. apt upgrade therefore picks up Jellyfin releases the ordinary way. Debian’s mirrors are there too. Nothing about this image requires a reflash to take a security update to the server.

A build installs whichever release that repository serves at build time. The solved manifest published beside the image, <recipe>.pkgs.lock, records the version. A Jellyfin major release can migrate the database with no path back, so back up /var/lib/jellyfin and /etc/jellyfin before an apt upgrade that crosses one.

The exception is ffmpeg-rk. It is built from source, pinned by commit in the recipe’s lock, and comes from no repository, so apt upgrade will never move it. It is also the component that parses untrusted media. Moving it means re-pinning and rebuilding:

boot2deb outdated turing-rk1/jellyfin-forky   # what has moved upstream
boot2deb update   turing-rk1/jellyfin-forky   # re-pin
boot2deb build    turing-rk1/jellyfin-forky

Hardware decode as well

The jellyfin-v4l2request feature builds the Jellyfin server from source with a patch series that lets decode and encode name different stacks. It seeds the pairing: decode on the stateless decoders through -hwaccel v4l2request, encode on the VEPU580.

boot2deb update turing-rk1/forky+media-accel-rockchip+jellyfin+jellyfin-v4l2request+vulkan+avs-decode
boot2deb build  turing-rk1/forky+media-accel-rockchip+jellyfin+jellyfin-v4l2request+vulkan+avs-decode --image-size 3G

It takes the place of jellyfin-rockchip in the selection, since both seed encoding.xml. What changes against the table above:

SettingValue
Hardware decoding typev4l2request
Hardware encoding typerkmpp
Hardware decodingH.264, HEVC, VP9, at 8 and 10 bits

H.264, HEVC and VP9 all decode on rkvdec, the decoder with the widest frame-size range for them, which is the one FFmpeg tries first. A 10-bit frame is unpacked from the decoder’s packed NV15 layout on the CPU, which is most of what a 10-bit transcode costs. A 4K HEVC Main 10 stream transcodes to 1080p at 1.6x realtime, on a quarter of the CPU that software decode takes. Two at once hold just under realtime each. The dashboard has no field for the two types. They live in /etc/jellyfin/encoding.xml, and setting the acceleration type in the dashboard leaves them as they are.

The server is held at its release. The built jellyfin-server carries an epoch, and depends on the jellyfin-web of its own release, so apt upgrade moves neither. The Jellyfin repository stays configured for everything else it serves. A newer Jellyfin is a change to the app’s ref in features/jellyfin-v4l2request.toml, then an update and a build. See features/jellyfin-v4l2request/README.md for how the build works.

Where the media lives

The recipes declare no data volume, on purpose. An RK1 running Jellyfin might keep its library on an M.2 disk, an external drive, or network storage, and the recipe cannot know which. To attach one, add the data-volume feature and a [[data_volumes]] block to your own copy of the recipe. See Data volumes.

Checking it is working

Play something that must be transcoded, such as a file in a codec the client cannot take, or one with subtitles burned in. Then look at the FFmpeg command Jellyfin logged:

sudo grep -h "ffmpeg" /var/log/jellyfin/*.log | tail -1

You want to see /opt/ffmpeg-rk/bin/ffmpeg, -c:v hevc_rkmpp or h264_rkmpp, and no -hwaccel rkmpp. If -hwaccel rkmpp is there, a codec got re-enabled in the hardware-decoding list.

Every run also prints mpp_platform: client N driver is not ready! for a handful of N. That is normal: libmpp probes for vendor client types a mainline kernel does not have. The one the encoders need, RKVENC, is present. RKVDEC (9) and JPEG_DEC (13) are among the absent ones, which is why the *_rkmpp decoders never open.

Status

Neither recipe is validated, and the gap is Jellyfin rather than the hardware underneath it.

The transcode path itself is measured on a boot2deb-built RK1 image. h264_rkmpp and hevc_rkmpp produce correct streams: every frame of a 90-frame clip in both codecs, from software frames and through hwupload alike. That was verified against a stock FFmpeg on another machine, rather than against the build that produced them.

Hardware decode through -hwaccel v4l2request cuts decode CPU cost by 53x at 1080p and by 70-162x at 4K, and HEVC decode is bit-exact against software.

Driving that path from Jellyfin has been measured on the board too. A patched server carrying a v4l2request acceleration type played files through the API on 2026-09-02. It emitted exactly the accelerated commands, with no software fallback. The two recipes install the stock server, so what ships there is the encoder half. The jellyfin-v4l2request build ships both. See the support matrix for what each recipe has been taken through.