mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-15 13:46:16 -08:00
* feat(embedded-mpv): Linux frame-copy helper via headless EGL Port the frame-copy engine's native layer to Linux (PORTING.md items 1-4): - frame_helper_gl.h: platform GlContext abstraction. macOS keeps the CGL path (moved verbatim); Linux acquires an EGL display in order surfaceless-Mesa -> default display -> GBM render node, binds a 3.2 core desktop-GL context surfaceless (1x1 pbuffer fallback), and hands mpv eglGetProcAddress. The helper's own GL calls link against glvnd libOpenGL, so no display server is required. - frame_shm.h: portable frame_shm_now_ns() (CLOCK_MONOTONIC) shared by the helper and the reader addon, replacing the macOS-only clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW); producer and consumer stay on the same clock. - embedded_mpv_frame_reader.c: real implementation now also on __linux__ (the code was already POSIX apart from the clock call). - binding.gyp: OS==linux executable branch for iptvnator_mpv_helper linking system libmpv (-lmpv) + EGL/OpenGL/gbm, with rpaths for $ORIGIN/lib and the build-time library dir. The in-process addon still does not link libmpv - the ban only binds in-process, the helper is out of process. - build-embedded-mpv.js: system-dev fallback on Linux (LIBMPV_INCLUDE_DIR or /usr/include) so a distro libmpv-dev install builds without staging a vendored runtime; a pre-set LINUX_NATIVE_LIBRARY_DIR now wins over the vendored lib dir. Verified on Ubuntu 25.04 / i7-1165G7 (Iris Xe): lavfi smoke per PORTING.md (idle->loading->playing snapshots at 4 Hz, aspect-fit generation bump g1 1280x720 -> g2 960x720 for a 4:3 source), reader probe 60 fps at 1080p60 with 0 torn reads, clean quit with no leaked processes or shm. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(embedded-mpv): enable the frame-copy engine gates on Linux Flip the TypeScript side of the Linux port (PORTING.md item 5). A shared dependency-free predicate, isFrameCopyPlatformSupported() (linux any-arch, darwin arm64-only), now backs all four gates so they cannot drift: - main.ts: the persisted Settings toggle promotes to the env flag on Linux too (this runs before window creation and controls the sandbox relax). - EmbeddedMpvNativeService.isFrameCopyEngineActive/isFrameCopyAvailable. - EmbeddedMpvFrameCopyAdapter.isSupported. getSupport() ordering: the frame-copy branch moves above the Linux-only native-engine prerequisites - the X11/Xwayland display-server check and the system-mpv-on-PATH probe only bind the --wid native engine, while the frame-copy helper renders offscreen (headless EGL) and links libmpv itself. createSession() also skips resolving the native window handle for frame-copy sessions, which the adapter ignores anyway, so native-Wayland sessions no longer trip the window-handle assertion. Settings copy: the i18n frame-copy description now says macOS (Apple Silicon) and Linux in all 18 languages; stale macOS-only doc comments in the settings/support interfaces updated alongside. Tests: platform-gate matrix for the adapter (darwin arm64/x64, linux x64/arm64, win32) and service specs covering Linux activation under native Wayland, macOS arm64 staying active, macOS x64 staying native, and the skipped window handle for frame-copy sessions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(packaging): CI + package guards for the Linux frame-copy helper - build-and-make.yaml: install libegl-dev/libgl-dev/libgbm-dev on the Linux runner (the helper's EGL backend needs them now that the helper target builds on Linux), and verify the built helper exists and DOES link libmpv - the inverse of the addon's no-libmpv rule, which still holds and stays validated. - electron-after-pack.cjs: strip iptvnator_mpv_helper from packaged Linux apps. It links the build host's system libmpv, which end-user systems cannot be assumed to have; the support probe treats the missing helper as frame-copy-unavailable (dev-build-only engine until the bundled-runtime staging milestone). - frame_helper_gl.h: log the chosen EGL display tier to stderr (the adapter mirrors helper stderr), so bring-up problems on exotic setups are diagnosable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(embedded-mpv): document the Linux frame-copy port - architecture doc: frame-copy section covers Linux (EGL display tiers, build deps, package strip), Linux support matrix notes the frame-copy exception to the X11 + system-mpv requirements, Linux measured baseline. - RESULTS.md: Ubuntu 25.04 / i7-1165G7 (Iris Xe) measurement rows via the production helper + reader probe; viewport-size claim reproduced. - PORTING.md: Linux marked done with pointers to what changed; Windows remains the open port and its perf gate the open decision. - CLAUDE.md + tools/embedded-mpv/README.md: platform scope, Linux dev build requirements, system-headers fallback, helper strip. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(embedded-mpv): commit the Linux frame-copy measurement probe linux-frame-probe.mjs reproduces the RESULTS.md Linux rows: spawns the production helper, attaches the frame-reader addon to the announced shm generation, and reports new-frame fps, copy wall time, produce->copy age, torn reads and pixel spread. Usage documented in RESULTS.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(embedded-mpv): address multi-agent review findings on the Linux port Confirmed findings (each verified by 3 adversarial reviewers): - CI would fail to link the helper: -lOpenGL needs the unversioned glvnd libOpenGL.so, shipped only by libopengl-dev, which neither the runner images nor the previous apt line provide. Added to the workflow and to every documented Linux build-dep list. - The new 'test -x' dist guard could never pass: webpack's dist asset copy drops file modes (helper arrives as 0644). The guard is now 'test -f'; electron-after-pack.cjs restores the execute bit on packaged helpers (also fixes packaged-macOS spawns); the support probe now requires X_OK, so a mode-stripped helper reads as frame-copy-unavailable and falls back to native instead of failing spawn with EACCES. - The Settings frame-copy toggle was unreachable in exactly the Linux states the port targets: the native-Wayland and missing-system-mpv unsupported payloads omitted frameCopyAvailable, and toggle visibility derives solely from it. Both returns now advertise availability. Also from review: - build-embedded-mpv.js keeps the old graceful-skip contract when the new system-dev fallback finds libmpv-dev but the GL/EGL/gbm dev stack is missing (previously such machines skipped; a hard electron-build failure was a regression). - createSession derives the window-handle skip from the dispatched addon instead of re-evaluating the engine gate, so the two cannot disagree. - The render thread logs the GL renderer string (surfaceless Mesa can silently pick llvmpipe on non-Mesa-primary systems; now diagnosable — verified 'Mesa Intel Iris Xe' on this machine). - Specs pin the new semantics: frameCopyAvailable advertised while native is unsupported (Wayland / no mpv), frame-copy supported without a system mpv, and the handle-skip test disposes its session through the owning adapter. - Docs: PORTING.md file map reflects the frame_helper_gl.h seam for the Windows porter; helper-strip removal correctly gated on milestone 4 (bundled libmpv), not milestone 3; RESULTS.md preamble notes the RAW->MONOTONIC clock change; stale '(macOS)' scope comments updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(embedded-mpv): address Greptile/Codex review comments - Sandbox gate requires a usable helper (Greptile P1, security): the main.ts env promotion now also probes for an executable iptvnator_mpv_helper before relaxing the window sandbox — a stale opt-in on packaged Linux (helper deliberately stripped) or after a cleaned native build no longer costs a sandboxless launch for an engine that cannot activate. Helper discovery (addon candidate paths + X_OK probe) moved into embedded-mpv-frame-copy-platform.util.ts, shared by main.ts and the service; the service keeps thin instance wrappers so tests can stub per scenario. New util spec pins the platform matrix, candidate resolution, and the execute-bit semantics. - Stale frame-copy artifacts on skipped builds (Codex P2): cleanOutput() now also removes iptvnator_mpv_helper and embedded_mpv_frame_reader.node, so a failed/skipped rebuild cannot leave a previous helper advertising frame-copy support against a runtime the build just declared unavailable. - Multiarch default lib dir (Greptile P1, partially refuted): -l resolution never depended on our -L (the compiler's built-in search paths include the Debian/Ubuntu multiarch dir — proven by the green CI run linking with a nonexistent -L dir), but the system-dev fallback now defaults to /usr/lib/<multiarch-triple> when present so the -L flag and the helper's baked rpath point somewhere real. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(embedded-mpv): harden Linux frame-copy port --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
152 lines
10 KiB
Markdown
152 lines
10 KiB
Markdown
# Embedded MPV Runtime
|
|
|
|
This folder contains tooling for preparing MPV runtime/build inputs for IPTVnator's experimental embedded MPV player. macOS and Windows bundle `libmpv`; Linux uses staged MPV headers for compilation and launches the system `mpv` executable at runtime. On Linux, `apps/electron-backend/build-embedded-mpv.js` also falls back to system headers when nothing is staged (`libmpv-dev`; override with `LIBMPV_INCLUDE_DIR`/`LINUX_NATIVE_LIBRARY_DIR`), so a plain distro dev setup builds without staging. The frame-copy helper (`iptvnator_mpv_helper`) additionally needs `libegl-dev`, `libgl-dev`, `libopengl-dev` (for the unversioned glvnd `libOpenGL.so` the linker resolves `-lOpenGL` against), and `libgbm-dev`, and links the system `libmpv` — allowed because it is a separate process; the in-process-libmpv ban still binds the addon.
|
|
|
|
## Runtime Policy
|
|
|
|
Release builds must use an LGPL-compatible runtime:
|
|
|
|
- FFmpeg must be built without `--enable-gpl` and without `--enable-nonfree`.
|
|
- mpv must be built with `-Dlibmpv=true` and `-Dgpl=false`.
|
|
- The runtime must be dynamically linked so users can inspect and replace LGPL libraries.
|
|
- The exact source URLs, versions, build flags, local patches, and checksums must be published with the release.
|
|
|
|
Do not ship the Homebrew `mpv` runtime. It is acceptable only for local development when `IPTVNATOR_EMBEDDED_MPV_ALLOW_HOMEBREW=1` is set, and release packaging rejects it.
|
|
|
|
## Expected Layout
|
|
|
|
The native addon build consumes:
|
|
|
|
```text
|
|
vendor/embedded-mpv/
|
|
darwin-arm64/
|
|
include/mpv/client.h
|
|
lib/*.dylib
|
|
runtime-manifest.json
|
|
darwin-x64/
|
|
include/mpv/client.h
|
|
lib/*.dylib
|
|
runtime-manifest.json
|
|
win32-x64/
|
|
include/mpv/client.h
|
|
lib/libmpv-2.dll # or mpv-2.dll/mpv.dll/libmpv.dll
|
|
lib/libmpv.dll.a # or mpv.lib/mpv-2.lib
|
|
runtime-manifest.json
|
|
linux-x64/
|
|
include/mpv/client.h
|
|
runtime-manifest.json
|
|
```
|
|
|
|
The generated `lib/` and `include/` directories are release inputs, not source files. They are ignored by git by default.
|
|
|
|
## Staging A Built Runtime
|
|
|
|
After building an LGPL-compatible prefix for one platform/architecture, stage it with:
|
|
|
|
```bash
|
|
pnpm embedded-mpv:stage-runtime -- darwin arm64 /path/to/lgpl-prefix
|
|
pnpm embedded-mpv:stage-runtime -- darwin x64 /path/to/lgpl-prefix
|
|
pnpm embedded-mpv:stage-runtime -- win32 x64 /path/to/lgpl-prefix
|
|
pnpm embedded-mpv:stage-runtime -- linux x64 /path/to/lgpl-prefix
|
|
```
|
|
|
|
For compatibility, the legacy macOS-only staging command is still available:
|
|
|
|
```bash
|
|
pnpm embedded-mpv:stage-runtime:macos -- arm64 /path/to/lgpl-prefix
|
|
pnpm embedded-mpv:stage-runtime:macos -- x64 /path/to/lgpl-prefix
|
|
```
|
|
|
|
The prefix must contain `include/mpv/client.h` and the platform runtime/build files:
|
|
|
|
- macOS: `lib/libmpv.2.dylib` or `lib/libmpv.dylib` plus all non-system dylib dependencies
|
|
- Windows: `lib/mpv.lib`, `lib/mpv-2.lib`, or `libmpv.dll.a`, and `bin/` or `lib/` containing `mpv-2.dll`, `libmpv-2.dll`, `mpv.dll`, or `libmpv.dll`
|
|
- Linux: `include/mpv/client.h`; CI also records the `libmpv-dev` and `mpv` package versions used as build inputs. Linux runtime playback uses the system `mpv` executable and does not bundle `libmpv.so`.
|
|
|
|
If the prefix contains `runtime-manifest.json`, the staging script copies its build metadata into the vendored manifest. At minimum, record:
|
|
|
|
- FFmpeg version, source URL, checksum, configure flags, and patches
|
|
- mpv version, source URL, checksum, Meson flags, and patches
|
|
- source-distribution URL for the corresponding release
|
|
|
|
## Building The CI Runtime
|
|
|
|
Tagged macOS release builds build the runtime from pinned source archives before `electron-backend:build`. The workflow can also enable this path temporarily for macOS PR artifact testing:
|
|
|
|
```bash
|
|
pnpm embedded-mpv:build-runtime -- arm64 /tmp/embedded-mpv-prefix
|
|
pnpm embedded-mpv:stage-runtime -- darwin arm64 /tmp/embedded-mpv-prefix
|
|
```
|
|
|
|
Linux CI does not build libmpv from source. It installs Ubuntu runner packages
|
|
(`libmpv-dev` and `mpv`), stages their headers and build metadata under
|
|
`vendor/embedded-mpv/linux-x64/`, and requires the native addon/package layout
|
|
to be present. Linux playback does not load or bundle `libmpv` in the Electron
|
|
process; the addon creates an X11 child window and starts a system `mpv --wid`
|
|
process at runtime.
|
|
|
|
Windows CI does not build libmpv from source. It restores an exact-keyed cache
|
|
for `vendor/embedded-mpv/win32-x64/`; on cache miss it stages a checksum-pinned
|
|
LGPL-compatible archive from repository configuration:
|
|
|
|
```text
|
|
IPTVNATOR_WINDOWS_EMBEDDED_MPV_RUNTIME_URL
|
|
IPTVNATOR_WINDOWS_EMBEDDED_MPV_RUNTIME_SHA256
|
|
```
|
|
|
|
The values can be repository variables or secrets. Prefer variables when PR
|
|
artifact builds from same-repository branches should include Embedded MPV. For
|
|
non-tag artifact builds only, the workflow falls back to a checksum-pinned
|
|
`zhongfly/mpv-winbuild` `mpv-dev-lgpl-x86_64` archive when those variables are
|
|
unset. Tagged release builds must provide the repository configuration
|
|
explicitly.
|
|
|
|
The Windows job is pinned to `windows-2022` while the current Electron
|
|
`node-gyp` toolchain cannot identify Visual Studio 18 from `windows-latest`.
|
|
|
|
The archive must contain a Windows x64 prefix with `include/mpv/client.h`, a
|
|
libmpv import library, and `mpv-2.dll`/`mpv.dll` or
|
|
`libmpv-2.dll`/`libmpv.dll`. The archive can use either the normal prefix layout
|
|
(`lib/` and `bin/`) or the common `mpv-dev-lgpl` flat layout with the import
|
|
library and DLL in the archive root. The staged runtime preserves the DLL
|
|
basename from the archive because Windows import libraries encode the DLL name
|
|
that `embedded_mpv.node` must load at runtime. If
|
|
`runtime-manifest.json` is missing, CI generates a minimal manifest from the
|
|
archive URL/path and checksum; release-ready runtime archives should still
|
|
provide full source/build metadata.
|
|
|
|
During temporary PR and `master` artifact testing, CI restores an exact-keyed GitHub Actions cache for the staged `vendor/embedded-mpv/<platform>-<arch>/` runtime before falling back to the macOS source build or Windows runtime archive where available. The cache key includes the target platform, architecture, macOS deployment target, Xcode version when available, a hash of the Windows runtime checksum when applicable, and hashes of the runtime build/staging scripts. Cache entries are saved only from trusted repository refs and are treated strictly as a speed optimization; tagged macOS release builds continue to rebuild from pinned sources unless a future signed and attested runtime artifact flow is introduced.
|
|
|
|
The builder currently pins:
|
|
|
|
- FFmpeg `8.1`, configured without `--enable-gpl` or `--enable-nonfree`, and with autodetected external libraries disabled
|
|
- mpv `0.41.0`, configured with `-Dlibmpv=true -Dgpl=false`
|
|
- libplacebo `7.360.1`, checked out from git with the `glad`, Python template, `fast_float`, and `Vulkan-Headers` submodules required by its Meson build
|
|
- libass `0.17.3` plus FreeType, FriBidi, and HarfBuzz
|
|
|
|
The build manifest records source URLs, downloaded archive SHA-256 values where applicable, libplacebo git commit/submodule metadata, and the exact FFmpeg/mpv flags. The staged macOS/Windows manifest is normalized to `origin: vendored-lgpl`, which is the only embedded MPV runtime origin allowed in required macOS/Windows release packaging.
|
|
|
|
## Build Integration
|
|
|
|
`apps/electron-backend/build-embedded-mpv.js` builds the native addon against the staged runtime/build inputs, copies macOS/Windows runtime libraries into `apps/electron-backend/native/build/Release/lib/`, rewrites macOS Mach-O paths to `@loader_path`, and writes `embedded-mpv-runtime.json`. Linux builds use the staged (or system) MPV headers and system X11 development libraries, write an `external-mpv-process` manifest, and the addon must not copy or link directly to `libmpv`; CI validates this with package checks and `ldd`. The frame-copy helper executable built by the same run is the inverse: CI verifies it DOES link `libmpv` (separate process).
|
|
|
|
For local macOS development with Homebrew `mpv`, use:
|
|
|
|
```bash
|
|
pnpm run serve:backend:embedded-mpv
|
|
```
|
|
|
|
The script rebuilds the native addon with `IPTVNATOR_EMBEDDED_MPV_ALLOW_HOMEBREW=1` before starting Electron with the experimental player enabled. Use this only for local testing; release packaging rejects the resulting `homebrew-dev` runtime manifest.
|
|
|
|
The `afterPack` hook copies `dist/apps/electron-backend/native/` into `app.asar.unpacked/electron-backend/native/` so the addon, runtime manifest, and runtime libraries are available as real files where needed. Linux packages include the addon and manifest, but no bundled `libmpv.so`, and the hook strips `iptvnator_mpv_helper` from Linux packages (it links the build host's system libmpv; the frame-copy engine stays dev-build-only on Linux until bundled-runtime staging lands).
|
|
|
|
During release packaging, `tools/packaging/electron-after-pack.cjs` verifies that macOS/Windows packages use a `vendored-lgpl` runtime/build input set. macOS artifacts additionally verify that Mach-O dependencies have no `/opt/homebrew` or `/usr/local` dynamic links for embedded MPV. Linux artifacts verify that the addon and `external-mpv-process` manifest are present, that no bundled `libmpv.so` files are present, and the runtime support check verifies that `mpv` is available on `PATH`.
|
|
|
|
Set `IPTVNATOR_REQUIRE_EMBEDDED_MPV=1` when packaging a release artifact that must include Embedded MPV. The same variable is temporarily enabled for macOS PR and `master` push artifacts while the bundled runtime is being tested. Linux CI packaging requires Embedded MPV after staging the Ubuntu package build inputs. Windows CI packaging now requires Embedded MPV for x64 artifacts: the job restores the staged runtime cache or stages the checksum-pinned runtime archive, then fails backend build, package make, or package-layout verification if the addon/runtime is missing.
|
|
|
|
## Platform Notes
|
|
|
|
- macOS keeps the existing libmpv render-context backend because mpv `wid` stays black inside Electron on macOS.
|
|
- Windows uses an embedded child `HWND` and passes it to mpv through `wid`.
|
|
- Linux uses an X11 child window and starts a system `mpv --wid` process for that window. Native Wayland is not supported in v1; run under X11/Xwayland so `DISPLAY` is set and `mpv` can honor the X11 window id. The experimental frame-copy engine has no window embedding at all (offscreen EGL into a renderer canvas) and therefore works under native Wayland — dev builds only for now.
|