Move Allocator to HlsSampleSource (from HlsChunkSource).

- This makes HLS consistent with Chunk/ExtractorSampleSource.
- It needs to be consistent for WebVTT in HLS.
- Also trim the allocator when done.
This commit is contained in:
Oliver Woodman 2015-05-01 20:32:42 +01:00
parent 0c5a1a6c35
commit 8673cafce8
4 changed files with 55 additions and 51 deletions

View file

@ -29,7 +29,6 @@ import com.google.android.exoplayer.metadata.MetadataTrackRenderer;
import com.google.android.exoplayer.text.eia608.Eia608TrackRenderer; import com.google.android.exoplayer.text.eia608.Eia608TrackRenderer;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
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.DefaultUriDataSource; import com.google.android.exoplayer.upstream.DefaultUriDataSource;
import com.google.android.exoplayer.util.ManifestFetcher; import com.google.android.exoplayer.util.ManifestFetcher;
import com.google.android.exoplayer.util.ManifestFetcher.ManifestCallback; import com.google.android.exoplayer.util.ManifestFetcher.ManifestCallback;
@ -46,6 +45,9 @@ import java.util.Map;
*/ */
public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<HlsPlaylist> { public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<HlsPlaylist> {
private static final int REQUESTED_BUFFER_SIZE = 18 * 1024 * 1024;
private static final long REQUESTED_BUFFER_DURATION_MS = 40000;
private final String userAgent; private final String userAgent;
private final String url; private final String url;
private final TextView debugTextView; private final TextView debugTextView;
@ -65,7 +67,7 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
this.callback = callback; this.callback = callback;
HlsPlaylistParser parser = new HlsPlaylistParser(); HlsPlaylistParser parser = new HlsPlaylistParser();
ManifestFetcher<HlsPlaylist> playlistFetcher = ManifestFetcher<HlsPlaylist> playlistFetcher =
new ManifestFetcher<HlsPlaylist>(url, new DefaultHttpDataSource(userAgent, null), parser); new ManifestFetcher<HlsPlaylist>(url, new DefaultUriDataSource(userAgent, null), parser);
playlistFetcher.singleLoad(player.getMainHandler().getLooper(), this); playlistFetcher.singleLoad(player.getMainHandler().getLooper(), this);
} }
@ -82,8 +84,8 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
DataSource dataSource = new DefaultUriDataSource(userAgent, bandwidthMeter); DataSource dataSource = new DefaultUriDataSource(userAgent, bandwidthMeter);
HlsChunkSource chunkSource = new HlsChunkSource(dataSource, url, manifest, bandwidthMeter, null, HlsChunkSource chunkSource = new HlsChunkSource(dataSource, url, manifest, bandwidthMeter, null,
HlsChunkSource.ADAPTIVE_MODE_SPLICE); HlsChunkSource.ADAPTIVE_MODE_SPLICE);
HlsSampleSource sampleSource = new HlsSampleSource(chunkSource, true, 3, mainHandler, player, HlsSampleSource sampleSource = new HlsSampleSource(chunkSource, true, 3, REQUESTED_BUFFER_SIZE,
DemoPlayer.TYPE_VIDEO); REQUESTED_BUFFER_DURATION_MS, mainHandler, player, DemoPlayer.TYPE_VIDEO);
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(sampleSource, MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(sampleSource,
MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, mainHandler, player, 50); MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, mainHandler, player, 50);
MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource); MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource);

View file

