VEX-5825: Android player error handling improvements (#10)
Improve error handling.
This commit is contained in:
parent
a0b679cc07
commit
2fcc1de626
@ -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
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user