Hardware video decoding in Kodi on the Radxa Dragon Q6A (Qualcomm QCS6490), using the
kernel's iris decoder (qcom_iris, /dev/video0) through V4L2 mem2mem.
Kodi as packaged by Ubuntu cannot use iris with the system FFmpeg. This project builds
rpi-ffmpeg 8.0.1 with a few fixes for iris,
installs it under /opt/ffmpeg-iris, and starts Kodi with those libraries through
LD_LIBRARY_PATH. rpi-ffmpeg is ABI-compatible with Ubuntu's libavcodec62. No system
package is replaced, and plain kodi keeps using the system FFmpeg.
- jc-kynesim/rpi-ffmpeg: the FFmpeg branch this work starts from. Its V4L2 mem2mem decoder with DRM PRIME output does almost all of the work. The patches here only adapt it to iris.
- rafaelwms/ubuntu-26.04.1-dragon-q6a: the Ubuntu 26.04.1 image for the Dragon Q6A (mainline 7.2 kernel with iris) that this was developed and tested on.
- Radxa Dragon Q6A, Ubuntu 26.04.1 LTS (rafaelwms image), kernel
7.2.9-edge-qcs6490 - Kodi 21.3 (Ubuntu package), GBM windowing
- Mesa 26.0.8 (freedreno, Adreno 643)
Other kernels or Kodi versions may behave differently, especially iris, whose behaviour depends on the driver version.
sudo apt install -y git build-essential pkg-config \
libdrm-dev libdav1d-dev libvpx-dev libxml2-dev libzvbi-dev \
libbluray-dev libmp3lame-dev libudev-dev libgnutls28-dev libsoxr-dev \
libopus-dev libvorbis-dev
sudo install -d -o "$USER" -g "$USER" /opt/ffmpeg-irisYour user must be in the video and render groups. If Kodi is started from SSH, it also
needs to be in audio and input.
./build.sh # first run: clones rpi-ffmpeg, applies patches/, configures, builds, installs
RECONF=1 ./build.sh # force a reconfigureOn the first run build.sh clones test/8.0.1/main from rpi-ffmpeg into $SRC, checks out
the base commit 2a287060cc6e, and applies patches/*.patch with git am.
Variables: SRC (default ~/src/ffmpeg-iris), PREFIX (/opt/ffmpeg-iris), BUILD
($SRC/out/build) and UPSTREAM (the rpi-ffmpeg URL).
Then build the EGL shim and install it with the launcher:
gcc -O2 -shared -fPIC -o /opt/ffmpeg-iris/lib/libegl_linear_shim.so shim/egl_linear_shim.c -ldl
install -m 755 kodi-iris /opt/ffmpeg-iris/bin/The launcher preloads libegl_linear_shim.so, enables the HDMI audio UCM route (lost on
every reboot), and turns on the shim's limited-range mode when Kodi's limited colour range
setting is on (see HDMI colour range).
The shim adds the explicit DRM_FORMAT_MOD_LINEAR modifier that Kodi leaves out when it
imports iris frames into EGL. Without it, Mesa refuses every frame (0x3003).
-
Start Kodi with
/opt/ffmpeg-iris/bin/kodi-iris [file]. Ubuntu'skodiwrapper splits file arguments on spaces, so pass paths without spaces (a symlink works). -
In Settings → Player → Videos (Expert mode):
- Allow using DRM PRIME decoder: on (
videoplayer.useprimedecoder). - Allow hardware acceleration with DRM PRIME: on
(
videoplayer.useprimedecoderforhw). This one is required: without it Kodi picks the software decoder. - PRIME render method: EGL (
videoplayer.useprimerenderer=1). It needs the shim installed above. Direct to plane (0) also works and uses a little less GPU, but cannot show video wider than 2400 px (see Limitations).
If you edit
~/.kodi/userdata/guisettings.xmlby hand while Kodi is closed, remove thedefault="true"attribute from the line. Otherwise Kodi ignores the value. - Allow using DRM PRIME decoder: on (
-
On a TV, turn on Settings → System → Display → Use limited colour range (16-235) and leave the TV's HDMI black level on Auto. The board's HDMI converter cannot tell the TV that it sends full range: see HDMI colour range.
-
To quit Kodi from a script, use
tests/kodi-stop.sh. It stops playback through JSON-RPC (port 8080, web server enabled), waits until/dev/video0is released, quits, and only then falls back tokill -9.
If iris ever gets stuck (every program fails with VIDIOC_REQBUFS failed: Invalid argument
while nothing holds /dev/video0), reload the driver:
sudo modprobe -r qcom_iris && sudo modprobe qcom_iris| Patch | Problem it fixes |
|---|---|
0001 |
iris returns empty capture buffers flagged as errors. They are now discarded instead of being output as frames. |
0002 |
If capture cannot be restarted after a resolution change, decoding fails promptly instead of hanging. |
0003 |
iris has no capture format above 8 bits, so >8-bit streams are refused at open (FF_V4L2_QUIRK_8BIT_ONLY) instead of failing after decoding has started. |
0004 |
When the hardware decoder refuses a stream, Kodi's DRM PRIME path retries with the software decoder. That path only accepts 8-bit formats, so 10-bit HEVC gave a black screen. hevcdec now refuses >8-bit streams at open when DRM_PRIME is requested, and Kodi falls back to its normal software decoder. |
0005 |
After a drain (Kodi drains before every seek), iris never decodes again in the same session, and the stalled session could leave the driver unusable for every client. On iris, flush now closes and reopens the decoder (FF_V4L2_QUIRK_REOPEN_AFTER_DRAIN). |
Patches 0001–0003 and 0005 only take effect when the driver is iris_driver. Patch
0004 only takes effect when the caller asks the software HEVC decoder for DRM_PRIME.
| Script | What it does |
|---|---|
tests/abi-check.sh |
Checks that every FFmpeg symbol kodi.bin imports exists in /opt/ffmpeg-iris. |
tests/gen-media.sh |
Generates the synthetic clips in tests/media/: H.264, HEVC and VP9 from 360p to 2160p, a mid-stream resolution change, and 10-bit HEVC. |
tests/run-matrix.sh |
Frame-by-frame comparison (framemd5) of the software decoder against iris, with NV12 and DRM PRIME output (tests/bin/drmframemd5). 10-bit must be refused at open. Exits 0 when everything passes. |
tests/bin/seektest FILE DEC N |
Does N seeks the way Kodi does them (NULL-packet drain, avcodec_flush_buffers, seek) and measures the time to the first frame. |
tests/bin/primefallback FILE 0|1 |
Checks that the software HEVC decoder refuses >8-bit streams when DRM_PRIME is preset. |
tests/kodi-check.sh |
While Kodi is playing: libraries from /opt, /dev/video0 open, V4L2 decoder in the log, CPU < 30 %. |
tests/kodi-seek.sh [N] |
While Kodi is playing: N seeks through JSON-RPC, with resume time (fails above 3 s). |
tests/kodi-stop.sh |
Quits Kodi without leaving iris stuck. |
tests/eglprobe.c |
Diagnostics: prints the DRM descriptor iris returns and tries to import it into EGL. |
tests/yuvhint.c |
Prints the RGB Mesa produces from NV12 under each EGL colour-space/range hint (with the shim: checks its limited-range mode). |
Build the C tools against /opt/ffmpeg-iris, for example:
mkdir -p tests/bin
gcc -Wall -O1 tests/seektest.c -I/opt/ffmpeg-iris/include -L/opt/ffmpeg-iris/lib \
-Wl,-rpath,/opt/ffmpeg-iris/lib -lavformat -lavcodec -lavutil -o tests/bin/seektestFor more realistic 8-bit HEVC, re-encode part of any H.264 video:
ffmpeg -ss 120 -t 60 -i INPUT.mkv -map 0:v:0 -map 0:a:0 -c:v libx265 -preset ultrafast \
-pix_fmt yuv420p -c:a copy tests/media/real_hevc_1080p.mkvKodi 21.3, Direct to plane, 1080p display:
| Content | Decoder | Kodi CPU | Notes |
|---|---|---|---|
| H.264 1080p | iris | ~15 % | software decode: 100–150 % |
| HEVC 8-bit 1080p | iris | ~15 % | |
| HEVC 8-bit 2160p | iris | ~16 % | decodes fine; see the 4K limitation below |
| HEVC 10-bit 1080p | software | ~100 % (one core) | real time, no errors |
| 10 forward/back seeks | iris | resumes in ~0.6 s in Kodi; seektest: ~55 ms (1080p), ~130 ms (2160p) |
|
| Switching video, quitting Kodi | iris | clean, iris stays usable |
tests/run-matrix.sh: 27/27 (H.264, HEVC and VP9 from 360p to 2160p, NV12 and DRM PRIME,
resolution change, 10-bit refused at open).
- 4K with Direct to plane. The display controller's planes accept at most 2400 px of
source width (
max_linewidthon SC7280), so 3840-wide frames fail the atomic test commit (DrmAtomicCommit - test commit failed) on every frame. Use the EGL renderer. - HDMI colour range and HDR. The onboard RA620 DP-to-HDMI converter never tells the TV the RGB range, and does not forward HDR metadata. Kodi is set to limited range as a workaround; HDR10 is tone-mapped to SDR. Details: HDMI colour range.
- 10-bit is not decoded by iris (the driver offers no >8-bit capture format) and falls back to software. 10-bit 4K in software is not smooth.
- 10-bit VP9 probably gives a black screen. VP9 has no bit depth information at open,
and patch
0004only covers HEVC. - 8K: iris refuses it ("current session not supported"). Software decoding manages ~32 fps.
- JSON-RPC
Player.OpenanswersInvalid paramsfor new files in a directory Kodi has already listed (directory cache). Use a new directory.
rm -rf /opt/ffmpeg-irisThen turn the two DRM PRIME options off again in Kodi.
The FFmpeg in /opt must keep the same LIBAVCODEC_VERSION_MAJOR/MINOR as the
libavcodec62 that Kodi was built against (62.11 at the time of writing):
git -C ~/src/ffmpeg-iris fetch origin
git -C ~/src/ffmpeg-iris show origin/test/8.0.x/main:libavcodec/version.h | grep VERSION_MINOR
git -C ~/src/ffmpeg-iris rebase --onto origin/test/8.0.x/main 2a287060cc6e iris
./build.sh && tests/abi-check.sh && tests/run-matrix.shThis project's own code (the shim, launcher, build script, tests and tools) is under the MIT licence.
The patches in patches/ modify FFmpeg and are under FFmpeg's licence (LGPL-2.1-or-later).
The build enables --enable-gpl, so the resulting libraries are GPL-2.0-or-later.