Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
a3d40ae
docs(player): define native mpv playback architecture
Lowband21 Jul 25, 2026
23e9031
test(player): add native playback baseline and fixtures
Lowband21 Jul 25, 2026
5bf2fb0
refactor(player): define the backend-neutral playback contract
Lowband21 Jul 25, 2026
a9c9a4b
fix(player): harden external mpv IPC transport
Lowband21 Jul 25, 2026
a452f6a
feat(streaming): add protected HLS transcode jobs
Lowband21 Jul 25, 2026
e3ee5f2
feat(player): add the libmpv control-plane foundation
Lowband21 Jul 25, 2026
9e92cd5
refactor(player): migrate playback through neutral sessions
Lowband21 Jul 25, 2026
ae8eec1
feat(player): add in-process mpv native-window playback
Lowband21 Jul 25, 2026
e1f9f37
test(player): record Wayland native-VO evidence and hybrid policy
Lowband21 Jul 25, 2026
2123504
build(player): package the LGPL libmpv profile on Linux
Lowband21 Jul 25, 2026
b04d6e9
feat(player): add the Win32 presenter and distribution handoff
Lowband21 Jul 25, 2026
68485f8
feat(player): add the AppKit presenter and app-bundle handoff
Lowband21 Jul 25, 2026
a4f77a0
ci(player): verify native playback backends
Lowband21 Jul 25, 2026
658db9d
docs(player): document playback policy diagnostics and rollout
Lowband21 Jul 25, 2026
019aff0
fix(macos): support foreign AppKit view hosts in winit
Lowband21 Jul 26, 2026
1d86f9c
feat(player): embed macOS controls in the mpv window
Lowband21 Jul 26, 2026
94411a1
fix(macos): harden bundled runtime staging
Lowband21 Jul 26, 2026
c71fdab
docs(player): document in-root macOS presentation
Lowband21 Jul 26, 2026
c3f78a9
feat(macos): make native mpv the default backend
Lowband21 Jul 26, 2026
aa29364
fix(macos): route auto playback through shell handoff
Lowband21 Jul 26, 2026
890685b
fix(macos): serialize overlay host allocation
Lowband21 Jul 26, 2026
450cb10
fix(macos): stop fallback after native-window failure
Lowband21 Jul 26, 2026
31748ff
ci(macos): validate the default native path
Lowband21 Jul 26, 2026
0ae5bde
docs(player): consolidate native playback guidance
Lowband21 Jul 26, 2026
c25ef18
fix(ci): install libmpv for SQLx checks
Lowband21 Jul 26, 2026
a1a178d
fix(deps): resolve current advisory failures
Lowband21 Jul 26, 2026
6230ecb
fix(deps): update anyhow past unsound release
Lowband21 Jul 26, 2026
f5e93e8
fix(macos): link iconv in pinned ffmpeg build
Lowband21 Jul 26, 2026
d865c67
fix(macos): keep mpv shaderc on win32 path
Lowband21 Jul 26, 2026
afb464a
fix(ci): retry GStreamer SDK downloads
Lowband21 Jul 26, 2026
daf571e
fix(test): decouple shutdown proof from media runtime
Lowband21 Jul 26, 2026
84e4ace
fix(ci): install autotools for Windows libass
Lowband21 Jul 26, 2026
be6706a
fix(windows): configure libass after bootstrap
Lowband21 Jul 26, 2026
92180ee
fix(ci): pin macOS deployment runner
Lowband21 Jul 26, 2026
fa426b8
fix(ci): link macOS smoke against staged dylibs
Lowband21 Jul 26, 2026
9ed6ed6
fix(ci): initialize AppKit in macOS runtime smoke
Lowband21 Jul 26, 2026
add11fe
fix(windows): install LuaJIT import library
Lowband21 Jul 27, 2026
839c261
ci: let platform validation finish independently
Lowband21 Jul 27, 2026
edfb2e7
fix(ci): separate macOS smoke compile and link
Lowband21 Jul 27, 2026
960257d
fix(windows): accept native GIO module names
Lowband21 Jul 27, 2026
3df6715
fix(macos): ship mpv-only runtime bundle
Lowband21 Jul 27, 2026
2f6fea3
fix(macos): disable foreign window systems
Lowband21 Jul 27, 2026
8766cc9
fix(windows): stage libproxy backend closure
Lowband21 Jul 27, 2026
098710c
fix(macos): disable X11 clipboard probing
Lowband21 Jul 27, 2026
c682160
fix(windows): accept normalized DLL manifest paths
Lowband21 Jul 27, 2026
4311eb8
fix(macos): compile out GStreamer playback
Lowband21 Jul 27, 2026
d625f32
fix(windows): normalize local HLS fixture URIs
Lowband21 Jul 27, 2026
2972aea
fix(macos): make unavailable backend branch exhaustive
Lowband21 Jul 27, 2026
f384787
fix(windows): normalize generated HLS segment path
Lowband21 Jul 27, 2026
0a0fc38
fix(windows): bound HLS runtime audit
Lowband21 Jul 27, 2026
bb31c78
fix(ci): avoid redundant Windows cache helper
Lowband21 Jul 27, 2026
3e19b0d
fix(windows): bound HLS playback by decoded buffers
Lowband21 Jul 27, 2026
ef92d31
fix(windows): verify HLS streams with timed probes
Lowband21 Jul 27, 2026
cebd050
fix(windows): decode HLS probes without playbin
Lowband21 Jul 27, 2026
c066c8b
fix(windows): link both HLS decode streams
Lowband21 Jul 27, 2026
0d245d5
fix(windows): constrain HLS decode pads
Lowband21 Jul 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
244 changes: 230 additions & 14 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ jobs:
libglib2.0-dev \
libgstreamer1.0-dev \
libgstreamer-plugins-base1.0-dev \
libmpv-dev \
libavcodec-dev \
libavformat-dev \
libavutil-dev \
Expand Down Expand Up @@ -100,14 +101,15 @@ jobs:
build:
name: Build
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
os: [ubuntu-latest, macos-15, windows-latest]
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: windows-latest
target: x86_64-pc-windows-msvc
- os: macos-latest
- os: macos-15
target: aarch64-apple-darwin
max-parallel: 3
runs-on: ${{ matrix.os }}
Expand All @@ -123,6 +125,9 @@ jobs:
libglib2.0-dev \
libgstreamer1.0-dev \
libgstreamer-plugins-base1.0-dev \
libmpv-dev \
ffmpeg \
fonts-dejavu-core \
libavcodec-dev \
libavformat-dev \
libavutil-dev \
Expand All @@ -135,18 +140,137 @@ jobs:
if: runner.os == 'macOS'
run: |
brew update
brew install pkg-config glib gstreamer gst-plugins-base ffmpeg
brew install \
pkg-config meson ninja nasm \
freetype fribidi harfbuzz \
molten-vk shaderc vulkan-headers vulkan-loader \
gstreamer

