VEX-5825: Android player error handling improvements (#10)

Improve error handling.
This commit is contained in:
Armands Malejev 2021-10-12 14:56:50 +03:00 committed by GitHub
parent a0b679cc07
commit 2fcc1de626
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 139 additions and 84 deletions

View File

@ -43,6 +43,7 @@ import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.drm.FrameworkMediaDrm; import com.google.android.exoplayer2.drm.FrameworkMediaDrm;
import com.google.android.exoplayer2.drm.HttpMediaDrmCallback; import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
import com.google.android.exoplayer2.drm.MediaDrmCallbackException;
import com.google.android.exoplayer2.drm.UnsupportedDrmException; import com.google.android.exoplayer2.drm.UnsupportedDrmException;
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo; import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer; import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer;
@ -459,94 +460,103 @@ class ReactExoplayerView extends FrameLayout implements
new Handler().postDelayed(new Runnable() { new Handler().postDelayed(new Runnable() {
@Override @Override
public void run() { public void run() {
if (player == null) { try {
ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(); if (player == null) {
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
trackSelector.setParameters(trackSelector.buildUponParameters() trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
.setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate)); trackSelector.setParameters(trackSelector.buildUponParameters()
.setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate));
DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE); DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
RNVLoadControl loadControl = new RNVLoadControl( RNVLoadControl loadControl = new RNVLoadControl(
allocator, allocator,
minBufferMs, minBufferMs,
maxBufferMs, maxBufferMs,
bufferForPlaybackMs, bufferForPlaybackMs,
bufferForPlaybackAfterRebufferMs, bufferForPlaybackAfterRebufferMs,
-1, -1,
true, true,
backBufferDurationMs, backBufferDurationMs,
DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME
);
DefaultRenderersFactory renderersFactory =
new DefaultRenderersFactory(getContext())
.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF);
player = new SimpleExoPlayer.Builder(getContext(), renderersFactory)
.setTrackSelector(trackSelector)
.setBandwidthMeter(bandwidthMeter)
.setLoadControl(loadControl)
.build();
player.addListener(self);
player.addMetadataOutput(self);
exoPlayerView.setPlayer(player);
audioBecomingNoisyReceiver.setListener(self);
bandwidthMeter.addEventListener(new Handler(), self);
setPlayWhenReady(!isPaused);
playerNeedsSource = true;
PlaybackParameters params = new PlaybackParameters(rate, 1f);
player.setPlaybackParameters(params);
}
if (playerNeedsSource && srcUri != null) {
exoPlayerView.invalidateAspectRatio();
// DRM
DrmSessionManager drmSessionManager = null;
if (self.drmUUID != null) {
try {
drmSessionManager = buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl,
self.drmLicenseHeader);
} catch (UnsupportedDrmException e) {
int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported
: (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown);
eventEmitter.error(getResources().getString(errorStringId), e);
return;
}
}
// End DRM
ArrayList<MediaSource> mediaSourceList = buildTextSources();
MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager);
MediaSource mediaSource;
if (mediaSourceList.size() == 0) {
mediaSource = videoSource;
} else {
mediaSourceList.add(0, videoSource);
MediaSource[] textSourceArray = mediaSourceList.toArray(
new MediaSource[mediaSourceList.size()]
); );
mediaSource = new MergingMediaSource(textSourceArray); DefaultRenderersFactory renderersFactory =
new DefaultRenderersFactory(getContext())
.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF);
player = new SimpleExoPlayer.Builder(getContext(), renderersFactory)
.setTrackSelector(trackSelector)
.setBandwidthMeter(bandwidthMeter)
.setLoadControl(loadControl)
.build();
player.addListener(self);
player.addMetadataOutput(self);
exoPlayerView.setPlayer(player);
audioBecomingNoisyReceiver.setListener(self);
bandwidthMeter.addEventListener(new Handler(), self);
setPlayWhenReady(!isPaused);
playerNeedsSource = true;
PlaybackParameters params = new PlaybackParameters(rate, 1f);
player.setPlaybackParameters(params);
}
if (playerNeedsSource && srcUri != null) {
exoPlayerView.invalidateAspectRatio();
// DRM
DrmSessionManager drmSessionManager = null;
if (self.drmUUID != null) {
try {
drmSessionManager = buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl,
self.drmLicenseHeader);
} catch (UnsupportedDrmException e) {
int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported
: (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown);
eventEmitter.error(getResources().getString(errorStringId), e, "3003");
return;
}
}
// End DRM
ArrayList<MediaSource> mediaSourceList = buildTextSources();
MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager);
MediaSource mediaSource;
if (mediaSourceList.size() == 0) {
mediaSource = videoSource;
} else {
mediaSourceList.add(0, videoSource);
MediaSource[] textSourceArray = mediaSourceList.toArray(
new MediaSource[mediaSourceList.size()]
);
mediaSource = new MergingMediaSource(textSourceArray);
}
boolean haveResumePosition = resumeWindow != C.INDEX_UNSET;
if (haveResumePosition) {
player.seekTo(resumeWindow, resumePosition);
}
player.prepare(mediaSource, !haveResumePosition, false);
playerNeedsSource = false;
reLayout(exoPlayerView);
eventEmitter.loadStart();
loadVideoStarted = true;
} }
boolean haveResumePosition = resumeWindow != C.INDEX_UNSET; // Initializing the playerControlView
if (haveResumePosition) { initializePlayerControl();
player.seekTo(resumeWindow, resumePosition); setControls(controls);
} applyModifiers();
player.prepare(mediaSource, !haveResumePosition, false); startBufferCheckTimer();
playerNeedsSource = false;
} catch (Exception ex) {
reLayout(exoPlayerView); self.playerNeedsSource = true;
eventEmitter.loadStart(); Log.e("ExoPlayer Exception", "Failed to initialize Player!");
loadVideoStarted = true; Log.e("ExoPlayer Exception", ex.toString());
eventEmitter.error(ex.toString(), ex, "1001");
} }
// Initializing the playerControlView
initializePlayerControl();
setControls(controls);
applyModifiers();
startBufferCheckTimer();
} }
}, 1); }, 1);
} }
private DrmSessionManager buildDrmSessionManager(UUID uuid, private DrmSessionManager buildDrmSessionManager(UUID uuid,
@ -567,6 +577,9 @@ class ReactExoplayerView extends FrameLayout implements
} }
private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager) { private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager) {
if (uri == null) {
throw new IllegalStateException("Invalid video uri");
}
int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension
: uri.getLastPathSegment()); : uri.getLastPathSegment());
config.setDisableDisconnectError(this.disableDisconnectError); config.setDisableDisconnectError(this.disableDisconnectError);
@ -1015,6 +1028,7 @@ class ReactExoplayerView extends FrameLayout implements
@Override @Override
public void onPlayerError(ExoPlaybackException e) { public void onPlayerError(ExoPlaybackException e) {
String errorString = "ExoPlaybackException type : " + e.type; String errorString = "ExoPlaybackException type : " + e.type;
String errorCode = "2001"; // Playback error code 2xxx (2001 - unknown playback exception)
Exception ex = e; Exception ex = e;
if (e.type == ExoPlaybackException.TYPE_RENDERER) { if (e.type == ExoPlaybackException.TYPE_RENDERER) {
Exception cause = e.getRendererException(); Exception cause = e.getRendererException();
@ -1024,24 +1038,45 @@ class ReactExoplayerView extends FrameLayout implements
(MediaCodecRenderer.DecoderInitializationException) cause; (MediaCodecRenderer.DecoderInitializationException) cause;
if (decoderInitializationException.codecInfo.name == null) { if (decoderInitializationException.codecInfo.name == null) {
if (decoderInitializationException.getCause() instanceof MediaCodecUtil.DecoderQueryException) { if (decoderInitializationException.getCause() instanceof MediaCodecUtil.DecoderQueryException) {
errorCode = "2011";
errorString = getResources().getString(R.string.error_querying_decoders); errorString = getResources().getString(R.string.error_querying_decoders);
} else if (decoderInitializationException.secureDecoderRequired) { } else if (decoderInitializationException.secureDecoderRequired) {
errorCode = "2012";
errorString = getResources().getString(R.string.error_no_secure_decoder, errorString = getResources().getString(R.string.error_no_secure_decoder,
decoderInitializationException.mimeType); decoderInitializationException.mimeType);
} else { } else {
errorCode = "2013";
errorString = getResources().getString(R.string.error_no_decoder, errorString = getResources().getString(R.string.error_no_decoder,
decoderInitializationException.mimeType); decoderInitializationException.mimeType);
} }
} else { } else {
errorCode = "2014";
errorString = getResources().getString(R.string.error_instantiating_decoder, errorString = getResources().getString(R.string.error_instantiating_decoder,
decoderInitializationException.codecInfo.name); decoderInitializationException.codecInfo.name);
} }
} }
} }
else if (e.type == ExoPlaybackException.TYPE_SOURCE) { else if (e.type == ExoPlaybackException.TYPE_SOURCE) {
errorString = getResources().getString(R.string.unrecognized_media_format); Exception cause = e.getSourceException();
if (cause instanceof DefaultDrmSessionManager.MissingSchemeDataException) {
errorCode = "3004";
errorString = getResources().getString(R.string.unrecognized_media_format);
} else if(cause instanceof MediaDrmCallbackException) {
errorCode = "3005";
errorString = getResources().getString(R.string.unrecognized_media_format);
} else {
errorCode = "2021";
errorString = getResources().getString(R.string.unrecognized_media_format);
}
if (cause != null) {
Throwable rootCause = cause.getCause();
if (rootCause instanceof MediaDrmCallbackException) {
errorCode = "3005";
errorString = getResources().getString(R.string.unrecognized_media_format);
}
}
} }
eventEmitter.error(errorString, ex); eventEmitter.error(errorString, ex, errorCode);
playerNeedsSource = true; playerNeedsSource = true;
if (isBehindLiveWindow(e)) { if (isBehindLiveWindow(e)) {
clearResumePosition(); clearResumePosition();
@ -1491,7 +1526,7 @@ class ReactExoplayerView extends FrameLayout implements
@Override @Override
public void onDrmSessionManagerError(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId, Exception e) { public void onDrmSessionManagerError(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId, Exception e) {
Log.d("DRM Info", "onDrmSessionManagerError"); Log.d("DRM Info", "onDrmSessionManagerError");
eventEmitter.error("onDrmSessionManagerError", e); eventEmitter.error("onDrmSessionManagerError", e, "3002");
} }
@Override @Override

View File

@ -15,6 +15,8 @@ import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.io.StringWriter;
import java.io.PrintWriter;
class VideoEventEmitter { class VideoEventEmitter {
@ -130,6 +132,8 @@ class VideoEventEmitter {
private static final String EVENT_PROP_ERROR = "error"; private static final String EVENT_PROP_ERROR = "error";
private static final String EVENT_PROP_ERROR_STRING = "errorString"; private static final String EVENT_PROP_ERROR_STRING = "errorString";
private static final String EVENT_PROP_ERROR_EXCEPTION = "errorException"; private static final String EVENT_PROP_ERROR_EXCEPTION = "errorException";
private static final String EVENT_PROP_ERROR_TRACE = "errorStackTrace";
private static final String EVENT_PROP_ERROR_CODE = "errorCode";
private static final String EVENT_PROP_TIMED_METADATA = "metadata"; private static final String EVENT_PROP_TIMED_METADATA = "metadata";
@ -243,9 +247,25 @@ class VideoEventEmitter {
} }
void error(String errorString, Exception exception) { void error(String errorString, Exception exception) {
_error(errorString, exception, "0001");
}
void error(String errorString, Exception exception, String errorCode) {
_error(errorString, exception, errorCode);
}
void _error(String errorString, Exception exception, String errorCode) {
// Prepare stack trace
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
exception.printStackTrace(pw);
String stackTrace = sw.toString();
WritableMap error = Arguments.createMap(); WritableMap error = Arguments.createMap();
error.putString(EVENT_PROP_ERROR_STRING, errorString); error.putString(EVENT_PROP_ERROR_STRING, errorString);
error.putString(EVENT_PROP_ERROR_EXCEPTION, exception.toString()); error.putString(EVENT_PROP_ERROR_EXCEPTION, exception.toString());
error.putString(EVENT_PROP_ERROR_CODE, errorCode);
error.putString(EVENT_PROP_ERROR_TRACE, stackTrace);
WritableMap event = Arguments.createMap(); WritableMap event = Arguments.createMap();
event.putMap(EVENT_PROP_ERROR, error); event.putMap(EVENT_PROP_ERROR, error);
receiveEvent(EVENT_ERROR, event); receiveEvent(EVENT_ERROR, event);