mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Reintroduce Allocation abstraction.
Play movies has an Allocator that attempts to allocate a single huge byte[] up front to minimize the risk of GC pauses. This abstraction will be required to keep that when updating them to the new Exo.
This commit is contained in:
parent
9b112cf94d
commit
54f97c952e
11 changed files with 277 additions and 215 deletions
|
|
@ -48,8 +48,8 @@ import com.google.android.exoplayer.drm.StreamingDrmSessionManager;
|
||||||
import com.google.android.exoplayer.text.TextTrackRenderer;
|
import com.google.android.exoplayer.text.TextTrackRenderer;
|
||||||
import com.google.android.exoplayer.text.ttml.TtmlParser;
|
import com.google.android.exoplayer.text.ttml.TtmlParser;
|
||||||
import com.google.android.exoplayer.text.webvtt.WebvttParser;
|
import com.google.android.exoplayer.text.webvtt.WebvttParser;
|
||||||
import com.google.android.exoplayer.upstream.BufferPool;
|
|
||||||
import com.google.android.exoplayer.upstream.DataSource;
|
import com.google.android.exoplayer.upstream.DataSource;
|
||||||
|
import com.google.android.exoplayer.upstream.DefaultAllocator;
|
||||||
import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
|
import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
|
||||||
import com.google.android.exoplayer.upstream.DefaultHttpDataSource;
|
import com.google.android.exoplayer.upstream.DefaultHttpDataSource;
|
||||||
import com.google.android.exoplayer.upstream.DefaultUriDataSource;
|
import com.google.android.exoplayer.upstream.DefaultUriDataSource;
|
||||||
|
|
@ -170,7 +170,7 @@ public class DashRendererBuilder implements RendererBuilder,
|
||||||
private void buildRenderers() {
|
private void buildRenderers() {
|
||||||
Period period = manifest.periods.get(0);
|
Period period = manifest.periods.get(0);
|
||||||
Handler mainHandler = player.getMainHandler();
|
Handler mainHandler = player.getMainHandler();
|
||||||
LoadControl loadControl = new DefaultLoadControl(new BufferPool(BUFFER_SEGMENT_SIZE));
|
LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE));
|
||||||
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(mainHandler, player);
|
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(mainHandler, player);
|
||||||
|
|
||||||
boolean hasContentProtection = false;
|
boolean hasContentProtection = false;
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,8 @@ import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.Trac
|
||||||
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifestParser;
|
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifestParser;
|
||||||
import com.google.android.exoplayer.text.TextTrackRenderer;
|
import com.google.android.exoplayer.text.TextTrackRenderer;
|
||||||
import com.google.android.exoplayer.text.ttml.TtmlParser;
|
import com.google.android.exoplayer.text.ttml.TtmlParser;
|
||||||
import com.google.android.exoplayer.upstream.BufferPool;
|
|
||||||
import com.google.android.exoplayer.upstream.DataSource;
|
import com.google.android.exoplayer.upstream.DataSource;
|
||||||
|
import com.google.android.exoplayer.upstream.DefaultAllocator;
|
||||||
import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
|
import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
|
||||||
import com.google.android.exoplayer.upstream.DefaultHttpDataSource;
|
import com.google.android.exoplayer.upstream.DefaultHttpDataSource;
|
||||||
import com.google.android.exoplayer.upstream.DefaultUriDataSource;
|
import com.google.android.exoplayer.upstream.DefaultUriDataSource;
|
||||||
|
|
@ -107,7 +107,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
|
||||||
@Override
|
@Override
|
||||||
public void onSingleManifest(SmoothStreamingManifest manifest) {
|
public void onSingleManifest(SmoothStreamingManifest manifest) {
|
||||||
Handler mainHandler = player.getMainHandler();
|
Handler mainHandler = player.getMainHandler();
|
||||||
LoadControl loadControl = new DefaultLoadControl(new BufferPool(BUFFER_SEGMENT_SIZE));
|
LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE));
|
||||||
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(mainHandler, player);
|
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(mainHandler, player);
|
||||||
|
|
||||||
// Check drm support if necessary.
|
// Check drm support if necessary.
|
||||||
|
|
|
||||||
|
|
@ -57,8 +57,8 @@ public class DefaultLoadControl implements LoadControl {
|
||||||
|
|
||||||
public static final int DEFAULT_LOW_WATERMARK_MS = 15000;
|
public static final int DEFAULT_LOW_WATERMARK_MS = 15000;
|
||||||
public static final int DEFAULT_HIGH_WATERMARK_MS = 30000;
|
public static final int DEFAULT_HIGH_WATERMARK_MS = 30000;
|
||||||
public static final float DEFAULT_LOW_POOL_LOAD = 0.2f;
|
public static final float DEFAULT_LOW_BUFFER_LOAD = 0.2f;
|
||||||
public static final float DEFAULT_HIGH_POOL_LOAD = 0.8f;
|
public static final float DEFAULT_HIGH_BUFFER_LOAD = 0.8f;
|
||||||
|
|
||||||
private static final int ABOVE_HIGH_WATERMARK = 0;
|
private static final int ABOVE_HIGH_WATERMARK = 0;
|
||||||
private static final int BETWEEN_WATERMARKS = 1;
|
private static final int BETWEEN_WATERMARKS = 1;
|
||||||
|
|
@ -72,12 +72,12 @@ public class DefaultLoadControl implements LoadControl {
|
||||||
|
|
||||||
private final long lowWatermarkUs;
|
private final long lowWatermarkUs;
|
||||||
private final long highWatermarkUs;
|
private final long highWatermarkUs;
|
||||||
private final float lowPoolLoad;
|
private final float lowBufferLoad;
|
||||||
private final float highPoolLoad;
|
private final float highBufferLoad;
|
||||||
|
|
||||||
private int targetBufferSize;
|
private int targetBufferSize;
|
||||||
private long maxLoadStartPositionUs;
|
private long maxLoadStartPositionUs;
|
||||||
private int bufferPoolState;
|
private int bufferState;
|
||||||
private boolean fillingBuffers;
|
private boolean fillingBuffers;
|
||||||
private boolean streamingPrioritySet;
|
private boolean streamingPrioritySet;
|
||||||
|
|
||||||
|
|
@ -101,7 +101,7 @@ public class DefaultLoadControl implements LoadControl {
|
||||||
public DefaultLoadControl(Allocator allocator, Handler eventHandler,
|
public DefaultLoadControl(Allocator allocator, Handler eventHandler,
|
||||||
EventListener eventListener) {
|
EventListener eventListener) {
|
||||||
this(allocator, eventHandler, eventListener, DEFAULT_LOW_WATERMARK_MS,
|
this(allocator, eventHandler, eventListener, DEFAULT_LOW_WATERMARK_MS,
|
||||||
DEFAULT_HIGH_WATERMARK_MS, DEFAULT_LOW_POOL_LOAD, DEFAULT_HIGH_POOL_LOAD);
|
DEFAULT_HIGH_WATERMARK_MS, DEFAULT_LOW_BUFFER_LOAD, DEFAULT_HIGH_BUFFER_LOAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -116,14 +116,14 @@ public class DefaultLoadControl implements LoadControl {
|
||||||
* the filling state.
|
* the filling state.
|
||||||
* @param highWatermarkMs The minimum duration of media that can be buffered for the control to
|
* @param highWatermarkMs The minimum duration of media that can be buffered for the control to
|
||||||
* transition from filling to draining.
|
* transition from filling to draining.
|
||||||
* @param lowPoolLoad The minimum fraction of the buffer that must be utilized for the control
|
* @param lowBufferLoad The minimum fraction of the buffer that must be utilized for the control
|
||||||
* to be in the draining state. If the utilization is lower, then the control will transition
|
* to be in the draining state. If the utilization is lower, then the control will transition
|
||||||
* to the filling state.
|
* to the filling state.
|
||||||
* @param highPoolLoad The minimum fraction of the buffer that must be utilized for the control
|
* @param highBufferLoad The minimum fraction of the buffer that must be utilized for the control
|
||||||
* to transition from the loading state to the draining state.
|
* to transition from the loading state to the draining state.
|
||||||
*/
|
*/
|
||||||
public DefaultLoadControl(Allocator allocator, Handler eventHandler, EventListener eventListener,
|
public DefaultLoadControl(Allocator allocator, Handler eventHandler, EventListener eventListener,
|
||||||
int lowWatermarkMs, int highWatermarkMs, float lowPoolLoad, float highPoolLoad) {
|
int lowWatermarkMs, int highWatermarkMs, float lowBufferLoad, float highBufferLoad) {
|
||||||
this.allocator = allocator;
|
this.allocator = allocator;
|
||||||
this.eventHandler = eventHandler;
|
this.eventHandler = eventHandler;
|
||||||
this.eventListener = eventListener;
|
this.eventListener = eventListener;
|
||||||
|
|
@ -131,8 +131,8 @@ public class DefaultLoadControl implements LoadControl {
|
||||||
this.loaderStates = new HashMap<Object, LoaderState>();
|
this.loaderStates = new HashMap<Object, LoaderState>();
|
||||||
this.lowWatermarkUs = lowWatermarkMs * 1000L;
|
this.lowWatermarkUs = lowWatermarkMs * 1000L;
|
||||||
this.highWatermarkUs = highWatermarkMs * 1000L;
|
this.highWatermarkUs = highWatermarkMs * 1000L;
|
||||||
this.lowPoolLoad = lowPoolLoad;
|
this.lowBufferLoad = lowBufferLoad;
|
||||||
this.highPoolLoad = highPoolLoad;
|
this.highBufferLoad = highBufferLoad;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -176,20 +176,20 @@ public class DefaultLoadControl implements LoadControl {
|
||||||
loaderState.failed = failed;
|
loaderState.failed = failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the buffer pool state.
|
// Update the buffer state.
|
||||||
int allocatedSize = allocator.getAllocatedSize();
|
int currentBufferSize = allocator.getTotalBytesAllocated();
|
||||||
int bufferPoolState = getBufferPoolState(allocatedSize);
|
int bufferState = getBufferState(currentBufferSize);
|
||||||
boolean bufferPoolStateChanged = this.bufferPoolState != bufferPoolState;
|
boolean bufferStateChanged = this.bufferState != bufferState;
|
||||||
if (bufferPoolStateChanged) {
|
if (bufferStateChanged) {
|
||||||
this.bufferPoolState = bufferPoolState;
|
this.bufferState = bufferState;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If either of the individual states have changed, update the shared control state.
|
// If either of the individual states have changed, update the shared control state.
|
||||||
if (loaderStateChanged || bufferPoolStateChanged) {
|
if (loaderStateChanged || bufferStateChanged) {
|
||||||
updateControlState();
|
updateControlState();
|
||||||
}
|
}
|
||||||
|
|
||||||
return allocatedSize < targetBufferSize && nextLoadPositionUs != -1
|
return currentBufferSize < targetBufferSize && nextLoadPositionUs != -1
|
||||||
&& nextLoadPositionUs <= maxLoadStartPositionUs;
|
&& nextLoadPositionUs <= maxLoadStartPositionUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,18 +204,18 @@ public class DefaultLoadControl implements LoadControl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getBufferPoolState(int allocatedSize) {
|
private int getBufferState(int currentBufferSize) {
|
||||||
float bufferPoolLoad = (float) allocatedSize / targetBufferSize;
|
float bufferLoad = (float) currentBufferSize / targetBufferSize;
|
||||||
return bufferPoolLoad > highPoolLoad ? ABOVE_HIGH_WATERMARK :
|
return bufferLoad > highBufferLoad ? ABOVE_HIGH_WATERMARK
|
||||||
bufferPoolLoad < lowPoolLoad ? BELOW_LOW_WATERMARK :
|
: bufferLoad < lowBufferLoad ? BELOW_LOW_WATERMARK
|
||||||
BETWEEN_WATERMARKS;
|
: BETWEEN_WATERMARKS;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateControlState() {
|
private void updateControlState() {
|
||||||
boolean loading = false;
|
boolean loading = false;
|
||||||
boolean failed = false;
|
boolean failed = false;
|
||||||
boolean haveNextLoadPosition = false;
|
boolean haveNextLoadPosition = false;
|
||||||
int highestState = bufferPoolState;
|
int highestState = bufferState;
|
||||||
for (int i = 0; i < loaders.size(); i++) {
|
for (int i = 0; i < loaders.size(); i++) {
|
||||||
LoaderState loaderState = loaderStates.get(loaders.get(i));
|
LoaderState loaderState = loaderStates.get(loaders.get(i));
|
||||||
loading |= loaderState.loading;
|
loading |= loaderState.loading;
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,9 @@ import com.google.android.exoplayer.TrackInfo;
|
||||||
import com.google.android.exoplayer.TrackRenderer;
|
import com.google.android.exoplayer.TrackRenderer;
|
||||||
import com.google.android.exoplayer.drm.DrmInitData;
|
import com.google.android.exoplayer.drm.DrmInitData;
|
||||||
import com.google.android.exoplayer.upstream.Allocator;
|
import com.google.android.exoplayer.upstream.Allocator;
|
||||||
import com.google.android.exoplayer.upstream.BufferPool;
|
|
||||||
import com.google.android.exoplayer.upstream.DataSource;
|
import com.google.android.exoplayer.upstream.DataSource;
|
||||||
import com.google.android.exoplayer.upstream.DataSpec;
|
import com.google.android.exoplayer.upstream.DataSpec;
|
||||||
|
import com.google.android.exoplayer.upstream.DefaultAllocator;
|
||||||
import com.google.android.exoplayer.upstream.Loader;
|
import com.google.android.exoplayer.upstream.Loader;
|
||||||
import com.google.android.exoplayer.upstream.Loader.Loadable;
|
import com.google.android.exoplayer.upstream.Loader.Loadable;
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
|
|
@ -57,7 +57,7 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
|
||||||
private static final int NO_RESET_PENDING = -1;
|
private static final int NO_RESET_PENDING = -1;
|
||||||
|
|
||||||
private final Extractor extractor;
|
private final Extractor extractor;
|
||||||
private final BufferPool bufferPool;
|
private final DefaultAllocator allocator;
|
||||||
private final int requestedBufferSize;
|
private final int requestedBufferSize;
|
||||||
private final SparseArray<InternalTrackOutput> sampleQueues;
|
private final SparseArray<InternalTrackOutput> sampleQueues;
|
||||||
private final int minLoadableRetryCount;
|
private final int minLoadableRetryCount;
|
||||||
|
|
@ -130,7 +130,7 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
|
||||||
this.requestedBufferSize = requestedBufferSize;
|
this.requestedBufferSize = requestedBufferSize;
|
||||||
this.minLoadableRetryCount = minLoadableRetryCount;
|
this.minLoadableRetryCount = minLoadableRetryCount;
|
||||||
sampleQueues = new SparseArray<InternalTrackOutput>();
|
sampleQueues = new SparseArray<InternalTrackOutput>();
|
||||||
bufferPool = new BufferPool(BUFFER_FRAGMENT_LENGTH);
|
allocator = new DefaultAllocator(BUFFER_FRAGMENT_LENGTH);
|
||||||
pendingResetPositionUs = NO_RESET_PENDING;
|
pendingResetPositionUs = NO_RESET_PENDING;
|
||||||
frameAccurateSeeking = true;
|
frameAccurateSeeking = true;
|
||||||
extractor.init(this);
|
extractor.init(this);
|
||||||
|
|
@ -203,7 +203,7 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
|
||||||
loader.cancelLoading();
|
loader.cancelLoading();
|
||||||
} else {
|
} else {
|
||||||
clearState();
|
clearState();
|
||||||
bufferPool.trim(0);
|
allocator.trim(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -332,7 +332,7 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
|
||||||
restartFrom(pendingResetPositionUs);
|
restartFrom(pendingResetPositionUs);
|
||||||
} else {
|
} else {
|
||||||
clearState();
|
clearState();
|
||||||
bufferPool.trim(0);
|
allocator.trim(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -351,7 +351,7 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
|
||||||
public TrackOutput track(int id) {
|
public TrackOutput track(int id) {
|
||||||
InternalTrackOutput sampleQueue = sampleQueues.get(id);
|
InternalTrackOutput sampleQueue = sampleQueues.get(id);
|
||||||
if (sampleQueue == null) {
|
if (sampleQueue == null) {
|
||||||
sampleQueue = new InternalTrackOutput(bufferPool);
|
sampleQueue = new InternalTrackOutput(allocator);
|
||||||
sampleQueues.put(id, sampleQueue);
|
sampleQueues.put(id, sampleQueue);
|
||||||
}
|
}
|
||||||
return sampleQueue;
|
return sampleQueue;
|
||||||
|
|
@ -476,11 +476,11 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExtractingLoadable createLoadableFromStart() {
|
private ExtractingLoadable createLoadableFromStart() {
|
||||||
return new ExtractingLoadable(uri, dataSource, extractor, bufferPool, requestedBufferSize, 0);
|
return new ExtractingLoadable(uri, dataSource, extractor, allocator, requestedBufferSize, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExtractingLoadable createLoadableFromPositionUs(long positionUs) {
|
private ExtractingLoadable createLoadableFromPositionUs(long positionUs) {
|
||||||
return new ExtractingLoadable(uri, dataSource, extractor, bufferPool, requestedBufferSize,
|
return new ExtractingLoadable(uri, dataSource, extractor, allocator, requestedBufferSize,
|
||||||
seekMap.getPosition(positionUs));
|
seekMap.getPosition(positionUs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -554,8 +554,8 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
|
||||||
private final Uri uri;
|
private final Uri uri;
|
||||||
private final DataSource dataSource;
|
private final DataSource dataSource;
|
||||||
private final Extractor extractor;
|
private final Extractor extractor;
|
||||||
private final BufferPool bufferPool;
|
private final DefaultAllocator allocator;
|
||||||
private final int bufferPoolSizeLimit;
|
private final int requestedBufferSize;
|
||||||
private final PositionHolder positionHolder;
|
private final PositionHolder positionHolder;
|
||||||
|
|
||||||
private volatile boolean loadCanceled;
|
private volatile boolean loadCanceled;
|
||||||
|
|
@ -563,12 +563,12 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
|
||||||
private boolean pendingExtractorSeek;
|
private boolean pendingExtractorSeek;
|
||||||
|
|
||||||
public ExtractingLoadable(Uri uri, DataSource dataSource, Extractor extractor,
|
public ExtractingLoadable(Uri uri, DataSource dataSource, Extractor extractor,
|
||||||
BufferPool bufferPool, int bufferPoolSizeLimit, long position) {
|
DefaultAllocator allocator, int requestedBufferSize, long position) {
|
||||||
this.uri = Assertions.checkNotNull(uri);
|
this.uri = Assertions.checkNotNull(uri);
|
||||||
this.dataSource = Assertions.checkNotNull(dataSource);
|
this.dataSource = Assertions.checkNotNull(dataSource);
|
||||||
this.extractor = Assertions.checkNotNull(extractor);
|
this.extractor = Assertions.checkNotNull(extractor);
|
||||||
this.bufferPool = Assertions.checkNotNull(bufferPool);
|
this.allocator = Assertions.checkNotNull(allocator);
|
||||||
this.bufferPoolSizeLimit = bufferPoolSizeLimit;
|
this.requestedBufferSize = requestedBufferSize;
|
||||||
positionHolder = new PositionHolder();
|
positionHolder = new PositionHolder();
|
||||||
positionHolder.position = position;
|
positionHolder.position = position;
|
||||||
pendingExtractorSeek = true;
|
pendingExtractorSeek = true;
|
||||||
|
|
@ -601,7 +601,7 @@ public class ExtractorSampleSource implements SampleSource, ExtractorOutput, Loa
|
||||||
}
|
}
|
||||||
input = new DefaultExtractorInput(dataSource, position, length);
|
input = new DefaultExtractorInput(dataSource, position, length);
|
||||||
while (result == Extractor.RESULT_CONTINUE && !loadCanceled) {
|
while (result == Extractor.RESULT_CONTINUE && !loadCanceled) {
|
||||||
bufferPool.blockWhileAllocatedSizeExceeds(bufferPoolSizeLimit);
|
allocator.blockWhileTotalBytesAllocatedExceeds(requestedBufferSize);
|
||||||
result = extractor.read(input, positionHolder);
|
result = extractor.read(input, positionHolder);
|
||||||
// TODO: Implement throttling to stop us from buffering data too often.
|
// TODO: Implement throttling to stop us from buffering data too often.
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer.extractor;
|
||||||
|
|
||||||
import com.google.android.exoplayer.C;
|
import com.google.android.exoplayer.C;
|
||||||
import com.google.android.exoplayer.SampleHolder;
|
import com.google.android.exoplayer.SampleHolder;
|
||||||
|
import com.google.android.exoplayer.upstream.Allocation;
|
||||||
import com.google.android.exoplayer.upstream.Allocator;
|
import com.google.android.exoplayer.upstream.Allocator;
|
||||||
import com.google.android.exoplayer.upstream.DataSource;
|
import com.google.android.exoplayer.upstream.DataSource;
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
|
|
@ -34,10 +35,10 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||||
private static final int INITIAL_SCRATCH_SIZE = 32;
|
private static final int INITIAL_SCRATCH_SIZE = 32;
|
||||||
|
|
||||||
private final Allocator allocator;
|
private final Allocator allocator;
|
||||||
private final int fragmentLength;
|
private final int allocationLength;
|
||||||
|
|
||||||
private final InfoQueue infoQueue;
|
private final InfoQueue infoQueue;
|
||||||
private final LinkedBlockingDeque<byte[]> dataQueue;
|
private final LinkedBlockingDeque<Allocation> dataQueue;
|
||||||
private final SampleExtrasHolder extrasHolder;
|
private final SampleExtrasHolder extrasHolder;
|
||||||
private final ParsableByteArray scratch;
|
private final ParsableByteArray scratch;
|
||||||
|
|
||||||
|
|
@ -46,20 +47,20 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||||
|
|
||||||
// Accessed only by the loading thread.
|
// Accessed only by the loading thread.
|
||||||
private long totalBytesWritten;
|
private long totalBytesWritten;
|
||||||
private byte[] lastFragment;
|
private Allocation lastAllocation;
|
||||||
private int lastFragmentOffset;
|
private int lastAllocationOffset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param allocator An {@link Allocator} from which allocations for sample data can be obtained.
|
* @param allocator An {@link Allocator} from which allocations for sample data can be obtained.
|
||||||
*/
|
*/
|
||||||
public RollingSampleBuffer(Allocator allocator) {
|
public RollingSampleBuffer(Allocator allocator) {
|
||||||
this.allocator = allocator;
|
this.allocator = allocator;
|
||||||
fragmentLength = allocator.getBufferLength();
|
allocationLength = allocator.getIndividualAllocationLength();
|
||||||
infoQueue = new InfoQueue();
|
infoQueue = new InfoQueue();
|
||||||
dataQueue = new LinkedBlockingDeque<byte[]>();
|
dataQueue = new LinkedBlockingDeque<Allocation>();
|
||||||
extrasHolder = new SampleExtrasHolder();
|
extrasHolder = new SampleExtrasHolder();
|
||||||
scratch = new ParsableByteArray(INITIAL_SCRATCH_SIZE);
|
scratch = new ParsableByteArray(INITIAL_SCRATCH_SIZE);
|
||||||
lastFragmentOffset = fragmentLength;
|
lastAllocationOffset = allocationLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called by the consuming thread, but only when there is no loading thread.
|
// Called by the consuming thread, but only when there is no loading thread.
|
||||||
|
|
@ -70,12 +71,12 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||||
public void clear() {
|
public void clear() {
|
||||||
infoQueue.clear();
|
infoQueue.clear();
|
||||||
while (!dataQueue.isEmpty()) {
|
while (!dataQueue.isEmpty()) {
|
||||||
allocator.releaseBuffer(dataQueue.remove());
|
allocator.release(dataQueue.remove());
|
||||||
}
|
}
|
||||||
totalBytesDropped = 0;
|
totalBytesDropped = 0;
|
||||||
totalBytesWritten = 0;
|
totalBytesWritten = 0;
|
||||||
lastFragment = null;
|
lastAllocation = null;
|
||||||
lastFragmentOffset = fragmentLength;
|
lastAllocationOffset = allocationLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -97,28 +98,28 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discards data from the write side of the buffer. Data is discarded from the specified absolute
|
* Discards data from the write side of the buffer. Data is discarded from the specified absolute
|
||||||
* position. Any fragments that are fully discarded are returned to the allocator.
|
* position. Any allocations that are fully discarded are returned to the allocator.
|
||||||
*
|
*
|
||||||
* @param absolutePosition The absolute position (inclusive) from which to discard data.
|
* @param absolutePosition The absolute position (inclusive) from which to discard data.
|
||||||
*/
|
*/
|
||||||
private void dropUpstreamFrom(long absolutePosition) {
|
private void dropUpstreamFrom(long absolutePosition) {
|
||||||
int relativePosition = (int) (absolutePosition - totalBytesDropped);
|
int relativePosition = (int) (absolutePosition - totalBytesDropped);
|
||||||
// Calculate the index of the fragment containing the position, and the offset within it.
|
// Calculate the index of the allocation containing the position, and the offset within it.
|
||||||
int fragmentIndex = relativePosition / fragmentLength;
|
int allocationIndex = relativePosition / allocationLength;
|
||||||
int fragmentOffset = relativePosition % fragmentLength;
|
int allocationOffset = relativePosition % allocationLength;
|
||||||
// We want to discard any fragments after the one at fragmentIndex.
|
// We want to discard any allocations after the one at allocationIdnex.
|
||||||
int fragmentDiscardCount = dataQueue.size() - fragmentIndex - 1;
|
int allocationDiscardCount = dataQueue.size() - allocationIndex - 1;
|
||||||
if (fragmentOffset == 0) {
|
if (allocationOffset == 0) {
|
||||||
// If the fragment at fragmentIndex is empty, we should discard that one too.
|
// If the allocation at allocationIndex is empty, we should discard that one too.
|
||||||
fragmentDiscardCount++;
|
allocationDiscardCount++;
|
||||||
}
|
}
|
||||||
// Discard the fragments.
|
// Discard the allocations.
|
||||||
for (int i = 0; i < fragmentDiscardCount; i++) {
|
for (int i = 0; i < allocationDiscardCount; i++) {
|
||||||
allocator.releaseBuffer(dataQueue.removeLast());
|
allocator.release(dataQueue.removeLast());
|
||||||
}
|
}
|
||||||
// Update lastFragment and lastFragmentOffset to reflect the new position.
|
// Update lastAllocation and lastAllocationOffset to reflect the new position.
|
||||||
lastFragment = dataQueue.peekLast();
|
lastAllocation = dataQueue.peekLast();
|
||||||
lastFragmentOffset = fragmentOffset == 0 ? fragmentLength : fragmentOffset;
|
lastAllocationOffset = allocationOffset == 0 ? allocationLength : allocationOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called by the consuming thread.
|
// Called by the consuming thread.
|
||||||
|
|
@ -279,9 +280,10 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||||
int remaining = length;
|
int remaining = length;
|
||||||
while (remaining > 0) {
|
while (remaining > 0) {
|
||||||
dropDownstreamTo(absolutePosition);
|
dropDownstreamTo(absolutePosition);
|
||||||
int positionInFragment = (int) (absolutePosition - totalBytesDropped);
|
int positionInAllocation = (int) (absolutePosition - totalBytesDropped);
|
||||||
int toCopy = Math.min(remaining, fragmentLength - positionInFragment);
|
int toCopy = Math.min(remaining, allocationLength - positionInAllocation);
|
||||||
target.put(dataQueue.peek(), positionInFragment, toCopy);
|
Allocation allocation = dataQueue.peek();
|
||||||
|
target.put(allocation.data, allocation.translateOffset(positionInAllocation), toCopy);
|
||||||
absolutePosition += toCopy;
|
absolutePosition += toCopy;
|
||||||
remaining -= toCopy;
|
remaining -= toCopy;
|
||||||
}
|
}
|
||||||
|
|
@ -299,26 +301,28 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||||
int bytesRead = 0;
|
int bytesRead = 0;
|
||||||
while (bytesRead < length) {
|
while (bytesRead < length) {
|
||||||
dropDownstreamTo(absolutePosition);
|
dropDownstreamTo(absolutePosition);
|
||||||
int positionInFragment = (int) (absolutePosition - totalBytesDropped);
|
int positionInAllocation = (int) (absolutePosition - totalBytesDropped);
|
||||||
int toCopy = Math.min(length - bytesRead, fragmentLength - positionInFragment);
|
int toCopy = Math.min(length - bytesRead, allocationLength - positionInAllocation);
|
||||||
System.arraycopy(dataQueue.peek(), positionInFragment, target, bytesRead, toCopy);
|
Allocation allocation = dataQueue.peek();
|
||||||
|
System.arraycopy(allocation.data, allocation.translateOffset(positionInAllocation), target,
|
||||||
|
bytesRead, toCopy);
|
||||||
absolutePosition += toCopy;
|
absolutePosition += toCopy;
|
||||||
bytesRead += toCopy;
|
bytesRead += toCopy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discard any fragments that hold data prior to the specified absolute position, returning
|
* Discard any allocations that hold data prior to the specified absolute position, returning
|
||||||
* them to the allocator.
|
* them to the allocator.
|
||||||
*
|
*
|
||||||
* @param absolutePosition The absolute position up to which fragments can be discarded.
|
* @param absolutePosition The absolute position up to which allocations can be discarded.
|
||||||
*/
|
*/
|
||||||
private void dropDownstreamTo(long absolutePosition) {
|
private void dropDownstreamTo(long absolutePosition) {
|
||||||
int relativePosition = (int) (absolutePosition - totalBytesDropped);
|
int relativePosition = (int) (absolutePosition - totalBytesDropped);
|
||||||
int fragmentIndex = relativePosition / fragmentLength;
|
int allocationIndex = relativePosition / allocationLength;
|
||||||
for (int i = 0; i < fragmentIndex; i++) {
|
for (int i = 0; i < allocationIndex; i++) {
|
||||||
allocator.releaseBuffer(dataQueue.remove());
|
allocator.release(dataQueue.remove());
|
||||||
totalBytesDropped += fragmentLength;
|
totalBytesDropped += allocationLength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -353,16 +357,17 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||||
*/
|
*/
|
||||||
public int appendData(DataSource dataSource, int length) throws IOException {
|
public int appendData(DataSource dataSource, int length) throws IOException {
|
||||||
ensureSpaceForWrite();
|
ensureSpaceForWrite();
|
||||||
int remainingFragmentCapacity = fragmentLength - lastFragmentOffset;
|
int remainingAllocationCapacity = allocationLength - lastAllocationOffset;
|
||||||
length = length != C.LENGTH_UNBOUNDED ? Math.min(length, remainingFragmentCapacity)
|
length = length != C.LENGTH_UNBOUNDED ? Math.min(length, remainingAllocationCapacity)
|
||||||
: remainingFragmentCapacity;
|
: remainingAllocationCapacity;
|
||||||
|
|
||||||
int bytesRead = dataSource.read(lastFragment, lastFragmentOffset, length);
|
int bytesRead = dataSource.read(lastAllocation.data,
|
||||||
|
lastAllocation.translateOffset(lastAllocationOffset), length);
|
||||||
if (bytesRead == C.RESULT_END_OF_INPUT) {
|
if (bytesRead == C.RESULT_END_OF_INPUT) {
|
||||||
return C.RESULT_END_OF_INPUT;
|
return C.RESULT_END_OF_INPUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
lastFragmentOffset += bytesRead;
|
lastAllocationOffset += bytesRead;
|
||||||
totalBytesWritten += bytesRead;
|
totalBytesWritten += bytesRead;
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
@ -377,9 +382,10 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||||
*/
|
*/
|
||||||
public int appendData(ExtractorInput input, int length) throws IOException, InterruptedException {
|
public int appendData(ExtractorInput input, int length) throws IOException, InterruptedException {
|
||||||
ensureSpaceForWrite();
|
ensureSpaceForWrite();
|
||||||
int thisWriteLength = Math.min(length, fragmentLength - lastFragmentOffset);
|
int thisWriteLength = Math.min(length, allocationLength - lastAllocationOffset);
|
||||||
input.readFully(lastFragment, lastFragmentOffset, thisWriteLength);
|
input.readFully(lastAllocation.data, lastAllocation.translateOffset(lastAllocationOffset),
|
||||||
lastFragmentOffset += thisWriteLength;
|
thisWriteLength);
|
||||||
|
lastAllocationOffset += thisWriteLength;
|
||||||
totalBytesWritten += thisWriteLength;
|
totalBytesWritten += thisWriteLength;
|
||||||
return thisWriteLength;
|
return thisWriteLength;
|
||||||
}
|
}
|
||||||
|
|
@ -394,9 +400,10 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||||
int remainingWriteLength = length;
|
int remainingWriteLength = length;
|
||||||
while (remainingWriteLength > 0) {
|
while (remainingWriteLength > 0) {
|
||||||
ensureSpaceForWrite();
|
ensureSpaceForWrite();
|
||||||
int thisWriteLength = Math.min(remainingWriteLength, fragmentLength - lastFragmentOffset);
|
int thisWriteLength = Math.min(remainingWriteLength, allocationLength - lastAllocationOffset);
|
||||||
buffer.readBytes(lastFragment, lastFragmentOffset, thisWriteLength);
|
buffer.readBytes(lastAllocation.data, lastAllocation.translateOffset(lastAllocationOffset),
|
||||||
lastFragmentOffset += thisWriteLength;
|
thisWriteLength);
|
||||||
|
lastAllocationOffset += thisWriteLength;
|
||||||
remainingWriteLength -= thisWriteLength;
|
remainingWriteLength -= thisWriteLength;
|
||||||
}
|
}
|
||||||
totalBytesWritten += length;
|
totalBytesWritten += length;
|
||||||
|
|
@ -417,13 +424,13 @@ import java.util.concurrent.LinkedBlockingDeque;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures at least one byte can be written, allocating a new fragment if necessary.
|
* Ensures at least one byte can be written, obtaining an additional allocation if necessary.
|
||||||
*/
|
*/
|
||||||
private void ensureSpaceForWrite() {
|
private void ensureSpaceForWrite() {
|
||||||
if (lastFragmentOffset == fragmentLength) {
|
if (lastAllocationOffset == allocationLength) {
|
||||||
lastFragmentOffset = 0;
|
lastAllocationOffset = 0;
|
||||||
lastFragment = allocator.allocateBuffer();
|
lastAllocation = allocator.allocate();
|
||||||
dataQueue.add(lastFragment);
|
dataQueue.add(lastAllocation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,9 @@ import com.google.android.exoplayer.extractor.Extractor;
|
||||||
import com.google.android.exoplayer.extractor.ts.AdtsExtractor;
|
import com.google.android.exoplayer.extractor.ts.AdtsExtractor;
|
||||||
import com.google.android.exoplayer.extractor.ts.TsExtractor;
|
import com.google.android.exoplayer.extractor.ts.TsExtractor;
|
||||||
import com.google.android.exoplayer.upstream.BandwidthMeter;
|
import com.google.android.exoplayer.upstream.BandwidthMeter;
|
||||||
import com.google.android.exoplayer.upstream.BufferPool;
|
|
||||||
import com.google.android.exoplayer.upstream.DataSource;
|
import com.google.android.exoplayer.upstream.DataSource;
|
||||||
import com.google.android.exoplayer.upstream.DataSpec;
|
import com.google.android.exoplayer.upstream.DataSpec;
|
||||||
|
import com.google.android.exoplayer.upstream.DefaultAllocator;
|
||||||
import com.google.android.exoplayer.upstream.HttpDataSource.InvalidResponseCodeException;
|
import com.google.android.exoplayer.upstream.HttpDataSource.InvalidResponseCodeException;
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
import com.google.android.exoplayer.util.MimeTypes;
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
|
|
@ -126,7 +126,7 @@ public class HlsChunkSource {
|
||||||
private static final String AAC_FILE_EXTENSION = ".aac";
|
private static final String AAC_FILE_EXTENSION = ".aac";
|
||||||
private static final float BANDWIDTH_FRACTION = 0.8f;
|
private static final float BANDWIDTH_FRACTION = 0.8f;
|
||||||
|
|
||||||
private final BufferPool bufferPool;
|
private final DefaultAllocator bufferPool;
|
||||||
private final DataSource dataSource;
|
private final DataSource dataSource;
|
||||||
private final HlsPlaylistParser playlistParser;
|
private final HlsPlaylistParser playlistParser;
|
||||||
private final List<Variant> variants;
|
private final List<Variant> variants;
|
||||||
|
|
@ -193,7 +193,7 @@ public class HlsChunkSource {
|
||||||
maxBufferDurationToSwitchDownUs = maxBufferDurationToSwitchDownMs * 1000;
|
maxBufferDurationToSwitchDownUs = maxBufferDurationToSwitchDownMs * 1000;
|
||||||
baseUri = playlist.baseUri;
|
baseUri = playlist.baseUri;
|
||||||
playlistParser = new HlsPlaylistParser();
|
playlistParser = new HlsPlaylistParser();
|
||||||
bufferPool = new BufferPool(256 * 1024);
|
bufferPool = new DefaultAllocator(256 * 1024);
|
||||||
|
|
||||||
if (playlist.type == HlsPlaylist.TYPE_MEDIA) {
|
if (playlist.type == HlsPlaylist.TYPE_MEDIA) {
|
||||||
variants = Collections.singletonList(new Variant(playlistUrl, 0, null, -1, -1));
|
variants = Collections.singletonList(new Variant(playlistUrl, 0, null, -1, -1));
|
||||||
|
|
@ -258,7 +258,7 @@ public class HlsChunkSource {
|
||||||
long playbackPositionUs) {
|
long playbackPositionUs) {
|
||||||
if (previousTsChunk != null && (previousTsChunk.isLastChunk
|
if (previousTsChunk != null && (previousTsChunk.isLastChunk
|
||||||
|| previousTsChunk.endTimeUs - playbackPositionUs >= targetBufferDurationUs)
|
|| previousTsChunk.endTimeUs - playbackPositionUs >= targetBufferDurationUs)
|
||||||
|| bufferPool.getAllocatedSize() >= targetBufferSize) {
|
|| bufferPool.getTotalBytesAllocated() >= targetBufferSize) {
|
||||||
// We're either finished, or we have the target amount of data or time buffered.
|
// We're either finished, or we have the target amount of data or time buffered.
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import com.google.android.exoplayer.extractor.ExtractorInput;
|
||||||
import com.google.android.exoplayer.extractor.ExtractorOutput;
|
import com.google.android.exoplayer.extractor.ExtractorOutput;
|
||||||
import com.google.android.exoplayer.extractor.SeekMap;
|
import com.google.android.exoplayer.extractor.SeekMap;
|
||||||
import com.google.android.exoplayer.extractor.TrackOutput;
|
import com.google.android.exoplayer.extractor.TrackOutput;
|
||||||
import com.google.android.exoplayer.upstream.BufferPool;
|
import com.google.android.exoplayer.upstream.DefaultAllocator;
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
|
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
@ -41,7 +41,7 @@ public final class HlsExtractorWrapper implements ExtractorOutput {
|
||||||
public final Format format;
|
public final Format format;
|
||||||
public final long startTimeUs;
|
public final long startTimeUs;
|
||||||
|
|
||||||
private final BufferPool bufferPool;
|
private final DefaultAllocator allocator;
|
||||||
private final Extractor extractor;
|
private final Extractor extractor;
|
||||||
private final SparseArray<DefaultTrackOutput> sampleQueues;
|
private final SparseArray<DefaultTrackOutput> sampleQueues;
|
||||||
private final boolean shouldSpliceIn;
|
private final boolean shouldSpliceIn;
|
||||||
|
|
@ -52,12 +52,12 @@ public final class HlsExtractorWrapper implements ExtractorOutput {
|
||||||
private boolean prepared;
|
private boolean prepared;
|
||||||
private boolean spliceConfigured;
|
private boolean spliceConfigured;
|
||||||
|
|
||||||
public HlsExtractorWrapper(int trigger, Format format, long startTimeUs, BufferPool bufferPool,
|
public HlsExtractorWrapper(int trigger, Format format, long startTimeUs,
|
||||||
Extractor extractor, boolean shouldSpliceIn) {
|
DefaultAllocator allocator, Extractor extractor, boolean shouldSpliceIn) {
|
||||||
this.trigger = trigger;
|
this.trigger = trigger;
|
||||||
this.format = format;
|
this.format = format;
|
||||||
this.startTimeUs = startTimeUs;
|
this.startTimeUs = startTimeUs;
|
||||||
this.bufferPool = bufferPool;
|
this.allocator = allocator;
|
||||||
this.extractor = extractor;
|
this.extractor = extractor;
|
||||||
this.shouldSpliceIn = shouldSpliceIn;
|
this.shouldSpliceIn = shouldSpliceIn;
|
||||||
sampleQueues = new SparseArray<DefaultTrackOutput>();
|
sampleQueues = new SparseArray<DefaultTrackOutput>();
|
||||||
|
|
@ -136,7 +136,7 @@ public final class HlsExtractorWrapper implements ExtractorOutput {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears queues for all tracks, returning all allocations to the buffer pool.
|
* Clears queues for all tracks, returning all allocations to the allocator.
|
||||||
*/
|
*/
|
||||||
public void clear() {
|
public void clear() {
|
||||||
for (int i = 0; i < sampleQueues.size(); i++) {
|
for (int i = 0; i < sampleQueues.size(); i++) {
|
||||||
|
|
@ -211,7 +211,7 @@ public final class HlsExtractorWrapper implements ExtractorOutput {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackOutput track(int id) {
|
public TrackOutput track(int id) {
|
||||||
DefaultTrackOutput sampleQueue = new DefaultTrackOutput(bufferPool);
|
DefaultTrackOutput sampleQueue = new DefaultTrackOutput(allocator);
|
||||||
sampleQueues.put(id, sampleQueue);
|
sampleQueues.put(id, sampleQueue);
|
||||||
return sampleQueue;
|
return sampleQueue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer.upstream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An allocation within a byte array.
|
||||||
|
* <p>
|
||||||
|
* The allocation's length is obtained by calling {@link Allocator#getIndividualAllocationLength()}
|
||||||
|
* on the {@link Allocator} from which it was obtained.
|
||||||
|
*/
|
||||||
|
public final class Allocation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The array containing the allocated space. The allocated space may not be at the start of the
|
||||||
|
* array, and so {@link #translateOffset(int)} method must be used when indexing into it.
|
||||||
|
*/
|
||||||
|
public final byte[] data;
|
||||||
|
|
||||||
|
private final int offset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param data The array containing the allocated space.
|
||||||
|
* @param offset The offset of the allocated space within the array.
|
||||||
|
*/
|
||||||
|
public Allocation(byte[] data, int offset) {
|
||||||
|
this.data = data;
|
||||||
|
this.offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates a zero-based offset into the allocation to the corresponding {@link #data} offset.
|
||||||
|
*
|
||||||
|
* @param offset The zero-based offset to translate.
|
||||||
|
* @return The corresponding offset in {@link #data}.
|
||||||
|
*/
|
||||||
|
public int translateOffset(int offset) {
|
||||||
|
return this.offset + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -21,21 +21,21 @@ package com.google.android.exoplayer.upstream;
|
||||||
public interface Allocator {
|
public interface Allocator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain a buffer from the allocator.
|
* Obtain an {@link Allocation}.
|
||||||
* <p>
|
* <p>
|
||||||
* When the caller has finished with the buffer, it should be returned by calling
|
* When the caller has finished with the {@link Allocation}, it should be returned by calling
|
||||||
* {@link #releaseBuffer(byte[])}.
|
* {@link #release(Allocation)}.
|
||||||
*
|
*
|
||||||
* @return The allocated buffer.
|
* @return The {@link Allocation}.
|
||||||
*/
|
*/
|
||||||
byte[] allocateBuffer();
|
Allocation allocate();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a buffer to the allocator.
|
* Return an {@link Allocation}.
|
||||||
*
|
*
|
||||||
* @param buffer The buffer being returned.
|
* @param allocation The {@link Allocation} being returned.
|
||||||
*/
|
*/
|
||||||
void releaseBuffer(byte[] buffer);
|
void release(Allocation allocation);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hints to the {@link Allocator} that it should make a best effort to release any memory that it
|
* Hints to the {@link Allocator} that it should make a best effort to release any memory that it
|
||||||
|
|
@ -46,13 +46,13 @@ public interface Allocator {
|
||||||
void trim(int targetSize);
|
void trim(int targetSize);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the total size of all allocated buffers.
|
* Returns the total number of bytes currently allocated.
|
||||||
*/
|
*/
|
||||||
int getAllocatedSize();
|
int getTotalBytesAllocated();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the length of each buffer provided by the allocator.
|
* Returns the length of each individual {@link Allocation}.
|
||||||
*/
|
*/
|
||||||
int getBufferLength();
|
int getIndividualAllocationLength();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2014 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.google.android.exoplayer.upstream;
|
|
||||||
|
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
|
||||||
import com.google.android.exoplayer.util.Util;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default implementation of {@link Allocator}.
|
|
||||||
*/
|
|
||||||
public final class BufferPool implements Allocator {
|
|
||||||
|
|
||||||
private static final int INITIAL_RECYCLED_BUFFERS_CAPACITY = 100;
|
|
||||||
|
|
||||||
private final int bufferLength;
|
|
||||||
|
|
||||||
private int allocatedCount;
|
|
||||||
private int recycledCount;
|
|
||||||
private byte[][] recycledBuffers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an empty pool.
|
|
||||||
*
|
|
||||||
* @param bufferLength The length of each buffer in the pool.
|
|
||||||
*/
|
|
||||||
public BufferPool(int bufferLength) {
|
|
||||||
Assertions.checkArgument(bufferLength > 0);
|
|
||||||
this.bufferLength = bufferLength;
|
|
||||||
this.recycledBuffers = new byte[INITIAL_RECYCLED_BUFFERS_CAPACITY][];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized byte[] allocateBuffer() {
|
|
||||||
allocatedCount++;
|
|
||||||
return recycledCount > 0 ? recycledBuffers[--recycledCount] : new byte[bufferLength];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void releaseBuffer(byte[] buffer) {
|
|
||||||
// Weak sanity check that the buffer probably originated from this pool.
|
|
||||||
Assertions.checkArgument(buffer.length == bufferLength);
|
|
||||||
allocatedCount--;
|
|
||||||
if (recycledCount == recycledBuffers.length) {
|
|
||||||
recycledBuffers = Arrays.copyOf(recycledBuffers, recycledBuffers.length * 2);
|
|
||||||
}
|
|
||||||
recycledBuffers[recycledCount++] = buffer;
|
|
||||||
// Wake up threads waiting for the allocated size to drop.
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void trim(int targetSize) {
|
|
||||||
int targetBufferCount = Util.ceilDivide(targetSize, bufferLength);
|
|
||||||
int targetRecycledBufferCount = Math.max(0, targetBufferCount - allocatedCount);
|
|
||||||
if (targetRecycledBufferCount < recycledCount) {
|
|
||||||
Arrays.fill(recycledBuffers, targetRecycledBufferCount, recycledCount, null);
|
|
||||||
recycledCount = targetRecycledBufferCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized int getAllocatedSize() {
|
|
||||||
return allocatedCount * bufferLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBufferLength() {
|
|
||||||
return bufferLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Blocks execution until the allocated size is not greater than the threshold, or the thread is
|
|
||||||
* interrupted.
|
|
||||||
*/
|
|
||||||
public synchronized void blockWhileAllocatedSizeExceeds(int limit) throws InterruptedException {
|
|
||||||
while (getAllocatedSize() > limit) {
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer.upstream;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
|
import com.google.android.exoplayer.util.Util;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of {@link Allocator}.
|
||||||
|
*/
|
||||||
|
public final class DefaultAllocator implements Allocator {
|
||||||
|
|
||||||
|
private static final int INITIAL_RECYCLED_ALLOCATION_CAPACITY = 100;
|
||||||
|
|
||||||
|
private final int individualAllocationSize;
|
||||||
|
|
||||||
|
private int allocatedCount;
|
||||||
|
private int recycledCount;
|
||||||
|
private Allocation[] recycledAllocations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an empty pool.
|
||||||
|
*
|
||||||
|
* @param individualAllocationSize The length of each individual allocation.
|
||||||
|
*/
|
||||||
|
public DefaultAllocator(int individualAllocationSize) {
|
||||||
|
Assertions.checkArgument(individualAllocationSize > 0);
|
||||||
|
this.individualAllocationSize = individualAllocationSize;
|
||||||
|
this.recycledAllocations = new Allocation[INITIAL_RECYCLED_ALLOCATION_CAPACITY];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Allocation allocate() {
|
||||||
|
allocatedCount++;
|
||||||
|
return recycledCount > 0 ? recycledAllocations[--recycledCount]
|
||||||
|
: new Allocation(new byte[individualAllocationSize], 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void release(Allocation allocation) {
|
||||||
|
// Weak sanity check that the allocation probably originated from this pool.
|
||||||
|
Assertions.checkArgument(allocation.data.length == individualAllocationSize);
|
||||||
|
allocatedCount--;
|
||||||
|
if (recycledCount == recycledAllocations.length) {
|
||||||
|
recycledAllocations = Arrays.copyOf(recycledAllocations, recycledAllocations.length * 2);
|
||||||
|
}
|
||||||
|
recycledAllocations[recycledCount++] = allocation;
|
||||||
|
// Wake up threads waiting for the allocated size to drop.
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void trim(int targetSize) {
|
||||||
|
int targetAllocationCount = Util.ceilDivide(targetSize, individualAllocationSize);
|
||||||
|
int targetRecycledAllocationCount = Math.max(0, targetAllocationCount - allocatedCount);
|
||||||
|
if (targetRecycledAllocationCount < recycledCount) {
|
||||||
|
Arrays.fill(recycledAllocations, targetRecycledAllocationCount, recycledCount, null);
|
||||||
|
recycledCount = targetRecycledAllocationCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized int getTotalBytesAllocated() {
|
||||||
|
return allocatedCount * individualAllocationSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIndividualAllocationLength() {
|
||||||
|
return individualAllocationSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocks execution until the allocated number of bytes allocated is not greater than the
|
||||||
|
* threshold, or the thread is interrupted.
|
||||||
|
*/
|
||||||
|
public synchronized void blockWhileTotalBytesAllocatedExceeds(int limit)
|
||||||
|
throws InterruptedException {
|
||||||
|
while (getTotalBytesAllocated() > limit) {
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue