Skip to content

Commit 044bab1

Browse files
authored
Merge pull request #59 from peerless2012/dev_overlay_gl
Add open gl render mode in overlay.
2 parents 2e7f6a6 + c476080 commit 044bab1

File tree

23 files changed

+608
-123
lines changed

23 files changed

+608
-123
lines changed

app/src/main/java/io/github/peerless2012/ass/demo/MainActivity.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import androidx.core.net.toUri
2727

2828
class MainActivity : AppCompatActivity() {
2929

30-
private var url = "http://192.168.0.254:80/files/c.mkv"
30+
private var url = "http://192.168.3.6:8080/files/c.mkv"
3131

3232
private lateinit var player: ExoPlayer
3333

@@ -50,27 +50,27 @@ class MainActivity : AppCompatActivity() {
5050
player = ExoPlayer.Builder(this)
5151
.buildWithAssSupport(
5252
this,
53-
AssRenderType.OVERLAY,
53+
AssRenderType.OVERLAY_OPEN_GL,
5454
playerView.subtitleView
5555
)
5656
playerView.player = player
5757
val enConfig = MediaItem.SubtitleConfiguration
58-
.Builder(Uri.parse("http://192.168.0.254:80/files/e.ass"))
58+
.Builder(Uri.parse("http://192.168.3.6:8080/files/e.ass"))
5959
.setMimeType(MimeTypes.TEXT_SSA)
6060
.setLanguage("en")
6161
.setLabel("External ass en")
6262
.setId("129")
6363
.setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
6464
.build()
6565
val jpConfig = MediaItem.SubtitleConfiguration
66-
.Builder(Uri.parse("http://192.168.0.254:80/files/f-jp.ass"))
66+
.Builder(Uri.parse("http://192.168.3.6:8080/files/f-jp.ass"))
6767
.setMimeType(MimeTypes.TEXT_SSA)
6868
.setLanguage("jp")
6969
.setLabel("External ass jp")
7070
.setId("130")
7171
.build()
7272
val zhConfig = MediaItem.SubtitleConfiguration
73-
.Builder(Uri.parse("http://192.168.0.254:80/files/f-zh.ass"))
73+
.Builder(Uri.parse("http://192.168.3.6:8080/files/f-zh.ass"))
7474
.setMimeType(MimeTypes.TEXT_SSA)
7575
.setLanguage("zh")
7676
.setLabel("External ass zh")

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ android.nonTransitiveRClass=true
2424

2525
# maven publish
2626
GROUP=io.github.peerless2012
27-
VERSION_NAME=0.3.0
27+
VERSION_NAME=0.4.0-alpha01
2828

2929
POM_URL=https://github.com/peerless2012/libass-android
3030
POM_INCEPTION_YEAR=2025

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[versions]
2-
agp = "8.12.0"
2+
agp = "8.13.1"
33
kotlin = "2.2.0"
44
coreKtx = "1.16.0"
55
annotation = "1.9.1"

lib_ass_kt/src/main/cpp/AssKt.c

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <jni.h>
1010
#include "ass/ass.h"
1111
#include "fontconfig/fontconfig.h"
12+
#include "GLES2/gl2.h"
13+
#include "GLES2/gl2ext.h"
1214

1315
#define LOG_TAG "SubtitleRenderer"
1416

@@ -265,6 +267,24 @@ jobject createAlphaBitmap(JNIEnv* env, const ASS_Image* image) {
265267
return bitmap;
266268
}
267269

270+
jint createTexture(JNIEnv* env, const ASS_Image* image) {
271+
GLuint texture;
272+
glGenTextures(1, &texture);
273+
if (texture <= 0) return 0;
274+
glBindTexture(GL_TEXTURE_2D, texture);
275+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
276+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
277+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
278+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
279+
280+
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
281+
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, image->stride);
282+
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, image->w, image->h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, image->bitmap);
283+
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0);
284+
glBindTexture(GL_TEXTURE_2D, 0);
285+
return texture;
286+
}
287+
268288
static int count_ass_images(ASS_Image *images) {
269289
int count = 0;
270290
for (ASS_Image *img = images; img != NULL; img = img->next) {
@@ -273,7 +293,7 @@ static int count_ass_images(ASS_Image *images) {
273293
return count;
274294
}
275295

276-
jobject nativeAssRenderFrame(JNIEnv* env, jclass clazz, jlong render, jlong track, jlong time, jboolean onlyAlpha) {
296+
jobject nativeAssRenderFrame(JNIEnv* env, jclass clazz, jlong render, jlong track, jlong time, jint type) {
277297
int changed;
278298
ASS_Image *image = ass_render_frame((ASS_Renderer *) render, (ASS_Track *) track, time, &changed);
279299
if (image == NULL) {
@@ -289,7 +309,7 @@ jobject nativeAssRenderFrame(JNIEnv* env, jclass clazz, jlong render, jlong trac
289309

290310
int size = count_ass_images(image);
291311
jclass assTexClass = (*env)->FindClass(env, "io/github/peerless2012/ass/AssTex");
292-
jmethodID assTexConstructor = (*env)->GetMethodID(env, assTexClass, "<init>", "(IIILandroid/graphics/Bitmap;)V");
312+
jmethodID assTexConstructor = (*env)->GetMethodID(env, assTexClass, "<init>", "(IIIIILandroid/graphics/Bitmap;I)V");
293313

294314
jobjectArray assTexArr = (*env)->NewObjectArray(env, size, assTexClass, NULL);
295315
if (assTexArr == NULL) {
@@ -299,13 +319,18 @@ jobject nativeAssRenderFrame(JNIEnv* env, jclass clazz, jlong render, jlong trac
299319
int index = 0;
300320
for (ASS_Image *img = image; img != NULL; img = img->next) {
301321
jobject bitmap = NULL;
322+
jint tex = 0;
302323
if (img->w > 0 && img->h > 0) {
303-
bitmap = onlyAlpha ? createAlphaBitmap(env, img) : createBitmap(env, img);
324+
if (type == 0) {
325+
bitmap = createBitmap(env, img);
326+
} else if (type == 1) {
327+
bitmap = createAlphaBitmap(env, img);
328+
} else if (type == 2) {
329+
tex = createTexture(env, img);
330+
}
304331
}
305332
int32_t color = (int32_t) img->color;
306-
307-
jobject assTexObject = (*env)->NewObject(env, assTexClass, assTexConstructor, img->dst_x, img->dst_y, color, bitmap);
308-
333+
jobject assTexObject = (*env)->NewObject(env, assTexClass, assTexConstructor, img->dst_x, img->dst_y, img->w, img->h, color, bitmap, tex);
309334
(*env)->SetObjectArrayElement(env, assTexArr, index, assTexObject);
310335
(*env)->DeleteLocalRef(env, assTexObject);
311336
if (bitmap != NULL) {
@@ -329,7 +354,7 @@ static JNINativeMethod renderMethodTable[] = {
329354
{"nativeAssRenderSetCacheLimit", "(JII)V", (void*)nativeAssRenderSetCacheLimit},
330355
{"nativeAssRenderSetStorageSize", "(JII)V", (void*) nativeAssRenderSetStorageSize},
331356
{"nativeAssRenderSetFrameSize", "(JII)V", (void*)nativeAssRenderSetFrameSize},
332-
{"nativeAssRenderFrame", "(JJJZ)Lio/github/peerless2012/ass/AssFrame;", (void*) nativeAssRenderFrame},
357+
{"nativeAssRenderFrame", "(JJJI)Lio/github/peerless2012/ass/AssFrame;", (void*) nativeAssRenderFrame},
333358
{"nativeAssRenderDeinit", "(J)V", (void*)nativeAssRenderDeinit},
334359
};
335360
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {

lib_ass_kt/src/main/cpp/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,6 @@ target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
3232
lib_ass::ass
3333
android
3434
jnigraphics
35+
GLESv2
3536
z
3637
log)

lib_ass_kt/src/main/java/io/github/peerless2012/ass/AssRender.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class AssRender(nativeAss: Long) {
2727
external fun nativeAssRenderSetFrameSize(render: Long, width: Int, height: Int)
2828

2929
@JvmStatic
30-
external fun nativeAssRenderFrame(render: Long, track: Long, time: Long, onlyAlpha: Boolean): AssFrame?
30+
external fun nativeAssRenderFrame(render: Long, track: Long, time: Long, type: Int): AssFrame?
3131

3232
@JvmStatic
3333
external fun nativeAssRenderDeinit(render: Long)
@@ -57,8 +57,8 @@ class AssRender(nativeAss: Long) {
5757
nativeAssRenderSetFrameSize(nativeRender, width, height)
5858
}
5959

60-
public fun renderFrame(time: Long, onlyAlpha: Boolean): AssFrame? {
61-
return track?.let { nativeAssRenderFrame(nativeRender, it.nativeAssTrack, time, onlyAlpha) }
60+
public fun renderFrame(time: Long, type: AssTexType): AssFrame? {
61+
return track?.let { nativeAssRenderFrame(nativeRender, it.nativeAssTrack, time, type.ordinal) }
6262
}
6363

6464
protected fun finalize() {

lib_ass_kt/src/main/java/io/github/peerless2012/ass/AssTex.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ import android.graphics.Bitmap
99
* @Version V1.0
1010
* @Description
1111
*/
12-
data class AssTex(val x: Int, val y: Int, val color: Int, val bitmap: Bitmap? = null)
12+
data class AssTex(val x: Int, val y: Int, val w: Int, val h: Int, val color: Int, val bitmap: Bitmap? = null, val tex: Int = 0)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.github.peerless2012.ass
2+
3+
enum class AssTexType {
4+
5+
BITMAP_RGBA,
6+
7+
BITMAP_ALPHA,
8+
9+
TEXTURE
10+
11+
}

lib_ass_media/README.md

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,50 @@ App use media3 can use this module to add ass for your player.
77
There are three ways to render ass subtitle.
88
Which is defined in `AssRenderType`.
99

10-
| Type | Feature | Anim | HDR/DV |
11-
| :----: | :----: | :----: | :----: |
12-
| LEGACY | SubtitleView & Cue |||
13-
| CANVAS | Effect |||
14-
| OPEN_GL | Effect |||
10+
| Type | Feature | Anim | HDR/DV | Block render/UI |
11+
| :----: | :----: | :----: | :----: | :----: |
12+
| CUES | SubtitleView & Cue ||||
13+
| EFFECTS_CANVAS | Effect ||||
14+
| EFFECTS_OPEN_GL | Effect ||||
15+
| OVERLAY_CANVAS | Overlay ||||
16+
| OVERLAY_OPEN_GL | Overlay ||||
1517

1618
* [OverlayShaderProgram does not support HDR colors yet](https://github.com/androidx/media/issues/723)
1719
* [Why does TextOverLay support hdr, but Bitmap not support?](https://github.com/androidx/media/issues/2383)
1820

19-
### 1. LEGACY
21+
### 1. CUES
2022
The ass/ssa subtitle will be parsed and transcode to bytes, and decode to bitmap when render.
2123

2224
This type not support dynamic feature, because all subtitle and it time is static.
2325

2426
But since the subtitle is transcode, it will not cost too much time when render. All work is done in parse thread.
2527

26-
### 2. CANVAS
28+
### 2. EFFECTS_CANVAS
2729
The ass/ssa subtitle will be cal and render at runtime use media3 effect feature, and this will support all dynamic features.
2830

2931
And this need to create a screen size offscreen bitmap to render the libass bitmap pieces.
3032

3133
But when the dynamic feature is too complex, and libass will cost too much time to cal, the render will be blocked.
3234

33-
### 3. OPEN_GL
34-
Just like `CANVAS`, but use OpenGL to render. and the offscreen tex is create to render the bitmap pieces.
35+
### 3. EFFECTS_OPEN_GL
36+
Just like `EFFECTS_CANVAS`, but use OpenGL to render. and the offscreen tex is create to render the bitmap pieces.
37+
38+
Due to test, the `EFFECTS_OPEN_GL` will save 1/3 time when render.
39+
40+
### 4. OVERLAY_CANVAS
41+
The ass/ssa subtitle will be cal at runtime, and add a `Overlay` widget in `SubtitleView` to render subtitle.
42+
43+
The `libass` render result will copy to bitmap, and draw in `Canvas`.
44+
45+
It will block UI thread when rendering.
46+
47+
### 4. OVERLAY_OPEN_GL
48+
Just like `OVERLAY_CANVAS`, but the `libass` render result will pass to `OpenGL` texture, and avoid create tmp bitmap.
49+
50+
It will save half memory than `OVERLAY_CANVAS`.
51+
52+
And the `libass` render and `OpenGL` draw on another separate thread, it will not block the UI thread like `OVERLAY_CANVAS`.
3553

36-
Due to test, the `OPEN_GL` will save 1/3 time when render.
3754

3855
## How to use
3956
1. Add MavenCenter to your project

lib_ass_media/src/main/java/io/github/peerless2012/ass/media/AssHandler.kt

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ import io.github.peerless2012.ass.Ass
1919
import io.github.peerless2012.ass.media.parser.AssHeaderParser
2020
import io.github.peerless2012.ass.media.render.AssOverlayManager
2121
import io.github.peerless2012.ass.media.type.AssRenderType
22-
import kotlin.math.floor
23-
import kotlin.math.roundToInt
2422

2523
/**
2624
* Handles ASS subtitle rendering and integration with ExoPlayer.
@@ -98,8 +96,8 @@ class AssHandler(val renderType: AssRenderType) : Listener {
9896
fun init(player: ExoPlayer) {
9997
player.addListener(this)
10098
handler = Handler(player.applicationLooper)
101-
if (renderType == AssRenderType.CANVAS || renderType == AssRenderType.OPEN_GL) {
102-
overlayManager = AssOverlayManager(this, player, renderType == AssRenderType.OPEN_GL)
99+
if (renderType == AssRenderType.EFFECTS_CANVAS || renderType == AssRenderType.EFFECTS_OPEN_GL) {
100+
overlayManager = AssOverlayManager(this, player, renderType == AssRenderType.EFFECTS_OPEN_GL)
103101
}
104102
}
105103

@@ -162,7 +160,7 @@ class AssHandler(val renderType: AssRenderType) : Listener {
162160
this.track = track
163161
val render = requireNotNull(render)
164162
render.setStorageSize(videoSize.width, videoSize.height)
165-
if (renderType == AssRenderType.OVERLAY) {
163+
if (renderType == AssRenderType.OVERLAY_CANVAS || renderType == AssRenderType.OVERLAY_OPEN_GL) {
166164
render.setFrameSize(surfaceSize.width, surfaceSize.height)
167165
} else {
168166
render.setFrameSize(videoSize.width, videoSize.height)
@@ -186,7 +184,7 @@ class AssHandler(val renderType: AssRenderType) : Listener {
186184
Log.i("AssHandler", "onSurfaceSizeChanged: width = $width, height = $height")
187185
if (surfaceSize.width == width && surfaceSize.height == height) return
188186
surfaceSize = Size(width, height)
189-
if (renderType == AssRenderType.OVERLAY && surfaceSize.isValid) {
187+
if ((renderType == AssRenderType.OVERLAY_CANVAS || renderType == AssRenderType.OVERLAY_OPEN_GL) && surfaceSize.isValid) {
190188
render?.setFrameSize(surfaceSize.width, surfaceSize.height)
191189
}
192190
}
@@ -229,7 +227,7 @@ class AssHandler(val renderType: AssRenderType) : Listener {
229227

230228
val track = ass.createTrack()
231229
if (format.initializationData.size > 0) {
232-
val header = AssHeaderParser.parse(format, renderType != AssRenderType.LEGACY)
230+
val header = AssHeaderParser.parse(format, renderType != AssRenderType.CUES)
233231
track.readBuffer(header)
234232
}
235233
availableTracks[format.id!!] = track
@@ -252,7 +250,7 @@ class AssHandler(val renderType: AssRenderType) : Listener {
252250
if (videoSize.isValid) {
253251
render.setFrameSize(videoSize.width, videoSize.height)
254252
}
255-
if (renderType == AssRenderType.OVERLAY) {
253+
if (renderType == AssRenderType.OVERLAY_CANVAS || renderType == AssRenderType.OVERLAY_OPEN_GL) {
256254
if (surfaceSize.isValid) {
257255
render.setFrameSize(surfaceSize.width, surfaceSize.height)
258256
}

0 commit comments

Comments
 (0)