Initial RK1 media-center image project
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
/out/
|
||||
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
# RK1 media stack
|
||||
|
||||
This directory builds an ARM64-only Kodi/GBM appliance stack for an RK3588
|
||||
running Armbian Debian 13 (Trixie) with the Rockchip vendor 6.1 kernel. All
|
||||
third-party inputs are immutable Git commits recorded in `sources.lock.json`.
|
||||
The result is a Debian package whose custom libraries and programs live under
|
||||
`/opt/rkmedia`; it does not replace Debian's FFmpeg or Kodi packages.
|
||||
|
||||
## Why the compatibility MPP and RGA branches are pinned
|
||||
|
||||
The current `kodi-rockchip-deb` recipe uses nyanmisaka's `jellyfin-mpp` and
|
||||
`jellyfin-rga` branches. FFmpeg-Rockchip 8.1 checks for MPP package version
|
||||
1.3.9 and the `mpp_buffer_sync_begin_f` symbol, and checks the RGA APIs
|
||||
`c_RkRgaBlit` and `querystring`. These exact branches satisfy that interface.
|
||||
The official Rockchip MPP/RGA heads inspected when this lock was generated are
|
||||
recorded as reference metadata, but are deliberately not substituted into the
|
||||
matched build without a complete hardware regression run.
|
||||
|
||||
## Build
|
||||
|
||||
Run this on an aarch64 Debian Trixie installation. The native build is large and
|
||||
can take several hours.
|
||||
|
||||
```sh
|
||||
sudo ./scripts/install-build-deps.sh --install
|
||||
./scripts/build-media.sh
|
||||
```
|
||||
|
||||
Outputs are written to `out/` by default:
|
||||
|
||||
- `rk1-media-stack_<version>_arm64.deb`
|
||||
- an unpacked `stage/` tree for inspection
|
||||
- cloned, detached source trees and component build directories
|
||||
|
||||
Useful options:
|
||||
|
||||
```sh
|
||||
./scripts/fetch-sources.sh --dest /path/to/sources
|
||||
./scripts/build-media.sh --source-dir /path/to/sources --work-dir /path/to/work --output-dir /path/to/output --jobs 8
|
||||
./scripts/build-media.sh --no-fetch
|
||||
```
|
||||
|
||||
Set `RKMEDIA_ALLOW_UNSUPPORTED_HOST=1` only for development. Packages built on
|
||||
anything other than Trixie are unsupported because their generated shared
|
||||
library dependencies will describe the build host, not the target image.
|
||||
|
||||
## Fast candidate repack
|
||||
|
||||
For image composition and hardware validation, the pinned July 2026 upstream
|
||||
Trixie package can be deterministically repacked in a few minutes instead of
|
||||
performing the multi-hour native compile:
|
||||
|
||||
```sh
|
||||
./scripts/repack-upstream-deb.sh
|
||||
```
|
||||
|
||||
The script downloads the exact asset in `upstream-deb.lock.json`, verifies its
|
||||
size and SHA-256 before extraction, removes approximately 270 MB of static
|
||||
FFmpeg archives and all development pkg-config files, and emits the same
|
||||
`rk1-media-stack` package format. Kodi remains at `/usr/local`, its compiled
|
||||
prefix. FFmpeg, FFprobe, MPP, RGA, dav1d, and display-info move under
|
||||
`/opt/rkmedia`; the launcher supplies the corresponding library path.
|
||||
|
||||
Use `--deb PATH` to repack an already-downloaded copy without network access.
|
||||
The prebuilt asset is reproducible as an input but its original recipe used
|
||||
moving source branches; its verified asset digest, rather than reconstructed
|
||||
Git commit guesses, is the provenance boundary.
|
||||
|
||||
## Install and operate
|
||||
|
||||
Install with APT so runtime dependencies are resolved:
|
||||
|
||||
```sh
|
||||
sudo apt install ./rk1-media-stack_<version>_arm64.deb
|
||||
sudo systemctl enable --now kodi-rk.service
|
||||
```
|
||||
|
||||
The package creates a locked `kodi` system account, grants it only the existing
|
||||
`video`, `render`, `audio`, and `input` group memberships, and gives it persistent
|
||||
state under `/var/lib/kodi`. Kodi takes DRM master on tty1, so a display manager
|
||||
or another program holding the KMS device must not be active.
|
||||
|
||||
The service is enabled during package configuration but is not started inside
|
||||
an image-build chroot. Edit `/etc/rkmedia/kodi.env` for supported environment
|
||||
overrides, then restart `kodi-rk.service`.
|
||||
|
||||
The following commands are exposed without replacing system FFmpeg:
|
||||
|
||||
```sh
|
||||
ffmpeg-rk -hide_banner -decoders
|
||||
ffmpeg-rk -hide_banner -encoders
|
||||
ffmpeg-rk -hide_banner -filters
|
||||
ffprobe-rk media-file.mkv
|
||||
```
|
||||
|
||||
Expected hardware entries include `h264_rkmpp`, `hevc_rkmpp`, `vp9_rkmpp`,
|
||||
`av1_rkmpp`, `mjpeg_rkmpp`, and the `scale_rkrga`, `vpp_rkrga`, and
|
||||
`overlay_rkrga` filters. Runtime access is provided through conservative udev
|
||||
rules for the DRM, dma-heap, RGA, IEP, VPU, and MPP device nodes.
|
||||
|
||||
## Boundaries
|
||||
|
||||
- The package does not install or select the kernel, device tree, Panthor
|
||||
overlay, Mesa, firmware, or bootloader. Those belong to the parent image.
|
||||
- The package does not claim HDMI link, HDR, passthrough, or codec acceptance;
|
||||
those require testing on the actual RK1/carrier/TV combination.
|
||||
- Kodi is pinned to a mainline commit because the current Rockchip GBM work is
|
||||
newer than a stable Kodi release. Update the lock only as a tested set.
|
||||
@@ -0,0 +1,2 @@
|
||||
20260816.1
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
Package: rk1-media-stack
|
||||
Version: @VERSION@
|
||||
Section: video
|
||||
Priority: optional
|
||||
Architecture: arm64
|
||||
Maintainer: RK1 Media Image Builder <root@localhost>
|
||||
Installed-Size: @INSTALLED_SIZE@
|
||||
Depends: @DEPENDS@
|
||||
Recommends: armbian-firmware, libgl1-mesa-dri, mesa-vulkan-drivers
|
||||
Homepage: https://github.com/armsurvivors/kodi-rockchip-deb
|
||||
X-RKMedia-Provenance-SHA256: @LOCK_SHA256@
|
||||
Description: isolated RK3588 Kodi GBM and FFmpeg hardware media stack
|
||||
Kodi for direct GBM/GLES rendering plus a matched FFmpeg-Rockchip, MPP,
|
||||
RGA, dav1d, and libdisplay-info runtime. Custom software is isolated under
|
||||
/opt/rkmedia and does not replace the distribution FFmpeg packages.
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
if ! getent group kodi >/dev/null 2>&1; then
|
||||
addgroup --system kodi
|
||||
fi
|
||||
if ! getent passwd kodi >/dev/null 2>&1; then
|
||||
adduser --system --ingroup kodi --home /var/lib/kodi --no-create-home \
|
||||
--disabled-login --shell /usr/sbin/nologin kodi
|
||||
fi
|
||||
|
||||
for group in video render audio input; do
|
||||
if getent group "${group}" >/dev/null 2>&1; then
|
||||
adduser kodi "${group}" >/dev/null
|
||||
fi
|
||||
done
|
||||
|
||||
systemd-tmpfiles --create rkmedia.conf >/dev/null 2>&1 || true
|
||||
udevadm control --reload-rules >/dev/null 2>&1 || true
|
||||
udevadm trigger --action=change --subsystem-match=drm >/dev/null 2>&1 || true
|
||||
udevadm trigger --action=change --subsystem-match=dma_heap >/dev/null 2>&1 || true
|
||||
udevadm trigger --action=change --subsystem-match=misc >/dev/null 2>&1 || true
|
||||
udevadm settle >/dev/null 2>&1 || true
|
||||
|
||||
if command -v deb-systemd-helper >/dev/null 2>&1; then
|
||||
deb-systemd-helper unmask kodi-rk.service >/dev/null || true
|
||||
deb-systemd-helper enable kodi-rk.service >/dev/null || true
|
||||
fi
|
||||
if [ -d /run/systemd/system ]; then
|
||||
systemctl daemon-reload >/dev/null || true
|
||||
fi
|
||||
|
||||
exit 0
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
if command -v deb-systemd-helper >/dev/null 2>&1; then
|
||||
if [ "$1" = purge ]; then
|
||||
deb-systemd-helper purge kodi-rk.service >/dev/null || true
|
||||
fi
|
||||
fi
|
||||
if [ -d /run/systemd/system ]; then
|
||||
systemctl daemon-reload >/dev/null || true
|
||||
fi
|
||||
|
||||
# Deliberately retain /var/lib/kodi and the locked account on removal. The
|
||||
# media database and settings must never be deleted by a package uninstall.
|
||||
exit 0
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$1" = remove ] && [ -d /run/systemd/system ]; then
|
||||
systemctl --no-reload stop kodi-rk.service >/dev/null 2>&1 || true
|
||||
fi
|
||||
exit 0
|
||||
@@ -0,0 +1,8 @@
|
||||
# Environment loaded by kodi-rk.service. This file is preserved across package
|
||||
# upgrades. Kodi command-line arguments live in the unit so values here never
|
||||
# undergo unsafe shell word splitting.
|
||||
KODI_DATA=/var/lib/kodi/.kodi
|
||||
CRASHLOG_DIR=/var/lib/kodi
|
||||
XDG_CACHE_HOME=/var/cache/kodi
|
||||
MESA_SHADER_CACHE_DIR=/var/cache/kodi/mesa_shader_cache
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
prefix=/opt/rkmedia
|
||||
export LD_LIBRARY_PATH="${prefix}/lib${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
|
||||
exec "${prefix}/bin/ffmpeg" "$@"
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
prefix=/opt/rkmedia
|
||||
export LD_LIBRARY_PATH="${prefix}/lib${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
|
||||
exec "${prefix}/bin/ffprobe" "$@"
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
prefix=/opt/rkmedia
|
||||
data_dir="${KODI_DATA:-${HOME}/.kodi}"
|
||||
userdata_dir="${data_dir}/userdata"
|
||||
|
||||
export PATH="${prefix}/bin:${PATH}"
|
||||
export LD_LIBRARY_PATH="${prefix}/lib${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
|
||||
|
||||
mkdir -p "${userdata_dir}"
|
||||
if [ ! -e "${userdata_dir}/guisettings.xml" ]; then
|
||||
cp /usr/share/rkmedia/kodi/guisettings.xml "${userdata_dir}/guisettings.xml"
|
||||
fi
|
||||
|
||||
if [ -x "${prefix}/bin/kodi" ]; then
|
||||
kodi_launcher="${prefix}/bin/kodi"
|
||||
elif [ -x /usr/local/bin/kodi ]; then
|
||||
kodi_launcher=/usr/local/bin/kodi
|
||||
else
|
||||
printf 'kodi-rk: no Kodi launcher found under /opt/rkmedia or /usr/local\n' >&2
|
||||
exit 127
|
||||
fi
|
||||
|
||||
exec "${kodi_launcher}" "$@"
|
||||
@@ -0,0 +1,51 @@
|
||||
[Unit]
|
||||
Description=Kodi RK3588 GBM appliance
|
||||
Documentation=https://github.com/armsurvivors/kodi-rockchip-deb
|
||||
Wants=network-online.target
|
||||
After=systemd-user-sessions.service network-online.target sound.target
|
||||
Conflicts=display-manager.service [email protected]
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=kodi
|
||||
Group=kodi
|
||||
SupplementaryGroups=video render audio input
|
||||
Environment=HOME=/var/lib/kodi
|
||||
Environment=XDG_RUNTIME_DIR=/run/kodi
|
||||
Environment=LD_LIBRARY_PATH=/opt/rkmedia/lib
|
||||
EnvironmentFile=-/etc/rkmedia/kodi.env
|
||||
RuntimeDirectory=kodi
|
||||
RuntimeDirectoryMode=0700
|
||||
StateDirectory=kodi
|
||||
StateDirectoryMode=0750
|
||||
CacheDirectory=kodi
|
||||
CacheDirectoryMode=0750
|
||||
PAMName=login
|
||||
UtmpIdentifier=tty1
|
||||
UtmpMode=user
|
||||
StandardInput=tty
|
||||
TTYPath=/dev/tty1
|
||||
TTYReset=yes
|
||||
TTYVHangup=yes
|
||||
TTYVTDisallocate=yes
|
||||
ExecStart=/opt/rkmedia/bin/kodi-rk --standalone --logging=console --windowing=gbm --audio-backend=alsa
|
||||
Restart=always
|
||||
RestartSec=2s
|
||||
TimeoutStopSec=15s
|
||||
LimitNOFILE=16384
|
||||
NoNewPrivileges=yes
|
||||
PrivateTmp=yes
|
||||
ProtectSystem=full
|
||||
ProtectHome=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectControlGroups=yes
|
||||
RestrictNamespaces=yes
|
||||
RestrictSUIDSGID=yes
|
||||
LockPersonality=yes
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
|
||||
CapabilityBoundingSet=CAP_SYS_TTY_CONFIG
|
||||
AmbientCapabilities=CAP_SYS_TTY_CONFIG
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,4 @@
|
||||
d /var/lib/kodi 0750 kodi kodi -
|
||||
d /var/lib/kodi/.kodi 0750 kodi kodi -
|
||||
d /var/cache/kodi 0750 kodi kodi -
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Primary and render DRM nodes used by Kodi GBM and DRM PRIME.
|
||||
SUBSYSTEM=="drm", KERNEL=="card[0-9]*", GROUP="video", MODE="0660", TAG+="uaccess"
|
||||
SUBSYSTEM=="drm", KERNEL=="renderD[0-9]*", GROUP="render", MODE="0660", TAG+="uaccess"
|
||||
|
||||
# Rockchip vendor-kernel media accelerators. Optional compatibility names are
|
||||
# included because their exact node names vary across BSP kernel revisions.
|
||||
SUBSYSTEM=="dma_heap", GROUP="video", MODE="0660"
|
||||
KERNEL=="rga", GROUP="video", MODE="0660"
|
||||
KERNEL=="iep", GROUP="video", MODE="0660"
|
||||
KERNEL=="mpp_service", GROUP="video", MODE="0660"
|
||||
KERNEL=="mpp-service", GROUP="video", MODE="0660"
|
||||
KERNEL=="vpu_service", GROUP="video", MODE="0660"
|
||||
KERNEL=="vpu-service", GROUP="video", MODE="0660"
|
||||
KERNEL=="hevc_service", GROUP="video", MODE="0660"
|
||||
KERNEL=="hevc-service", GROUP="video", MODE="0660"
|
||||
KERNEL=="rkvdec", GROUP="video", MODE="0660"
|
||||
KERNEL=="rkvenc", GROUP="video", MODE="0660"
|
||||
KERNEL=="vepu", GROUP="video", MODE="0660"
|
||||
KERNEL=="h265e", GROUP="video", MODE="0660"
|
||||
|
||||
# Rockchip NPU control/render compatibility nodes used by RKNN diagnostics.
|
||||
KERNEL=="rknpu*", GROUP="render", MODE="0660"
|
||||
@@ -0,0 +1,5 @@
|
||||
<settings version="2">
|
||||
<setting id="videoplayer.useprimedecoder">true</setting>
|
||||
<setting id="videoplayer.useprimedecoderforhw" default="true">true</setting>
|
||||
<setting id="videoplayer.useprimerenderer">0</setting>
|
||||
</settings>
|
||||
Executable
+226
@@ -0,0 +1,226 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
# shellcheck source=lib.sh
|
||||
. "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
LOCK_FILE="${RKMEDIA_ROOT}/sources.lock.json"
|
||||
WORK_DIR="${RKMEDIA_ROOT}/out/work"
|
||||
SOURCE_DIR="${RKMEDIA_ROOT}/out/sources"
|
||||
OUTPUT_DIR="${RKMEDIA_ROOT}/out"
|
||||
JOBS="$(nproc)"
|
||||
FETCH=1
|
||||
PACKAGE_VERSION="$(tr -d '[:space:]' < "${RKMEDIA_ROOT}/VERSION")"
|
||||
RUNTIME_PREFIX=/opt/rkmedia
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: build-media.sh [options]
|
||||
|
||||
--source-dir DIR Locked source checkouts (default: out/sources)
|
||||
--work-dir DIR Build work directory (default: out/work)
|
||||
--output-dir DIR Package output directory (default: out)
|
||||
--jobs N Parallel jobs (default: nproc)
|
||||
--no-fetch Require sources to exist; never access the network
|
||||
--version VERSION Debian package version (default: VERSION file)
|
||||
EOF
|
||||
}
|
||||
|
||||
while (($#)); do
|
||||
case "$1" in
|
||||
--source-dir) [[ $# -ge 2 ]] || die "--source-dir requires a directory"; SOURCE_DIR="$2"; shift 2 ;;
|
||||
--work-dir) [[ $# -ge 2 ]] || die "--work-dir requires a directory"; WORK_DIR="$2"; shift 2 ;;
|
||||
--output-dir) [[ $# -ge 2 ]] || die "--output-dir requires a directory"; OUTPUT_DIR="$2"; shift 2 ;;
|
||||
--jobs) [[ $# -ge 2 ]] || die "--jobs requires a number"; JOBS="$2"; shift 2 ;;
|
||||
--no-fetch) FETCH=0; shift ;;
|
||||
--version) [[ $# -ge 2 ]] || die "--version requires a value"; PACKAGE_VERSION="$2"; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown argument: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ "${JOBS}" =~ ^[1-9][0-9]*$ ]] || die "--jobs must be a positive integer"
|
||||
[[ "${PACKAGE_VERSION}" =~ ^[0-9A-Za-z.+:~-]+$ ]] || die "invalid Debian package version: ${PACKAGE_VERSION}"
|
||||
require_arm64_trixie
|
||||
for command_name in cmake git jq meson ninja pkg-config make patch readelf strip; do
|
||||
need_command "${command_name}"
|
||||
done
|
||||
|
||||
SOURCE_DIR="$(absolute_path "${SOURCE_DIR}")"
|
||||
WORK_DIR="$(absolute_path "${WORK_DIR}")"
|
||||
OUTPUT_DIR="$(absolute_path "${OUTPUT_DIR}")"
|
||||
mkdir -p -- "${SOURCE_DIR}" "${WORK_DIR}" "${OUTPUT_DIR}"
|
||||
|
||||
if ((FETCH)); then
|
||||
"${SCRIPT_DIR}/fetch-sources.sh" --lock "${LOCK_FILE}" --dest "${SOURCE_DIR}"
|
||||
fi
|
||||
|
||||
while IFS=$'\t' read -r name commit; do
|
||||
dir="${SOURCE_DIR}/${name}"
|
||||
[[ -d "${dir}/.git" ]] || die "missing locked source: ${dir}"
|
||||
[[ "$(git -C "${dir}" rev-parse HEAD)" == "${commit}" ]] || die "source commit mismatch: ${name}"
|
||||
done < <(jq -r '.sources[] | select(.build == true) | [.name, .commit] | @tsv' "${LOCK_FILE}")
|
||||
|
||||
BUILD_ROOT="${WORK_DIR}/build"
|
||||
BUILD_PREFIX="${WORK_DIR}/prefix"
|
||||
STAGE_DIR="${WORK_DIR}/stage"
|
||||
reset_child_dir "${WORK_DIR}" "${BUILD_ROOT}"
|
||||
reset_child_dir "${WORK_DIR}" "${BUILD_PREFIX}"
|
||||
reset_child_dir "${WORK_DIR}" "${STAGE_DIR}"
|
||||
|
||||
export SOURCE_DATE_EPOCH
|
||||
SOURCE_DATE_EPOCH="$(jq -er '.source_date_epoch' "${LOCK_FILE}")"
|
||||
export CFLAGS="${CFLAGS:--O2 -pipe -ffile-prefix-map=${WORK_DIR}=. -fdebug-prefix-map=${WORK_DIR}=.}"
|
||||
export CXXFLAGS="${CXXFLAGS:--O2 -pipe -ffile-prefix-map=${WORK_DIR}=. -fdebug-prefix-map=${WORK_DIR}=.}"
|
||||
export LDFLAGS="${LDFLAGS:-}"
|
||||
|
||||
log "building MPP"
|
||||
cmake -S "${SOURCE_DIR}/mpp" -B "${BUILD_ROOT}/mpp" -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="${BUILD_PREFIX}" \
|
||||
-DCMAKE_INSTALL_LIBDIR=lib \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DBUILD_TEST=OFF
|
||||
cmake --build "${BUILD_ROOT}/mpp" --parallel "${JOBS}"
|
||||
cmake --install "${BUILD_ROOT}/mpp"
|
||||
|
||||
log "building RGA"
|
||||
meson setup "${BUILD_ROOT}/rga" "${SOURCE_DIR}/rga" \
|
||||
--prefix="${BUILD_PREFIX}" \
|
||||
--libdir=lib \
|
||||
--buildtype=release \
|
||||
--default-library=shared \
|
||||
-Dcpp_args=-fpermissive \
|
||||
-Dlibdrm=false \
|
||||
-Dlibrga_demo=false
|
||||
meson compile -C "${BUILD_ROOT}/rga" -j "${JOBS}"
|
||||
meson install -C "${BUILD_ROOT}/rga"
|
||||
|
||||
log "building dav1d"
|
||||
meson setup "${BUILD_ROOT}/dav1d" "${SOURCE_DIR}/dav1d" \
|
||||
--prefix="${BUILD_PREFIX}" \
|
||||
--libdir=lib \
|
||||
--buildtype=release \
|
||||
--default-library=shared \
|
||||
-Denable_tools=false \
|
||||
-Denable_tests=false \
|
||||
-Denable_examples=false \
|
||||
-Denable_docs=false
|
||||
meson compile -C "${BUILD_ROOT}/dav1d" -j "${JOBS}"
|
||||
meson install -C "${BUILD_ROOT}/dav1d"
|
||||
|
||||
export PKG_CONFIG_PATH="${BUILD_PREFIX}/lib/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}"
|
||||
export LD_LIBRARY_PATH="${BUILD_PREFIX}/lib${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
|
||||
|
||||
log "building FFmpeg-Rockchip"
|
||||
mkdir -p -- "${BUILD_ROOT}/ffmpeg"
|
||||
pushd "${BUILD_ROOT}/ffmpeg" >/dev/null
|
||||
"${SOURCE_DIR}/ffmpeg-rockchip/configure" \
|
||||
--prefix="${BUILD_PREFIX}" \
|
||||
--libdir="${BUILD_PREFIX}/lib" \
|
||||
--enable-shared \
|
||||
--disable-static \
|
||||
--disable-debug \
|
||||
--disable-doc \
|
||||
--enable-pic \
|
||||
--enable-gpl \
|
||||
--enable-version3 \
|
||||
--enable-libdrm \
|
||||
--enable-rkmpp \
|
||||
--enable-rkrga \
|
||||
--enable-libdav1d \
|
||||
--extra-cflags="-I${BUILD_PREFIX}/include" \
|
||||
--extra-ldflags="-L${BUILD_PREFIX}/lib -Wl,-rpath,${RUNTIME_PREFIX}/lib"
|
||||
make -j "${JOBS}"
|
||||
make install
|
||||
popd >/dev/null
|
||||
|
||||
FFMPEG_BUILD="${BUILD_PREFIX}/bin/ffmpeg"
|
||||
for decoder in h264_rkmpp hevc_rkmpp vp9_rkmpp av1_rkmpp mjpeg_rkmpp; do
|
||||
"${FFMPEG_BUILD}" -hide_banner -decoders 2>/dev/null | awk '{print $2}' | grep -Fxq "${decoder}" \
|
||||
|| die "FFmpeg is missing decoder ${decoder}"
|
||||
done
|
||||
for encoder in h264_rkmpp hevc_rkmpp mjpeg_rkmpp; do
|
||||
"${FFMPEG_BUILD}" -hide_banner -encoders 2>/dev/null | awk '{print $2}' | grep -Fxq "${encoder}" \
|
||||
|| die "FFmpeg is missing encoder ${encoder}"
|
||||
done
|
||||
for filter in scale_rkrga vpp_rkrga overlay_rkrga; do
|
||||
"${FFMPEG_BUILD}" -hide_banner -filters 2>/dev/null | awk '{print $2}' | grep -Fxq "${filter}" \
|
||||
|| die "FFmpeg is missing filter ${filter}"
|
||||
done
|
||||
|
||||
log "building libdisplay-info"
|
||||
meson setup "${BUILD_ROOT}/libdisplay-info" "${SOURCE_DIR}/libdisplay-info" \
|
||||
--prefix="${BUILD_PREFIX}" \
|
||||
--libdir=lib \
|
||||
--buildtype=release \
|
||||
--default-library=shared
|
||||
meson compile -C "${BUILD_ROOT}/libdisplay-info" -j "${JOBS}"
|
||||
meson install -C "${BUILD_ROOT}/libdisplay-info"
|
||||
|
||||
log "building Kodi for GBM/GLES with external FFmpeg-Rockchip"
|
||||
cmake -S "${SOURCE_DIR}/kodi" -B "${BUILD_ROOT}/kodi" -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="${RUNTIME_PREFIX}" \
|
||||
-DCMAKE_INSTALL_RPATH="${RUNTIME_PREFIX}/lib" \
|
||||
-DCMAKE_BUILD_RPATH="${BUILD_PREFIX}/lib" \
|
||||
-DCMAKE_PREFIX_PATH="${BUILD_PREFIX}" \
|
||||
-DCORE_PLATFORM_NAME=gbm \
|
||||
-DAPP_RENDER_SYSTEM=gles \
|
||||
-DENABLE_INTERNAL_FFMPEG=OFF \
|
||||
-DFFMPEG_PATH="${BUILD_PREFIX}" \
|
||||
-DENABLE_INTERNAL_FMT=ON \
|
||||
-DENABLE_INTERNAL_FLATBUFFERS=ON
|
||||
cmake --build "${BUILD_ROOT}/kodi" --parallel "${JOBS}"
|
||||
|
||||
mkdir -p -- "${STAGE_DIR}${RUNTIME_PREFIX}"
|
||||
cp -a -- "${BUILD_PREFIX}/." "${STAGE_DIR}${RUNTIME_PREFIX}/"
|
||||
DESTDIR="${STAGE_DIR}" cmake --install "${BUILD_ROOT}/kodi"
|
||||
|
||||
log "adding runtime integration and source provenance"
|
||||
cp -a -- "${RKMEDIA_ROOT}/packaging/rootfs/." "${STAGE_DIR}/"
|
||||
mkdir -p -- "${STAGE_DIR}/usr/bin" "${STAGE_DIR}/usr/share/doc/rk1-media-stack/licenses"
|
||||
ln -sfn /opt/rkmedia/bin/ffmpeg-rk "${STAGE_DIR}/usr/bin/ffmpeg-rk"
|
||||
ln -sfn /opt/rkmedia/bin/ffprobe-rk "${STAGE_DIR}/usr/bin/ffprobe-rk"
|
||||
ln -sfn /opt/rkmedia/bin/kodi-rk "${STAGE_DIR}/usr/bin/kodi-rk"
|
||||
install -m 0644 "${LOCK_FILE}" "${STAGE_DIR}/usr/share/doc/rk1-media-stack/sources.lock.json"
|
||||
|
||||
while IFS=$'\t' read -r name license_path; do
|
||||
source_license="${SOURCE_DIR}/${name}/${license_path}"
|
||||
destination="${STAGE_DIR}/usr/share/doc/rk1-media-stack/licenses/${name}"
|
||||
[[ -e "${source_license}" ]] || die "locked license path is missing: ${name}/${license_path}"
|
||||
mkdir -p -- "${destination}"
|
||||
cp -a -- "${source_license}" "${destination}/"
|
||||
done < <(jq -r '.sources[] | select(.build == true) as $source | $source.license_files[] | [$source.name, .] | @tsv' "${LOCK_FILE}")
|
||||
|
||||
rm -rf -- \
|
||||
"${STAGE_DIR}${RUNTIME_PREFIX}/include" \
|
||||
"${STAGE_DIR}${RUNTIME_PREFIX}/lib/pkgconfig" \
|
||||
"${STAGE_DIR}${RUNTIME_PREFIX}/share/pkgconfig"
|
||||
find "${STAGE_DIR}${RUNTIME_PREFIX}" -type f \( -name '*.a' -o -name '*.la' \) -delete
|
||||
|
||||
while IFS= read -r -d '' candidate; do
|
||||
if file --brief "${candidate}" | grep -q '^ELF '; then
|
||||
strip --strip-unneeded "${candidate}" || die "failed to strip ${candidate}"
|
||||
fi
|
||||
done < <(find "${STAGE_DIR}${RUNTIME_PREFIX}" -type f -print0)
|
||||
|
||||
for required in \
|
||||
"${STAGE_DIR}${RUNTIME_PREFIX}/bin/ffmpeg" \
|
||||
"${STAGE_DIR}${RUNTIME_PREFIX}/bin/ffprobe" \
|
||||
"${STAGE_DIR}${RUNTIME_PREFIX}/bin/kodi" \
|
||||
"${STAGE_DIR}/usr/lib/systemd/system/kodi-rk.service"; do
|
||||
[[ -e "${required}" ]] || die "staged runtime file is missing: ${required}"
|
||||
done
|
||||
|
||||
if find "${STAGE_DIR}${RUNTIME_PREFIX}" -type f -exec grep -IlF "${WORK_DIR}" {} + | grep -q .; then
|
||||
die "build path leaked into a staged text file"
|
||||
fi
|
||||
|
||||
"${SCRIPT_DIR}/package-media.sh" \
|
||||
--stage "${STAGE_DIR}" \
|
||||
--output-dir "${OUTPUT_DIR}" \
|
||||
--version "${PACKAGE_VERSION}"
|
||||
|
||||
log "media build complete"
|
||||
Executable
+85
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
# shellcheck source=lib.sh
|
||||
. "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
LOCK_FILE="${RKMEDIA_ROOT}/sources.lock.json"
|
||||
DEST_DIR="${RKMEDIA_ROOT}/out/sources"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: fetch-sources.sh [--lock FILE] [--dest DIRECTORY]
|
||||
|
||||
Fetch every build input at the exact commit recorded in the source lock.
|
||||
Existing repositories are accepted only when their origin and HEAD match.
|
||||
EOF
|
||||
}
|
||||
|
||||
while (($#)); do
|
||||
case "$1" in
|
||||
--lock)
|
||||
[[ $# -ge 2 ]] || die "--lock requires a file"
|
||||
LOCK_FILE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--dest)
|
||||
[[ $# -ge 2 ]] || die "--dest requires a directory"
|
||||
DEST_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*) die "unknown argument: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
need_command git
|
||||
need_command jq
|
||||
LOCK_FILE="$(absolute_path "${LOCK_FILE}")"
|
||||
DEST_DIR="$(absolute_path "${DEST_DIR}")"
|
||||
[[ -f "${LOCK_FILE}" ]] || die "source lock not found: ${LOCK_FILE}"
|
||||
jq -e '.schema_version == 1 and (.sources | type == "array")' "${LOCK_FILE}" >/dev/null \
|
||||
|| die "invalid source lock: ${LOCK_FILE}"
|
||||
mkdir -p -- "${DEST_DIR}"
|
||||
|
||||
verify_checkout() {
|
||||
local dir="$1" expected_repo="$2" expected_commit="$3"
|
||||
local actual_repo actual_commit
|
||||
[[ -d "${dir}/.git" ]] || return 1
|
||||
actual_repo="$(git -C "${dir}" remote get-url origin 2>/dev/null || true)"
|
||||
actual_commit="$(git -C "${dir}" rev-parse HEAD 2>/dev/null || true)"
|
||||
[[ "${actual_repo}" == "${expected_repo}" && "${actual_commit}" == "${expected_commit}" ]]
|
||||
}
|
||||
|
||||
while IFS=$'\t' read -r name repository ref commit; do
|
||||
[[ "${name}" =~ ^[a-z0-9][a-z0-9._-]*$ ]] || die "unsafe source name in lock: ${name}"
|
||||
[[ "${commit}" =~ ^[0-9a-f]{40}$ ]] || die "invalid commit for ${name}: ${commit}"
|
||||
source_dir="${DEST_DIR}/${name}"
|
||||
|
||||
if verify_checkout "${source_dir}" "${repository}" "${commit}"; then
|
||||
log "source already verified: ${name} ${commit}"
|
||||
continue
|
||||
fi
|
||||
if [[ -e "${source_dir}" ]]; then
|
||||
die "${source_dir} exists but does not match the lock; move it aside and retry"
|
||||
fi
|
||||
|
||||
log "fetching ${name} at ${commit}"
|
||||
mkdir -p -- "${source_dir}"
|
||||
git -C "${source_dir}" init --quiet
|
||||
git -C "${source_dir}" remote add origin "${repository}"
|
||||
if ! git -C "${source_dir}" fetch --quiet --depth=1 origin "${commit}"; then
|
||||
log "direct commit fetch was unavailable; fetching history from ${ref}"
|
||||
git -C "${source_dir}" fetch --quiet --filter=blob:none origin "${ref}"
|
||||
fi
|
||||
git -C "${source_dir}" checkout --quiet --detach "${commit}"
|
||||
verify_checkout "${source_dir}" "${repository}" "${commit}" \
|
||||
|| die "checkout verification failed for ${name}"
|
||||
done < <(jq -r '.sources[] | select(.build == true) | [.name, .repository, .ref, .commit] | @tsv' "${LOCK_FILE}")
|
||||
|
||||
log "all locked sources are present in ${DEST_DIR}"
|
||||
|
||||
Executable
+63
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
# shellcheck source=lib.sh
|
||||
. "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
MODE=check
|
||||
case "${1:---check}" in
|
||||
--check) MODE=check ;;
|
||||
--install) MODE=install ;;
|
||||
-h|--help)
|
||||
printf 'Usage: install-build-deps.sh [--check|--install]\n'
|
||||
exit 0
|
||||
;;
|
||||
*) die "unknown argument: $1" ;;
|
||||
esac
|
||||
|
||||
require_arm64_trixie
|
||||
need_command dpkg-query
|
||||
|
||||
packages=(
|
||||
adduser autoconf automake autopoint autotools-dev build-essential ca-certificates
|
||||
cmake default-jre devscripts dpkg-dev file gawk gcc g++ gdc gettext git gperf
|
||||
hwdata jq libasound2-dev libass-dev libavahi-client-dev libavahi-common-dev
|
||||
libbluetooth-dev libbluray-dev libbz2-dev libcdio++-dev libcdio-dev libcec-dev
|
||||
libcrossguid-dev libcwiid-dev libcurl4-openssl-dev libdbus-1-dev libdrm-dev
|
||||
libegl1-mesa-dev libenca-dev libexiv2-dev libflac-dev libfmt-dev
|
||||
libfontconfig-dev libfreetype6-dev libfribidi-dev libfstrcmp-dev libgbm-dev
|
||||
libgcrypt20-dev libgif-dev libgl1-mesa-dev libgles2-mesa-dev libglu1-mesa-dev
|
||||
libgnutls28-dev libgpg-error-dev libgtest-dev libinput-dev libiso9660-dev
|
||||
libjpeg-dev liblcms2-dev liblirc-dev libltdl-dev liblzo2-dev libmariadb-dev
|
||||
libmicrohttpd-dev libnfs-dev libogg-dev libp8-platform-dev libpcre2-dev
|
||||
libplist-dev libpng-dev libpulse-dev libshairplay-dev libsmbclient-dev
|
||||
libspdlog-dev libsqlite3-dev libssl-dev libtag1-dev libtiff-dev
|
||||
libtinyxml2-dev libtinyxml-dev libtool libudev-dev libunistring-dev
|
||||
libvorbis-dev libxkbcommon-dev libxslt1-dev libxt-dev lsb-release meson nasm
|
||||
ninja-build nlohmann-json3-dev patch pkg-config python3-dev python3-pil
|
||||
python3-pip rapidjson-dev swig unzip uuid-dev zip zlib1g-dev
|
||||
)
|
||||
|
||||
missing=()
|
||||
for package in "${packages[@]}"; do
|
||||
if ! dpkg-query -W -f='${db:Status-Abbrev}' "${package}" 2>/dev/null | grep -q '^ii '; then
|
||||
missing+=("${package}")
|
||||
fi
|
||||
done
|
||||
|
||||
if ((${#missing[@]} == 0)); then
|
||||
log "all build dependencies are installed"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "${MODE}" == "check" ]]; then
|
||||
printf 'Missing build packages:\n'
|
||||
printf ' %s\n' "${missing[@]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
[[ "${EUID}" -eq 0 ]] || die "--install must run as root (use sudo)"
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends "${missing[@]}"
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
RKMEDIA_SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
RKMEDIA_ROOT="$(cd -- "${RKMEDIA_SCRIPT_DIR}/.." && pwd -P)"
|
||||
|
||||
log() {
|
||||
printf '[rkmedia] %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
die() {
|
||||
printf '[rkmedia] ERROR: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
need_command() {
|
||||
command -v "$1" >/dev/null 2>&1 || die "required command not found: $1"
|
||||
}
|
||||
|
||||
absolute_path() {
|
||||
realpath -m -- "$1"
|
||||
}
|
||||
|
||||
assert_child_path() {
|
||||
local parent child
|
||||
parent="$(absolute_path "$1")"
|
||||
child="$(absolute_path "$2")"
|
||||
case "${child}" in
|
||||
"${parent}"/*) ;;
|
||||
*) die "refusing operation outside ${parent}: ${child}" ;;
|
||||
esac
|
||||
[[ "${child}" != "${parent}" ]] || die "refusing operation on parent directory itself: ${parent}"
|
||||
}
|
||||
|
||||
reset_child_dir() {
|
||||
local parent="$1"
|
||||
local child="$2"
|
||||
assert_child_path "${parent}" "${child}"
|
||||
rm -rf -- "${child}"
|
||||
mkdir -p -- "${child}"
|
||||
}
|
||||
|
||||
require_arm64_trixie() {
|
||||
local arch codename
|
||||
arch="$(dpkg --print-architecture 2>/dev/null || true)"
|
||||
codename=""
|
||||
if [[ -r /etc/os-release ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
. /etc/os-release
|
||||
codename="${VERSION_CODENAME:-}"
|
||||
fi
|
||||
|
||||
if [[ "${arch}" != "arm64" || "$(uname -m)" != "aarch64" || "${codename}" != "trixie" ]]; then
|
||||
if [[ "${RKMEDIA_ALLOW_UNSUPPORTED_HOST:-0}" == "1" ]]; then
|
||||
log "warning: unsupported build host (dpkg=${arch}, uname=$(uname -m), suite=${codename:-unknown})"
|
||||
return
|
||||
fi
|
||||
die "native Debian Trixie arm64 is required (set RKMEDIA_ALLOW_UNSUPPORTED_HOST=1 only for development)"
|
||||
fi
|
||||
}
|
||||
|
||||
Executable
+134
@@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
umask 022
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
# shellcheck source=lib.sh
|
||||
. "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
STAGE_DIR=""
|
||||
OUTPUT_DIR="${RKMEDIA_ROOT}/out"
|
||||
PACKAGE_VERSION="$(tr -d '[:space:]' < "${RKMEDIA_ROOT}/VERSION")"
|
||||
DEPENDS_FROM_DEB=""
|
||||
|
||||
usage() {
|
||||
printf 'Usage: package-media.sh --stage DIRECTORY [--output-dir DIRECTORY] [--version VERSION] [--depends-from-deb FILE]\n'
|
||||
}
|
||||
|
||||
while (($#)); do
|
||||
case "$1" in
|
||||
--stage) [[ $# -ge 2 ]] || die "--stage requires a directory"; STAGE_DIR="$2"; shift 2 ;;
|
||||
--output-dir) [[ $# -ge 2 ]] || die "--output-dir requires a directory"; OUTPUT_DIR="$2"; shift 2 ;;
|
||||
--version) [[ $# -ge 2 ]] || die "--version requires a value"; PACKAGE_VERSION="$2"; shift 2 ;;
|
||||
--depends-from-deb) [[ $# -ge 2 ]] || die "--depends-from-deb requires a file"; DEPENDS_FROM_DEB="$2"; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown argument: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -n "${STAGE_DIR}" ]] || die "--stage is required"
|
||||
[[ "${PACKAGE_VERSION}" =~ ^[0-9A-Za-z.+:~-]+$ ]] || die "invalid Debian package version: ${PACKAGE_VERSION}"
|
||||
for command_name in dpkg-deb dpkg-shlibdeps file jq md5sum sed sha256sum; do
|
||||
need_command "${command_name}"
|
||||
done
|
||||
|
||||
STAGE_DIR="$(absolute_path "${STAGE_DIR}")"
|
||||
OUTPUT_DIR="$(absolute_path "${OUTPUT_DIR}")"
|
||||
[[ -x "${STAGE_DIR}/opt/rkmedia/bin/ffmpeg" ]] || die "stage does not contain the RKMedia runtime"
|
||||
mkdir -p -- "${OUTPUT_DIR}"
|
||||
|
||||
PACKAGE_ROOT="${OUTPUT_DIR}/package-root"
|
||||
reset_child_dir "${OUTPUT_DIR}" "${PACKAGE_ROOT}"
|
||||
cp -a -- "${STAGE_DIR}/." "${PACKAGE_ROOT}/"
|
||||
mkdir -p -- "${PACKAGE_ROOT}/DEBIAN" "${PACKAGE_ROOT}/usr/share/doc/rk1-media-stack"
|
||||
|
||||
cp -- "${RKMEDIA_ROOT}/packaging/postinst" "${PACKAGE_ROOT}/DEBIAN/postinst"
|
||||
cp -- "${RKMEDIA_ROOT}/packaging/prerm" "${PACKAGE_ROOT}/DEBIAN/prerm"
|
||||
cp -- "${RKMEDIA_ROOT}/packaging/postrm" "${PACKAGE_ROOT}/DEBIAN/postrm"
|
||||
chmod 0755 "${PACKAGE_ROOT}/DEBIAN/postinst" "${PACKAGE_ROOT}/DEBIAN/prerm" "${PACKAGE_ROOT}/DEBIAN/postrm"
|
||||
printf '/etc/rkmedia/kodi.env\n' > "${PACKAGE_ROOT}/DEBIAN/conffiles"
|
||||
|
||||
# A collaborative workspace or permissive host umask must never make core
|
||||
# target directories such as /etc, /usr, or /opt group-writable via dpkg.
|
||||
find "${PACKAGE_ROOT}" -type d -exec chmod 0755 {} +
|
||||
find "${PACKAGE_ROOT}" -type f ! -perm /0111 -exec chmod 0644 {} +
|
||||
find "${PACKAGE_ROOT}" -type f -perm /0111 -exec chmod 0755 {} +
|
||||
if find "${PACKAGE_ROOT}" \( -type d -o -type f \) -perm /0022 -print -quit \
|
||||
| grep -q .; then
|
||||
die "package contains a group/world-writable path"
|
||||
fi
|
||||
|
||||
if [[ -n "${DEPENDS_FROM_DEB}" ]]; then
|
||||
DEPENDS_FROM_DEB="$(absolute_path "${DEPENDS_FROM_DEB}")"
|
||||
[[ -f "${DEPENDS_FROM_DEB}" ]] || die "dependency source package not found: ${DEPENDS_FROM_DEB}"
|
||||
shlib_depends="$(dpkg-deb --field "${DEPENDS_FROM_DEB}" Depends)"
|
||||
[[ -n "${shlib_depends}" ]] || die "upstream package has an empty Depends field"
|
||||
else
|
||||
analysis_root="${OUTPUT_DIR}/shlibdeps"
|
||||
reset_child_dir "${OUTPUT_DIR}" "${analysis_root}"
|
||||
mkdir -p -- "${analysis_root}/debian"
|
||||
cat > "${analysis_root}/debian/control" <<'EOF'
|
||||
Source: rk1-media-stack
|
||||
Section: video
|
||||
Priority: optional
|
||||
|
||||
Package: rk1-media-stack
|
||||
Architecture: arm64
|
||||
Description: temporary metadata for dpkg-shlibdeps
|
||||
EOF
|
||||
|
||||
elf_files=()
|
||||
while IFS= read -r -d '' candidate; do
|
||||
if file --brief "${candidate}" | grep -q '^ELF '; then
|
||||
elf_files+=("${candidate}")
|
||||
fi
|
||||
done < <(find "${PACKAGE_ROOT}/opt/rkmedia" -type f -print0)
|
||||
((${#elf_files[@]} > 0)) || die "no ELF files found in media stage"
|
||||
|
||||
pushd "${analysis_root}" >/dev/null
|
||||
shlib_output="$(dpkg-shlibdeps --ignore-missing-info -O \
|
||||
-l"${PACKAGE_ROOT}/opt/rkmedia/lib" "${elf_files[@]}")"
|
||||
popd >/dev/null
|
||||
shlib_depends="${shlib_output#shlibs:Depends=}"
|
||||
[[ -n "${shlib_depends}" && "${shlib_depends}" != "${shlib_output}" ]] \
|
||||
|| die "dpkg-shlibdeps did not produce runtime dependencies"
|
||||
fi
|
||||
|
||||
depends="adduser, systemd, udev, ${shlib_depends}"
|
||||
installed_size="$(du -sk "${PACKAGE_ROOT}" | awk '{print $1}')"
|
||||
if [[ -f "${PACKAGE_ROOT}/usr/share/doc/rk1-media-stack/upstream-deb.lock.json" ]]; then
|
||||
provenance_file="${PACKAGE_ROOT}/usr/share/doc/rk1-media-stack/upstream-deb.lock.json"
|
||||
else
|
||||
provenance_file="${PACKAGE_ROOT}/usr/share/doc/rk1-media-stack/sources.lock.json"
|
||||
fi
|
||||
lock_sha256="$(sha256sum "${provenance_file}" | awk '{print $1}')"
|
||||
|
||||
sed \
|
||||
-e "s/@VERSION@/${PACKAGE_VERSION}/g" \
|
||||
-e "s/@INSTALLED_SIZE@/${installed_size}/g" \
|
||||
-e "s/@LOCK_SHA256@/${lock_sha256}/g" \
|
||||
-e "s/@DEPENDS@/${depends}/g" \
|
||||
"${RKMEDIA_ROOT}/packaging/control.in" > "${PACKAGE_ROOT}/DEBIAN/control"
|
||||
|
||||
pushd "${PACKAGE_ROOT}" >/dev/null
|
||||
find etc opt usr -type f -print0 \
|
||||
| sort -z \
|
||||
| xargs -0 md5sum > DEBIAN/md5sums
|
||||
popd >/dev/null
|
||||
|
||||
if find "${PACKAGE_ROOT}" \( -type d -o -type f \) -perm /0022 -print -quit \
|
||||
| grep -q .; then
|
||||
die "package contains a group/world-writable path after metadata generation"
|
||||
fi
|
||||
|
||||
output_deb="${OUTPUT_DIR}/rk1-media-stack_${PACKAGE_VERSION}_arm64.deb"
|
||||
rm -f -- "${output_deb}"
|
||||
SOURCE_DATE_EPOCH="$(jq -er '.source_date_epoch' "${RKMEDIA_ROOT}/sources.lock.json")" \
|
||||
dpkg-deb --root-owner-group --build "${PACKAGE_ROOT}" "${output_deb}"
|
||||
(
|
||||
cd "${OUTPUT_DIR}"
|
||||
sha256sum "$(basename -- "${output_deb}")" \
|
||||
>"$(basename -- "${output_deb}").sha256"
|
||||
)
|
||||
dpkg-deb --info "${output_deb}" >/dev/null
|
||||
log "created ${output_deb}"
|
||||
Executable
+144
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
# shellcheck source=lib.sh
|
||||
. "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
LOCK_FILE="${RKMEDIA_ROOT}/upstream-deb.lock.json"
|
||||
WORK_DIR="${RKMEDIA_ROOT}/out/repack-work"
|
||||
OUTPUT_DIR="${RKMEDIA_ROOT}/out"
|
||||
CACHE_DIR="${RKMEDIA_ROOT}/out/downloads"
|
||||
INPUT_DEB=""
|
||||
PACKAGE_VERSION="20260721.1741+rk1.1"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: repack-upstream-deb.sh [options]
|
||||
|
||||
--deb FILE Use an existing upstream .deb instead of downloading
|
||||
--work-dir DIR Temporary extraction directory
|
||||
--output-dir DIR Repacked package output directory
|
||||
--cache-dir DIR Download cache directory
|
||||
--version VERSION Output Debian package version
|
||||
EOF
|
||||
}
|
||||
|
||||
while (($#)); do
|
||||
case "$1" in
|
||||
--deb) [[ $# -ge 2 ]] || die "--deb requires a file"; INPUT_DEB="$2"; shift 2 ;;
|
||||
--work-dir) [[ $# -ge 2 ]] || die "--work-dir requires a directory"; WORK_DIR="$2"; shift 2 ;;
|
||||
--output-dir) [[ $# -ge 2 ]] || die "--output-dir requires a directory"; OUTPUT_DIR="$2"; shift 2 ;;
|
||||
--cache-dir) [[ $# -ge 2 ]] || die "--cache-dir requires a directory"; CACHE_DIR="$2"; shift 2 ;;
|
||||
--version) [[ $# -ge 2 ]] || die "--version requires a value"; PACKAGE_VERSION="$2"; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown argument: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
for command_name in curl dpkg-deb file jq readelf sha256sum stat; do
|
||||
need_command "${command_name}"
|
||||
done
|
||||
[[ -f "${LOCK_FILE}" ]] || die "upstream asset lock is missing: ${LOCK_FILE}"
|
||||
jq -e '.schema_version == 1 and (.asset_sha256 | test("^[0-9a-f]{64}$"))' "${LOCK_FILE}" >/dev/null \
|
||||
|| die "invalid upstream asset lock"
|
||||
|
||||
WORK_DIR="$(absolute_path "${WORK_DIR}")"
|
||||
OUTPUT_DIR="$(absolute_path "${OUTPUT_DIR}")"
|
||||
CACHE_DIR="$(absolute_path "${CACHE_DIR}")"
|
||||
mkdir -p -- "${WORK_DIR}" "${OUTPUT_DIR}" "${CACHE_DIR}"
|
||||
|
||||
asset_name="$(jq -er '.asset_name' "${LOCK_FILE}")"
|
||||
asset_url="$(jq -er '.asset_url' "${LOCK_FILE}")"
|
||||
expected_size="$(jq -er '.asset_size' "${LOCK_FILE}")"
|
||||
expected_sha256="$(jq -er '.asset_sha256' "${LOCK_FILE}")"
|
||||
|
||||
if [[ -z "${INPUT_DEB}" ]]; then
|
||||
INPUT_DEB="${CACHE_DIR}/${asset_name}"
|
||||
if [[ ! -f "${INPUT_DEB}" ]]; then
|
||||
partial="${INPUT_DEB}.partial"
|
||||
rm -f -- "${partial}"
|
||||
log "downloading pinned upstream Kodi package"
|
||||
curl -L --fail --show-error --output "${partial}" "${asset_url}"
|
||||
mv -- "${partial}" "${INPUT_DEB}"
|
||||
fi
|
||||
fi
|
||||
INPUT_DEB="$(absolute_path "${INPUT_DEB}")"
|
||||
[[ -f "${INPUT_DEB}" ]] || die "upstream package not found: ${INPUT_DEB}"
|
||||
|
||||
actual_size="$(stat -c '%s' "${INPUT_DEB}")"
|
||||
actual_sha256="$(sha256sum "${INPUT_DEB}" | awk '{print $1}')"
|
||||
[[ "${actual_size}" == "${expected_size}" ]] || die "upstream package size mismatch"
|
||||
[[ "${actual_sha256}" == "${expected_sha256}" ]] || die "upstream package SHA-256 mismatch"
|
||||
[[ "$(dpkg-deb --field "${INPUT_DEB}" Package)" == "$(jq -er '.package' "${LOCK_FILE}")" ]] \
|
||||
|| die "upstream package name mismatch"
|
||||
[[ "$(dpkg-deb --field "${INPUT_DEB}" Version)" == "$(jq -er '.package_version' "${LOCK_FILE}")" ]] \
|
||||
|| die "upstream package version mismatch"
|
||||
[[ "$(dpkg-deb --field "${INPUT_DEB}" Architecture)" == "arm64" ]] \
|
||||
|| die "upstream package is not arm64"
|
||||
|
||||
STAGE_DIR="${WORK_DIR}/stage"
|
||||
reset_child_dir "${WORK_DIR}" "${STAGE_DIR}"
|
||||
dpkg-deb --extract "${INPUT_DEB}" "${STAGE_DIR}"
|
||||
|
||||
local_bin="${STAGE_DIR}/usr/local/bin"
|
||||
local_lib="${STAGE_DIR}/usr/local/lib"
|
||||
runtime_bin="${STAGE_DIR}/opt/rkmedia/bin"
|
||||
runtime_lib="${STAGE_DIR}/opt/rkmedia/lib"
|
||||
mkdir -p -- "${runtime_bin}" "${runtime_lib}"
|
||||
|
||||
for program in ffmpeg ffprobe; do
|
||||
[[ -x "${local_bin}/${program}" ]] || die "upstream package is missing ${program}"
|
||||
mv -- "${local_bin}/${program}" "${runtime_bin}/${program}"
|
||||
done
|
||||
if [[ -x "${local_bin}/di-edid-decode" ]]; then
|
||||
mv -- "${local_bin}/di-edid-decode" "${runtime_bin}/di-edid-decode"
|
||||
fi
|
||||
|
||||
shopt -s nullglob
|
||||
runtime_libraries=(
|
||||
"${local_lib}"/libdav1d.so*
|
||||
"${local_lib}"/librga.so*
|
||||
"${local_lib}"/librockchip_mpp.so*
|
||||
"${local_lib}"/librockchip_vpu.so*
|
||||
"${local_lib}"/aarch64-linux-gnu/libdisplay-info.so*
|
||||
)
|
||||
((${#runtime_libraries[@]} > 0)) || die "no Rockchip runtime libraries found in upstream package"
|
||||
mv -- "${runtime_libraries[@]}" "${runtime_lib}/"
|
||||
shopt -u nullglob
|
||||
|
||||
find "${local_lib}" -type f \( -name '*.a' -o -name '*.la' \) -delete
|
||||
rm -rf -- "${local_lib}/pkgconfig" "${local_lib}/aarch64-linux-gnu/pkgconfig"
|
||||
rm -f -- \
|
||||
"${STAGE_DIR}/usr/lib/systemd/system/kodi.service" \
|
||||
"${STAGE_DIR}/usr/lib/systemd/system/kodi-pulse.service" \
|
||||
"${STAGE_DIR}/usr/lib/systemd/system/pulseaudio.service"
|
||||
|
||||
cp -a -- "${RKMEDIA_ROOT}/packaging/rootfs/." "${STAGE_DIR}/"
|
||||
mkdir -p -- "${STAGE_DIR}/usr/bin" "${STAGE_DIR}/usr/share/doc/rk1-media-stack"
|
||||
ln -sfn /opt/rkmedia/bin/ffmpeg-rk "${STAGE_DIR}/usr/bin/ffmpeg-rk"
|
||||
ln -sfn /opt/rkmedia/bin/ffprobe-rk "${STAGE_DIR}/usr/bin/ffprobe-rk"
|
||||
ln -sfn /opt/rkmedia/bin/kodi-rk "${STAGE_DIR}/usr/bin/kodi-rk"
|
||||
install -m 0644 "${LOCK_FILE}" "${STAGE_DIR}/usr/share/doc/rk1-media-stack/upstream-deb.lock.json"
|
||||
|
||||
[[ -x "${STAGE_DIR}/usr/local/bin/kodi" ]] || die "Kodi launcher was not retained at its compiled prefix"
|
||||
[[ -x "${STAGE_DIR}/usr/local/lib/kodi/kodi-gbm" ]] || die "Kodi GBM binary is missing"
|
||||
for soname in librga.so.2 libdav1d.so.7 librockchip_mpp.so.1 libdisplay-info.so.4; do
|
||||
[[ -e "${runtime_lib}/${soname}" ]] || die "relocated runtime is missing ${soname}"
|
||||
readelf -d "${STAGE_DIR}/usr/local/lib/kodi/kodi-gbm" | grep -Fq "Shared library: [${soname}]" \
|
||||
|| die "Kodi binary does not declare expected dependency ${soname}"
|
||||
done
|
||||
if find "${STAGE_DIR}" -type f -name '*.a' -print -quit | grep -q .; then
|
||||
die "static library remained after repack"
|
||||
fi
|
||||
if find "${STAGE_DIR}/usr/local/lib" -type d -name pkgconfig -print -quit | grep -q .; then
|
||||
die "pkg-config development directory remained after repack"
|
||||
fi
|
||||
|
||||
"${SCRIPT_DIR}/package-media.sh" \
|
||||
--stage "${STAGE_DIR}" \
|
||||
--output-dir "${OUTPUT_DIR}" \
|
||||
--version "${PACKAGE_VERSION}" \
|
||||
--depends-from-deb "${INPUT_DEB}"
|
||||
|
||||
log "repack complete; authoritative input SHA-256: ${actual_sha256}"
|
||||
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"generated_at": "2026-08-16T16:00:00Z",
|
||||
"source_date_epoch": 1786886768,
|
||||
"target": {
|
||||
"architecture": "arm64",
|
||||
"gnu_machine": "aarch64-linux-gnu",
|
||||
"distribution": "debian",
|
||||
"suite": "trixie",
|
||||
"install_prefix": "/opt/rkmedia"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "mpp",
|
||||
"description": "Rockchip MPP compatibility branch used by ffmpeg-rockchip and kodi-rockchip-deb",
|
||||
"repository": "https://github.com/nyanmisaka/mpp.git",
|
||||
"ref": "refs/heads/jellyfin-mpp",
|
||||
"commit": "a9380ef333102ac318628f83b5f7a460d377749e",
|
||||
"committed_at": "2025-12-26T12:59:40Z",
|
||||
"build": true,
|
||||
"license_files": ["LICENSES"]
|
||||
},
|
||||
{
|
||||
"name": "rga",
|
||||
"description": "Rockchip RGA compatibility branch used by ffmpeg-rockchip and kodi-rockchip-deb",
|
||||
"repository": "https://github.com/nyanmisaka/rk-mirrors.git",
|
||||
"ref": "refs/heads/jellyfin-rga",
|
||||
"commit": "1d330cc28551943bed3380261a5a9c6fbd58ff53",
|
||||
"committed_at": "2025-10-19T08:11:31Z",
|
||||
"build": true,
|
||||
"license_files": ["COPYING"]
|
||||
},
|
||||
{
|
||||
"name": "dav1d",
|
||||
"description": "AV1 software fallback required by the matched FFmpeg/Kodi build",
|
||||
"repository": "https://code.videolan.org/videolan/dav1d.git",
|
||||
"ref": "refs/tags/1.5.3",
|
||||
"commit": "b546257f770768b2c88258c533da38b91a06f737",
|
||||
"committed_at": "2025-12-31T14:50:45Z",
|
||||
"build": true,
|
||||
"license_files": ["COPYING"]
|
||||
},
|
||||
{
|
||||
"name": "ffmpeg-rockchip",
|
||||
"description": "FFmpeg 8.1 with RKMPP decoders/encoders and RKRGA filters",
|
||||
"repository": "https://github.com/nyanmisaka/ffmpeg-rockchip.git",
|
||||
"ref": "refs/heads/8.1",
|
||||
"commit": "d90e3a1c18d7929383cf88c1b3da2e2d1c966cbf",
|
||||
"committed_at": "2026-08-10T08:42:00Z",
|
||||
"build": true,
|
||||
"license_files": ["COPYING.GPLv2", "COPYING.GPLv3", "COPYING.LGPLv2.1", "COPYING.LGPLv3"]
|
||||
},
|
||||
{
|
||||
"name": "libdisplay-info",
|
||||
"description": "EDID and DisplayID parser required by Kodi's GBM platform",
|
||||
"repository": "https://gitlab.freedesktop.org/emersion/libdisplay-info.git",
|
||||
"ref": "refs/heads/main",
|
||||
"commit": "f1b75310181e364f97a16464efcd92bc82e57ccc",
|
||||
"committed_at": "2026-07-27T10:12:35Z",
|
||||
"build": true,
|
||||
"license_files": ["LICENSE"]
|
||||
},
|
||||
{
|
||||
"name": "kodi",
|
||||
"description": "Kodi mainline with current GBM DRM PRIME support",
|
||||
"repository": "https://github.com/xbmc/xbmc.git",
|
||||
"ref": "refs/heads/master",
|
||||
"commit": "9c56bf593441a6c246fdc6931177e71f349ee2bc",
|
||||
"committed_at": "2026-08-16T13:26:08Z",
|
||||
"build": true,
|
||||
"license_files": ["LICENSE.md"]
|
||||
}
|
||||
],
|
||||
"reference_heads": [
|
||||
{
|
||||
"name": "kodi-rockchip-deb",
|
||||
"repository": "https://github.com/armsurvivors/kodi-rockchip-deb.git",
|
||||
"ref": "refs/heads/main",
|
||||
"commit": "8540dbd24f180aa43cf2d6e55f2ad17bd4f12530",
|
||||
"observed_at": "2026-08-16T16:00:00Z"
|
||||
},
|
||||
{
|
||||
"name": "rockchip-linux-mpp-official",
|
||||
"repository": "https://github.com/rockchip-linux/mpp.git",
|
||||
"ref": "refs/heads/develop",
|
||||
"commit": "c08762ebfadeb4e986d2fed993bc7a54862d3ebe",
|
||||
"observed_at": "2026-08-16T16:00:00Z"
|
||||
},
|
||||
{
|
||||
"name": "airockchip-librga-official",
|
||||
"repository": "https://github.com/airockchip/librga.git",
|
||||
"ref": "refs/heads/main",
|
||||
"commit": "2b32edcb97b601b25683e2941d888c8515da6d55",
|
||||
"observed_at": "2026-08-16T16:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"release": "20260721-1741",
|
||||
"release_url": "https://github.com/armsurvivors/kodi-rockchip-deb/releases/tag/20260721-1741",
|
||||
"recipe_repository": "https://github.com/armsurvivors/kodi-rockchip-deb.git",
|
||||
"recipe_commit": "4ceb39b453dbbb1de13bd355e2be2542427e2b1e",
|
||||
"asset_id": 484995753,
|
||||
"asset_name": "kodi-rockchip-gbm_arm64_kodi_master_ffmpeg_81_trixie.deb",
|
||||
"asset_url": "https://github.com/armsurvivors/kodi-rockchip-deb/releases/download/20260721-1741/kodi-rockchip-gbm_arm64_kodi_master_ffmpeg_81_trixie.deb",
|
||||
"asset_size": 147977956,
|
||||
"asset_sha256": "f9eed4c02d80f12b443604026fb00c4a6ff5ddd1fe5e6c554d81852a849d261a",
|
||||
"package": "kodi-rockchip-gbm",
|
||||
"package_version": "20260721-1741-kodi-master-ffmpeg-81",
|
||||
"architecture": "arm64",
|
||||
"embedded_ffmpeg_revision": "388741a",
|
||||
"notes": "The release recipe cloned moving branches without commit locks. The GitHub asset SHA-256 is therefore the authoritative immutable input."
|
||||
}
|
||||
Reference in New Issue
Block a user