Skip to content

Commit 292b539

Browse files
VEX-6030: Reduce buffer size based on heap (#13)
This PR changes the behavior on old devices that have poor memory management. Jira: VEX-6030 https://jira.tenkasu.net/browse/VEX-6030 The solution implied customizing the method shouldContinueLoading from RNVLoadControl to use only the available heap, performing tests on an old Nexus 5 it was determined the ideal bytes allocation to half the reported heap, that provided some buffering during ads but smooth playback during the video with no crashes (23:39 of 23:39 at the moment of writing this, video kept playing as expected after 3 ad breaks) The fix is only targeting Marshmallow as the reduction of buffer is substantial and other versions that work properly should not get affected. Depending on the test result of VEX-5758, this same fix can be applied to Nougat Reviews Major reviewer (domain expert): @armadilio3 Minor reviewer: @nickfujita
2 parents f712eec + 7dbc5eb commit 292b539

4 files changed

Lines changed: 22 additions & 3 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ minBufferMs | number | The default minimum duration of media that the player wil
385385
maxBufferMs | number | The default maximum duration of media that the player will attempt to buffer, in milliseconds.
386386
bufferForPlaybackMs | number | The default duration of media that must be buffered for playback to start or resume following a user action such as a seek, in milliseconds.
387387
bufferForPlaybackAfterRebufferMs | number | The default duration of media that must be buffered for playback to resume after a rebuffer, in milliseconds. A rebuffer is defined to be caused by buffer depletion rather than a user action.
388+
maxHeapAllocationPercent | number | The percentage of available heap that the video can use to buffer, between 0 and 1
388389

389390
This prop should only be set when you are setting the source, changing it after the media is loaded will cause it to be reloaded.
390391

Video.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ Video.propTypes = {
468468
maxBufferMs: PropTypes.number,
469469
bufferForPlaybackMs: PropTypes.number,
470470
bufferForPlaybackAfterRebufferMs: PropTypes.number,
471+
maxHeapAllocationPercent: PropTypes.number,
471472
}),
472473
stereoPan: PropTypes.number,
473474
rate: PropTypes.number,

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import android.annotation.SuppressLint;
44
import android.app.Activity;
5+
import android.app.ActivityManager;
56
import android.content.Context;
67
import android.media.AudioManager;
78
import android.net.Uri;
@@ -111,6 +112,8 @@ class ReactExoplayerView extends FrameLayout implements
111112
MetadataOutput,
112113
DrmSessionEventListener {
113114

115+
public static final double DEFAULT_MAX_HEAP_ALLOCATION_PERCENT = 1;
116+
114117
private static final String TAG = "ReactExoplayerView";
115118

116119
private static final CookieManager DEFAULT_COOKIE_MANAGER;
@@ -157,6 +160,7 @@ class ReactExoplayerView extends FrameLayout implements
157160
private int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS;
158161
private int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS;
159162
private int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS;
163+
private double maxHeapAllocationPercent = ReactExoplayerView.DEFAULT_MAX_HEAP_ALLOCATION_PERCENT;
160164

161165
private Handler mainHandler;
162166
private Timer bufferCheckTimer;
@@ -215,7 +219,7 @@ public void handleMessage(Message msg) {
215219

216220
public double getPositionInFirstPeriodMsForCurrentWindow(long currentPosition) {
217221
Timeline.Window window = new Timeline.Window();
218-
if(!player.getCurrentTimeline().isEmpty()) {
222+
if(!player.getCurrentTimeline().isEmpty()) {
219223
player.getCurrentTimeline().getWindow(player.getCurrentWindowIndex(), window);
220224
}
221225
return window.windowStartTimeMs + currentPosition;
@@ -418,6 +422,7 @@ private void reLayout(View view) {
418422
}
419423

420424
private class RNVLoadControl extends DefaultLoadControl {
425+
private int availableHeapInBytes = 0;
421426
public RNVLoadControl(DefaultAllocator allocator, int minBufferMs, int maxBufferMs, int bufferForPlaybackMs, int bufferForPlaybackAfterRebufferMs, int targetBufferBytes, boolean prioritizeTimeOverSizeThresholds, int backBufferDurationMs, boolean retainBackBufferFromKeyframe) {
422427
super(allocator,
423428
minBufferMs,
@@ -428,13 +433,20 @@ public RNVLoadControl(DefaultAllocator allocator, int minBufferMs, int maxBuffer
428433
prioritizeTimeOverSizeThresholds,
429434
backBufferDurationMs,
430435
retainBackBufferFromKeyframe);
436+
ActivityManager activityManager = (ActivityManager) themedReactContext.getSystemService(themedReactContext.ACTIVITY_SERVICE);
437+
availableHeapInBytes = (int) Math.floor(activityManager.getMemoryClass() * maxHeapAllocationPercent * 1024 * 1024);
431438
}
432439

433440
@Override
434441
public boolean shouldContinueLoading(long playbackPositionUs, long bufferedDurationUs, float playbackSpeed) {
435442
if (ReactExoplayerView.this.disableBuffering) {
436443
return false;
437444
}
445+
int loadedBytes = getAllocator().getTotalBytesAllocated();
446+
boolean isHeapReached = availableHeapInBytes > 0 && loadedBytes >= availableHeapInBytes;
447+
if (isHeapReached) {
448+
return false;
449+
}
438450
return super.shouldContinueLoading(playbackPositionUs, bufferedDurationUs, playbackSpeed);
439451
}
440452
}
@@ -1660,11 +1672,12 @@ public void setHideShutterView(boolean hideShutterView) {
16601672
exoPlayerView.setHideShutterView(hideShutterView);
16611673
}
16621674

1663-
public void setBufferConfig(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs) {
1675+
public void setBufferConfig(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs, double newMaxHeapAllocationPercent) {
16641676
minBufferMs = newMinBufferMs;
16651677
maxBufferMs = newMaxBufferMs;
16661678
bufferForPlaybackMs = newBufferForPlaybackMs;
16671679
bufferForPlaybackAfterRebufferMs = newBufferForPlaybackAfterRebufferMs;
1680+
maxHeapAllocationPercent = newMaxHeapAllocationPercent;
16681681
releasePlayer();
16691682
initializePlayer();
16701683
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
5555
private static final String PROP_BUFFER_CONFIG_MAX_BUFFER_MS = "maxBufferMs";
5656
private static final String PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS = "bufferForPlaybackMs";
5757
private static final String PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = "bufferForPlaybackAfterRebufferMs";
58+
private static final String PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT = "maxHeapAllocationPercent";
5859
private static final String PROP_PREVENTS_DISPLAY_SLEEP_DURING_VIDEO_PLAYBACK = "preventsDisplaySleepDuringVideoPlayback";
5960
private static final String PROP_PROGRESS_UPDATE_INTERVAL = "progressUpdateInterval";
6061
private static final String PROP_REPORT_BANDWIDTH = "reportBandwidth";
@@ -344,6 +345,7 @@ public void setBufferConfig(final ReactExoplayerView videoView, @Nullable Readab
344345
int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS;
345346
int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS;
346347
int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS;
348+
double maxHeapAllocationPercent = ReactExoplayerView.DEFAULT_MAX_HEAP_ALLOCATION_PERCENT;
347349
if (bufferConfig != null) {
348350
minBufferMs = bufferConfig.hasKey(PROP_BUFFER_CONFIG_MIN_BUFFER_MS)
349351
? bufferConfig.getInt(PROP_BUFFER_CONFIG_MIN_BUFFER_MS) : minBufferMs;
@@ -353,7 +355,9 @@ public void setBufferConfig(final ReactExoplayerView videoView, @Nullable Readab
353355
? bufferConfig.getInt(PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS) : bufferForPlaybackMs;
354356
bufferForPlaybackAfterRebufferMs = bufferConfig.hasKey(PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS)
355357
? bufferConfig.getInt(PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS) : bufferForPlaybackAfterRebufferMs;
356-
videoView.setBufferConfig(minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs);
358+
maxHeapAllocationPercent = bufferConfig.hasKey(PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT)
359+
? bufferConfig.getDouble(PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT) : maxHeapAllocationPercent;
360+
videoView.setBufferConfig(minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs, maxHeapAllocationPercent);
357361
}
358362
}
359363

0 commit comments

Comments
 (0)