Commit 54f97c95 by Oliver Woodman

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.
parent 9b112cf9
...@@ -48,8 +48,8 @@ import com.google.android.exoplayer.drm.StreamingDrmSessionManager; ...@@ -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, ...@@ -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 ...@@ -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, ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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; ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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.
} }
......
...@@ -25,9 +25,9 @@ import com.google.android.exoplayer.extractor.Extractor; ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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; ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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;
} }
......
/*
* 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; ...@@ -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 { ...@@ -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();
} }
...@@ -23,72 +23,74 @@ import java.util.Arrays; ...@@ -23,72 +23,74 @@ import java.util.Arrays;
/** /**
* Default implementation of {@link Allocator}. * Default implementation of {@link Allocator}.
*/ */
public final class BufferPool implements Allocator { public final class DefaultAllocator implements Allocator {
private static final int INITIAL_RECYCLED_BUFFERS_CAPACITY = 100; private static final int INITIAL_RECYCLED_ALLOCATION_CAPACITY = 100;
private final int bufferLength; private final int individualAllocationSize;
private int allocatedCount; private int allocatedCount;
private int recycledCount; private int recycledCount;
private byte[][] recycledBuffers; private Allocation[] recycledAllocations;
/** /**
* Constructs an empty pool. * Constructs an empty pool.
* *
* @param bufferLength The length of each buffer in the pool. * @param individualAllocationSize The length of each individual allocation.
*/ */
public BufferPool(int bufferLength) { public DefaultAllocator(int individualAllocationSize) {
Assertions.checkArgument(bufferLength > 0); Assertions.checkArgument(individualAllocationSize > 0);
this.bufferLength = bufferLength; this.individualAllocationSize = individualAllocationSize;
this.recycledBuffers = new byte[INITIAL_RECYCLED_BUFFERS_CAPACITY][]; this.recycledAllocations = new Allocation[INITIAL_RECYCLED_ALLOCATION_CAPACITY];
} }
@Override @Override
public synchronized byte[] allocateBuffer() { public synchronized Allocation allocate() {
allocatedCount++; allocatedCount++;
return recycledCount > 0 ? recycledBuffers[--recycledCount] : new byte[bufferLength]; return recycledCount > 0 ? recycledAllocations[--recycledCount]
: new Allocation(new byte[individualAllocationSize], 0);
} }
@Override @Override
public synchronized void releaseBuffer(byte[] buffer) { public synchronized void release(Allocation allocation) {
// Weak sanity check that the buffer probably originated from this pool. // Weak sanity check that the allocation probably originated from this pool.
Assertions.checkArgument(buffer.length == bufferLength); Assertions.checkArgument(allocation.data.length == individualAllocationSize);
allocatedCount--; allocatedCount--;
if (recycledCount == recycledBuffers.length) { if (recycledCount == recycledAllocations.length) {
recycledBuffers = Arrays.copyOf(recycledBuffers, recycledBuffers.length * 2); recycledAllocations = Arrays.copyOf(recycledAllocations, recycledAllocations.length * 2);
} }
recycledBuffers[recycledCount++] = buffer; recycledAllocations[recycledCount++] = allocation;
// Wake up threads waiting for the allocated size to drop. // Wake up threads waiting for the allocated size to drop.
notifyAll(); notifyAll();
} }
@Override @Override
public synchronized void trim(int targetSize) { public synchronized void trim(int targetSize) {
int targetBufferCount = Util.ceilDivide(targetSize, bufferLength); int targetAllocationCount = Util.ceilDivide(targetSize, individualAllocationSize);
int targetRecycledBufferCount = Math.max(0, targetBufferCount - allocatedCount); int targetRecycledAllocationCount = Math.max(0, targetAllocationCount - allocatedCount);
if (targetRecycledBufferCount < recycledCount) { if (targetRecycledAllocationCount < recycledCount) {
Arrays.fill(recycledBuffers, targetRecycledBufferCount, recycledCount, null); Arrays.fill(recycledAllocations, targetRecycledAllocationCount, recycledCount, null);
recycledCount = targetRecycledBufferCount; recycledCount = targetRecycledAllocationCount;
} }
} }
@Override @Override
public synchronized int getAllocatedSize() { public synchronized int getTotalBytesAllocated() {
return allocatedCount * bufferLength; return allocatedCount * individualAllocationSize;
} }
@Override @Override
public int getBufferLength() { public int getIndividualAllocationLength() {
return bufferLength; return individualAllocationSize;
} }
/** /**
* Blocks execution until the allocated size is not greater than the threshold, or the thread is * Blocks execution until the allocated number of bytes allocated is not greater than the
* interrupted. * threshold, or the thread is interrupted.
*/ */
public synchronized void blockWhileAllocatedSizeExceeds(int limit) throws InterruptedException { public synchronized void blockWhileTotalBytesAllocatedExceeds(int limit)
while (getAllocatedSize() > limit) { throws InterruptedException {
while (getTotalBytesAllocated() > limit) {
wait(); wait();
} }
} }
......
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