Restrict HLS buffering by total buffer size as well as duration.

This prevents excessive memory consumption when switching to
very high bitrate streams.

Issue: #278
This commit is contained in:
Oliver Woodman 2015-02-13 20:13:11 +00:00
parent c3788c0931
commit 5f64a1fde7

View File

@ -82,6 +82,11 @@ 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. * The default target buffer duration in milliseconds.
*/ */
@ -111,6 +116,7 @@ public class HlsChunkSource {
private final Uri baseUri; private final Uri 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 targetBufferDurationUs;
private final long minBufferDurationToSwitchUpUs; private final long minBufferDurationToSwitchUpUs;
private final long maxBufferDurationToSwitchDownUs; private final long maxBufferDurationToSwitchDownUs;
@ -131,8 +137,8 @@ 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_DURATION_MS, DEFAULT_MIN_BUFFER_TO_SWITCH_UP_MS, DEFAULT_TARGET_BUFFER_SIZE, DEFAULT_TARGET_BUFFER_DURATION_MS,
DEFAULT_MAX_BUFFER_TO_SWITCH_DOWN_MS); DEFAULT_MIN_BUFFER_TO_SWITCH_UP_MS, DEFAULT_MAX_BUFFER_TO_SWITCH_DOWN_MS);
} }
/** /**
@ -145,9 +151,10 @@ 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 * @param targetBufferDurationMs The targeted duration of media to buffer ahead of the current
* playback position. Note that the greater this value, the greater the amount of memory * playback position. The buffer will not be filled more than one chunk beyond this position.
* that will be consumed.
* @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
@ -155,11 +162,12 @@ 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,
long targetBufferDurationMs, long minBufferDurationToSwitchUpMs, int targetBufferSize, long targetBufferDurationMs, long minBufferDurationToSwitchUpMs,
long maxBufferDurationToSwitchDownMs) { long maxBufferDurationToSwitchDownMs) {
this.upstreamDataSource = dataSource; this.upstreamDataSource = dataSource;
this.bandwidthMeter = bandwidthMeter; this.bandwidthMeter = bandwidthMeter;
this.adaptiveMode = adaptiveMode; this.adaptiveMode = adaptiveMode;
this.targetBufferSize = targetBufferSize;
targetBufferDurationUs = targetBufferDurationMs * 1000; targetBufferDurationUs = targetBufferDurationMs * 1000;
minBufferDurationToSwitchUpUs = minBufferDurationToSwitchUpMs * 1000; minBufferDurationToSwitchUpUs = minBufferDurationToSwitchUpMs * 1000;
maxBufferDurationToSwitchDownUs = maxBufferDurationToSwitchDownMs * 1000; maxBufferDurationToSwitchDownUs = maxBufferDurationToSwitchDownMs * 1000;
@ -226,8 +234,9 @@ public class HlsChunkSource {
public HlsChunk getChunkOperation(TsChunk previousTsChunk, long seekPositionUs, public HlsChunk getChunkOperation(TsChunk previousTsChunk, long seekPositionUs,
long playbackPositionUs) { long playbackPositionUs) {
if (previousTsChunk != null && (previousTsChunk.isLastChunk if (previousTsChunk != null && (previousTsChunk.isLastChunk
|| previousTsChunk.endTimeUs - playbackPositionUs >= targetBufferDurationUs)) { || previousTsChunk.endTimeUs - playbackPositionUs >= targetBufferDurationUs)
// We're either finished, or we have the target amount of data buffered. || bufferPool.getAllocatedSize() >= targetBufferSize) {
// We're either finished, or we have the target amount of data or time buffered.
return null; return null;
} }