Skip to content

Add libass native decoder #2324

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ tmp
.externalNativeBuild
.cxx

# ASS decoder extension
libraries/decoder_ass/src/main/jni/ass

# VP9 decoder extension
libraries/decoder_vp9/src/main/jni/libvpx
libraries/decoder_vp9/src/main/jni/libvpx_android_configs
Expand Down
2 changes: 2 additions & 0 deletions core_settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ project(modulePrefix + 'lib-datasource-okhttp').projectDir = new File(rootDir, '

include modulePrefix + 'lib-decoder'
project(modulePrefix + 'lib-decoder').projectDir = new File(rootDir, 'libraries/decoder')
include modulePrefix + 'lib-decoder-ass'
project(modulePrefix + 'lib-decoder-ass').projectDir = new File(rootDir, 'libraries/decoder_ass')
include modulePrefix + 'lib-decoder-av1'
project(modulePrefix + 'lib-decoder-av1').projectDir = new File(rootDir, 'libraries/decoder_av1')
include modulePrefix + 'lib-decoder-ffmpeg'
Expand Down
1 change: 1 addition & 0 deletions demos/main/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ dependencies {
implementation project(modulePrefix + 'lib-ui')
implementation project(modulePrefix + 'lib-datasource-cronet')
implementation project(modulePrefix + 'lib-exoplayer-ima')
withDecoderExtensionsImplementation project(modulePrefix + 'lib-decoder-ass')
withDecoderExtensionsImplementation project(modulePrefix + 'lib-decoder-av1')
withDecoderExtensionsImplementation project(modulePrefix + 'lib-decoder-ffmpeg')
withDecoderExtensionsImplementation project(modulePrefix + 'lib-decoder-flac')
Expand Down
141 changes: 141 additions & 0 deletions libraries/decoder_ass/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# ASS decoder module

The ASS module provides `AssRenderer`, which uses libass for decoding
and can render ssa/ass subtitle.

## License note

Please note that whilst the code in this repository is licensed under
[Apache 2.0][], using this module also requires building and including one or
more external libraries as described below. These are licensed separately.

[Apache 2.0]: ../../LICENSE

## Build instructions

### Prerequisites

Before running the build script for libass, you will need the following dependencies installed on
your system. You may use whatever package manager to install these:

* build-essential (or equivalent build tools on non-Debian systems)
* pkg-config
* autoconf
* automake
* libtool
* wget
* gperf
* meson and its dependencies (ninja-build, python3, etc.)

### Building on Linux or macOS

1. In a terminal, navigate to this current directory, to make sure the build of libass is done in
the correct directory, as it will create an `ass` directory directly inside `jni`:
```bash
cd /path/to/media/libraries/decoder_ass/src/main/jni
```
2. Locate the path of your Android NDK. You can manually download it from the
official [Android NDK website](https://developer.android.com/ndk/downloads):
```bash
NDK_PATH="<path to Android NDK>"
```
3. Set the host platform:
* For Linux:
```bash
HOST_PLATFORM="linux-x86_64"
```
* For macOS:
```bash
HOST_PLATFORM="darwin-x86_64"
```
4. Set the ABI version for native code (typically equal to your minSdk, and must not exceed it):
```bash
ANDROID_ABI_VERSION=21
```
5. Execute `build_libass.sh` to build libass for all architectures:
```bash
./build_libass.sh "${NDK_PATH}" "${HOST_PLATFORM}" "${ANDROID_ABI_VERSION}"
```

Be aware that you can always edit the script to only build for a specific architecture. The build
process may take some time minutes depending on your system. When complete, the built libraries will
be available in `ass/<architecture>/usr/local/lib/ directories`.

## Build instructions (Windows)

We do not provide official support for building this module directly on Windows. However, it is
possible to build using Windows Subsystem for Linux
[WSL2](https://learn.microsoft.com/en-us/windows/wsl/install) with the appropriate tools installed:

1. Install WSL2 and a Linux distribution (like Ubuntu) from the Microsoft Store
2. Download the [Android NDK](https://developer.android.com/ndk/downloads) for Linux within your
WSL2 environment
3. Follow the Linux build instructions above, setting the NDK path to its location in your WSL2
filesystem

For example, if you downloaded and extracted the NDK to your home directory in WSL2, the command
might look like:

```bash
./build_libass.sh ~/android-ndk-r27c linux-x86_64 21
```

## Note about the build script

The following script, `build_libass.sh`, as the name suggests, builds libass and its dependencies
for Android platforms. It automates the process of cross-compiling libass and all its dependencies
for Android. This enables Android applications to render complex subtitle formats with advanced
styling and positioning.

The script builds the following libraries in sequence:

1. [HarfBuzz (v11.0.0)](https://github.com/harfbuzz/harfbuzz) - An OpenType text shaping engine
2. [FreeType (v2.13.3)](https://freetype.org/) - A font rendering library
3. [FriBidi (v1.0.16)](https://github.com/fribidi/fribidi/) - A library implementing the Unicode
Bidirectional Algorithm
4. [UniBreak (v6.1)](https://github.com/adah1972/libunibreak/) - A line breaking library
implementing the Unicode Line Breaking Algorithm
5. [Expat (v2.7.1)](https://github.com/libexpat/libexpat) - An XML parser library
6. [Fontconfig (v2.16.0)](https://gitlab.freedesktop.org/fontconfig/fontconfig) - A library for font
customization and configuration
7. [libass (v0.17.3)](https://github.com/libass/libass) - The subtitle rendering library

All libraries are built as static libraries for four Android architectures:

* x86_64
* x86 (i686)
* armeabi-v7a (armv7a)
* arm64-v8a (aarch64)


## Troubleshooting

If you encounter issues during the build process:

1. Ensure all prerequisites are properly installed
2. Verify your NDK path is correct and the NDK version is compatible
3. Check that the HOST_PLATFORM matches your system
4. Make sure ANDROID_ABI_VERSION is appropriate for your project

Common errors:

* `meson: command not found` - Install meson using pip:
```bash
pip3 install meson
```
* NDK-related errors - Double-check your NDK path and ensure it's a complete installation
* Library download failures - Verify your internet connection and try again

For issues with specific dependencies, individual build functions in the script can be modified as
needed.

## Using the Built Libraries

After a successful build, the compiled libraries and headers can be used in your Android project.
They will be located in:

* Libraries: ass/<architecture>/usr/local/lib/
* Headers: ass/<architecture>/usr/local/include/

These files are already be referenced in your project's CMakeLists.txt file to link against the
static libraries.
46 changes: 46 additions & 0 deletions libraries/decoder_ass/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
apply from: "$gradle.ext.androidxMediaSettingsDir/common_library_config.gradle"

android {
namespace 'androidx.media3.decoder.ass'

// TODO(Internal: b/372449691): Remove packagingOptions once AGP is updated
// to version 8.5.1 or higher.
packagingOptions {
jniLibs {
useLegacyPackaging true
}
}
}

// Configure the native build only if ass is present to avoid gradle sync
// failures if ass hasn't been built according to the README instructions.
if (project.file('src/main/jni/ass').exists()) {
android.externalNativeBuild.cmake.path = 'src/main/jni/CMakeLists.txt'
// LINT.IfChange
// Should match cmake_minimum_required.
android.externalNativeBuild.cmake.version = '3.21.0+'
// LINT.ThenChange(src/main/jni/CMakeLists.txt)
}

dependencies {
api project(modulePrefix + 'lib-decoder')
// TODO(b/203752526): Remove this dependency.
implementation project(modulePrefix + 'lib-exoplayer')
implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
androidTestImplementation project(modulePrefix + 'test-utils')
androidTestImplementation 'androidx.test:runner:' + androidxTestRunnerVersion
}
16 changes: 16 additions & 0 deletions libraries/decoder_ass/proguard-rules.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Proguard rules specific to the libass extension.

# This prevents the names of native methods from being obfuscated.
-keepclasseswithmembernames class * {
native <methods>;
}

# Some members of these classes are being accessed from other modules via reflection. Keep them unobfuscated.
-keep class androidx.media3.decoder.ass.AssLibrary {
*;
}

# Some members of this class are being accessed from native methods. Keep them unobfuscated.
-keep class androidx.media3.decoder.ass.AssRenderResult {
*;
}
32 changes: 32 additions & 0 deletions libraries/decoder_ass/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2025 The Android Open Source Project

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="androidx.media3.decoder.ass.test">

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-sdk/>

<application
android:allowBackup="false"
tools:ignore="MissingApplicationIcon,HardcodedDebugMode"/>

<instrumentation
android:targetPackage="androidx.media3.decoder.ass.test"
android:name="androidx.test.runner.AndroidJUnitRunner"/>

</manifest>
Loading