@ -27,7 +27,6 @@ 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.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;
@ -95,16 +94,6 @@ public class HlsChunkSource {
*/ */
public static final int ADAPTIVE_MODE_ABRUPT = 3; public static final int ADAPTIVE_MODE_ABRUPT = 3;
/**
* The default target buffer size in bytes.
*/
public static final int DEFAULT_TARGET_BUFFER_SIZE = 18 * 1024 * 1024;
/**
* The default target buffer duration in milliseconds.
*/
public static final long DEFAULT_TARGET_BUFFER_DURATION_MS = 40000;
/** /**
* The default minimum duration of media that needs to be buffered for a switch to a higher * The default minimum duration of media that needs to be buffered for a switch to a higher
* quality variant to be considered. * quality variant to be considered.
@ -126,7 +115,6 @@ 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 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;
@ -136,8 +124,6 @@ public class HlsChunkSource {
private final String baseUri; private final String baseUri;
private final int maxWidth; private final int maxWidth;
private final int maxHeight; private final int maxHeight;
private final int targetBufferSize;
private final long targetBufferDurationUs;
private final long minBufferDurationToSwitchUpUs; private final long minBufferDurationToSwitchUpUs;
private final long maxBufferDurationToSwitchDownUs; private final long maxBufferDurationToSwitchDownUs;
@ -157,7 +143,6 @@ public class HlsChunkSource {
public HlsChunkSource(DataSource dataSource, String playlistUrl, HlsPlaylist playlist, public HlsChunkSource(DataSource dataSource, String playlistUrl, HlsPlaylist playlist,
BandwidthMeter bandwidthMeter, int[] variantIndices, int adaptiveMode) { BandwidthMeter bandwidthMeter, int[] variantIndices, int adaptiveMode) {
this(dataSource, playlistUrl, playlist, bandwidthMeter, variantIndices, adaptiveMode, this(dataSource, playlistUrl, playlist, bandwidthMeter, variantIndices, adaptiveMode,
DEFAULT_TARGET_BUFFER_SIZE, DEFAULT_TARGET_BUFFER_DURATION_MS,
DEFAULT_MIN_BUFFER_TO_SWITCH_UP_MS, DEFAULT_MAX_BUFFER_TO_SWITCH_DOWN_MS); DEFAULT_MIN_BUFFER_TO_SWITCH_UP_MS, DEFAULT_MAX_BUFFER_TO_SWITCH_DOWN_MS);
} }
@ -171,10 +156,6 @@ public class HlsChunkSource {
* @param adaptiveMode The mode for switching from one variant to another. One of * @param adaptiveMode The mode for switching from one variant to another. One of
* {@link #ADAPTIVE_MODE_NONE}, {@link #ADAPTIVE_MODE_ABRUPT} and * {@link #ADAPTIVE_MODE_NONE}, {@link #ADAPTIVE_MODE_ABRUPT} and
* {@link #ADAPTIVE_MODE_SPLICE}. * {@link #ADAPTIVE_MODE_SPLICE}.
* @param targetBufferSize The targeted buffer size in bytes. The buffer will not be filled more
* than one chunk beyond this amount of data.
* @param targetBufferDurationMs The targeted duration of media to buffer ahead of the current
* playback position. The buffer will not be filled more than one chunk beyond this position.
* @param minBufferDurationToSwitchUpMs The minimum duration of media that needs to be buffered * @param minBufferDurationToSwitchUpMs The minimum duration of media that needs to be buffered
* for a switch to a higher quality variant to be considered. * for a switch to a higher quality variant to be considered.
* @param maxBufferDurationToSwitchDownMs The maximum duration of media that needs to be buffered * @param maxBufferDurationToSwitchDownMs The maximum duration of media that needs to be buffered
@ -182,18 +163,14 @@ public class HlsChunkSource {
*/ */
public HlsChunkSource(DataSource dataSource, String playlistUrl, HlsPlaylist playlist, public HlsChunkSource(DataSource dataSource, String playlistUrl, HlsPlaylist playlist,
BandwidthMeter bandwidthMeter, int[] variantIndices, int adaptiveMode, BandwidthMeter bandwidthMeter, int[] variantIndices, int adaptiveMode,
int targetBufferSize, long targetBufferDurationMs, long minBufferDurationToSwitchUpMs, long minBufferDurationToSwitchUpMs, long maxBufferDurationToSwitchDownMs) {
long maxBufferDurationToSwitchDownMs) {
this.dataSource = dataSource; this.dataSource = dataSource;
this.bandwidthMeter = bandwidthMeter; this.bandwidthMeter = bandwidthMeter;
this.adaptiveMode = adaptiveMode; this.adaptiveMode = adaptiveMode;
this.targetBufferSize = targetBufferSize;
targetBufferDurationUs = targetBufferDurationMs * 1000;
minBufferDurationToSwitchUpUs = minBufferDurationToSwitchUpMs * 1000; minBufferDurationToSwitchUpUs = minBufferDurationToSwitchUpMs * 1000;
maxBufferDurationToSwitchDownUs = maxBufferDurationToSwitchDownMs * 1000; maxBufferDurationToSwitchDownUs = maxBufferDurationToSwitchDownMs * 1000;
baseUri = playlist.baseUri; baseUri = playlist.baseUri;
playlistParser = new HlsPlaylistParser(); playlistParser = new HlsPlaylistParser();
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));
@ -256,13 +233,6 @@ public class HlsChunkSource {
*/ */
public Chunk getChunkOperation(TsChunk previousTsChunk, long seekPositionUs, public Chunk getChunkOperation(TsChunk previousTsChunk, long seekPositionUs,
long playbackPositionUs) { long playbackPositionUs) {
if (previousTsChunk != null && (previousTsChunk.isLastChunk
|| previousTsChunk.endTimeUs - playbackPositionUs >= targetBufferDurationUs)
|| bufferPool.getTotalBytesAllocated() >= targetBufferSize) {
// We're either finished, or we have the target amount of data or time buffered.
return null;
}
int nextFormatIndex; int nextFormatIndex;
boolean switchingVariant; boolean switchingVariant;
boolean switchingVariantSpliced; boolean switchingVariantSpliced;
@ -364,8 +334,8 @@ public class HlsChunkSource {
Extractor extractor = chunkUri.getLastPathSegment().endsWith(AAC_FILE_EXTENSION) Extractor extractor = chunkUri.getLastPathSegment().endsWith(AAC_FILE_EXTENSION)
? new AdtsExtractor(startTimeUs) ? new AdtsExtractor(startTimeUs)
: new TsExtractor(startTimeUs); : new TsExtractor(startTimeUs);
extractorWrapper = new HlsExtractorWrapper(trigger, format, startTimeUs, bufferPool, extractorWrapper = new HlsExtractorWrapper(trigger, format, startTimeUs, extractor,
extractor, switchingVariantSpliced); switchingVariantSpliced);
} else { } else {
extractorWrapper = previousTsChunk.extractorWrapper; extractorWrapper = previousTsChunk.extractorWrapper;
} }

View file

@ -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.DefaultAllocator; import com.google.android.exoplayer.upstream.Allocator;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import android.util.SparseArray; import android.util.SparseArray;
@ -41,29 +41,38 @@ public final class HlsExtractorWrapper implements ExtractorOutput {
public final Format format; public final Format format;
public final long startTimeUs; public final long startTimeUs;
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;
private Allocator allocator;
private volatile boolean tracksBuilt; private volatile boolean tracksBuilt;
// Accessed only by the consuming thread. // Accessed only by the consuming thread.
private boolean prepared; private boolean prepared;
private boolean spliceConfigured; private boolean spliceConfigured;
public HlsExtractorWrapper(int trigger, Format format, long startTimeUs, public HlsExtractorWrapper(int trigger, Format format, long startTimeUs, Extractor extractor,
DefaultAllocator allocator, Extractor extractor, boolean shouldSpliceIn) { boolean shouldSpliceIn) {
this.trigger = trigger; this.trigger = trigger;
this.format = format; this.format = format;
this.startTimeUs = startTimeUs; this.startTimeUs = startTimeUs;
this.allocator = allocator;
this.extractor = extractor; this.extractor = extractor;
this.shouldSpliceIn = shouldSpliceIn; this.shouldSpliceIn = shouldSpliceIn;
sampleQueues = new SparseArray<DefaultTrackOutput>(); sampleQueues = new SparseArray<DefaultTrackOutput>();
extractor.init(this); extractor.init(this);
} }
/**
* Initializes the wrapper for use.
*
* @param allocator An allocator for obtaining allocations into which extracted data is written.
*/
public void init(Allocator allocator) {
this.allocator = allocator;
}
/** /**
* Attempts to configure a splice from this extractor to the next. * Attempts to configure a splice from this extractor to the next.
* <p> * <p>

View file

@ -25,6 +25,8 @@ import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.chunk.BaseChunkSampleSourceEventListener; import com.google.android.exoplayer.chunk.BaseChunkSampleSourceEventListener;
import com.google.android.exoplayer.chunk.Chunk; import com.google.android.exoplayer.chunk.Chunk;
import com.google.android.exoplayer.chunk.Format; import com.google.android.exoplayer.chunk.Format;
import com.google.android.exoplayer.upstream.Allocator;
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;
@ -50,12 +52,16 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
*/ */
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT = 3; public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT = 3;
private static final int BUFFER_FRAGMENT_LENGTH = 256 * 1024;
private static final int NO_RESET_PENDING = -1; private static final int NO_RESET_PENDING = -1;
private final HlsChunkSource chunkSource; private final HlsChunkSource chunkSource;
private final LinkedList<HlsExtractorWrapper> extractors; private final LinkedList<HlsExtractorWrapper> extractors;
private final Allocator allocator;
private final boolean frameAccurateSeeking; private final boolean frameAccurateSeeking;
private final int minLoadableRetryCount; private final int minLoadableRetryCount;
private final int requestedBufferSize;
private final long requestedBufferDurationUs;
private final int eventSourceId; private final int eventSourceId;
private final Handler eventHandler; private final Handler eventHandler;
@ -87,29 +93,35 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
private long currentLoadStartTimeMs; private long currentLoadStartTimeMs;
public HlsSampleSource(HlsChunkSource chunkSource, boolean frameAccurateSeeking, public HlsSampleSource(HlsChunkSource chunkSource, boolean frameAccurateSeeking,
int downstreamRendererCount) { int downstreamRendererCount, int requestedBufferSize, long requestedBufferDurationMs) {
this(chunkSource, frameAccurateSeeking, downstreamRendererCount, null, null, 0); this(chunkSource, frameAccurateSeeking, downstreamRendererCount, requestedBufferSize,
requestedBufferDurationMs, null, null, 0);
} }
public HlsSampleSource(HlsChunkSource chunkSource, boolean frameAccurateSeeking, public HlsSampleSource(HlsChunkSource chunkSource, boolean frameAccurateSeeking,
int downstreamRendererCount, Handler eventHandler, EventListener eventListener, int downstreamRendererCount, int requestedBufferSize, long requestedBufferDurationMs,
int eventSourceId) { Handler eventHandler, EventListener eventListener, int eventSourceId) {
this(chunkSource, frameAccurateSeeking, downstreamRendererCount, eventHandler, eventListener, this(chunkSource, frameAccurateSeeking, downstreamRendererCount, requestedBufferSize,
eventSourceId, DEFAULT_MIN_LOADABLE_RETRY_COUNT); requestedBufferDurationMs, eventHandler, eventListener, eventSourceId,
DEFAULT_MIN_LOADABLE_RETRY_COUNT);
} }
public HlsSampleSource(HlsChunkSource chunkSource, boolean frameAccurateSeeking, public HlsSampleSource(HlsChunkSource chunkSource, boolean frameAccurateSeeking,
int downstreamRendererCount, Handler eventHandler, EventListener eventListener, int downstreamRendererCount, int requestedBufferSize, long requestedBufferDurationMs,
Handler eventHandler, EventListener eventListener,
int eventSourceId, int minLoadableRetryCount) { int eventSourceId, int minLoadableRetryCount) {
this.chunkSource = chunkSource; this.chunkSource = chunkSource;
this.frameAccurateSeeking = frameAccurateSeeking; this.frameAccurateSeeking = frameAccurateSeeking;
this.remainingReleaseCount = downstreamRendererCount; this.remainingReleaseCount = downstreamRendererCount;
this.requestedBufferSize = requestedBufferSize;
this.requestedBufferDurationUs = requestedBufferDurationMs * 1000;
this.minLoadableRetryCount = minLoadableRetryCount; this.minLoadableRetryCount = minLoadableRetryCount;
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.eventListener = eventListener; this.eventListener = eventListener;
this.eventSourceId = eventSourceId; this.eventSourceId = eventSourceId;
this.pendingResetPositionUs = NO_RESET_PENDING; this.pendingResetPositionUs = NO_RESET_PENDING;
extractors = new LinkedList<HlsExtractorWrapper>(); extractors = new LinkedList<HlsExtractorWrapper>();
allocator = new DefaultAllocator(BUFFER_FRAGMENT_LENGTH);
} }
@Override @Override
@ -179,6 +191,7 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
loader.cancelLoading(); loader.cancelLoading();
} else { } else {
clearState(); clearState();
allocator.trim(0);
} }
} }
} }
@ -342,6 +355,7 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
restartFrom(pendingResetPositionUs); restartFrom(pendingResetPositionUs);
} else { } else {
clearState(); clearState();
allocator.trim(0);
} }
} }
@ -451,6 +465,13 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
return; return;
} }
if (previousTsLoadable != null
&& (previousTsLoadable.endTimeUs - downstreamPositionUs >= requestedBufferDurationUs
|| allocator.getTotalBytesAllocated() >= requestedBufferSize)) {
// We already have the target amount of data or time buffered.
return;
}
Chunk nextLoadable = chunkSource.getChunkOperation(previousTsLoadable, Chunk nextLoadable = chunkSource.getChunkOperation(previousTsLoadable,
pendingResetPositionUs, downstreamPositionUs); pendingResetPositionUs, downstreamPositionUs);
if (nextLoadable == null) { if (nextLoadable == null) {
@ -464,8 +485,10 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
if (isPendingReset()) { if (isPendingReset()) {
pendingResetPositionUs = NO_RESET_PENDING; pendingResetPositionUs = NO_RESET_PENDING;
} }
if (extractors.isEmpty() || extractors.getLast() != tsChunk.extractorWrapper) { HlsExtractorWrapper extractorWrapper = tsChunk.extractorWrapper;
extractors.addLast(tsChunk.extractorWrapper); if (extractors.isEmpty() || extractors.getLast() != extractorWrapper) {
extractorWrapper.init(allocator);
extractors.addLast(extractorWrapper);
} }
notifyLoadStarted(tsChunk.dataSpec.length, tsChunk.type, tsChunk.trigger, tsChunk.format, notifyLoadStarted(tsChunk.dataSpec.length, tsChunk.type, tsChunk.trigger, tsChunk.format,
tsChunk.startTimeUs, tsChunk.endTimeUs); tsChunk.startTimeUs, tsChunk.endTimeUs);