Commit 69af3897 by olly Committed by Oliver Woodman

Trim allocator on stop/reset by default

This prevents a large amount of memory from being held
in the case that a player instance is released, but the
application is holding dangling references to the player
that are preventing it from being garbage collected.

Issue: #1855

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=134992945
parent f75f3d75
...@@ -68,7 +68,7 @@ public final class DefaultLoadControl implements LoadControl { ...@@ -68,7 +68,7 @@ public final class DefaultLoadControl implements LoadControl {
* Constructs a new instance, using the {@code DEFAULT_*} constants defined in this class. * Constructs a new instance, using the {@code DEFAULT_*} constants defined in this class.
*/ */
public DefaultLoadControl() { public DefaultLoadControl() {
this(new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE)); this(new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE));
} }
/** /**
...@@ -105,6 +105,11 @@ public final class DefaultLoadControl implements LoadControl { ...@@ -105,6 +105,11 @@ public final class DefaultLoadControl implements LoadControl {
} }
@Override @Override
public void onPrepared() {
reset(false);
}
@Override
public void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups, public void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups,
TrackSelections<?> trackSelections) { TrackSelections<?> trackSelections) {
targetBufferSize = 0; targetBufferSize = 0;
...@@ -117,9 +122,13 @@ public final class DefaultLoadControl implements LoadControl { ...@@ -117,9 +122,13 @@ public final class DefaultLoadControl implements LoadControl {
} }
@Override @Override
public void onTracksDisabled() { public void onStopped() {
targetBufferSize = 0; reset(true);
isBuffering = false; }
@Override
public void onReleased() {
reset(true);
} }
@Override @Override
...@@ -147,4 +156,12 @@ public final class DefaultLoadControl implements LoadControl { ...@@ -147,4 +156,12 @@ public final class DefaultLoadControl implements LoadControl {
: (bufferedDurationUs < minBufferUs ? BELOW_LOW_WATERMARK : BETWEEN_WATERMARKS); : (bufferedDurationUs < minBufferUs ? BELOW_LOW_WATERMARK : BETWEEN_WATERMARKS);
} }
private void reset(boolean resetAllocator) {
targetBufferSize = 0;
isBuffering = false;
if (resetAllocator) {
allocator.reset();
}
}
} }
...@@ -106,7 +106,7 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -106,7 +106,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
@Override @Override
public void prepare(MediaSource mediaSource, boolean resetPosition) { public void prepare(MediaSource mediaSource, boolean resetPosition) {
timeline = null; timeline = null;
internalPlayer.setMediaSource(mediaSource, resetPosition); internalPlayer.prepare(mediaSource, resetPosition);
} }
@Override @Override
......
...@@ -75,7 +75,7 @@ import java.io.IOException; ...@@ -75,7 +75,7 @@ import java.io.IOException;
public static final int MSG_ERROR = 6; public static final int MSG_ERROR = 6;
// Internal messages // Internal messages
private static final int MSG_SET_MEDIA_SOURCE = 0; private static final int MSG_PREPARE = 0;
private static final int MSG_SET_PLAY_WHEN_READY = 1; private static final int MSG_SET_PLAY_WHEN_READY = 1;
private static final int MSG_DO_SOME_WORK = 2; private static final int MSG_DO_SOME_WORK = 2;
private static final int MSG_SEEK_TO = 3; private static final int MSG_SEEK_TO = 3;
...@@ -164,8 +164,8 @@ import java.io.IOException; ...@@ -164,8 +164,8 @@ import java.io.IOException;
handler = new Handler(internalPlaybackThread.getLooper(), this); handler = new Handler(internalPlaybackThread.getLooper(), this);
} }
public void setMediaSource(MediaSource mediaSource, boolean resetPosition) { public void prepare(MediaSource mediaSource, boolean resetPosition) {
handler.obtainMessage(MSG_SET_MEDIA_SOURCE, resetPosition ? 1 : 0, 0, mediaSource) handler.obtainMessage(MSG_PREPARE, resetPosition ? 1 : 0, 0, mediaSource)
.sendToTarget(); .sendToTarget();
} }
...@@ -253,8 +253,8 @@ import java.io.IOException; ...@@ -253,8 +253,8 @@ import java.io.IOException;
public boolean handleMessage(Message msg) { public boolean handleMessage(Message msg) {
try { try {
switch (msg.what) { switch (msg.what) {
case MSG_SET_MEDIA_SOURCE: { case MSG_PREPARE: {
setMediaSourceInternal((MediaSource) msg.obj, msg.arg1 != 0); prepareInternal((MediaSource) msg.obj, msg.arg1 != 0);
return true; return true;
} }
case MSG_SET_PLAY_WHEN_READY: { case MSG_SET_PLAY_WHEN_READY: {
...@@ -335,9 +335,10 @@ import java.io.IOException; ...@@ -335,9 +335,10 @@ import java.io.IOException;
} }
} }
private void setMediaSourceInternal(MediaSource mediaSource, boolean resetPosition) private void prepareInternal(MediaSource mediaSource, boolean resetPosition)
throws ExoPlaybackException { throws ExoPlaybackException {
resetInternal(); resetInternal();
loadControl.onPrepared();
if (resetPosition) { if (resetPosition) {
playbackInfo = new PlaybackInfo(0, C.TIME_UNSET); playbackInfo = new PlaybackInfo(0, C.TIME_UNSET);
} }
...@@ -597,11 +598,13 @@ import java.io.IOException; ...@@ -597,11 +598,13 @@ import java.io.IOException;
private void stopInternal() { private void stopInternal() {
resetInternal(); resetInternal();
loadControl.onStopped();
setState(ExoPlayer.STATE_IDLE); setState(ExoPlayer.STATE_IDLE);
} }
private void releaseInternal() { private void releaseInternal() {
resetInternal(); resetInternal();
loadControl.onReleased();
setState(ExoPlayer.STATE_IDLE); setState(ExoPlayer.STATE_IDLE);
synchronized (this) { synchronized (this) {
released = true; released = true;
...@@ -638,7 +641,6 @@ import java.io.IOException; ...@@ -638,7 +641,6 @@ import java.io.IOException;
loadingPeriodHolder = null; loadingPeriodHolder = null;
timeline = null; timeline = null;
bufferAheadPeriodCount = 0; bufferAheadPeriodCount = 0;
loadControl.onTracksDisabled();
setIsLoading(false); setIsLoading(false);
} }
......
...@@ -26,6 +26,11 @@ import com.google.android.exoplayer2.upstream.Allocator; ...@@ -26,6 +26,11 @@ import com.google.android.exoplayer2.upstream.Allocator;
public interface LoadControl { public interface LoadControl {
/** /**
* Called by the player when prepared with a new source.
*/
void onPrepared();
/**
* Called by the player when a track selection occurs. * Called by the player when a track selection occurs.
* *
* @param renderers The renderers. * @param renderers The renderers.
...@@ -36,9 +41,14 @@ public interface LoadControl { ...@@ -36,9 +41,14 @@ public interface LoadControl {
TrackSelections<?> trackSelections); TrackSelections<?> trackSelections);
/** /**
* Called by the player when all tracks are disabled. * Called by the player when stopped.
*/
void onStopped();
/**
* Called by the player when released.
*/ */
void onTracksDisabled(); void onReleased();
/** /**
* Returns the {@link Allocator} that should be used to obtain media buffer allocations. * Returns the {@link Allocator} that should be used to obtain media buffer allocations.
......
...@@ -26,6 +26,7 @@ public final class DefaultAllocator implements Allocator { ...@@ -26,6 +26,7 @@ public final class DefaultAllocator implements Allocator {
private static final int AVAILABLE_EXTRA_CAPACITY = 100; private static final int AVAILABLE_EXTRA_CAPACITY = 100;
private final boolean trimOnReset;
private final int individualAllocationSize; private final int individualAllocationSize;
private final byte[] initialAllocationBlock; private final byte[] initialAllocationBlock;
private final Allocation[] singleAllocationReleaseHolder; private final Allocation[] singleAllocationReleaseHolder;
...@@ -38,10 +39,12 @@ public final class DefaultAllocator implements Allocator { ...@@ -38,10 +39,12 @@ public final class DefaultAllocator implements Allocator {
/** /**
* Constructs an instance without creating any {@link Allocation}s up front. * Constructs an instance without creating any {@link Allocation}s up front.
* *
* @param trimOnReset Whether memory is freed when the allocator is reset. Should be true unless
* the allocator will be re-used by multiple player instances.
* @param individualAllocationSize The length of each individual {@link Allocation}. * @param individualAllocationSize The length of each individual {@link Allocation}.
*/ */
public DefaultAllocator(int individualAllocationSize) { public DefaultAllocator(boolean trimOnReset, int individualAllocationSize) {
this(individualAllocationSize, 0); this(trimOnReset, individualAllocationSize, 0);
} }
/** /**
...@@ -49,12 +52,16 @@ public final class DefaultAllocator implements Allocator { ...@@ -49,12 +52,16 @@ public final class DefaultAllocator implements Allocator {
* <p> * <p>
* Note: {@link Allocation}s created up front will never be discarded by {@link #trim()}. * Note: {@link Allocation}s created up front will never be discarded by {@link #trim()}.
* *
* @param trimOnReset Whether memory is freed when the allocator is reset. Should be true unless
* the allocator will be re-used by multiple player instances.
* @param individualAllocationSize The length of each individual {@link Allocation}. * @param individualAllocationSize The length of each individual {@link Allocation}.
* @param initialAllocationCount The number of allocations to create up front. * @param initialAllocationCount The number of allocations to create up front.
*/ */
public DefaultAllocator(int individualAllocationSize, int initialAllocationCount) { public DefaultAllocator(boolean trimOnReset, int individualAllocationSize,
int initialAllocationCount) {
Assertions.checkArgument(individualAllocationSize > 0); Assertions.checkArgument(individualAllocationSize > 0);
Assertions.checkArgument(initialAllocationCount >= 0); Assertions.checkArgument(initialAllocationCount >= 0);
this.trimOnReset = trimOnReset;
this.individualAllocationSize = individualAllocationSize; this.individualAllocationSize = individualAllocationSize;
this.availableCount = initialAllocationCount; this.availableCount = initialAllocationCount;
this.availableAllocations = new Allocation[initialAllocationCount + AVAILABLE_EXTRA_CAPACITY]; this.availableAllocations = new Allocation[initialAllocationCount + AVAILABLE_EXTRA_CAPACITY];
...@@ -70,6 +77,12 @@ public final class DefaultAllocator implements Allocator { ...@@ -70,6 +77,12 @@ public final class DefaultAllocator implements Allocator {
singleAllocationReleaseHolder = new Allocation[1]; singleAllocationReleaseHolder = new Allocation[1];
} }
public synchronized void reset() {
if (trimOnReset) {
setTargetBufferSize(0);
}
}
public synchronized void setTargetBufferSize(int targetBufferSize) { public synchronized void setTargetBufferSize(int targetBufferSize) {
boolean targetBufferSizeReduced = targetBufferSize < this.targetBufferSize; boolean targetBufferSizeReduced = targetBufferSize < this.targetBufferSize;
this.targetBufferSize = targetBufferSize; this.targetBufferSize = targetBufferSize;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment