Skip to content

Commit 5bb5be8

Browse files
Buthrakaurdoranteseduardo
authored andcommitted
fix video crashing on Android because of unresolved ExoPlayer breaking changes (mainly because of the new threading model https://developer.android.com/reference/androidx/media3/exoplayer/ExoPlayer#threading-model) ReactVision/viro#229
1 parent 1d6852f commit 5bb5be8

File tree

1 file changed

+142
-45
lines changed
  • android/sharedCode/src/main/java/com/viro/core/internal

1 file changed

+142
-45
lines changed

android/sharedCode/src/main/java/com/viro/core/internal/AVPlayer.java

Lines changed: 142 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -25,33 +25,37 @@
2525

2626
import android.content.Context;
2727
import android.net.Uri;
28+
import android.os.Handler;
29+
import android.os.Looper;
2830
import android.util.Log;
2931
import android.view.Surface;
3032

3133
import androidx.annotation.NonNull;
32-
3334
import androidx.media3.common.C;
34-
import androidx.media3.exoplayer.DefaultLoadControl;
35-
import androidx.media3.exoplayer.ExoPlayer;
35+
import androidx.media3.common.MediaItem;
3636
import androidx.media3.common.PlaybackException;
3737
import androidx.media3.common.Player;
38-
import androidx.media3.extractor.DefaultExtractorsFactory;
39-
import androidx.media3.extractor.ExtractorsFactory;
40-
import androidx.media3.exoplayer.source.MediaSource;
41-
import androidx.media3.exoplayer.source.ProgressiveMediaSource;
38+
import androidx.media3.common.util.Util;
39+
import androidx.media3.datasource.DataSource;
40+
import androidx.media3.datasource.DefaultDataSourceFactory;
41+
import androidx.media3.datasource.RawResourceDataSource;
42+
import androidx.media3.exoplayer.DefaultLoadControl;
43+
import androidx.media3.exoplayer.ExoPlayer;
4244
import androidx.media3.exoplayer.dash.DashMediaSource;
4345
import androidx.media3.exoplayer.hls.HlsMediaSource;
4446
import androidx.media3.exoplayer.smoothstreaming.SsMediaSource;
45-
import androidx.media3.exoplayer.trackselection.AdaptiveTrackSelection;
47+
import androidx.media3.exoplayer.source.MediaSource;
48+
import androidx.media3.exoplayer.source.ProgressiveMediaSource;
4649
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
47-
import androidx.media3.datasource.DataSource;
48-
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter;
49-
import androidx.media3.datasource.DefaultDataSourceFactory;
50-
import androidx.media3.datasource.RawResourceDataSource;
51-
import androidx.media3.common.util.Util;
52-
import androidx.media3.common.MediaItem;
50+
import androidx.media3.extractor.DefaultExtractorsFactory;
51+
import androidx.media3.extractor.ExtractorsFactory;
52+
5353
import com.google.common.base.Ascii;
5454

55+
import java.util.concurrent.Callable;
56+
import java.util.concurrent.ExecutionException;
57+
import java.util.concurrent.FutureTask;
58+
5559
/**
5660
* Wraps the Android ExoPlayer and can be controlled via JNI.
5761
*/
@@ -72,6 +76,9 @@ private enum State {
7276
}
7377

7478
private final ExoPlayer mExoPlayer;
79+
80+
private Handler mainThreadHandler = new Handler(Looper.getMainLooper());
81+
7582
private float mVolume;
7683
private final long mNativeReference;
7784
private boolean mLoop;
@@ -96,6 +103,7 @@ public AVPlayer(long nativeReference, Context context) {
96103

97104
@Override
98105
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
106+
Log.i(TAG, "AVPlayer onPlayerStateChanged " + mPrevExoPlayerState + " => " + playbackState);
99107
// this function sometimes gets called back w/ the same playbackState.
100108
if (mPrevExoPlayerState == playbackState) {
101109
return;
@@ -131,6 +139,24 @@ public void onPlayerError(@NonNull PlaybackException error) {
131139
});
132140
}
133141

142+
@FunctionalInterface
143+
public interface PlayerAction<T> {
144+
T performAction(ExoPlayer player);
145+
}
146+
147+
private <T> T runSynchronouslyOnMainThread(PlayerAction<T> action) throws ExecutionException, InterruptedException {
148+
Callable<T> callable = () -> action.performAction(mExoPlayer);
149+
FutureTask<T> future = new FutureTask<>(callable);
150+
151+
mainThreadHandler.post(future);
152+
try {
153+
return future.get();
154+
} catch (Exception e) {
155+
Log.e(TAG, "AVPlayer ExoPlayer failed to run action on the main thread", e);
156+
throw e;
157+
}
158+
}
159+
134160
public boolean setDataSourceURL(String resourceOrURL, final Context context) {
135161
try {
136162
reset();
@@ -157,14 +183,15 @@ public DataSource createDataSource() {
157183

158184
MediaSource mediaSource = buildMediaSource(uri, dataSourceFactory, extractorsFactory);
159185

160-
mExoPlayer.prepare(mediaSource);
161-
mExoPlayer.seekToDefaultPosition();
162-
mState = State.PREPARED;
163-
164-
Log.i(TAG, "AVPlayer prepared for playback");
165-
nativeOnPrepared(mNativeReference);
166-
167-
return true;
186+
return runSynchronouslyOnMainThread(player -> {
187+
player.setMediaSource(mediaSource);
188+
player.prepare();
189+
player.seekToDefaultPosition();
190+
mState = State.PREPARED;
191+
Log.i(TAG, "AVPlayer prepared for playback");
192+
nativeOnPrepared(mNativeReference);
193+
return true;
194+
});
168195
} catch (Exception e) {
169196
Log.w(TAG, "AVPlayer failed to load video at URL [" + resourceOrURL + "]", e);
170197
reset();
@@ -208,15 +235,28 @@ private int inferContentType(String fileName) {
208235
}
209236

210237
public void setVideoSink(Surface videoSink) {
211-
mExoPlayer.setVideoSurface(videoSink);
238+
try {
239+
runSynchronouslyOnMainThread(player -> {
240+
player.setVideoSurface(videoSink);
241+
return null;
242+
});
243+
} catch (Exception e) {
244+
Log.e(TAG, "AVPlayer failed to set video", e);
245+
}
212246
}
213247

214248
public void reset() {
215-
mExoPlayer.stop();
216-
mExoPlayer.seekToDefaultPosition();
217-
mState = State.IDLE;
218-
219-
Log.i(TAG, "AVPlayer reset");
249+
try {
250+
runSynchronouslyOnMainThread(player -> {
251+
player.stop();
252+
player.seekToDefaultPosition();
253+
mState = State.IDLE;
254+
return null;
255+
});
256+
Log.i(TAG, "AVPlayer reset");
257+
} catch (Exception e) {
258+
Log.e(TAG, "AVPlayer failed reset", e);
259+
}
220260
}
221261

222262
public void destroy() {
@@ -228,17 +268,31 @@ public void destroy() {
228268

229269
public void play() {
230270
if (mState == State.PREPARED || mState == State.PAUSED) {
231-
mExoPlayer.setPlayWhenReady(true);
232-
mState = State.STARTED;
271+
try {
272+
runSynchronouslyOnMainThread(player -> {
273+
player.setPlayWhenReady(true);
274+
mState = State.STARTED;
275+
return null;
276+
});
277+
} catch (Exception e) {
278+
Log.e(TAG, "AVPlayer failed to play video", e);
279+
}
233280
} else {
234281
Log.w(TAG, "AVPlayer could not play video in " + mState.toString() + " state");
235282
}
236283
}
237284

238285
public void pause() {
239286
if (mState == State.STARTED) {
240-
mExoPlayer.setPlayWhenReady(false);
241-
mState = State.PAUSED;
287+
try {
288+
runSynchronouslyOnMainThread(player -> {
289+
player.setPlayWhenReady(false);
290+
mState = State.PAUSED;
291+
return null;
292+
});
293+
} catch (Exception e) {
294+
Log.e(TAG, "AVPlayer failed to pause video", e);
295+
}
242296
} else {
243297
Log.w(TAG, "AVPlayer could not pause video in " + mState.toString() + " state");
244298
}
@@ -250,24 +304,46 @@ public boolean isPaused() {
250304

251305
public void setLoop(boolean loop) {
252306
mLoop = loop;
253-
if (mExoPlayer.getPlaybackState() == ExoPlayer.STATE_ENDED) {
254-
mExoPlayer.seekToDefaultPosition();
307+
try {
308+
runSynchronouslyOnMainThread(player -> {
309+
if (player.getPlaybackState() == ExoPlayer.STATE_ENDED) {
310+
player.seekToDefaultPosition();
311+
}
312+
return null;
313+
});
314+
} catch (Exception e) {
315+
Log.e(TAG, "AVPlayer failed to set loop", e);
255316
}
256317
}
257318

258319
public void setVolume(float volume) {
259320
mVolume = volume;
260-
if (!mMute) {
261-
mExoPlayer.setVolume(mVolume);
321+
if (mMute) {
322+
return;
323+
}
324+
try {
325+
runSynchronouslyOnMainThread(player -> {
326+
player.setVolume(mVolume);
327+
return null;
328+
});
329+
} catch (Exception e) {
330+
Log.e(TAG, "AVPlayer failed to set volume", e);
262331
}
263332
}
264333

265334
public void setMuted(boolean muted) {
266335
mMute = muted;
267-
if (muted) {
268-
mExoPlayer.setVolume(0);
269-
} else {
270-
mExoPlayer.setVolume(mVolume);
336+
try {
337+
runSynchronouslyOnMainThread(player -> {
338+
if (muted) {
339+
player.setVolume(0);
340+
} else {
341+
player.setVolume(mVolume);
342+
}
343+
return null;
344+
});
345+
} catch (Exception e) {
346+
Log.e(TAG, "AVPlayer failed to set muted " + muted, e);
271347
}
272348
}
273349

@@ -276,8 +352,14 @@ public void seekToTime(float seconds) {
276352
Log.w(TAG, "AVPlayer could not seek while in IDLE state");
277353
return;
278354
}
279-
280-
mExoPlayer.seekTo((long) (seconds * 1000));
355+
try {
356+
runSynchronouslyOnMainThread(player -> {
357+
player.seekTo((long) (seconds * 1000));
358+
return null;
359+
});
360+
} catch (Exception e) {
361+
Log.e(TAG, "AVPlayer failed to seek", e);
362+
}
281363
}
282364

283365
public float getCurrentTimeInSeconds() {
@@ -286,18 +368,33 @@ public float getCurrentTimeInSeconds() {
286368
return 0;
287369
}
288370

289-
return mExoPlayer.getCurrentPosition() / 1000.0f;
371+
long currentPosition = 0;
372+
try {
373+
currentPosition = runSynchronouslyOnMainThread(player -> player.getCurrentPosition());
374+
} catch (Exception e) {
375+
Log.e(TAG, "AVPlayer could not get video current position", e);
376+
}
377+
378+
return currentPosition / 1000.0f;
290379
}
291380

292381
public float getVideoDurationInSeconds() {
293382
if (mState == State.IDLE) {
294383
Log.w(TAG, "AVPlayer could not get video duration in IDLE state");
295384
return 0;
296-
} else if (mExoPlayer.getDuration() == C.TIME_UNSET) {
385+
}
386+
387+
long duration = 0;
388+
try {
389+
duration = runSynchronouslyOnMainThread(player -> player.getDuration());
390+
} catch (Exception e) {
391+
Log.e(TAG, "AVPlayer could not get video duration", e);
392+
}
393+
if (duration == C.TIME_UNSET) {
297394
return 0;
298395
}
299396

300-
return mExoPlayer.getDuration() / 1000.0f;
397+
return duration / 1000.0f;
301398
}
302399

303400
/**

0 commit comments

Comments
 (0)