Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions apps/starry/video-codec/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# video-codec (browser multimedia dependency stack)

Umbrella carpet for the Chromium-class multimedia dependency stack on StarryOS,
delivered as native-library API + CLI carpets and validated on all four
architectures (x86_64 / aarch64 / riscv64 / loongarch64) headless.

This is the umbrella tracker. Each wave lands as an independent focused app so
it can be verified on-target and merged without blocking the rest. The
`video-codec` app itself carpets the video-codec libraries; sibling apps
(audio-codec, image-codec, font-text, shader-toolchain, stream-rtc, audio-io,
container-parse) carpet the other categories.

## Scope

Native-library carpets (each library's own API and CLI), which are distinct
from the ffmpeg-mediated coverage in the existing `ffmpeg` app: that app
exercises codecs through libav*; this stack exercises each library directly
(own encoder/decoder binaries, own bitstream tooling, deterministic
round-trip / checksum / PSNR proofs).

Supplements, does not duplicate, the existing multimedia work:
`ffmpeg` (#1086), `ffplay` (#1268), `doom` (#1415), `qt-calc` (#1396), the
Weston/`wayland` compositor, and the GPU compute carpets (#1574, #1575, #1576,
#1578, #1609, #1610, #1648).

## Wave plan

`video-codec` (this app): x264, dav1d, x265 + libde265, libvpx, openh264,
libaom, SVT-AV1 - synthetic frame -> encode -> decode round-trip with NAL/OBU
structural assertions and PSNR / md5 checks. All pure CPU, file-based, no
display or audio.

Sibling apps (subsequent waves):

| app | libraries |
|-----|-----------|
| audio-codec | FLAC, opus, vorbis + ogg, lame + mpg123, speex, fdk-aac |
| image-codec | libjpeg-turbo, libpng, giflib, libwebp, libtiff, openjpeg (avif/heif later) |
| font-text | FreeType, HarfBuzz, FontConfig, Cairo, libass, Pango |
| shader-toolchain | glslang, SPIRV-Tools, SPIRV-Cross, GLAD, GLM (offline SPIR-V pipeline) |
| stream-rtc | RTP, libsrtp, usrsctp (loopback); libdatachannel later |
| audio-io | ALSA, SDL2-audio, OpenAL-soft (file / null / wave backends) |
| container-parse | double-conversion, expat, libxml2, zlib, brotli, libebml, libmatroska |

The existing `ffmpeg` app is extended in place (not duplicated) to the three
non-x86_64 architectures and to currently-untested codecs/protocols/filters.

## Platform constraints (scoped honestly, not silently)

- No audio device (`/dev/snd`): audio codecs transcode to buffers/files and
are fully testable, but audio-io libraries can only prove the software
pipeline (open / format-negotiate / resample / mix / encode-to-file) plus
correct graceful failure when no card exists - not physical playback.
- Software GPU only (llvmpipe / lavapipe, no virgl): irrelevant to codecs,
containers, fonts and the offline shader toolchain (all CPU); only hardware
video decode / real 3D accel are out of scope.
- No interactive display server (no X11; Wayland is compositor-only; display
apps are x86_64-only): windowing libraries are used through their headless
paths (EGL-surfaceless, null platform, offscreen draw-data), not interactive
windows on aarch64 / riscv64 / loongarch64.

## Status

Work in progress. The `video-codec` app carpets x264 and dav1d first; the
remaining libraries and sibling apps land in subsequent waves. Every wave is
gated on real on-target 4-arch green with real assertions (no silent skips).
10 changes: 10 additions & 0 deletions apps/starry/video-codec/build-aarch64-unknown-none-softfloat.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
target = "aarch64-unknown-none-softfloat"
log = "Warn"
features = [
"qemu",
"ax-driver/virtio-blk",
"ax-driver/virtio-net",
"ax-driver/virtio-gpu",
"ax-driver/virtio-input",
"ax-driver/virtio-socket",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
target = "loongarch64-unknown-none-softfloat"
log = "Warn"
features = [
"axplat-dyn/efi",
"ax-driver/virtio-blk",
"ax-driver/virtio-net",
"ax-driver/virtio-gpu",
"ax-driver/virtio-input",
"ax-driver/virtio-socket",
]
14 changes: 14 additions & 0 deletions apps/starry/video-codec/build-riscv64gc-unknown-none-elf.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
features = [
"ax-runtime/display",
"ax-runtime/rtc",
"ax-driver/serial",
"ax-driver/virtio-blk",
"ax-driver/virtio-net",
"ax-driver/virtio-gpu",
"ax-driver/virtio-input",
"ax-driver/virtio-socket",
"starry-kernel/input",
"starry-kernel/vsock",
]
log = "Warn"
target = "riscv64gc-unknown-none-elf"
9 changes: 9 additions & 0 deletions apps/starry/video-codec/build-x86_64-unknown-none.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
target = "x86_64-unknown-none"
log = "Warn"
features = [
"ax-driver/virtio-blk",
"ax-driver/virtio-net",
"ax-driver/virtio-gpu",
"ax-driver/virtio-input",
"ax-driver/virtio-socket",
]
145 changes: 145 additions & 0 deletions apps/starry/video-codec/prebuild.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#!/usr/bin/env bash
set -euo pipefail

# video-codec carpet prebuild: install the native codec CLIs and libraries from
# Alpine (x264, dav1d - both available on all four arches), cross-compile the
# synthetic-frame generator, and stage everything into the guest overlay.
# Mirrors the apk-add + overlay model used by the ffmpeg / redis apps.

app_dir="${STARRY_APP_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}"
rootfs="${STARRY_ROOTFS:-}"
staging_root="${STARRY_STAGING_ROOT:-}"
overlay_dir="${STARRY_OVERLAY_DIR:-}"
arch="${STARRY_ARCH:-}"
apk_cache="${STARRY_WORKSPACE:-$(cd "$app_dir/../../.." && pwd)}/target/video-codec-apk-cache"

require_env() {
local name="$1" value="$2"
if [[ -z "$value" ]]; then
echo "error: $name is required" >&2
exit 1
fi
}

ensure_host_packages() {
local missing=()
command -v debugfs >/dev/null 2>&1 || missing+=(e2fsprogs)
command -v install >/dev/null 2>&1 || missing+=(coreutils)
command -v readelf >/dev/null 2>&1 || missing+=(binutils)
[[ ${#missing[@]} -eq 0 ]] && return
echo "error: missing required host packages: ${missing[*]}" >&2
echo "error: install with: sudo apt-get install -y --no-install-recommends ${missing[*]}" >&2
exit 1
}

qemu_runner_candidates() {
case "$arch" in
aarch64) printf '%s\n' qemu-aarch64-static qemu-aarch64 ;;
riscv64) printf '%s\n' qemu-riscv64-static qemu-riscv64 ;;
x86_64) printf '%s\n' qemu-x86_64-static qemu-x86_64 ;;
loongarch64) printf '%s\n' qemu-loongarch64-static qemu-loongarch64 ;;
*) echo "error: unsupported arch for video-codec prebuild: $arch" >&2; exit 1 ;;
esac
}

find_qemu_runner() {
local candidate
while IFS= read -r candidate; do
if command -v "$candidate" >/dev/null 2>&1; then
command -v "$candidate"; return 0
fi
done < <(qemu_runner_candidates)
echo "error: missing qemu-user runner for arch $arch" >&2
exit 1
}

run_guest_apk_once() {
local qemu_runner; qemu_runner="$(find_qemu_runner)"
if [[ ! -x "$staging_root/sbin/apk" ]]; then
echo "error: staging root is missing guest apk: $staging_root/sbin/apk" >&2
exit 1
fi
mkdir -p "$apk_cache"
QEMU_LD_PREFIX="$staging_root" \
LD_LIBRARY_PATH="$staging_root/lib:$staging_root/usr/lib" \
"$qemu_runner" -L "$staging_root" "$staging_root/sbin/apk" \
--root "$staging_root" \
--repositories-file "$staging_root/etc/apk/repositories" \
--keys-dir "$staging_root/etc/apk/keys" \
--cache-dir "$apk_cache" \
--update-cache --timeout 60 --no-interactive \
--force-no-chroot --scripts=no "$@"
}

run_guest_apk() {
local attempt max_attempts=4
[[ -f /etc/resolv.conf ]] && cp /etc/resolv.conf "$staging_root/etc/resolv.conf"
for attempt in $(seq 1 "$max_attempts"); do
if run_guest_apk_once "$@"; then return 0; fi
[[ "$attempt" -eq "$max_attempts" ]] && return 1
echo "apk failed, retrying ($attempt/$max_attempts)..." >&2
sleep $((attempt * 3))
done
}

extract_rootfs() { debugfs -R "rdump / $staging_root" "$rootfs"; }

# x264 (H.264 encoder CLI+libs) and dav1d (AV1 decoder CLI+libs) are both in
# Alpine main/community on all four arches (APKINDEX-verified). x264-libs
# provides the shared library the CLI links against.
install_codec_packages() { run_guest_apk add x264 x264-libs dav1d libdav1d; }

copy_file_to_overlay() {
local guest_path="$1" mode="$2"
local source="$staging_root${guest_path}" target="$overlay_dir${guest_path}"
if [[ ! -e "$source" ]]; then
echo "error: missing guest file after codec install: $guest_path" >&2
exit 1
fi
[[ -L "$source" ]] && source="$(readlink -f "$source")"
install -Dm"$mode" "$source" "$target"
}

find_library_path() {
local library="$1" dir
for dir in lib usr/lib usr/local/lib; do
if [[ -e "$staging_root/$dir/$library" ]]; then
printf '/%s/%s\n' "$dir" "$library"; return 0
fi
done
return 1
}

copy_runtime_dependencies() {
local pending=("$@") seen=" " guest_path library
while [[ ${#pending[@]} -gt 0 ]]; do
guest_path="${pending[0]}"; pending=("${pending[@]:1}")
[[ "$seen" == *" $guest_path "* ]] && continue
seen+="$guest_path "
while IFS= read -r library; do
local library_path
find_library_path "$library" >/dev/null 2>&1 || continue
library_path="$(find_library_path "$library")"
copy_file_to_overlay "$library_path" 0644
pending+=("$library_path")
done < <(readelf -d "$staging_root$guest_path" 2>/dev/null |
sed -n 's/.*Shared library: \[\(.*\)\].*/\1/p')
done
}

populate_overlay() {
copy_file_to_overlay /usr/bin/x264 0755
copy_file_to_overlay /usr/bin/dav1d 0755
copy_runtime_dependencies /usr/bin/x264 /usr/bin/dav1d
install -Dm0755 "$app_dir/video-codec-tests.sh" "$overlay_dir/usr/bin/video-codec-tests.sh"
}

require_env STARRY_ROOTFS "$rootfs"
require_env STARRY_STAGING_ROOT "$staging_root"
require_env STARRY_OVERLAY_DIR "$overlay_dir"
require_env STARRY_ARCH "$arch"

ensure_host_packages
extract_rootfs
install_codec_packages
populate_overlay
22 changes: 22 additions & 0 deletions apps/starry/video-codec/qemu-aarch64.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
args = [
"-nographic",
"-m",
"512M",
"-cpu",
"cortex-a53",
"-device",
"virtio-blk-pci,drive=disk0",
"-drive",
"id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-aarch64-alpine.img",
"-device",
"virtio-net-pci,netdev=net0",
"-netdev",
"user,id=net0",
]
uefi = false
to_bin = true
shell_prefix = "root@starry:"
shell_init_cmd = "/usr/bin/video-codec-tests.sh"
success_regex = ["(?m)^VIDEO_CODEC_TEST_PASSED\\s*$"]
fail_regex = ['(?i)\bpanic(?:ked)?\b', "(?m)^VIDEO_CODEC_TEST_FAILED\\s*$"]
timeout = 900
24 changes: 24 additions & 0 deletions apps/starry/video-codec/qemu-loongarch64.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
args = [
"-machine",
"virt",
"-cpu",
"la464",
"-nographic",
"-m",
"512M",
"-device",
"virtio-blk-pci,drive=disk0",
"-drive",
"id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-loongarch64-alpine.img",
"-device",
"virtio-net-pci,netdev=net0",
"-netdev",
"user,id=net0",
]
uefi = false
to_bin = true
shell_prefix = "root@starry:"
shell_init_cmd = "/usr/bin/video-codec-tests.sh"
success_regex = ["(?m)^VIDEO_CODEC_TEST_PASSED\\s*$"]
fail_regex = ['(?i)\bpanic(?:ked)?\b', "(?m)^VIDEO_CODEC_TEST_FAILED\\s*$"]
timeout = 900
22 changes: 22 additions & 0 deletions apps/starry/video-codec/qemu-riscv64.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
args = [
"-nographic",
"-m",
"512M",
"-cpu",
"rv64",
"-device",
"virtio-blk-pci,drive=disk0",
"-drive",
"id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-riscv64-alpine.img",
"-device",
"virtio-net-pci,netdev=net0",
"-netdev",
"user,id=net0",
]
uefi = false
to_bin = true
shell_prefix = "root@starry:"
shell_init_cmd = "/usr/bin/video-codec-tests.sh"
success_regex = ["(?m)^VIDEO_CODEC_TEST_PASSED\\s*$"]
fail_regex = ['(?i)\bpanic(?:ked)?\b', "(?m)^VIDEO_CODEC_TEST_FAILED\\s*$"]
timeout = 900
20 changes: 20 additions & 0 deletions apps/starry/video-codec/qemu-x86_64.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
args = [
"-nographic",
"-m",
"512M",
"-device",
"virtio-blk-pci,drive=disk0",
"-drive",
"id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img",
"-device",
"virtio-net-pci,netdev=net0",
"-netdev",
"user,id=net0",
]
uefi = false
to_bin = false
shell_prefix = "root@starry:"
shell_init_cmd = "/usr/bin/video-codec-tests.sh"
success_regex = ["(?m)^VIDEO_CODEC_TEST_PASSED\\s*$"]
fail_regex = ['(?i)\bpanic(?:ked)?\b', "(?m)^VIDEO_CODEC_TEST_FAILED\\s*$"]
timeout = 900
Loading