Skip to content

Commit f952bc6

Browse files
committed
Add thread safety and lifecycle for native pointers
1 parent c10b71a commit f952bc6

5 files changed

Lines changed: 123 additions & 14 deletions

File tree

lib_ass_kt/src/main/cpp/AssKt.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,12 @@ jlong nativeAssTrackInit(JNIEnv* env, jclass clazz, jlong ass) {
6969
}
7070

7171
jint nativeAssTrackGetWidth(JNIEnv* env, jclass clazz, jlong track) {
72+
if (!track) return 0;
7273
return ((ASS_Track *) track)->PlayResX;
7374
}
7475

7576
jobjectArray nativeAssTrackGetEvents(JNIEnv* env, jclass clazz, jlong track) {
77+
if (!track) return NULL;
7678
jclass eventClass = (*env)->FindClass(env, "io/github/peerless2012/ass/AssEvent");
7779
if (eventClass == NULL) {
7880
return NULL;
@@ -121,6 +123,7 @@ jobjectArray nativeAssTrackGetEvents(JNIEnv* env, jclass clazz, jlong track) {
121123
}
122124

123125
void nativeAssTrackClearEvents(JNIEnv* env, jclass clazz, jlong track) {
126+
if (!track) return;
124127
ASS_Track* tr = (ASS_Track *) track;
125128
for (int i = 0; i < tr->n_events; i++) {
126129
ass_free_event(tr, i);
@@ -129,10 +132,12 @@ void nativeAssTrackClearEvents(JNIEnv* env, jclass clazz, jlong track) {
129132
}
130133

131134
jint nativeAssTrackGetHeight(JNIEnv* env, jclass clazz, jlong track) {
135+
if (!track) return 0;
132136
return ((ASS_Track *) track)->PlayResY;
133137
}
134138

135139
void nativeAssTrackReadBuffer(JNIEnv* env, jclass clazz, jlong track, jbyteArray buffer, jint offset, jint length) {
140+
if (!track) return;
136141
jboolean isCopy;
137142
jbyte* elements = (*env)->GetByteArrayElements(env, buffer, &isCopy);
138143
if (elements == NULL) {
@@ -143,6 +148,7 @@ void nativeAssTrackReadBuffer(JNIEnv* env, jclass clazz, jlong track, jbyteArray
143148
}
144149

145150
void nativeAssTrackReadChunk(JNIEnv* env, jclass clazz, jlong track, jlong start, jlong duration, jbyteArray buffer, jint offset, jint length) {
151+
if (!track) return;
146152
jboolean isCopy;
147153
jbyte* elements = (*env)->GetByteArrayElements(env, buffer, &isCopy);
148154
if (elements == NULL) {
@@ -153,6 +159,7 @@ void nativeAssTrackReadChunk(JNIEnv* env, jclass clazz, jlong track, jlong start
153159
}
154160

155161
void nativeAssTrackDeinit(JNIEnv* env, jclass clazz, jlong track) {
162+
if (!track) return;
156163
ass_free_track((ASS_Track *) track);
157164
}
158165

@@ -175,18 +182,22 @@ jlong nativeAssRenderInit(JNIEnv* env, jclass clazz, jlong ass) {
175182
}
176183

177184
void nativeAssRenderSetFontScale(JNIEnv* env, jclass clazz, jlong render, jfloat scale) {
185+
if (!render) return;
178186
ass_set_font_scale((ASS_Renderer *) render, scale);
179187
}
180188

181189
void nativeAssRenderSetCacheLimit(JNIEnv* env, jclass clazz, jlong render, jint glyphMax, jint bitmapMaxSize) {
190+
if (!render) return;
182191
ass_set_cache_limits((ASS_Renderer *) render, glyphMax, bitmapMaxSize);
183192
}
184193

185194
void nativeAssRenderSetFrameSize(JNIEnv* env, jclass clazz, jlong render, jint width, jint height) {
195+
if (!render) return;
186196
ass_set_frame_size((ASS_Renderer *) render, width, height);
187197
}
188198

189199
void nativeAssRenderSetStorageSize(JNIEnv* env, jclass clazz, jlong render, jint width, jint height) {
200+
if (!render) return;
190201
ass_set_storage_size((ASS_Renderer *) render, width, height);
191202
}
192203

@@ -294,6 +305,7 @@ static int count_ass_images(ASS_Image *images) {
294305
}
295306

296307
jobject nativeAssRenderFrame(JNIEnv* env, jclass clazz, jlong render, jlong track, jlong time, jint type) {
308+
if (!render || !track) return NULL;
297309
int changed;
298310
ASS_Image *image = ass_render_frame((ASS_Renderer *) render, (ASS_Track *) track, time, &changed);
299311
if (image == NULL) {

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,43 @@ class Ass {
2929

3030
}
3131

32-
private val nativeAss: Long = nativeAssInit()
32+
private var nativeAss: Long = nativeAssInit()
33+
34+
@Volatile
35+
var released = false
36+
private set
3337

3438
public fun createTrack(): AssTrack {
39+
if (released || nativeAss == 0L) throw IllegalStateException("Ass already released")
3540
return AssTrack(nativeAss)
3641
}
3742

3843
public fun createRender(): AssRender {
44+
if (released || nativeAss == 0L) throw IllegalStateException("Ass already released")
3945
return AssRender(nativeAss)
4046
}
4147

4248
public fun addFont(name: String, buffer: ByteArray) {
49+
if (released || nativeAss == 0L) return
4350
nativeAssAddFont(nativeAss, name, buffer)
4451
}
4552

4653
public fun clearFont() {
54+
if (released || nativeAss == 0L) return
4755
nativeAssClearFont(nativeAss)
4856
}
4957

58+
fun release() {
59+
if (released) return
60+
released = true
61+
if (nativeAss != 0L) {
62+
nativeAssDeinit(nativeAss)
63+
nativeAss = 0
64+
}
65+
}
66+
5067
protected fun finalize() {
51-
nativeAssDeinit(nativeAss)
68+
release()
5269
}
5370

54-
}
71+
}

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

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package io.github.peerless2012.ass
22

3+
import java.util.concurrent.locks.ReentrantLock
4+
import kotlin.concurrent.withLock
5+
36
/**
47
* @Author peerless2012
58
* @Email peerless2012@126.com
@@ -33,36 +36,65 @@ class AssRender(nativeAss: Long) {
3336
external fun nativeAssRenderDeinit(render: Long)
3437
}
3538

36-
private val nativeRender: Long = nativeAssRenderInit(nativeAss)
39+
val lock = ReentrantLock()
40+
41+
private var nativeRender: Long = nativeAssRenderInit(nativeAss)
42+
43+
@Volatile
44+
var released = false
45+
private set
3746

3847
private var track: AssTrack? = null
3948

4049
public fun setTrack(track: AssTrack?) {
41-
this.track = track
50+
lock.withLock {
51+
this.track = track
52+
}
4253
}
4354

4455
public fun setFontScale(scale: Float) {
56+
if (released || nativeRender == 0L) return
4557
nativeAssRenderSetFontScale(nativeRender, scale)
4658
}
4759

4860
public fun setCacheLimit(glyphMax: Int, bitmapMaxSize: Int) {
61+
if (released || nativeRender == 0L) return
4962
nativeAssRenderSetCacheLimit(nativeRender, glyphMax, bitmapMaxSize)
5063
}
5164

5265
public fun setStorageSize(width: Int, height: Int) {
66+
if (released || nativeRender == 0L) return
5367
nativeAssRenderSetStorageSize(nativeRender, width, height)
5468
}
5569

5670
public fun setFrameSize(width: Int, height: Int) {
71+
if (released || nativeRender == 0L) return
5772
nativeAssRenderSetFrameSize(nativeRender, width, height)
5873
}
5974

6075
public fun renderFrame(time: Long, type: AssTexType): AssFrame? {
61-
return track?.let { nativeAssRenderFrame(nativeRender, it.nativeAssTrack, time, type.ordinal) }
76+
lock.withLock {
77+
if (released || nativeRender == 0L) return null
78+
val t = track ?: return null
79+
if (t.released || t.nativeAssTrack == 0L) return null
80+
return nativeAssRenderFrame(nativeRender, t.nativeAssTrack, time, type.ordinal)
81+
}
82+
}
83+
84+
fun release() {
85+
lock.withLock {
86+
if (released) return
87+
released = true
88+
track = null
89+
if (nativeRender != 0L) {
90+
nativeAssRenderDeinit(nativeRender)
91+
nativeRender = 0
92+
}
93+
}
6294
}
6395

6496
protected fun finalize() {
65-
nativeAssRenderDeinit(nativeRender)
97+
release()
6698
}
6799

68-
}
100+
}

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

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,34 +36,54 @@ class AssTrack(private val ass: Long) {
3636
external fun nativeAssTrackDeinit(track: Long)
3737
}
3838

39-
public val nativeAssTrack = nativeAssTrackInit(ass)
39+
var nativeAssTrack = nativeAssTrackInit(ass)
40+
private set
41+
42+
@Volatile
43+
var released = false
44+
private set
4045

4146
public fun getWidth(): Int {
47+
if (released || nativeAssTrack == 0L) return 0
4248
return nativeAssTrackGetWidth(nativeAssTrack)
4349
}
4450

4551
public fun getHeight(): Int {
52+
if (released || nativeAssTrack == 0L) return 0
4653
return nativeAssTrackGetHeight(nativeAssTrack)
4754
}
4855

4956
public fun getEvents(): Array<AssEvent>? {
57+
if (released || nativeAssTrack == 0L) return null
5058
return nativeAssTrackGetEvents(nativeAssTrack)
5159
}
5260

5361
public fun clearEvent() {
62+
if (released || nativeAssTrack == 0L) return
5463
nativeAssTrackClearEvents(nativeAssTrack)
5564
}
5665

5766
public fun readBuffer(array: ByteArray, offset: Int = 0, length : Int = array.size) {
67+
if (released || nativeAssTrack == 0L) return
5868
nativeAssTrackReadBuffer(nativeAssTrack, array, offset, length)
5969
}
6070

6171
public fun readChunk(start: Long, duration: Long, array: ByteArray, offset: Int = 0, length: Int = array.size) {
72+
if (released || nativeAssTrack == 0L) return
6273
nativeAssTrackReadChunk(nativeAssTrack, start, duration, array, offset, length)
6374
}
6475

76+
fun release() {
77+
if (released) return
78+
released = true
79+
if (nativeAssTrack != 0L) {
80+
nativeAssTrackDeinit(nativeAssTrack)
81+
nativeAssTrack = 0
82+
}
83+
}
84+
6585
protected fun finalize() {
66-
nativeAssTrackDeinit(nativeAssTrack)
86+
release()
6787
}
6888

69-
}
89+
}

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

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ 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.concurrent.withLock
2223

2324
/**
2425
* Handles ASS subtitle rendering and integration with ExoPlayer.
@@ -35,7 +36,8 @@ class AssHandler(
3536

3637
/** The ASS instance used for creating tracks and renderers. This is lazy to avoid loading
3738
* libass if the played media does not have ASS tracks. */
38-
val ass by lazy { Ass() }
39+
private val assDelegate = lazy { Ass() }
40+
val ass by assDelegate
3941

4042
/** The current ASS renderer. It's created as soon as a ASS track is detected. */
4143
var render: AssRender? = null
@@ -296,7 +298,8 @@ class AssHandler(
296298

297299
/**
298300
* Reads a dialogue into the track of the given [trackId].
299-
* TODO This should move to executor.
301+
* Synchronized with renderFrame via the render lock to prevent concurrent
302+
* ass_process_chunk / ass_render_frame on the same track.
300303
*/
301304
fun readTrackDialogue(
302305
trackId: String?,
@@ -306,7 +309,15 @@ class AssHandler(
306309
offset: Int = 0,
307310
length: Int = data.size
308311
) {
309-
availableTracks[trackId]?.readChunk(start, duration, data, offset, length)
312+
val t = availableTracks[trackId] ?: return
313+
val r = render
314+
if (r != null) {
315+
r.lock.withLock {
316+
t.readChunk(start, duration, data, offset, length)
317+
}
318+
} else {
319+
t.readChunk(start, duration, data, offset, length)
320+
}
310321
}
311322

312323
/**
@@ -343,6 +354,23 @@ class AssHandler(
343354
}?.getTrackFormat(0)
344355
}
345356

357+
/**
358+
* Releases all native resources held by this handler.
359+
*/
360+
fun release() {
361+
videoTimeCallback = null
362+
overlayManager?.disable()
363+
render?.release()
364+
render = null
365+
availableTracks.values.forEach { it.release() }
366+
availableTracks.clear()
367+
track = null
368+
pendingFonts.clear()
369+
if (assDelegate.isInitialized()) {
370+
ass.release()
371+
}
372+
}
373+
346374
/**
347375
* Checks if the size is valid (both width and height are greater than 0).
348376
*/

0 commit comments

Comments
 (0)