# On Windows leverage GitHub action to install official MSVC GStreamer runtime + devel packages
- name: Setup GStreamer (Windows)
- name: Build pinned LGPL libmpv (macOS)
if: runner.os == 'macOS'
shell: bash
run: |
prefix="${RUNNER_TEMP}/ferrex-libmpv"
export MACOSX_DEPLOYMENT_TARGET="15.0"
echo "MACOSX_DEPLOYMENT_TARGET=$MACOSX_DEPLOYMENT_TARGET" >> "$GITHUB_ENV"
export PKG_CONFIG_PATH="$(brew --prefix vulkan-loader)/lib/pkgconfig:$(brew --prefix shaderc)/lib/pkgconfig:${PKG_CONFIG_PATH:-}"
bash scripts/release/macos-build-libmpv.sh "$prefix"
echo "PKG_CONFIG_PATH=$prefix/lib/pkgconfig:${PKG_CONFIG_PATH:-}" >> "$GITHUB_ENV"
echo "LIBRARY_PATH=$prefix/lib:${LIBRARY_PATH:-}" >> "$GITHUB_ENV"
echo "DYLD_FALLBACK_LIBRARY_PATH=$prefix/lib:${DYLD_FALLBACK_LIBRARY_PATH:-}" >> "$GITHUB_ENV"

