mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
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
This commit is contained in:
parent
f75f3d75b2
commit
69af389730
5 changed files with 59 additions and 17 deletions
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -104,6 +104,11 @@ public final class DefaultLoadControl implements LoadControl {
|
||||||
bufferForPlaybackAfterRebufferUs = bufferForPlaybackAfterRebufferMs * 1000L;
|
bufferForPlaybackAfterRebufferUs = bufferForPlaybackAfterRebufferMs * 1000L;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPrepared() {
|
||||||
|
reset(false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups,
|
public void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups,
|
||||||
TrackSelections<?> trackSelections) {
|
TrackSelections<?> trackSelections) {
|
||||||
|
|
@ -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 {
|
||||||
: (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;
|
||||||
@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;
|
||||||
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;
|
||||||
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;
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
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;
|
||||||
loadingPeriodHolder = null;
|
loadingPeriodHolder = null;
|
||||||
timeline = null;
|
timeline = null;
|
||||||
bufferAheadPeriodCount = 0;
|
bufferAheadPeriodCount = 0;
|
||||||
loadControl.onTracksDisabled();
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,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.
|
||||||
*
|
*
|
||||||
|
|
@ -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 onTracksDisabled();
|
void onStopped();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the player when released.
|
||||||
|
*/
|
||||||
|
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 {
|
||||||
|
|
||||||
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 {
|
||||||
/**
|
/**
|
||||||
* 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 {
|
||||||
* <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 {
|
||||||
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;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue