Skip to content

Commit 2fcc1de

Browse files
VEX-5825: Android player error handling improvements (#10)
Improve error handling.
1 parent a0b679c commit 2fcc1de

2 files changed

Lines changed: 138 additions & 83 deletions

File tree

android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java

Lines changed: 118 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
4444
import com.google.android.exoplayer2.drm.FrameworkMediaDrm;
4545
import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
46+
import com.google.android.exoplayer2.drm.MediaDrmCallbackException;
4647
import com.google.android.exoplayer2.drm.UnsupportedDrmException;
4748
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
4849
import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer;
@@ -459,94 +460,103 @@ private void initializePlayer() {
459460
new Handler().postDelayed(new Runnable() {
460461
@Override
461462
public void run() {
462-
if (player == null) {
463-
ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
464-
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
465-
trackSelector.setParameters(trackSelector.buildUponParameters()
466-
.setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate));
467-
468-
DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
469-
RNVLoadControl loadControl = new RNVLoadControl(
470-
allocator,
471-
minBufferMs,
472-
maxBufferMs,
473-
bufferForPlaybackMs,
474-
bufferForPlaybackAfterRebufferMs,
475-
-1,
476-
true,
477-
backBufferDurationMs,
478-
DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME
479-
);
480-
DefaultRenderersFactory renderersFactory =
481-
new DefaultRenderersFactory(getContext())
482-
.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF);
483-
player = new SimpleExoPlayer.Builder(getContext(), renderersFactory)
484-
.setTrackSelector​(trackSelector)
485-
.setBandwidthMeter(bandwidthMeter)
486-
.setLoadControl(loadControl)
487-
.build();
488-
player.addListener(self);
489-
player.addMetadataOutput(self);
490-
exoPlayerView.setPlayer(player);
491-
audioBecomingNoisyReceiver.setListener(self);
492-
bandwidthMeter.addEventListener(new Handler(), self);
493-
setPlayWhenReady(!isPaused);
494-
playerNeedsSource = true;
495-
496-
PlaybackParameters params = new PlaybackParameters(rate, 1f);
497-
player.setPlaybackParameters(params);
498-
}
499-
if (playerNeedsSource && srcUri != null) {
500-
exoPlayerView.invalidateAspectRatio();
501-
502-
// DRM
503-
DrmSessionManager drmSessionManager = null;
504-
if (self.drmUUID != null) {
505-
try {
506-
drmSessionManager = buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl,
507-
self.drmLicenseHeader);
508-
} catch (UnsupportedDrmException e) {
509-
int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported
510-
: (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
511-
? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown);
512-
eventEmitter.error(getResources().getString(errorStringId), e);
513-
return;
514-
}
515-
}
516-
// End DRM
517-
518-
ArrayList<MediaSource> mediaSourceList = buildTextSources();
519-
MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager);
520-
MediaSource mediaSource;
521-
if (mediaSourceList.size() == 0) {
522-
mediaSource = videoSource;
523-
} else {
524-
mediaSourceList.add(0, videoSource);
525-
MediaSource[] textSourceArray = mediaSourceList.toArray(
526-
new MediaSource[mediaSourceList.size()]
463+
try {
464+
if (player == null) {
465+
ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
466+
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
467+
trackSelector.setParameters(trackSelector.buildUponParameters()
468+
.setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate));
469+
470+
DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
471+
RNVLoadControl loadControl = new RNVLoadControl(
472+
allocator,
473+
minBufferMs,
474+
maxBufferMs,
475+
bufferForPlaybackMs,
476+
bufferForPlaybackAfterRebufferMs,
477+
-1,
478+
true,
479+
backBufferDurationMs,
480+
DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME
527481
);
528-
mediaSource = new MergingMediaSource(textSourceArray);
482+
DefaultRenderersFactory renderersFactory =
483+
new DefaultRenderersFactory(getContext())
484+
.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF);
485+
player = new SimpleExoPlayer.Builder(getContext(), renderersFactory)
486+
.setTrackSelector​(trackSelector)
487+
.setBandwidthMeter(bandwidthMeter)
488+
.setLoadControl(loadControl)
489+
.build();
490+
player.addListener(self);
491+
player.addMetadataOutput(self);
492+
exoPlayerView.setPlayer(player);
493+
audioBecomingNoisyReceiver.setListener(self);
494+
bandwidthMeter.addEventListener(new Handler(), self);
495+
setPlayWhenReady(!isPaused);
496+
playerNeedsSource = true;
497+
498+
PlaybackParameters params = new PlaybackParameters(rate, 1f);
499+
player.setPlaybackParameters(params);
529500
}
501+
if (playerNeedsSource && srcUri != null) {
502+
exoPlayerView.invalidateAspectRatio();
503+
504+
// DRM
505+
DrmSessionManager drmSessionManager = null;
506+
if (self.drmUUID != null) {
507+
try {
508+
drmSessionManager = buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl,
509+
self.drmLicenseHeader);
510+
} catch (UnsupportedDrmException e) {
511+
int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported
512+
: (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
513+
? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown);
514+
eventEmitter.error(getResources().getString(errorStringId), e, "3003");
515+
return;
516+
}
517+
}
518+
// End DRM
519+
520+
ArrayList<MediaSource> mediaSourceList = buildTextSources();
521+
MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager);
522+
MediaSource mediaSource;
523+
if (mediaSourceList.size() == 0) {
524+
mediaSource = videoSource;
525+
} else {
526+
mediaSourceList.add(0, videoSource);
527+
MediaSource[] textSourceArray = mediaSourceList.toArray(
528+
new MediaSource[mediaSourceList.size()]
529+
);
530+
mediaSource = new MergingMediaSource(textSourceArray);
531+
}
530532

531-
boolean haveResumePosition = resumeWindow != C.INDEX_UNSET;
532-
if (haveResumePosition) {
533-
player.seekTo(resumeWindow, resumePosition);
533+
boolean haveResumePosition = resumeWindow != C.INDEX_UNSET;
534+
if (haveResumePosition) {
535+
player.seekTo(resumeWindow, resumePosition);
536+
}
537+
player.prepare(mediaSource, !haveResumePosition, false);
538+
playerNeedsSource = false;
539+
540+
reLayout(exoPlayerView);
541+
eventEmitter.loadStart();
542+
loadVideoStarted = true;
534543
}
535-
player.prepare(mediaSource, !haveResumePosition, false);
536-
playerNeedsSource = false;
537544

538-
reLayout(exoPlayerView);
539-
eventEmitter.loadStart();
540-
loadVideoStarted = true;
545+
// Initializing the playerControlView
546+
initializePlayerControl();
547+
setControls(controls);
548+
applyModifiers();
549+
startBufferCheckTimer();
550+
551+
} catch (Exception ex) {
552+
self.playerNeedsSource = true;
553+
Log.e("ExoPlayer Exception", "Failed to initialize Player!");
554+
Log.e("ExoPlayer Exception", ex.toString());
555+
eventEmitter.error(ex.toString(), ex, "1001");
541556
}
542-
543-
// Initializing the playerControlView
544-
initializePlayerControl();
545-
setControls(controls);
546-
applyModifiers();
547-
startBufferCheckTimer();
548557
}
549558
}, 1);
559+
550560
}
551561

552562
private DrmSessionManager buildDrmSessionManager(UUID uuid,
@@ -567,6 +577,9 @@ private DrmSessionManager buildDrmSessionManager(UUID uuid,
567577
}
568578

569579
private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager) {
580+
if (uri == null) {
581+
throw new IllegalStateException("Invalid video uri");
582+
}
570583
int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension
571584
: uri.getLastPathSegment());
572585
config.setDisableDisconnectError(this.disableDisconnectError);
@@ -1015,6 +1028,7 @@ public void onPlaybackParametersChanged(PlaybackParameters params) {
10151028
@Override
10161029
public void onPlayerError(ExoPlaybackException e) {
10171030
String errorString = "ExoPlaybackException type : " + e.type;
1031+
String errorCode = "2001"; // Playback error code 2xxx (2001 - unknown playback exception)
10181032
Exception ex = e;
10191033
if (e.type == ExoPlaybackException.TYPE_RENDERER) {
10201034
Exception cause = e.getRendererException();
@@ -1024,24 +1038,45 @@ public void onPlayerError(ExoPlaybackException e) {
10241038
(MediaCodecRenderer.DecoderInitializationException) cause;
10251039
if (decoderInitializationException.codecInfo.name == null) {
10261040
if (decoderInitializationException.getCause() instanceof MediaCodecUtil.DecoderQueryException) {
1041+
errorCode = "2011";
10271042
errorString = getResources().getString(R.string.error_querying_decoders);
10281043
} else if (decoderInitializationException.secureDecoderRequired) {
1044+
errorCode = "2012";
10291045
errorString = getResources().getString(R.string.error_no_secure_decoder,
10301046
decoderInitializationException.mimeType);
10311047
} else {
1048+
errorCode = "2013";
10321049
errorString = getResources().getString(R.string.error_no_decoder,
10331050
decoderInitializationException.mimeType);
10341051
}
10351052
} else {
1053+
errorCode = "2014";
10361054
errorString = getResources().getString(R.string.error_instantiating_decoder,
10371055
decoderInitializationException.codecInfo.name);
10381056
}
10391057
}
10401058
}
10411059
else if (e.type == ExoPlaybackException.TYPE_SOURCE) {
1042-
errorString = getResources().getString(R.string.unrecognized_media_format);
1060+
Exception cause = e.getSourceException();
1061+
if (cause instanceof DefaultDrmSessionManager.MissingSchemeDataException) {
1062+
errorCode = "3004";
1063+
errorString = getResources().getString(R.string.unrecognized_media_format);
1064+
} else if(cause instanceof MediaDrmCallbackException) {
1065+
errorCode = "3005";
1066+
errorString = getResources().getString(R.string.unrecognized_media_format);
1067+
} else {
1068+
errorCode = "2021";
1069+
errorString = getResources().getString(R.string.unrecognized_media_format);
1070+
}
1071+
if (cause != null) {
1072+
Throwable rootCause = cause.getCause();
1073+
if (rootCause instanceof MediaDrmCallbackException) {
1074+
errorCode = "3005";
1075+
errorString = getResources().getString(R.string.unrecognized_media_format);
1076+
}
1077+
}
10431078
}
1044-
eventEmitter.error(errorString, ex);
1079+
eventEmitter.error(errorString, ex, errorCode);
10451080
playerNeedsSource = true;
10461081
if (isBehindLiveWindow(e)) {
10471082
clearResumePosition();
@@ -1491,7 +1526,7 @@ public void onDrmKeysLoaded(int windowIndex, MediaSource.MediaPeriodId mediaPeri
14911526
@Override
14921527
public void onDrmSessionManagerError(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId, Exception e) {
14931528
Log.d("DRM Info", "onDrmSessionManagerError");
1494-
eventEmitter.error("onDrmSessionManagerError", e);
1529+
eventEmitter.error("onDrmSessionManagerError", e, "3002");
14951530
}
14961531

14971532
@Override

android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
import java.lang.annotation.Retention;
1717
import java.lang.annotation.RetentionPolicy;
18+
import java.io.StringWriter;
19+
import java.io.PrintWriter;
1820

1921
class VideoEventEmitter {
2022

@@ -130,6 +132,8 @@ class VideoEventEmitter {
130132
private static final String EVENT_PROP_ERROR = "error";
131133
private static final String EVENT_PROP_ERROR_STRING = "errorString";
132134
private static final String EVENT_PROP_ERROR_EXCEPTION = "errorException";
135+
private static final String EVENT_PROP_ERROR_TRACE = "errorStackTrace";
136+
private static final String EVENT_PROP_ERROR_CODE = "errorCode";
133137

134138
private static final String EVENT_PROP_TIMED_METADATA = "metadata";
135139

@@ -243,9 +247,25 @@ void fullscreenDidDismiss() {
243247
}
244248

245249
void error(String errorString, Exception exception) {
250+
_error(errorString, exception, "0001");
251+
}
252+
253+
void error(String errorString, Exception exception, String errorCode) {
254+
_error(errorString, exception, errorCode);
255+
}
256+
257+
void _error(String errorString, Exception exception, String errorCode) {
258+
// Prepare stack trace
259+
StringWriter sw = new StringWriter();
260+
PrintWriter pw = new PrintWriter(sw);
261+
exception.printStackTrace(pw);
262+
String stackTrace = sw.toString();
263+
246264
WritableMap error = Arguments.createMap();
247265
error.putString(EVENT_PROP_ERROR_STRING, errorString);
248266
error.putString(EVENT_PROP_ERROR_EXCEPTION, exception.toString());
267+
error.putString(EVENT_PROP_ERROR_CODE, errorCode);
268+
error.putString(EVENT_PROP_ERROR_TRACE, stackTrace);
249269
WritableMap event = Arguments.createMap();
250270
event.putMap(EVENT_PROP_ERROR, error);
251271
receiveEvent(EVENT_ERROR, event);

0 commit comments

Comments
 (0)