- name: Test macOS bundle policy parser
if: runner.os == 'macOS'
run: |
python3 -m unittest \
scripts/release/test_macos_bundle.py \
scripts/release/test_macos_build_profile.py

- name: Generate and validate native playback fixtures (Linux)
if: runner.os == 'Linux'
run: |
python3 -m unittest \
scripts/qa/test_native_playback_fixtures.py \
scripts/qa/test_native_playback_wayland_trace.py
./scripts/qa/native_playback_fixtures.py generate \
--output "${RUNNER_TEMP}/native-playback-fixtures"
./scripts/qa/native_playback_fixtures.py verify \
--output "${RUNNER_TEMP}/native-playback-fixtures"

- name: Enforce native mpv architecture boundaries (Linux)
if: runner.os == 'Linux'
run: ./scripts/check-native-mpv-boundaries.sh

- name: Cache pinned GStreamer MSVC SDK (Windows)
if: runner.os == 'Windows'
uses: actions/cache@v4
with:
path: ${{ runner.temp }}\gstreamer-msvc-x86_64
key: windows-gstreamer-msvc-1.28.4-1a745d67225e4339

- name: Install pinned GStreamer MSVC SDK (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$root = Join-Path $env:RUNNER_TEMP 'gstreamer-msvc-x86_64'
& .\utils\build-windows\install-gstreamer.ps1 -Destination $root
"GSTREAMER_1_0_ROOT_MSVC_X86_64=$root" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
"PKG_CONFIG=$root\bin\pkg-config.exe" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
"PKG_CONFIG_PATH=$root\lib\pkgconfig" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
(Join-Path $root 'bin') | Out-File $env:GITHUB_PATH -Append -Encoding utf8

- name: Setup MSYS2 UCRT64 for pinned libmpv (Windows)
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
update: true
install: >-
base-devel
autoconf
autoconf-archive
automake
git
libtool
mingw-w64-ucrt-x86_64-toolchain
mingw-w64-ucrt-x86_64-meson
mingw-w64-ucrt-x86_64-ninja
mingw-w64-ucrt-x86_64-pkgconf
mingw-w64-ucrt-x86_64-nasm
mingw-w64-ucrt-x86_64-python
mingw-w64-ucrt-x86_64-python-mako
mingw-w64-ucrt-x86_64-python-jinja
mingw-w64-ucrt-x86_64-python-glad
mingw-w64-ucrt-x86_64-harfbuzz
mingw-w64-ucrt-x86_64-freetype
mingw-w64-ucrt-x86_64-fribidi
mingw-w64-ucrt-x86_64-libpng
mingw-w64-ucrt-x86_64-lcms2
mingw-w64-ucrt-x86_64-shaderc
mingw-w64-ucrt-x86_64-spirv-cross
mingw-w64-ucrt-x86_64-xxhash
mingw-w64-ucrt-x86_64-zlib

- name: Cache pinned LGPL libmpv SDK (Windows)
if: runner.os == 'Windows'
# v.1.5.3 not v1.5.3. The action installs MSI packages;
# upstream Windows 1.28.x artifacts are EXE-only, so pin the
# newest available MSI release until the action supports EXE installers.
uses: marcpabst/setup-gstreamer@v.1.5.3
uses: actions/cache@v4
with:
version: '1.26.11'
arch: x86_64
path: C:\ferrex-libmpv-sdk
key: windows-libmpv-lgpl-${{ hashFiles('utils/build-windows/build-libmpv-lgpl.sh') }}

- name: Build pinned LGPL libmpv SDK (Windows)
if: runner.os == 'Windows'
shell: msys2 {0}
run: |
if [[ ! -f /c/ferrex-libmpv-sdk/share/licenses/ferrex-libmpv/BUILD_PROFILE ]]; then
./utils/build-windows/build-libmpv-lgpl.sh /c/ferrex-libmpv-sdk
fi

- name: Enter MSVC developer environment (Windows)
if: runner.os == 'Windows'
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64

- name: Generate MSVC import library and export libmpv paths (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$root = 'C:\ferrex-libmpv-sdk'
& .\utils\build-windows\new-libmpv-import-library.ps1 -SdkRoot $root
$dll = Get-ChildItem (Join-Path $root 'bin') -File |
Where-Object { $_.Name -in @('libmpv-2.dll', 'mpv-2.dll', 'mpv.dll') } |
Select-Object -First 1
if (-not $dll) { throw "libmpv runtime DLL missing from $root\bin" }
"LIBMPV_ROOT=$root" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
"LIBMPV_LIB_DIR=$(Join-Path $root 'lib')" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
"LIBMPV_INCLUDE_DIR=$(Join-Path $root 'include')" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
"LIBMPV_DLL_DIR=$(Join-Path $root 'bin')" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
"LIBMPV_DLL=$($dll.FullName)" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
"FERREX_MPV_WINDOWS_PRESENTER=spike" | Out-File $env:GITHUB_ENV -Append -Encoding utf8
(Join-Path $root 'bin') | Out-File $env:GITHUB_PATH -Append -Encoding utf8

- uses: moonrepo/setup-rust@v1
if: runner.os != 'Windows'
Expand All @@ -165,6 +289,9 @@ jobs:
targets: ${{ matrix.target }}
components: clippy,rustfmt
profile: minimal
# The explicit actions/cache step below owns Cargo caching. Avoid
# setup-rust's cargo-cache helper and its external binary mirrors.
cache: false

- name: Cache Rust/Cargo artifacts
uses: actions/cache@v4
Expand Down Expand Up @@ -194,6 +321,49 @@ jobs:
if: runner.os != 'Windows'
run: cargo build --workspace --all-features --target ${{ matrix.target }}

- name: Stage and audit relocatable app bundle (macOS)
if: runner.os == 'macOS'
shell: bash
run: |
version="$(cargo metadata --no-deps --format-version 1 | python3 -c 'import json,sys; data=json.load(sys.stdin); print(next(package["version"] for package in data["packages"] if package["name"] == "ferrex-player"))')"
prefix="${RUNNER_TEMP}/ferrex-libmpv"
search_roots=(--search-root "$prefix/lib")
while IFS= read -r formula; do
formula_lib="$(brew --prefix "$formula")/lib"
if [[ -d "$formula_lib" ]]; then
search_roots+=(--search-root "$formula_lib")
fi
done <"$prefix/share/ferrex/native-mpv/homebrew-formulae.txt"
molten_vk_prefix="$(brew --prefix molten-vk)"
python3 scripts/release/macos_bundle.py stage \
--binary "target/${{ matrix.target }}/debug/ferrex-player" \
--app "${RUNNER_TEMP}/Ferrex Player.app" \
--architecture arm64 \
--version "$version" \
--minimum-macos "$MACOSX_DEPLOYMENT_TARGET" \
--presenter-mode enabled \
"${search_roots[@]}" \
--resource "$prefix/share/ferrex/native-mpv" \
--extra-library "$molten_vk_prefix/lib/libMoltenVK.dylib" \
--require-library libMoltenVK.dylib \
--vulkan-icd "$molten_vk_prefix/etc/vulkan/icd.d/MoltenVK_icd.json"

- name: macOS presenter capability and fallback tests
if: runner.os == 'macOS'
run: cargo test -p ferrex-player-playback --features mpv --target ${{ matrix.target }} --lib macos_presenter::tests -- --test-threads=1

- name: libmpv control-plane smoke (non-Windows)
if: runner.os != 'Windows'
run: cargo test -p ferrex-player-mpv --features linked --target ${{ matrix.target }} -- --test-threads=1

- name: Playback contract and libmpv adapter tests (Linux)
if: runner.os == 'Linux'
run: cargo test -p ferrex-player-playback --features mpv --target ${{ matrix.target }} --lib -- --test-threads=1

- name: Backend-disabled rollback build (Linux)
if: runner.os == 'Linux'
run: cargo check -p ferrex-player --no-default-features --target ${{ matrix.target }}

- name: Doctests (non-Windows)
if: runner.os != 'Windows'
run: cargo test --doc --workspace --all-features --target ${{ matrix.target }}
Expand All @@ -215,7 +385,8 @@ jobs:
"gst-app: $(pkg-config --modversion gstreamer-app-1.0)" | Write-Host
"gst-video: $(pkg-config --modversion gstreamer-video-1.0)" | Write-Host

# Run cargo in Windows shell with MinGW toolchain and official GStreamer
# Run the MSVC Rust target against the pinned MSVC GStreamer SDK and the
# import library generated from the MSYS2-built LGPL libmpv runtime.
- name: Fmt (Windows)
if: runner.os == 'Windows'
shell: pwsh
Expand All @@ -224,11 +395,56 @@ jobs:
- name: Clippy (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: cargo clippy -p ferrex-player --target ${{ matrix.target }} -- -W warnings
run: cargo clippy -p ferrex-player --features mpv --target ${{ matrix.target }} -- -W warnings

- name: Build (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: cargo build -p ferrex-player --target ${{ matrix.target }}
run: cargo build -p ferrex-player --features mpv --target ${{ matrix.target }}

- name: Windows libmpv and presenter tests
if: runner.os == 'Windows'
shell: pwsh
run: |
cargo test -p ferrex-player-mpv --features linked --target ${{ matrix.target }} -- --test-threads=1
cargo test -p ferrex-player-playback --all-features --target ${{ matrix.target }} --lib windows_presenter -- --test-threads=1

- name: Audit staged Windows runtime closure
if: runner.os == 'Windows'
timeout-minutes: 5
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$stage = Join-Path $env:RUNNER_TEMP 'ferrex-ci-runtime'
$bin = Join-Path $stage 'bin'
$plugins = Join-Path $stage 'lib\gstreamer-1.0'
$libexec = Join-Path $stage 'libexec\gstreamer-1.0'
$notices = Join-Path $stage 'share\licenses\ferrex-libmpv'
New-Item -ItemType Directory -Force -Path $bin, $plugins, $libexec, $notices | Out-Null
Copy-Item -Force "target\${{ matrix.target }}\debug\ferrex-player.exe" $stage
$buildMetadata = Join-Path $stage 'share\ferrex-player'
New-Item -ItemType Directory -Force -Path $buildMetadata | Out-Null
$env:FERREX_MPV_WINDOWS_PRESENTER | Set-Content (Join-Path $buildMetadata 'PRESENTER_BUILD_MODE')
Copy-Item -Force (Join-Path $env:LIBMPV_ROOT 'bin\*.dll') $bin
$pluginRoots = Join-Path (Get-Location).Path 'utils\build-windows\gstreamer-plugin-roots.txt'
& .\utils\build-windows\stage-gstreamer-runtime.ps1 -SourceRoot $env:GSTREAMER_1_0_ROOT_MSVC_X86_64 -StageRoot $stage -PluginManifest $pluginRoots
$gstLicenseSource = @(
(Join-Path $env:GSTREAMER_1_0_ROOT_MSVC_X86_64 'share\licenses'),
(Join-Path $env:GSTREAMER_1_0_ROOT_MSVC_X86_64 'share\gstreamer-1.0\licenses')
) | Where-Object { Test-Path $_ -PathType Container } | Select-Object -First 1
if (-not $gstLicenseSource) { throw 'GStreamer license directory is missing' }
$gstNotices = Join-Path $stage 'share\licenses\gstreamer'
New-Item -ItemType Directory -Force -Path $gstNotices | Out-Null
Copy-Item -Force -Recurse (Join-Path $gstLicenseSource '*') $gstNotices
@(
'gstreamer=1.28.4',
'abi=msvc-x86_64',
'installer_sha256=1a745d67225e43394a4a5db929c97397cb56e74b1c38bb77c6ded4b037d3c040',
'plugin_policy=explicit-recursive-pe-v1',
'codec_policy=openh264-mediafoundation-v1',
"plugin_roots_sha256=$((Get-FileHash $pluginRoots -Algorithm SHA256).Hash.ToLowerInvariant())"
) | Set-Content (Join-Path $gstNotices 'FERREX_BUILD_PROFILE')
Copy-Item -Force -Recurse (Join-Path $env:LIBMPV_ROOT 'share\licenses\ferrex-libmpv\*') $notices
& .\utils\build-windows\verify-runtime-closure.ps1 -StageRoot $stage
# - name: Build documentation (TODO)
# run: cargo doc --workspace --no-deps --all-features -- -D warnings
21 changes: 21 additions & 0 deletions .github/workflows/flatpak.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
paths:
- 'flatpak/**'
- 'crates/ferrex-player/**'
- 'crates/ferrex-player-*/**'
- 'crates/ferrex-core/**'
- 'crates/ferrex-model/**'
- 'crates/ferrex-contracts/**'
Expand All @@ -18,6 +19,7 @@ on:
paths:
- 'flatpak/**'
- 'crates/ferrex-player/**'
- 'crates/ferrex-player-*/**'
- 'crates/ferrex-core/**'
- 'crates/ferrex-model/**'
- 'crates/ferrex-contracts/**'
Expand Down Expand Up @@ -48,6 +50,25 @@ jobs:
bundle: ferrex-player.flatpak
cache-key: flatpak-builder-${{ github.sha }}

- name: Smoke test bundled libmpv closure
shell: bash
run: |
set -euo pipefail
flatpak install --user --noninteractive ./ferrex-player.flatpak
trap 'flatpak uninstall --user --noninteractive io.github.lowband21.FerrexPlayer >/dev/null' EXIT
flatpak run --user --command=sh io.github.lowband21.FerrexPlayer -c '
set -eu
test -x /app/bin/ferrex-player
test -e /app/lib/libmpv.so.2
test -e /app/lib/libavcodec.so.62
test -e /app/lib/libplacebo.so.360
ldd /app/bin/ferrex-player | grep -q "libmpv.so.2 => /app/lib/libmpv.so.2"
grep -qx "gpl=false" /app/share/licenses/io.github.lowband21.FerrexPlayer/mpv/BUILD_PROFILE
grep -qx "x11=disabled" /app/share/licenses/io.github.lowband21.FerrexPlayer/mpv/BUILD_PROFILE
grep -qx "gpl=no" /app/share/licenses/io.github.lowband21.FerrexPlayer/ffmpeg/BUILD_PROFILE
grep -qx "vulkan=enabled" /app/share/licenses/io.github.lowband21.FerrexPlayer/libplacebo/BUILD_PROFILE
'

- name: Upload Flatpak artifact
uses: actions/upload-artifact@v4
with:
Expand Down
8 changes: 0 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ jobs:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: aarch64-apple-darwin
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
Expand All @@ -41,12 +39,6 @@ jobs:
libswscale-dev \
libswresample-dev

- name: Install dependencies (macOS)
if: runner.os == 'macOS'
run: |
brew update
brew install pkg-config glib gstreamer gst-plugins-base ffmpeg

- name: Setup Rust
uses: dtolnay/rust-toolchain@stable

Expand Down
Loading
Loading