From ddbe31dc028b8d8770f3464ab9544637df22f6ee Mon Sep 17 00:00:00 2001 From: Matteo Benedetto Date: Mon, 23 Mar 2026 21:42:25 +0100 Subject: [PATCH] player: probe Rockchip MPP/V4L2 HW decoders at pipeline init, add setup_hw_decode.sh --- deploy/arkos/setup_hw_decode.sh | 89 +++++++++++++++++++ .../player/gstreamer_backend.py | 70 +++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 deploy/arkos/setup_hw_decode.sh diff --git a/deploy/arkos/setup_hw_decode.sh b/deploy/arkos/setup_hw_decode.sh new file mode 100644 index 0000000..da9101f --- /dev/null +++ b/deploy/arkos/setup_hw_decode.sh @@ -0,0 +1,89 @@ +#!/bin/bash +# setup_hw_decode.sh — Enable Rockchip hardware video decoding on ArkOS / R36S +# +# What this script does: +# 1. Adds a udev rule so the 'video' group can access /dev/vpu_service +# (the VPU device used by Rockchip MPP on the kernel 4.4 BSP). +# 2. Tries to install librockchip-mpp0 and gstreamer1.0-rockchip-mpp. +# If they are not in apt, it prints instructions for manual installation. +# +# Requirements: +# - Must be run as root or via sudo. +# - Internet access (for apt-get). +# - Target: RK3326 / PX30 device running ArkOS with Ubuntu eoan arm64. +# +# After this script succeeds, restart the DLNA browser app. The GStreamer +# backend will detect mppvideodec and prefer it over software avdec_h264. + +set -euo pipefail + +info() { echo "[setup-hw-decode] $*"; } +warn() { echo "[setup-hw-decode] WARNING: $*" >&2; } +die() { echo "[setup-hw-decode] ERROR: $*" >&2; exit 1; } + +[[ $EUID -eq 0 ]] || die "This script must be run as root (use sudo)." + +# --------------------------------------------------------------------------- +# 1. udev rule — /dev/vpu_service accessible to 'video' group +# --------------------------------------------------------------------------- +UDEV_RULE="/etc/udev/rules.d/50-vpu-service.rules" + +info "Writing udev rule: $UDEV_RULE" +cat > "$UDEV_RULE" <<'EOF' +# Rockchip VPU service — allow the 'video' group to access the hardware encoder/decoder. +KERNEL=="vpu_service", GROUP="video", MODE="0660" +KERNEL=="mpp_service", GROUP="video", MODE="0660" +EOF + +udevadm control --reload-rules +udevadm trigger --name-match=vpu_service 2>/dev/null || true +udevadm trigger --name-match=mpp_service 2>/dev/null || true + +info "/dev/vpu_service access granted to 'video' group." +info "Verify with: ls -la /dev/vpu_service (should show crw-rw---- root video)" + +# --------------------------------------------------------------------------- +# 2. librockchip-mpp0 + gstreamer1.0-rockchip-mpp +# --------------------------------------------------------------------------- +# +# These packages are NOT in the standard Ubuntu eoan repos. +# Sources known to carry arm64 builds for RK3326/PX30: +# +# A) ODROID community apt mirror (requires adding the source manually): +# deb https://oph.mdrjr.net/meveric/ stretch main +# apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A0A3B33F +# +# B) Pre-built .deb files from the OGA / ArkOS forum / Discord. +# Typical package names: +# librockchip-mpp0_1.x.x_arm64.deb +# gstreamer1.0-rockchip-mpp_1.x.x_arm64.deb +# +# C) Build from source (advanced): +# https://github.com/rockchip-linux/mpp +# https://github.com/JeffyCN/gst-mpp +# +# This script tries apt-get first; if the package is not found it falls back +# to printing the manual instructions and exits with success (non-blocking). + +MPP_PKGS="librockchip-mpp0 gstreamer1.0-rockchip-mpp" + +if apt-cache show librockchip-mpp0 &>/dev/null; then + info "Found librockchip-mpp0 in apt — installing..." + apt-get install -y $MPP_PKGS + info "MPP packages installed successfully." +else + warn "librockchip-mpp0 is not available in the current apt sources." + echo + echo " To enable hardware H.264/H.265/VP8/VP9 decoding, install these arm64 packages:" + echo " librockchip-mpp0 — Rockchip MPP runtime library" + echo " gstreamer1.0-rockchip-mpp — GStreamer plugin that exposes mppvideodec" + echo + echo " Option A — Add a community apt source with RK3326 support, then re-run this script." + echo " Option B — Download pre-built .deb files and run:" + echo " sudo dpkg -i librockchip-mpp0_*.deb gstreamer1.0-rockchip-mpp_*.deb" + echo + echo " The udev rule above has already been applied regardless." + echo " Once the packages are installed, restart the DLNA browser app." +fi + +info "Done." diff --git a/src/r36s_dlna_browser/player/gstreamer_backend.py b/src/r36s_dlna_browser/player/gstreamer_backend.py index 1714bc9..0caaab9 100644 --- a/src/r36s_dlna_browser/player/gstreamer_backend.py +++ b/src/r36s_dlna_browser/player/gstreamer_backend.py @@ -25,6 +25,72 @@ log = logging.getLogger(__name__) Gst.init(None) +# GStreamer element names for hardware video decoders, in priority order. +# mppvideodec: Rockchip MPP plugin (gst-mpp), uses /dev/vpu_service on kernel 4.4 BSP. +# v4l2*dec: Linux V4L2 stateful decoders, available on kernel 5.4+ (rkvdec/hantro). +_HW_DECODER_ELEMENTS = [ + "mppvideodec", + "v4l2h264dec", + "v4l2h265dec", + "v4l2vp8dec", + "v4l2vp9dec", +] + +# VPU device nodes that must be accessible for hardware decode on RK3326. +_HW_VPU_DEVICES = [ + "/dev/vpu_service", # kernel 4.4 BSP (RK3326 / PX30) + "/dev/mpp_service", # newer BSP variants + "/dev/video10", # V4L2 vcodec on mainline/5.10+ kernels + "/dev/video11", +] + + +def _vpu_device_accessible() -> bool: + """Return True if at least one VPU device node can be opened by this process.""" + for path in _HW_VPU_DEVICES: + try: + fd = os.open(path, os.O_RDWR | os.O_NONBLOCK) + os.close(fd) + log.info("HW decode: VPU device accessible: %s", path) + return True + except OSError: + pass + return False + + +def _probe_hw_decoders(gst_module) -> list: + """ + Probe for available hardware video decoder GStreamer elements and boost + their rank so that playbin's internal decodebin prefers them over software + decoders. Returns the list of element names that were found and boosted. + If no VPU device node is accessible the probe is skipped entirely so that + a half-initialised hardware element cannot stall the pipeline. + """ + if not _vpu_device_accessible(): + log.info( + "HW decode: no accessible VPU device node — using software decode. " + "Run deploy/arkos/setup_hw_decode.sh as root to fix permissions." + ) + return [] + + found = [] + for name in _HW_DECODER_ELEMENTS: + factory = gst_module.ElementFactory.find(name) + if factory is not None: + try: + factory.set_rank(gst_module.Rank.PRIMARY + 1) + found.append(name) + log.info("HW decode: boosted rank of %s", name) + except Exception as exc: # pragma: no cover + log.warning("HW decode: could not boost rank of %s: %s", name, exc) + + if not found: + log.info( + "HW decode: VPU device is accessible but no gst-mpp / V4L2 plugin found. " + "Run deploy/arkos/setup_hw_decode.sh to install gstreamer1.0-rockchip-mpp." + ) + return found + def _wm_subsystem_name(subsystem: int) -> str: mapping = { @@ -116,6 +182,7 @@ class GStreamerBackend(PlayerBackend): self._texture_renderer = None self._texture_size = (0, 0) self._resolution = "" + self._hw_decoders: list | None = None # None = not yet probed def attach_window(self, window: object) -> None: self._window = window @@ -259,6 +326,9 @@ class GStreamerBackend(PlayerBackend): if self._pipeline is not None: return self._pipeline + if self._hw_decoders is None: + self._hw_decoders = _probe_hw_decoders(self._gst) + self._pipeline = self._playbin_factory() self._configure_playbin_flags(self._pipeline) self._video_sink = self._create_appsink()