feat(prebuilt): ffmpeg and OCR stacks for dk.nikse.subtitleedit - #7
Merged
Conversation
Groundwork for moving dk.nikse.subtitleedit's build dependencies out of FlatPark's OSTree repository (flatpark/flatpark#238). Each is its own release rather than one bundle, so an app can pin exactly what it needs and a version bump touches one stack instead of a monolith: x264 libx264.so.165 H.264 encoder x265 libx265.so.216 H.265 encoder, 8-bit only lame libmp3lame.so.0 MP3 encoder rubberband librubberband.so.3 time-stretch / pitch-shift libass libass.so.9 (0.17.4) ASS/SSA subtitle rendering libvpl libvpl.so.2 Intel QSV dispatcher uchardet libuchardet.so.0 charset detection leptonica libleptonica.so.6 image processing, Tesseract's base sevenzip bin/7zr .7z extractor, one dependency-free binary All dev-complete (library + headers + pkg-config, and CMake config where upstream installs one) so ffmpeg-full.yml and tesseract.yml can build against them, which is what they are for in the first place. libass is deliberately its own release. mpv-stack builds its own copy today, so an app shipping both mpv-stack and an ffmpeg built against a different libass gets two libass.so.9 files contending for one soname in /app/lib, decided by module order. One release consumed from both ends settles that by construction rather than per app. Two things settled by building rather than by assumption: * rubberband's meson picks its BUILT-IN FFT and resampler even though fftw3, libsamplerate and speexdsp are all in the GNOME SDK. Pinned explicitly with -Dfft=builtin -Dresampler=builtin so a change in upstream's defaults cannot silently add a DT_NEEDED. The result links nothing beyond libstdc++/libm/ libgcc/libc. Its LADSPA plugin is dropped: unpacked under an app's /app/extra no LADSPA host can ever find it. * leptonica's dependency closure resolves completely inside org.gnome.Platform//50 — libtiff, libgif, libopenjp2, libwebp, libzstd and the rest are all there, nothing has to travel with the archive. Every stack drops *.a and *.la; a .la records absolute build paths and misleads a downstream libtool link. Each built locally against org.gnome.Sdk//50 and its contents checked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The two stacks that build against the nine leaf releases from the previous commit, completing the set dk.nikse.subtitleedit needs (flatpark/flatpark#238). ffmpeg-full: ffmpeg n9.0 + ffprobe, shared. The runtimes do ship an ffmpeg, but without libass — so the `ass` and `subtitles` filters do not exist and burning subtitles into a video fails at filter-graph parse time. The encoder set covers what a media application's export dialog offers end to end: libx264, libx265, libvpx-vp9 and prores_ks in software, NVENC / AMF / QSV / VAAPI where the hardware provides them, aac / ac3 / libmp3lame / libopus / libvorbis for audio. tesseract: the OCR engine and its command-line driver. Both consume their dependencies from the leaf releases at build time and then clean every trace of them out of the archive — x264, x265, lame, rubberband, libass and libvpl for ffmpeg-full, leptonica for tesseract. The consuming app pins those releases itself and puts each lib/ on LD_LIBRARY_PATH, the same split wemeet-screenshare-hook already uses for opencv-imgproc. That is what makes the granularity worth having: an app keeps ONE libass even when it also ships mpv, and x265 can move to a new version without re-cutting ffmpeg for apps that only ever wanted the encoder. tesseract ships no language data — a .traineddata file is ~20 MB, versioned independently of the engine, and every app wants a different set. It does ship share/tessdata/{configs,tessconfigs}, the output-format presets read from TESSDATA_PREFIX, so a consuming app stages its .traineddata into that same directory and points TESSDATA_PREFIX at it. tesseract verified against leptonica-v1: the archive carries no libleptonica, and inside org.gnome.Platform//50 with only the leptonica release's lib/ added to LD_LIBRARY_PATH, bin/tesseract resolves with zero unresolved sonames. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two things the first cut of ffmpeg-full-v1 and tesseract-v1 got wrong, both found by unpacking the released artifacts. ffplay was being built and shipped. It is an SDL2 debug player nothing here drives, and shipping it puts an `ffplay` command on the consuming app's PATH — a behaviour surface this stack has no business adding, since an application that probes for ffplay would find one. --disable-ffplay. Each `cp -a ./. /app/` also copied the source archive's own manifest.json. flatpak-builder then renamed it to manifest-base-1.json and wrote its own, so ffmpeg-full shipped libvpl's build record under a name suggesting it described ffmpeg. It cannot be cleaned away — flatpak-builder writes those files after cleanup runs — so the copy skips manifest.json instead. Both tags are re-cut rather than superseded by a -v2: nothing consumes them yet, and a v2 differing only in this would be noise in the release list. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
QSV and AMF cannot work in a Flatpak, and enabling them is worse than leaving them out. Both are two-layer. ffmpeg links a dispatcher (libvpl for QSV) or dlopens a vendor library (libamfrt64.so.1 for AMF); the layer that actually encodes ships with the vendor's userspace — libmfx-gen.so from intel-media-driver, or AMD's proprietary driver. Flatpak has no extension for either, so neither can ever be present in the sandbox. Measured inside one on an Intel Arc 140V, the hardware QSV exists for, with --device=dri: h264_vaapi encode succeeded, exit 0 h264_qsv Error creating a MFX session: -9 (dispatcher found no runtime) h264_amf DLL libamfrt64.so.1 failed to open The GPU and the render node were never the problem: VAAPI encoded through the same /dev/dri/renderD128, because org.freedesktop.Platform.VAAPI.Intel and the GL.* extensions do put a driver in the sandbox. NVENC stays for that same reason — org.freedesktop.Platform.GL.nvidia-* ships NVIDIA's userspace (463 such extensions on Flathub), so on an NVIDIA host the library is there. Not verified here; no NVIDIA GPU. Leaving them enabled would actively recreate a bug upstream just fixed. Subtitle Edit 5.2 probes `ffmpeg -encoders` and hides what is missing (SubtitleEdit/subtitleedit#13915), but that probe sees "the encoder is compiled in" and cannot see "the vendor runtime is absent" — so h264_qsv and h264_amf would stay in the user's dropdown and fail at encode time, which is exactly what the probe was added to prevent. Upstream reached the same conclusion in that PR's follow-up note. Also --enable-libharfbuzz: freetype alone stopped being enough for the drawtext filter in FFmpeg 7+, so drawtext was silently absent from the first cut. Both the SDK (harfbuzz 11.4.5) and the Platform (libharfbuzz.so.0) have it. libvpl.yml keeps its release but leads with the caveat, so the next person to reach for it learns why ffmpeg-full does not. ffmpeg-full-v1 is re-cut rather than superseded: nothing consumes it yet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…to opencv v2 libvpl is deleted, manifest and release both. It was cut for ffmpeg-full's QSV support, which the previous commit removed for good: the dispatcher alone cannot encode, the oneVPL runtime that can ships with Intel's media driver, and no Flatpak extension provides it — so no app in this ecosystem can ever consume this stack usefully. Keeping it with a warning in the header would just be a trap with a sign on it. wemeet-screenshare-hook now pins opencv-imgproc-v2 for its build. v1 is the unstripped 140 MB tree; v2 is the same sources stripped and without the unusable objdetect cascade data. The released hook-v1 artifact is unaffected and is not re-cut: re-pinning it would change com.tencent.wemeet's commit and make every user re-download the 197 MB payload for no functional gain. opencv-v1 is kept until the hook is next cut for a reason of its own, which is the point at which it can be deleted. README: the ten new stacks in the table, and the extra-data list brought up to date. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
freetype and harfbuzz get the drawtext filter compiled in, but without fontconfig it only accepts fontfile=/abs/path — font=Sans fails with "No font filename provided". Verified in a sandbox: drawtext rendered correctly with an explicit fontfile and the text came back through tesseract exactly, so the filter itself was fine; only the lookup was missing. fontconfig is in org.gnome.Sdk//50 and the Platform, and libass in this same stack already resolves fonts through it, so nothing new travels. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Eleven releases so
dk.nikse.subtitleeditcan stop building its dependencies into FlatPark's OSTree repository (flatpark/flatpark#238). That app's ref goes from 12.2 MB to 1.6 MB.Cut individually rather than as one bundle, so an app can pin just the encoder it needs and a version bump touches one stack:
ffmpeg-fulllibav*tesseractshare/tessdatapresetsleptonicalibleptonica.so.6x265libx265.so.216, 8-bitx264libx264.so.165sevenzip7zr, one dependency-free binaryrubberbandlibrubberband.so.3lamelibmp3lame.so.0libasslibass.so.9(0.17.4)uchardetlibuchardet.so.0All dev-complete so
ffmpeg-fullandtesseractcan build against them, and both clean their inputs out of their own archive — the consuming app pins those releases too and puts eachlib/onLD_LIBRARY_PATH, the splitwemeet-screenshare-hookalready uses foropencv-imgproc.libassis deliberately its own release.mpv-stackbuilds its own copy, so an app shipping both ends up with twolibass.so.9contending for one soname and the winner decided by module order — the problem flatpark/flatpark#238 had to work around withpost-install: rm -f libass.so.9.3.1. One release consumed from both ends settles it by construction. Follow-up:mpv-stackshould consume this rather than build its own.Decided by building, not by assumption
rubberband picks its built-in FFT and resampler even though fftw3, libsamplerate and speexdsp are all in the SDK. Pinned explicitly with
-Dfft=builtin -Dresampler=builtinso a change in upstream's defaults cannot silently add a DT_NEEDED. The result links nothing beyond libstdc++/libm/libgcc/libc.leptonica's dependency closure resolves completely inside
org.gnome.Platform//50— libtiff, libgif, libopenjp2, libwebp, libzstd all present.tesseract verified against
leptonica-v1: the archive carries no libleptonica, and with only that release'slib/added,bin/tesseracthas zero unresolved sonames.QSV and AMF are not enabled in
ffmpeg-full, andlibvplis deleted. Both are two-layer: ffmpeg links a dispatcher or dlopens a vendor library, and the layer that encodes ships with Intel's media driver or AMD's proprietary stack, neither of which any Flatpak extension provides. Measured in a sandbox on an Intel Arc 140V with--device=dri:VAAPI encoded through the same
/dev/dri/renderD128, so the GPU was never the issue.libvplwas cut for QSV and then deleted, manifest and release both: no app in this ecosystem can consume it usefully, and keeping it with a warning would be a trap with a sign on it.--enable-libharfbuzzand--enable-libfontconfig: freetype alone stopped being enough fordrawtextin FFmpeg 7+, and without fontconfig it only takesfontfile=/abs/path. Verified:drawtextwithfont=sansrenders and tesseract reads the text back exactly.ffplayis disabled, and thecpof each input archive skips itsmanifest.json— the first cut shippedffplay(putting an unwanted command on the consuming app's PATH) and libvpl's build record renamed to a misleadingmanifest-base-1.json.Also here
wemeet-screenshare-hooknow pinsopencv-imgproc-v2for its build — v1 is the unstripped 140 MB tree. The released hook artifact is not re-cut: re-pinning it would changecom.tencent.wemeet's commit and make every user re-download a 197 MB payload for no functional gain.opencv-imgproc-v1stays until the hook is next cut for a reason of its own.README: the ten new stacks in the table, the extra-data list brought up to date, and the strip rule recorded.
🤖 Generated with Claude Code