Skip to content

Commit

Permalink
VEX-4579: Network loss handling (#5)
Browse files Browse the repository at this point in the history
Add support for customizing back buffer duration and handle network errors gracefully to prevent releasing the player when network is lost.
  • Loading branch information
armands-malejevs authored May 17, 2021
1 parent f6cce0d commit 8087310
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 2 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,10 +276,12 @@ var styles = StyleSheet.create({
* [allowsExternalPlayback](#allowsexternalplayback)
* [audioOnly](#audioonly)
* [automaticallyWaitsToMinimizeStalling](#automaticallyWaitsToMinimizeStalling)
* [backBufferDurationMs](#backBufferDurationMs)
* [bufferConfig](#bufferconfig)
* [controls](#controls)
* [currentPlaybackTime](#currentPlaybackTime)
* [disableFocus](#disableFocus)
* [disableDisconnectError](#disableDisconnectError)
* [filter](#filter)
* [filterEnabled](#filterEnabled)
* [fullscreen](#fullscreen)
Expand Down Expand Up @@ -367,6 +369,11 @@ A Boolean value that indicates whether the player should automatically delay pla

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
Adjust the buffer settings. This prop takes an object with one or more of the properties listed below.

Expand Down Expand Up @@ -416,6 +423,13 @@ Determines whether video audio should override background music/audio in Android

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
To setup DRM please follow [this guide](./DRM.md)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,28 @@
public class DefaultReactExoplayerConfig implements ReactExoplayerConfig {

private final DefaultBandwidthMeter bandwidthMeter;
private boolean disableDisconnectError = false;

public DefaultReactExoplayerConfig(Context context) {
this.bandwidthMeter = new DefaultBandwidthMeter.Builder(context).build();
}

@Override
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);
}

public void setDisableDisconnectError(boolean disableDisconnectError) {
this.disableDisconnectError = disableDisconnectError;
}

public boolean getDisableDisconnectError() {
return this.disableDisconnectError;
}

@Override
public DefaultBandwidthMeter getBandwidthMeter() {
return bandwidthMeter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@
public interface ReactExoplayerConfig {
LoadErrorHandlingPolicy buildLoadErrorHandlingPolicy(int minLoadRetryCount);

void setDisableDisconnectError(boolean disableDisconnectError);
boolean getDisableDisconnectError();

DefaultBandwidthMeter getBandwidthMeter();
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ class ReactExoplayerView extends FrameLayout implements
private Handler mainHandler;

// Props from React
private int backBufferDurationMs = DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS;
private Uri srcUri;
private String extension;
private boolean repeat;
Expand All @@ -151,6 +152,7 @@ class ReactExoplayerView extends FrameLayout implements
private ReadableArray textTracks;
private boolean disableFocus;
private boolean disableBuffering;
private boolean disableDisconnectError;
private boolean preventsDisplaySleepDuringVideoPlayback = true;
private float mProgressUpdateInterval = 250.0f;
private boolean playInBackground = false;
Expand Down Expand Up @@ -434,7 +436,7 @@ public void run() {
bufferForPlaybackAfterRebufferMs,
-1,
true,
DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS,
backBufferDurationMs,
DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME
);
DefaultRenderersFactory renderersFactory =
Expand Down Expand Up @@ -527,6 +529,7 @@ private DrmSessionManager buildDrmSessionManager(UUID uuid,
private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager) {
int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension
: uri.getLastPathSegment());
config.setDisableDisconnectError(this.disableDisconnectError);
switch (type) {
case C.TYPE_SS:
return new SsMediaSource.Factory(
Expand Down Expand Up @@ -1312,10 +1315,18 @@ public void setDisableFocus(boolean disableFocus) {
this.disableFocus = disableFocus;
}

public void setBackBufferDurationMs(int backBufferDurationMs) {
this.backBufferDurationMs = backBufferDurationMs;
}

public void setDisableBuffering(boolean disableBuffering) {
this.disableBuffering = disableBuffering;
}

public void setDisableDisconnectError(boolean disableDisconnectError) {
this.disableDisconnectError = disableDisconnectError;
}

public void setFullscreen(boolean fullscreen) {
if (fullscreen == isFullscreen) {
return; // Avoid generating events when nothing is changing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
private static final String PROP_PAUSED = "paused";
private static final String PROP_MUTED = "muted";
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_MIN_BUFFER_MS = "minBufferMs";
private static final String PROP_BUFFER_CONFIG_MAX_BUFFER_MS = "maxBufferMs";
Expand All @@ -64,6 +65,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
private static final String PROP_PLAY_IN_BACKGROUND = "playInBackground";
private static final String PROP_DISABLE_FOCUS = "disableFocus";
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_USE_TEXTURE_VIEW = "useTextureView";
private static final String PROP_SELECTED_VIDEO_TRACK = "selectedVideoTrack";
Expand Down Expand Up @@ -294,11 +296,21 @@ public void setDisableFocus(final ReactExoplayerView videoView, final boolean di
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)
public void setDisableBuffering(final ReactExoplayerView videoView, final boolean 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)
public void setFullscreen(final ReactExoplayerView videoView, final boolean fullscreen) {
videoView.setFullscreen(fullscreen);
Expand Down

0 comments on commit 8087310

Please sign in to comment.