Commit f09b86a1 by olly Committed by Oliver Woodman

Fix SampleSource limbo state - Part II

This change optimizes startup and track selection for HLS. Changes
in HlsChunkSource avoid unnecessary re-requests for media playlists.
Changes in HlsSampleSource optimize exit from the limbo state (i.e.
when endTrackSelection is first called).
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=118026962
parent b3ce415e
...@@ -99,15 +99,14 @@ public class HlsChunkSource { ...@@ -99,15 +99,14 @@ public class HlsChunkSource {
private byte[] encryptionIv; private byte[] encryptionIv;
// Properties of exposed tracks. // Properties of exposed tracks.
private Variant[] exposedVariants; private Variant[] variants;
private HlsMediaPlaylist[] variantPlaylists;
private long[] variantLastPlaylistLoadTimesMs;
// Properties of enabled variants. // Properties of enabled variants.
private Variant[] enabledVariants; private Variant[] enabledVariants;
private HlsMediaPlaylist[] enabledVariantPlaylists;
private long[] enabledVariantLastPlaylistLoadTimesMs;
private long[] enabledVariantBlacklistTimes; private long[] enabledVariantBlacklistTimes;
private boolean[] enabledVariantBlacklistFlags; private boolean[] enabledVariantBlacklistFlags;
private int selectedVariantIndex;
/** /**
* @param manifestFetcher A fetcher for the playlist. * @param manifestFetcher A fetcher for the playlist.
...@@ -182,8 +181,11 @@ public class HlsChunkSource { ...@@ -182,8 +181,11 @@ public class HlsChunkSource {
Collections.<Variant>emptyList(), Collections.<Variant>emptyList(), null, null); Collections.<Variant>emptyList(), Collections.<Variant>emptyList(), null, null);
} }
processMasterPlaylist(masterPlaylist); processMasterPlaylist(masterPlaylist);
if (exposedVariants.length > 0) { if (variants.length > 0) {
// TODO[REFACTOR]: Come up with a sane default here. if (playlist.type == HlsPlaylist.TYPE_MEDIA) {
setMediaPlaylist(0, (HlsMediaPlaylist) playlist);
}
// Select the first variant listed in the master playlist.
selectTracks(new int[] {0}); selectTracks(new int[] {0});
} }
} }
...@@ -221,7 +223,7 @@ public class HlsChunkSource { ...@@ -221,7 +223,7 @@ public class HlsChunkSource {
* @return The number of tracks. * @return The number of tracks.
*/ */
public int getTrackCount() { public int getTrackCount() {
return exposedVariants.length; return variants.length;
} }
/** /**
...@@ -233,7 +235,7 @@ public class HlsChunkSource { ...@@ -233,7 +235,7 @@ public class HlsChunkSource {
* @return The format of the track. * @return The format of the track.
*/ */
public Format getTrackFormat(int index) { public Format getTrackFormat(int index) {
return exposedVariants[index].format; return variants[index].format;
} }
/** /**
...@@ -264,17 +266,15 @@ public class HlsChunkSource { ...@@ -264,17 +266,15 @@ public class HlsChunkSource {
* This method should only be called after the source has been prepared. * This method should only be called after the source has been prepared.
* *
* @param tracks The track indices. * @param tracks The track indices.
* @return True if one or more tracks was unselected. False otherwise.
*/ */
public void selectTracks(int[] tracks) { public boolean selectTracks(int[] tracks) {
evaluation.clear(); Variant[] oldEnabledVariants = enabledVariants;
enabledVariants = new Variant[tracks.length];
enabledVariantPlaylists = new HlsMediaPlaylist[enabledVariants.length];
enabledVariantLastPlaylistLoadTimesMs = new long[enabledVariants.length];
enabledVariantBlacklistTimes = new long[enabledVariants.length];
enabledVariantBlacklistFlags = new boolean[enabledVariants.length];
// Construct and sort the enabled variants. // Construct and sort the enabled variants.
enabledVariants = new Variant[tracks.length];
for (int i = 0; i < tracks.length; i++) { for (int i = 0; i < tracks.length; i++) {
enabledVariants[i] = exposedVariants[tracks[i]]; enabledVariants[i] = variants[tracks[i]];
} }
Arrays.sort(enabledVariants, new Comparator<Variant>() { Arrays.sort(enabledVariants, new Comparator<Variant>() {
private final Comparator<Format> formatComparator = private final Comparator<Format> formatComparator =
...@@ -284,14 +284,28 @@ public class HlsChunkSource { ...@@ -284,14 +284,28 @@ public class HlsChunkSource {
return formatComparator.compare(first.format, second.format); return formatComparator.compare(first.format, second.format);
} }
}); });
// Reset the enabled variant blacklist flags.
enabledVariantBlacklistTimes = new long[enabledVariants.length];
enabledVariantBlacklistFlags = new boolean[enabledVariants.length];
if (enabledVariants.length > 1) { if (enabledVariants.length > 1) {
// TODO[REFACTOR]: We need to disable this at some point.
Format[] formats = new Format[enabledVariants.length]; Format[] formats = new Format[enabledVariants.length];
for (int i = 0; i < formats.length; i++) { for (int i = 0; i < formats.length; i++) {
formats[i] = enabledVariants[i].format; formats[i] = enabledVariants[i].format;
} }
// TODO[REFACTOR]: We need to disable this at some point.
adaptiveFormatEvaluator.enable(formats); adaptiveFormatEvaluator.enable(formats);
} }
if (oldEnabledVariants != null) {
for (Variant oldVariant : oldEnabledVariants) {
if (!Util.contains(enabledVariants, oldVariant)) {
return true;
}
}
}
return false;
} }
/** /**
...@@ -327,22 +341,21 @@ public class HlsChunkSource { ...@@ -327,22 +341,21 @@ public class HlsChunkSource {
*/ */
public void getChunkOperation(TsChunk previousTsChunk, long playbackPositionUs, public void getChunkOperation(TsChunk previousTsChunk, long playbackPositionUs,
ChunkOperationHolder out) { ChunkOperationHolder out) {
int nextVariantIndex = getNextVariantIndex(previousTsChunk, playbackPositionUs); int variantIndex = getNextVariantIndex(previousTsChunk, playbackPositionUs);
boolean switchingVariant = previousTsChunk != null boolean switchingVariant = previousTsChunk != null
&& enabledVariants[nextVariantIndex].format != previousTsChunk.format; && variants[variantIndex].format != previousTsChunk.format;
HlsMediaPlaylist mediaPlaylist = enabledVariantPlaylists[nextVariantIndex]; HlsMediaPlaylist mediaPlaylist = variantPlaylists[variantIndex];
if (mediaPlaylist == null) { if (mediaPlaylist == null) {
// We don't have the media playlist for the next variant. Request it now. // We don't have the media playlist for the next variant. Request it now.
out.chunk = newMediaPlaylistChunk(nextVariantIndex); out.chunk = newMediaPlaylistChunk(variantIndex);
return; return;
} }
selectedVariantIndex = nextVariantIndex;
int chunkMediaSequence = 0; int chunkMediaSequence = 0;
if (live) { if (live) {
if (previousTsChunk == null) { if (previousTsChunk == null) {
chunkMediaSequence = getLiveStartChunkMediaSequence(nextVariantIndex); chunkMediaSequence = getLiveStartChunkMediaSequence(variantIndex);
} else { } else {
chunkMediaSequence = switchingVariant ? previousTsChunk.chunkIndex chunkMediaSequence = switchingVariant ? previousTsChunk.chunkIndex
: previousTsChunk.chunkIndex + 1; : previousTsChunk.chunkIndex + 1;
...@@ -366,8 +379,8 @@ public class HlsChunkSource { ...@@ -366,8 +379,8 @@ public class HlsChunkSource {
if (chunkIndex >= mediaPlaylist.segments.size()) { if (chunkIndex >= mediaPlaylist.segments.size()) {
if (!mediaPlaylist.live) { if (!mediaPlaylist.live) {
out.endOfStream = true; out.endOfStream = true;
} else if (shouldRerequestLiveMediaPlaylist(nextVariantIndex)) { } else if (shouldRerequestLiveMediaPlaylist(variantIndex)) {
out.chunk = newMediaPlaylistChunk(nextVariantIndex); out.chunk = newMediaPlaylistChunk(variantIndex);
} }
return; return;
} }
...@@ -380,7 +393,7 @@ public class HlsChunkSource { ...@@ -380,7 +393,7 @@ public class HlsChunkSource {
Uri keyUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, segment.encryptionKeyUri); Uri keyUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, segment.encryptionKeyUri);
if (!keyUri.equals(encryptionKeyUri)) { if (!keyUri.equals(encryptionKeyUri)) {
// Encryption is specified and the key has changed. // Encryption is specified and the key has changed.
out.chunk = newEncryptionKeyChunk(keyUri, segment.encryptionIV, selectedVariantIndex); out.chunk = newEncryptionKeyChunk(keyUri, segment.encryptionIV, variantIndex);
return; return;
} }
if (!Util.areEqual(segment.encryptionIV, encryptionIvString)) { if (!Util.areEqual(segment.encryptionIV, encryptionIvString)) {
...@@ -409,7 +422,7 @@ public class HlsChunkSource { ...@@ -409,7 +422,7 @@ public class HlsChunkSource {
} }
long endTimeUs = startTimeUs + (long) (segment.durationSecs * C.MICROS_PER_SECOND); long endTimeUs = startTimeUs + (long) (segment.durationSecs * C.MICROS_PER_SECOND);
int trigger = Chunk.TRIGGER_UNSPECIFIED; int trigger = Chunk.TRIGGER_UNSPECIFIED;
Format format = enabledVariants[selectedVariantIndex].format; Format format = variants[variantIndex].format;
// Configure the extractor that will read the chunk. // Configure the extractor that will read the chunk.
HlsExtractorWrapper extractorWrapper; HlsExtractorWrapper extractorWrapper;
...@@ -449,7 +462,7 @@ public class HlsChunkSource { ...@@ -449,7 +462,7 @@ public class HlsChunkSource {
return; return;
} }
int workaroundFlags = 0; int workaroundFlags = 0;
String codecs = enabledVariants[selectedVariantIndex].codecs; String codecs = variants[variantIndex].codecs;
if (!TextUtils.isEmpty(codecs)) { if (!TextUtils.isEmpty(codecs)) {
// Sometimes AAC and H264 streams are declared in TS chunks even though they don't really // Sometimes AAC and H264 streams are declared in TS chunks even though they don't really
// exist. If we know from the codec attribute that they don't exist, then we can explicitly // exist. If we know from the codec attribute that they don't exist, then we can explicitly
...@@ -509,17 +522,7 @@ public class HlsChunkSource { ...@@ -509,17 +522,7 @@ public class HlsChunkSource {
InvalidResponseCodeException responseCodeException = (InvalidResponseCodeException) e; InvalidResponseCodeException responseCodeException = (InvalidResponseCodeException) e;
int responseCode = responseCodeException.responseCode; int responseCode = responseCodeException.responseCode;
if (responseCode == 404 || responseCode == 410) { if (responseCode == 404 || responseCode == 410) {
int enabledVariantIndex; int enabledVariantIndex = getEnabledVariantIndex(chunk.format);
if (chunk instanceof TsChunk) {
TsChunk tsChunk = (TsChunk) chunk;
enabledVariantIndex = getEnabledVariantIndex(tsChunk.format);
} else if (chunk instanceof MediaPlaylistChunk) {
MediaPlaylistChunk playlistChunk = (MediaPlaylistChunk) chunk;
enabledVariantIndex = playlistChunk.variantIndex;
} else {
EncryptionKeyChunk encryptionChunk = (EncryptionKeyChunk) chunk;
enabledVariantIndex = encryptionChunk.variantIndex;
}
boolean alreadyBlacklisted = enabledVariantBlacklistFlags[enabledVariantIndex]; boolean alreadyBlacklisted = enabledVariantBlacklistFlags[enabledVariantIndex];
enabledVariantBlacklistFlags[enabledVariantIndex] = true; enabledVariantBlacklistFlags[enabledVariantIndex] = true;
enabledVariantBlacklistTimes[enabledVariantIndex] = SystemClock.elapsedRealtime(); enabledVariantBlacklistTimes[enabledVariantIndex] = SystemClock.elapsedRealtime();
...@@ -528,7 +531,7 @@ public class HlsChunkSource { ...@@ -528,7 +531,7 @@ public class HlsChunkSource {
Log.w(TAG, "Already blacklisted variant (" + responseCode + "): " Log.w(TAG, "Already blacklisted variant (" + responseCode + "): "
+ chunk.dataSpec.uri); + chunk.dataSpec.uri);
return false; return false;
} else if (!allVariantsBlacklisted()) { } else if (!allEnabledVariantsBlacklisted()) {
// We've handled the 404/410 by blacklisting the variant. // We've handled the 404/410 by blacklisting the variant.
Log.w(TAG, "Blacklisted variant (" + responseCode + "): " Log.w(TAG, "Blacklisted variant (" + responseCode + "): "
+ chunk.dataSpec.uri); + chunk.dataSpec.uri);
...@@ -549,13 +552,15 @@ public class HlsChunkSource { ...@@ -549,13 +552,15 @@ public class HlsChunkSource {
private void processMasterPlaylist(HlsMasterPlaylist playlist) { private void processMasterPlaylist(HlsMasterPlaylist playlist) {
if (type == TYPE_SUBTITLE || type == TYPE_AUDIO) { if (type == TYPE_SUBTITLE || type == TYPE_AUDIO) {
List<Variant> variants = type == TYPE_AUDIO ? playlist.audios : playlist.subtitles; List<Variant> variantList = type == TYPE_AUDIO ? playlist.audios : playlist.subtitles;
if (variants != null && !variants.isEmpty()) { if (variantList != null && !variantList.isEmpty()) {
exposedVariants = new Variant[variants.size()]; variants = new Variant[variantList.size()];
variants.toArray(exposedVariants); variantList.toArray(variants);
} else { } else {
exposedVariants = new Variant[0]; variants = new Variant[0];
} }
variantPlaylists = new HlsMediaPlaylist[variants.length];
variantLastPlaylistLoadTimesMs = new long[variants.length];
return; return;
} }
...@@ -585,8 +590,10 @@ public class HlsChunkSource { ...@@ -585,8 +590,10 @@ public class HlsChunkSource {
// Leave the enabled variants unchanged. They're likely either all video or all audio. // Leave the enabled variants unchanged. They're likely either all video or all audio.
} }
exposedVariants = new Variant[enabledVariantList.size()]; variants = new Variant[enabledVariantList.size()];
enabledVariantList.toArray(exposedVariants); enabledVariantList.toArray(variants);
variantPlaylists = new HlsMediaPlaylist[variants.length];
variantLastPlaylistLoadTimesMs = new long[variants.length];
} }
private static boolean variantHasExplicitCodecWithPrefix(Variant variant, String prefix) { private static boolean variantHasExplicitCodecWithPrefix(Variant variant, String prefix) {
...@@ -621,40 +628,41 @@ public class HlsChunkSource { ...@@ -621,40 +628,41 @@ public class HlsChunkSource {
evaluation.format = enabledVariants[0].format; evaluation.format = enabledVariants[0].format;
evaluation.trigger = Chunk.TRIGGER_MANUAL; evaluation.trigger = Chunk.TRIGGER_MANUAL;
} }
for (int i = 0; i < enabledVariants.length; i++) { for (int i = 0; i < variants.length; i++) {
if (enabledVariants[i].format == evaluation.format) { if (variants[i].format == evaluation.format) {
return i; return i;
} }
} }
throw new IllegalStateException(); throw new IllegalStateException();
} }
private boolean shouldRerequestLiveMediaPlaylist(int nextVariantIndex) { private boolean shouldRerequestLiveMediaPlaylist(int variantIndex) {
// Don't re-request media playlist more often than one-half of the target duration. HlsMediaPlaylist mediaPlaylist = variantPlaylists[variantIndex];
HlsMediaPlaylist mediaPlaylist = enabledVariantPlaylists[nextVariantIndex];
long timeSinceLastMediaPlaylistLoadMs = long timeSinceLastMediaPlaylistLoadMs =
SystemClock.elapsedRealtime() - enabledVariantLastPlaylistLoadTimesMs[nextVariantIndex]; SystemClock.elapsedRealtime() - variantLastPlaylistLoadTimesMs[variantIndex];
// Don't re-request media playlist more often than one-half of the target duration.
return timeSinceLastMediaPlaylistLoadMs >= (mediaPlaylist.targetDurationSecs * 1000) / 2; return timeSinceLastMediaPlaylistLoadMs >= (mediaPlaylist.targetDurationSecs * 1000) / 2;
} }
private int getLiveStartChunkMediaSequence(int variantIndex) { private int getLiveStartChunkMediaSequence(int variantIndex) {
HlsMediaPlaylist mediaPlaylist = variantPlaylists[variantIndex];
// For live start playback from the third chunk from the end. // For live start playback from the third chunk from the end.
HlsMediaPlaylist mediaPlaylist = enabledVariantPlaylists[variantIndex];
int chunkIndex = mediaPlaylist.segments.size() > 3 ? mediaPlaylist.segments.size() - 3 : 0; int chunkIndex = mediaPlaylist.segments.size() > 3 ? mediaPlaylist.segments.size() - 3 : 0;
return chunkIndex + mediaPlaylist.mediaSequence; return chunkIndex + mediaPlaylist.mediaSequence;
} }
private MediaPlaylistChunk newMediaPlaylistChunk(int variantIndex) { private MediaPlaylistChunk newMediaPlaylistChunk(int variantIndex) {
Uri mediaPlaylistUri = UriUtil.resolveToUri(baseUri, enabledVariants[variantIndex].url); Uri mediaPlaylistUri = UriUtil.resolveToUri(baseUri, variants[variantIndex].url);
DataSpec dataSpec = new DataSpec(mediaPlaylistUri, 0, C.LENGTH_UNBOUNDED, null, DataSpec dataSpec = new DataSpec(mediaPlaylistUri, 0, C.LENGTH_UNBOUNDED, null,
DataSpec.FLAG_ALLOW_GZIP); DataSpec.FLAG_ALLOW_GZIP);
return new MediaPlaylistChunk(dataSource, dataSpec, scratchSpace, playlistParser, variantIndex, return new MediaPlaylistChunk(dataSource, dataSpec, variants[variantIndex].format, scratchSpace,
mediaPlaylistUri.toString()); playlistParser, variantIndex, mediaPlaylistUri.toString());
} }
private EncryptionKeyChunk newEncryptionKeyChunk(Uri keyUri, String iv, int variantIndex) { private EncryptionKeyChunk newEncryptionKeyChunk(Uri keyUri, String iv, int variantIndex) {
DataSpec dataSpec = new DataSpec(keyUri, 0, C.LENGTH_UNBOUNDED, null, DataSpec.FLAG_ALLOW_GZIP); DataSpec dataSpec = new DataSpec(keyUri, 0, C.LENGTH_UNBOUNDED, null, DataSpec.FLAG_ALLOW_GZIP);
return new EncryptionKeyChunk(dataSource, dataSpec, scratchSpace, iv, variantIndex); return new EncryptionKeyChunk(dataSource, dataSpec, variants[variantIndex].format, scratchSpace,
iv);
} }
private void setEncryptionData(Uri keyUri, String iv, byte[] secretKey) { private void setEncryptionData(Uri keyUri, String iv, byte[] secretKey) {
...@@ -685,13 +693,13 @@ public class HlsChunkSource { ...@@ -685,13 +693,13 @@ public class HlsChunkSource {
} }
private void setMediaPlaylist(int variantIndex, HlsMediaPlaylist mediaPlaylist) { private void setMediaPlaylist(int variantIndex, HlsMediaPlaylist mediaPlaylist) {
enabledVariantLastPlaylistLoadTimesMs[variantIndex] = SystemClock.elapsedRealtime(); variantLastPlaylistLoadTimesMs[variantIndex] = SystemClock.elapsedRealtime();
enabledVariantPlaylists[variantIndex] = mediaPlaylist; variantPlaylists[variantIndex] = mediaPlaylist;
live |= mediaPlaylist.live; live |= mediaPlaylist.live;
durationUs = live ? C.UNKNOWN_TIME_US : mediaPlaylist.durationUs; durationUs = live ? C.UNKNOWN_TIME_US : mediaPlaylist.durationUs;
} }
private boolean allVariantsBlacklisted() { private boolean allEnabledVariantsBlacklisted() {
for (int i = 0; i < enabledVariantBlacklistFlags.length; i++) { for (int i = 0; i < enabledVariantBlacklistFlags.length; i++) {
if (!enabledVariantBlacklistFlags[i]) { if (!enabledVariantBlacklistFlags[i]) {
return false; return false;
...@@ -731,9 +739,10 @@ public class HlsChunkSource { ...@@ -731,9 +739,10 @@ public class HlsChunkSource {
private HlsMediaPlaylist result; private HlsMediaPlaylist result;
public MediaPlaylistChunk(DataSource dataSource, DataSpec dataSpec, byte[] scratchSpace, public MediaPlaylistChunk(DataSource dataSource, DataSpec dataSpec, Format format,
HlsPlaylistParser playlistParser, int variantIndex, String playlistUrl) { byte[] scratchSpace, HlsPlaylistParser playlistParser, int variantIndex,
super(dataSource, dataSpec, Chunk.TYPE_MANIFEST, Chunk.TRIGGER_UNSPECIFIED, null, String playlistUrl) {
super(dataSource, dataSpec, Chunk.TYPE_MANIFEST, Chunk.TRIGGER_UNSPECIFIED, format,
Chunk.NO_PARENT_ID, scratchSpace); Chunk.NO_PARENT_ID, scratchSpace);
this.variantIndex = variantIndex; this.variantIndex = variantIndex;
this.playlistParser = playlistParser; this.playlistParser = playlistParser;
...@@ -755,16 +764,14 @@ public class HlsChunkSource { ...@@ -755,16 +764,14 @@ public class HlsChunkSource {
private static final class EncryptionKeyChunk extends DataChunk { private static final class EncryptionKeyChunk extends DataChunk {
public final String iv; public final String iv;
public final int variantIndex;
private byte[] result; private byte[] result;
public EncryptionKeyChunk(DataSource dataSource, DataSpec dataSpec, byte[] scratchSpace, public EncryptionKeyChunk(DataSource dataSource, DataSpec dataSpec, Format format,
String iv, int variantIndex) { byte[] scratchSpace, String iv) {
super(dataSource, dataSpec, Chunk.TYPE_DRM, Chunk.TRIGGER_UNSPECIFIED, null, super(dataSource, dataSpec, Chunk.TYPE_DRM, Chunk.TRIGGER_UNSPECIFIED, format,
Chunk.NO_PARENT_ID, scratchSpace); Chunk.NO_PARENT_ID, scratchSpace);
this.iv = iv; this.iv = iv;
this.variantIndex = variantIndex;
} }
@Override @Override
......
...@@ -83,8 +83,9 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback { ...@@ -83,8 +83,9 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
// Indexed by track (as exposed by this source). // Indexed by track (as exposed by this source).
private TrackGroupArray trackGroups; private TrackGroupArray trackGroups;
private int primaryTrackGroupIndex; private int primaryTrackGroupIndex;
private int[] primarySelectedTracks; private boolean isFirstTrackSelection;
private boolean primarySelectedTracksChanged; private boolean newTracksSelected;
private boolean primaryTracksDeselected;
// Indexed by group. // Indexed by group.
private boolean[] groupEnabledStates; private boolean[] groupEnabledStates;
private boolean[] pendingResets; private boolean[] pendingResets;
...@@ -146,6 +147,7 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback { ...@@ -146,6 +147,7 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
if (chunkSource.getTrackCount() == 0) { if (chunkSource.getTrackCount() == 0) {
trackGroups = new TrackGroupArray(); trackGroups = new TrackGroupArray();
state = STATE_SELECTING_TRACKS; state = STATE_SELECTING_TRACKS;
isFirstTrackSelection = true;
return true; return true;
} }
if (!extractors.isEmpty()) { if (!extractors.isEmpty()) {
...@@ -155,6 +157,7 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback { ...@@ -155,6 +157,7 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
if (extractor.isPrepared()) { if (extractor.isPrepared()) {
buildTracks(extractor); buildTracks(extractor);
state = STATE_SELECTING_TRACKS; state = STATE_SELECTING_TRACKS;
isFirstTrackSelection = true;
maybeStartLoading(); // Update the load control. maybeStartLoading(); // Update the load control.
return true; return true;
} else if (extractors.size() > 1) { } else if (extractors.size() > 1) {
...@@ -197,7 +200,9 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback { ...@@ -197,7 +200,9 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
public void startTrackSelection() { public void startTrackSelection() {
Assertions.checkState(state == STATE_READING); Assertions.checkState(state == STATE_READING);
state = STATE_SELECTING_TRACKS; state = STATE_SELECTING_TRACKS;
primarySelectedTracksChanged = false; isFirstTrackSelection = false;
newTracksSelected = false;
primaryTracksDeselected = false;
} }
@Override @Override
...@@ -208,9 +213,9 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback { ...@@ -208,9 +213,9 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
setTrackGroupEnabledState(group, true); setTrackGroupEnabledState(group, true);
downstreamSampleFormats[group] = null; downstreamSampleFormats[group] = null;
pendingResets[group] = false; pendingResets[group] = false;
if (group == primaryTrackGroupIndex && !Arrays.equals(tracks, primarySelectedTracks)) { newTracksSelected = true;
primarySelectedTracks = tracks; if (group == primaryTrackGroupIndex) {
primarySelectedTracksChanged = true; primaryTracksDeselected |= chunkSource.selectTracks(tracks);
} }
return new TrackStreamImpl(group); return new TrackStreamImpl(group);
} }
...@@ -242,24 +247,12 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback { ...@@ -242,24 +247,12 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
loadControl.trimAllocator(); loadControl.trimAllocator();
} }
} }
} else { } else if (primaryTracksDeselected || (!isFirstTrackSelection && newTracksSelected)) {
if (!loadControlRegistered) { if (!loadControlRegistered) {
loadControl.register(this, bufferSizeContribution); loadControl.register(this, bufferSizeContribution);
loadControlRegistered = true; loadControlRegistered = true;
} }
// Treat enabling of a live stream as occurring at t=0 in both of the blocks below. seekToInternal(positionUs);
positionUs = chunkSource.isLive() ? 0 : positionUs;
if (primarySelectedTracksChanged) {
// If the primary tracks change then this will affect other exposed tracks that are enabled
// as well. Hence we implement the restart as a seek so that all downstream renderers
// receive a discontinuity event.
chunkSource.selectTracks(primarySelectedTracks);
seekToInternal(positionUs);
} else {
lastSeekPositionUs = positionUs;
downstreamPositionUs = positionUs;
restartFrom(positionUs);
}
} }
} }
...@@ -370,7 +363,7 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback { ...@@ -370,7 +363,7 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
if (enabledTrackCount == 0) { if (enabledTrackCount == 0) {
return; return;
} }
seekToInternal(chunkSource.isLive() ? 0 : positionUs); seekToInternal(positionUs);
} }
@Override @Override
...@@ -595,6 +588,8 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback { ...@@ -595,6 +588,8 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
* @param positionUs The position to seek to. * @param positionUs The position to seek to.
*/ */
private void seekToInternal(long positionUs) { private void seekToInternal(long positionUs) {
// Treat all seeks into non-seekable media as being to t=0.
positionUs = chunkSource.isLive() ? 0 : positionUs;
lastSeekPositionUs = positionUs; lastSeekPositionUs = positionUs;
downstreamPositionUs = positionUs; downstreamPositionUs = positionUs;
Arrays.fill(pendingResets, true); Arrays.fill(pendingResets, true);
......
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