VEX-4579: Network loss handling (#5)
Add support for customizing back buffer duration and handle network errors gracefully to prevent releasing the player when network is lost.
This commit is contained in:
parent
f6cce0d819
commit
80873102a4
14
README.md
14
README.md
@ -276,10 +276,12 @@ var styles = StyleSheet.create({
|
|||||||
* [allowsExternalPlayback](#allowsexternalplayback)
|
* [allowsExternalPlayback](#allowsexternalplayback)
|
||||||
* [audioOnly](#audioonly)
|
* [audioOnly](#audioonly)
|
||||||
* [automaticallyWaitsToMinimizeStalling](#automaticallyWaitsToMinimizeStalling)
|
* [automaticallyWaitsToMinimizeStalling](#automaticallyWaitsToMinimizeStalling)
|
||||||
|
* [backBufferDurationMs](#backBufferDurationMs)
|
||||||
* [bufferConfig](#bufferconfig)
|
* [bufferConfig](#bufferconfig)
|
||||||
* [controls](#controls)
|
* [controls](#controls)
|
||||||
* [currentPlaybackTime](#currentPlaybackTime)
|
* [currentPlaybackTime](#currentPlaybackTime)
|
||||||
* [disableFocus](#disableFocus)
|
* [disableFocus](#disableFocus)
|
||||||
|
* [disableDisconnectError](#disableDisconnectError)
|
||||||
* [filter](#filter)
|
* [filter](#filter)
|
||||||
* [filterEnabled](#filterEnabled)
|
* [filterEnabled](#filterEnabled)
|
||||||
* [fullscreen](#fullscreen)
|
* [fullscreen](#fullscreen)
|
||||||
@ -367,6 +369,11 @@ A Boolean value that indicates whether the player should automatically delay pla
|
|||||||
|
|
||||||
Platforms: iOS
|
Platforms: iOS
|
||||||
|
|
||||||
|
#### backBufferDurationMs
|
||||||
|
The number of milliseconds of buffer to keep before the current position. This allows rewinding without rebuffering within that duration.
|
||||||
|
|
||||||
|
Platforms: Android ExoPlayer
|
||||||
|
|
||||||
#### bufferConfig
|
#### bufferConfig
|
||||||
Adjust the buffer settings. This prop takes an object with one or more of the properties listed below.
|
Adjust the buffer settings. This prop takes an object with one or more of the properties listed below.
|
||||||
|
|
||||||
@ -416,6 +423,13 @@ Determines whether video audio should override background music/audio in Android
|
|||||||
|
|
||||||
Platforms: Android Exoplayer
|
Platforms: Android Exoplayer
|
||||||
|
|
||||||
|
#### disableDisconnectError
|
||||||
|
Determines if the player needs to throw an error when connection is lost or not
|
||||||
|
* **false (default)** - Player will throw an error when connection is lost
|
||||||
|
* **true** - Player will keep trying to buffer when network connect is lost
|
||||||
|
|
||||||
|
Platforms: Android Exoplayer
|
||||||
|
|
||||||
### DRM
|
### DRM
|
||||||
To setup DRM please follow [this guide](./DRM.md)
|
To setup DRM please follow [this guide](./DRM.md)
|
||||||
|
|
||||||
|
@ -9,16 +9,28 @@ import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
|
|||||||
public class DefaultReactExoplayerConfig implements ReactExoplayerConfig {
|
public class DefaultReactExoplayerConfig implements ReactExoplayerConfig {
|
||||||
|
|
||||||
private final DefaultBandwidthMeter bandwidthMeter;
|
private final DefaultBandwidthMeter bandwidthMeter;
|
||||||
|
private boolean disableDisconnectError = false;
|
||||||
|
|
||||||
public DefaultReactExoplayerConfig(Context context) {
|
public DefaultReactExoplayerConfig(Context context) {
|
||||||
this.bandwidthMeter = new DefaultBandwidthMeter.Builder(context).build();
|
this.bandwidthMeter = new DefaultBandwidthMeter.Builder(context).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public LoadErrorHandlingPolicy buildLoadErrorHandlingPolicy(int minLoadRetryCount) {
|
public LoadErrorHandlingPolicy buildLoadErrorHandlingPolicy(int minLoadRetryCount) {
|
||||||
|
if (this.disableDisconnectError) {
|
||||||
|
// Use custom error handling policy to prevent throwing an error when losing network connection
|
||||||
|
return new ReactExoplayerLoadErrorHandlingPolicy(minLoadRetryCount);
|
||||||
|
}
|
||||||
return new DefaultLoadErrorHandlingPolicy(minLoadRetryCount);
|
return new DefaultLoadErrorHandlingPolicy(minLoadRetryCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDisableDisconnectError(boolean disableDisconnectError) {
|
||||||
|
this.disableDisconnectError = disableDisconnectError;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getDisableDisconnectError() {
|
||||||
|
return this.disableDisconnectError;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DefaultBandwidthMeter getBandwidthMeter() {
|
public DefaultBandwidthMeter getBandwidthMeter() {
|
||||||
return bandwidthMeter;
|
return bandwidthMeter;
|
||||||
|
@ -9,5 +9,8 @@ import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
|
|||||||
public interface ReactExoplayerConfig {
|
public interface ReactExoplayerConfig {
|
||||||
LoadErrorHandlingPolicy buildLoadErrorHandlingPolicy(int minLoadRetryCount);
|
LoadErrorHandlingPolicy buildLoadErrorHandlingPolicy(int minLoadRetryCount);
|
||||||
|
|
||||||
|
void setDisableDisconnectError(boolean disableDisconnectError);
|
||||||
|
boolean getDisableDisconnectError();
|
||||||
|
|
||||||
DefaultBandwidthMeter getBandwidthMeter();
|
DefaultBandwidthMeter getBandwidthMeter();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.brentvatne.exoplayer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy;
|
||||||
|
import com.google.android.exoplayer2.upstream.HttpDataSource.HttpDataSourceException;
|
||||||
|
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy.LoadErrorInfo;
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
|
|
||||||
|
public final class ReactExoplayerLoadErrorHandlingPolicy extends DefaultLoadErrorHandlingPolicy {
|
||||||
|
private int minLoadRetryCount = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
public ReactExoplayerLoadErrorHandlingPolicy(int minLoadRetryCount) {
|
||||||
|
super(minLoadRetryCount);
|
||||||
|
this.minLoadRetryCount = minLoadRetryCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) {
|
||||||
|
if (loadErrorInfo.exception instanceof HttpDataSourceException) {
|
||||||
|
// Capture the error we get when there is no network connectivity and keep retrying it
|
||||||
|
return 1000; // Retry every second
|
||||||
|
} else if(loadErrorInfo.errorCount < this.minLoadRetryCount) {
|
||||||
|
return Math.min((loadErrorInfo.errorCount - 1) * 1000, 5000); // Default timeout handling
|
||||||
|
} else {
|
||||||
|
return C.TIME_UNSET; // Done retrying and will return the error immediately
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMinimumLoadableRetryCount(int dataType) {
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
}
|
@ -139,6 +139,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
private Handler mainHandler;
|
private Handler mainHandler;
|
||||||
|
|
||||||
// Props from React
|
// Props from React
|
||||||
|
private int backBufferDurationMs = DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS;
|
||||||
private Uri srcUri;
|
private Uri srcUri;
|
||||||
private String extension;
|
private String extension;
|
||||||
private boolean repeat;
|
private boolean repeat;
|
||||||
@ -151,6 +152,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
private ReadableArray textTracks;
|
private ReadableArray textTracks;
|
||||||
private boolean disableFocus;
|
private boolean disableFocus;
|
||||||
private boolean disableBuffering;
|
private boolean disableBuffering;
|
||||||
|
private boolean disableDisconnectError;
|
||||||
private boolean preventsDisplaySleepDuringVideoPlayback = true;
|
private boolean preventsDisplaySleepDuringVideoPlayback = true;
|
||||||
private float mProgressUpdateInterval = 250.0f;
|
private float mProgressUpdateInterval = 250.0f;
|
||||||
private boolean playInBackground = false;
|
private boolean playInBackground = false;
|
||||||
@ -434,7 +436,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
bufferForPlaybackAfterRebufferMs,
|
bufferForPlaybackAfterRebufferMs,
|
||||||
-1,
|
-1,
|
||||||
true,
|
true,
|
||||||
DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS,
|
backBufferDurationMs,
|
||||||
DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME
|
DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME
|
||||||
);
|
);
|
||||||
DefaultRenderersFactory renderersFactory =
|
DefaultRenderersFactory renderersFactory =
|
||||||
@ -527,6 +529,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager) {
|
private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager) {
|
||||||
int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension
|
int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension
|
||||||
: uri.getLastPathSegment());
|
: uri.getLastPathSegment());
|
||||||
|
config.setDisableDisconnectError(this.disableDisconnectError);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case C.TYPE_SS:
|
case C.TYPE_SS:
|
||||||
return new SsMediaSource.Factory(
|
return new SsMediaSource.Factory(
|
||||||
@ -1312,10 +1315,18 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
this.disableFocus = disableFocus;
|
this.disableFocus = disableFocus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setBackBufferDurationMs(int backBufferDurationMs) {
|
||||||
|
this.backBufferDurationMs = backBufferDurationMs;
|
||||||
|
}
|
||||||
|
|
||||||
public void setDisableBuffering(boolean disableBuffering) {
|
public void setDisableBuffering(boolean disableBuffering) {
|
||||||
this.disableBuffering = disableBuffering;
|
this.disableBuffering = disableBuffering;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDisableDisconnectError(boolean disableDisconnectError) {
|
||||||
|
this.disableDisconnectError = disableDisconnectError;
|
||||||
|
}
|
||||||
|
|
||||||
public void setFullscreen(boolean fullscreen) {
|
public void setFullscreen(boolean fullscreen) {
|
||||||
if (fullscreen == isFullscreen) {
|
if (fullscreen == isFullscreen) {
|
||||||
return; // Avoid generating events when nothing is changing
|
return; // Avoid generating events when nothing is changing
|
||||||
|
@ -49,6 +49,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
private static final String PROP_PAUSED = "paused";
|
private static final String PROP_PAUSED = "paused";
|
||||||
private static final String PROP_MUTED = "muted";
|
private static final String PROP_MUTED = "muted";
|
||||||
private static final String PROP_VOLUME = "volume";
|
private static final String PROP_VOLUME = "volume";
|
||||||
|
private static final String PROP_BACK_BUFFER_DURATION_MS = "backBufferDurationMs";
|
||||||
private static final String PROP_BUFFER_CONFIG = "bufferConfig";
|
private static final String PROP_BUFFER_CONFIG = "bufferConfig";
|
||||||
private static final String PROP_BUFFER_CONFIG_MIN_BUFFER_MS = "minBufferMs";
|
private static final String PROP_BUFFER_CONFIG_MIN_BUFFER_MS = "minBufferMs";
|
||||||
private static final String PROP_BUFFER_CONFIG_MAX_BUFFER_MS = "maxBufferMs";
|
private static final String PROP_BUFFER_CONFIG_MAX_BUFFER_MS = "maxBufferMs";
|
||||||
@ -64,6 +65,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
private static final String PROP_PLAY_IN_BACKGROUND = "playInBackground";
|
private static final String PROP_PLAY_IN_BACKGROUND = "playInBackground";
|
||||||
private static final String PROP_DISABLE_FOCUS = "disableFocus";
|
private static final String PROP_DISABLE_FOCUS = "disableFocus";
|
||||||
private static final String PROP_DISABLE_BUFFERING = "disableBuffering";
|
private static final String PROP_DISABLE_BUFFERING = "disableBuffering";
|
||||||
|
private static final String PROP_DISABLE_DISCONNECT_ERROR = "disableDisconnectError";
|
||||||
private static final String PROP_FULLSCREEN = "fullscreen";
|
private static final String PROP_FULLSCREEN = "fullscreen";
|
||||||
private static final String PROP_USE_TEXTURE_VIEW = "useTextureView";
|
private static final String PROP_USE_TEXTURE_VIEW = "useTextureView";
|
||||||
private static final String PROP_SELECTED_VIDEO_TRACK = "selectedVideoTrack";
|
private static final String PROP_SELECTED_VIDEO_TRACK = "selectedVideoTrack";
|
||||||
@ -294,11 +296,21 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
videoView.setDisableFocus(disableFocus);
|
videoView.setDisableFocus(disableFocus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ReactProp(name = PROP_BACK_BUFFER_DURATION_MS, defaultInt = 0)
|
||||||
|
public void setBackBufferDurationMs(final ReactExoplayerView videoView, final int backBufferDurationMs) {
|
||||||
|
videoView.setBackBufferDurationMs(backBufferDurationMs);
|
||||||
|
}
|
||||||
|
|
||||||
@ReactProp(name = PROP_DISABLE_BUFFERING, defaultBoolean = false)
|
@ReactProp(name = PROP_DISABLE_BUFFERING, defaultBoolean = false)
|
||||||
public void setDisableBuffering(final ReactExoplayerView videoView, final boolean disableBuffering) {
|
public void setDisableBuffering(final ReactExoplayerView videoView, final boolean disableBuffering) {
|
||||||
videoView.setDisableBuffering(disableBuffering);
|
videoView.setDisableBuffering(disableBuffering);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ReactProp(name = PROP_DISABLE_DISCONNECT_ERROR, defaultBoolean = false)
|
||||||
|
public void setDisableDisconnectError(final ReactExoplayerView videoView, final boolean disableDisconnectError) {
|
||||||
|
videoView.setDisableDisconnectError(disableDisconnectError);
|
||||||
|
}
|
||||||
|
|
||||||
@ReactProp(name = PROP_FULLSCREEN, defaultBoolean = false)
|
@ReactProp(name = PROP_FULLSCREEN, defaultBoolean = false)
|
||||||
public void setFullscreen(final ReactExoplayerView videoView, final boolean fullscreen) {
|
public void setFullscreen(final ReactExoplayerView videoView, final boolean fullscreen) {
|
||||||
videoView.setFullscreen(fullscreen);
|
videoView.setFullscreen(fullscreen);
|
||||||
|
Loading…
Reference in New Issue
Block a user