player: probe Rockchip MPP/V4L2 HW decoders at pipeline init, add setup_hw_decode.sh
This commit is contained in:
@@ -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."
|
||||||
@@ -25,6 +25,72 @@ log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
Gst.init(None)
|
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:
|
def _wm_subsystem_name(subsystem: int) -> str:
|
||||||
mapping = {
|
mapping = {
|
||||||
@@ -116,6 +182,7 @@ class GStreamerBackend(PlayerBackend):
|
|||||||
self._texture_renderer = None
|
self._texture_renderer = None
|
||||||
self._texture_size = (0, 0)
|
self._texture_size = (0, 0)
|
||||||
self._resolution = ""
|
self._resolution = ""
|
||||||
|
self._hw_decoders: list | None = None # None = not yet probed
|
||||||
|
|
||||||
def attach_window(self, window: object) -> None:
|
def attach_window(self, window: object) -> None:
|
||||||
self._window = window
|
self._window = window
|
||||||
@@ -259,6 +326,9 @@ class GStreamerBackend(PlayerBackend):
|
|||||||
if self._pipeline is not None:
|
if self._pipeline is not None:
|
||||||
return self._pipeline
|
return self._pipeline
|
||||||
|
|
||||||
|
if self._hw_decoders is None:
|
||||||
|
self._hw_decoders = _probe_hw_decoders(self._gst)
|
||||||
|
|
||||||
self._pipeline = self._playbin_factory()
|
self._pipeline = self._playbin_factory()
|
||||||
self._configure_playbin_flags(self._pipeline)
|
self._configure_playbin_flags(self._pipeline)
|
||||||
self._video_sink = self._create_appsink()
|
self._video_sink = self._create_appsink()
|
||||||
|
|||||||
Reference in New Issue
Block a user