Skip to content

Render ASS/SSA subtitles with libass - #3260

Open
MattiaSt1 wants to merge 2 commits into
recloudstream:masterfrom
MattiaSt1:libass-subtitles
Open

MattiaSt1 wants to merge 2 commits into
recloudstream:masterfrom
MattiaSt1:libass-subtitles

Conversation

@MattiaSt1

@MattiaSt1 MattiaSt1 commented Oct 16, 2026 •

Copy link
Copy Markdown

What

Adds full ASS/SSA subtitle rendering through libass 0.17.5, built from source.

How

  • LibassSubtitleSupport bridges ExoPlayer and libass. It gets the playback
    position from a NoSampleRenderer, renders on a dedicated thread and draws the
    result in a transparent overlay view inside exo_content_frame, so it follows
    resize modes and pinch zoom.
  • External files (URL, downloaded, OpenSubtitles): CustomDecoder loads the
    whole file into libass and stops forwarding text cues. SsaParser still runs,
    so subtitle sync keeps working.
  • MKV: UpdatedMatroskaExtractor sends each dialogue and the attached fonts to
    libass; the wrapped subtitle parser factory returns a no-op parser for those
    tracks.
  • Subtitle offset is applied to libass as well.
  • New setting "ASS/SSA rendering" in Settings > Player > Subtitles
  • Other subtitle formats are unchanged. If libass fails to load, the normal
    subtitle pipeline is used.

Native build

  • app/src/main/cpp/libass-deps.cmake downloads the official release tarballs of libass 0.17.5,
    FreeType 2.15.0, FriBidi 1.0.17 and HarfBuzz 14.6.0. Each tarball is checked against a pinned
    SHA-256 and the build fails on any mismatch. Nothing prebuilt is downloaded. The libass and
    FreeType hashes come from tarballs with a verified PGP signature (FriBidi and HarfBuzz do not
    sign their releases).
  • The sources (~25 MB) are downloaded once into app/build/libass-deps and shared between ABIs
    and variants. No CI changes are needed, Gradle installs the NDK if missing.
  • CMakeLists.txt builds everything as static libraries: FreeType without external
    dependencies, FriBidi with its config headers written by CMake (it only has a meson build),
    HarfBuzz as a single file without optional integrations, and libass with its ARM64 assembly
    on arm64 (the x86 assembly needs NASM, so the C version is used there).
  • Everything is linked into libcs3ass.so together with the static C++ runtime, so it can not
    clash with the other native libraries. It only exports the JNI functions, only depends on
    system libraries and is 16 KB page aligned. About 1.5–2.2 MB per ABI.
  • No new Gradle dependencies.
  • Updating a dependency means changing its version and hash in libass-deps.cmake.

Testing

Attached libass_effects_test.ass: 16 scenes covering positioning, colors,
fades, movement, 3D rotations, karaoke, clips, vector drawings, transparency
and non-Latin scripts. Load it on any video.

Tested on: Pixel 11 Pro XL

AI USED (Claude opus 5.5)
libass_effects_test.ass.txt

@MattiaSt1
MattiaSt1 marked this pull request as draft October 20, 2026 19:33
Render ASS/SSA subtitles with libass 0.17.5 in an overlay on top of the
video, like mpv and VLC do, instead of converting them to plain cues.
Works with external .ass/.ssa files and with ASS tracks embedded in MKV,
including attached fonts. It can be turned off in the player settings.

libass is built from source together with FreeType 2.15.0, FriBidi
1.0.17 and HarfBuzz 14.6.0:
- The official release tarballs are downloaded at configure time and
  checked against pinned SHA-256 hashes, nothing prebuilt is used. The
  libass and FreeType hashes come from tarballs with a verified PGP
  signature.
- Everything is linked statically into libcs3ass.so, with the static
  C++ runtime so it can not clash with the other native libraries.
@MattiaSt1
MattiaSt1 marked this pull request as ready for review October 20, 2026 20:44
@fire-light42

fire-light42 commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Nice!

I am planning on making this a dependency within the recloudstream organization, with your code as the basis. Is that okay? I want to remove the C++ build process entirely within CloudStream.

Your pull request will still remain, the build will just be substituted with an implementation(...)

@MattiaSt1

MattiaSt1 commented Oct 22, 2026 •

Copy link
Copy Markdown
Author

Feel free to do whatever you think is best.

@fire-light42 fire-light42 added Maintainer action needed This issue is blocked by a maintainer action AI Assisted This pull request was AI assisted (or generated). Review needed The pull request contains no obvious blockers. It just needs a review before merging. labels Nov 1, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI Assisted This pull request was AI assisted (or generated). Maintainer action needed This issue is blocked by a maintainer action Review needed The pull request contains no obvious blockers. It just needs a review before merging.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants