fluxa/tools/libass
KhooLy 852373a4c1 Fix libass fonts and glyph rendering to match mpv
Embedded MKV font attachments now reach libass reliably. The font scan
was disabled for the local torrent proxy, which is where all torrent
playback lives, so styled fansub tracks fell back to system fonts.

Font sourcing is now layered: a persistent PrefixCaptureBuffer harvests
attachments from the bytes ExoPlayer already downloads (the old capture
died with each ExtractorInput ExoPlayer recreated per load), and an HTTP
scan locates the Attachments element via the SeekHead and fetches it
exactly instead of guessing with an 8 MB prefix. The scan waits for the
player's own connection to open before touching the proxy, backs off
hard between retries, and aborts once fonts exist from any source.
Fonts that arrive after renderer creation recreate it and replay
buffered events. Truncated attachments are dropped instead of being fed
to FreeType half-parsed.

Two compositing bugs caused the sheared "fake italic" glyphs: coverage
rows are packed 4-byte aligned but ALPHA_8 copyPixelsFromBuffer consumes
bitmap rowBytes, so odd-width glyphs skewed one pixel per row; and the
glyph cache was keyed by the native ASS_Image bitmap pointer, which
libass reuses across frames, serving stale glyphs. Rows are repacked
when strides differ and the cache is keyed by content hash.

Startup cost is now near zero: renderer creation moved off ExoPlayer's
loader thread onto the libass HandlerThread, and XDG_CACHE_HOME points
at app storage so fontconfig's /system/fonts scan (~2 s) is cached and
subsequent inits take ~80 ms. libass diagnostics stream to logcat via
ass_set_message_cb.
2026-07-03 23:43:50 +03:00
..
generate-exoplayer-libass-fixture.sh Fix libass fonts and glyph rendering to match mpv 2026-07-03 23:43:50 +03:00
README.txt Fix libass fonts and glyph rendering to match mpv 2026-07-03 23:43:50 +03:00

Testing ExoPlayer Libass Support
================================

Fluxa renders ASS/SSA subtitles for ExoPlayer through a native libass overlay because Media3 does not preserve the full ASS styling model. The verification needs to run on Android, not only on the JVM, because NativeLibassRenderer loads fluxa_libass_renderer and resolves libass symbols from the packaged libmpv.so.

Fast device probe
-----------------

Run the always-available instrumentation tests:

./gradlew :app:connectedMobileDebugAndroidTest \
  -Pandroid.testInstrumentationRunnerArguments.class=com.fluxa.app.player.LibassExoPlayerInstrumentationTest

These tests verify:

- NativeLibassRenderer.create(...) succeeds on-device.
- ASS timing produces pixels only inside the dialogue window.
- ASS positioning draws around the expected frame region.
- LibassEventRelay converts raw MKV ASS packet bodies into renderable ASS dialogue lines.
- Fluxa-created ExoPlayer instances register a LibassEventRelay.

Full embedded MKV probe
-----------------------

Generate a small MKV with an embedded ASS subtitle track:

tools/libass/generate-exoplayer-libass-fixture.sh /tmp/fluxa-libass-fixture
adb push /tmp/fluxa-libass-fixture/libass-exoplayer-probe.mkv /data/local/tmp/libass-exoplayer-probe.mkv

Run the optional end-to-end ExoPlayer playback probe:

./gradlew :app:connectedMobileDebugAndroidTest \
  -Pandroid.testInstrumentationRunnerArguments.class=com.fluxa.app.player.LibassExoPlayerInstrumentationTest \
  -Pandroid.testInstrumentationRunnerArguments.libassMkvUrl=file:///data/local/tmp/libass-exoplayer-probe.mkv \
  -Pandroid.testInstrumentationRunnerArguments.libassProbeMs=1500

The optional probe starts a real Fluxa ExoPlayer, waits for an embedded SSA/ASS text track, enables it, waits for the relay renderer, then asks libass to render at the probe timestamp and checks that subtitle pixels exist.

Logcat
------

Keep this running while testing:

adb logcat -s FluxaLibassRenderer NativeLibassOverlay PlayerScreen

Failures to investigate:

- Could not open libmpv.so
- Missing libass symbol
- libass not available via libmpv.so
- External renderer failed
- Embedded renderer failed