mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
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
This commit is contained in:
parent
b3ce415e88
commit
f09b86a1bb
2 changed files with 92 additions and 90 deletions
|
|
@ -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 {
|
||||||
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 {
|
||||||
* @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 {
|
||||||
* @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 {
|
||||||
* 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 {
|
||||||
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 {
|
||||||
*/
|
*/
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
}
|
}
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
|
|
||||||
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 {
|
||||||
// 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 {
|
||||||
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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
|
||||||
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 {
|
||||||
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 {
|
||||||
// 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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
* @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);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue