Decouple TrackGroups from SampleQueues in HlsSampleStreamWrapper

This CL does not aim to introduce any functionality changes.

Issue:#3149

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=174864875
This commit is contained in:
aquilescanta 2017-11-07 09:16:47 -08:00 committed by Oliver Woodman
parent 98301467dc
commit 15543f13b7
2 changed files with 58 additions and 48 deletions

View File

@ -21,22 +21,22 @@ import com.google.android.exoplayer2.source.SampleStream;
import java.io.IOException; import java.io.IOException;
/** /**
* {@link SampleStream} for a particular track group in HLS. * {@link SampleStream} for a particular sample queue in HLS.
*/ */
/* package */ final class HlsSampleStream implements SampleStream { /* package */ final class HlsSampleStream implements SampleStream {
public final int group; public final int sampleQueueIndex;
private final HlsSampleStreamWrapper sampleStreamWrapper; private final HlsSampleStreamWrapper sampleStreamWrapper;
public HlsSampleStream(HlsSampleStreamWrapper sampleStreamWrapper, int group) { public HlsSampleStream(HlsSampleStreamWrapper sampleStreamWrapper, int sampleQueueIndex) {
this.sampleStreamWrapper = sampleStreamWrapper; this.sampleStreamWrapper = sampleStreamWrapper;
this.group = group; this.sampleQueueIndex = sampleQueueIndex;
} }
@Override @Override
public boolean isReady() { public boolean isReady() {
return sampleStreamWrapper.isReady(group); return sampleStreamWrapper.isReady(sampleQueueIndex);
} }
@Override @Override
@ -46,12 +46,12 @@ import java.io.IOException;
@Override @Override
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean requireFormat) { public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean requireFormat) {
return sampleStreamWrapper.readData(group, formatHolder, buffer, requireFormat); return sampleStreamWrapper.readData(sampleQueueIndex, formatHolder, buffer, requireFormat);
} }
@Override @Override
public int skipData(long positionUs) { public int skipData(long positionUs) {
return sampleStreamWrapper.skipData(group, positionUs); return sampleStreamWrapper.skipData(sampleQueueIndex, positionUs);
} }
} }

View File

@ -89,18 +89,19 @@ import java.util.LinkedList;
private int[] sampleQueueTrackIds; private int[] sampleQueueTrackIds;
private boolean sampleQueuesBuilt; private boolean sampleQueuesBuilt;
private boolean prepared; private boolean prepared;
private int enabledTrackCount; private int enabledSampleQueueCount;
private Format downstreamTrackFormat; private Format downstreamTrackFormat;
private boolean released; private boolean released;
// Tracks are complicated in HLS. See documentation of buildTracks for details. // Tracks are complicated in HLS. See documentation of buildTracks for details.
// 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 boolean haveAudioVideoTrackGroups;
// Indexed by track group. // Indexed by track group.
private boolean[] trackGroupEnabledStates; private int[] trackGroupToSampleQueueIndex;
private boolean[] trackGroupIsAudioVideoFlags; private int primaryTrackGroupIndex;
private boolean haveAudioVideoSampleQueues;
private boolean[] sampleQueuesEnabledStates;
private boolean[] sampleQueueIsAudioVideoFlags;
private long sampleOffsetUs; private long sampleOffsetUs;
private long lastSeekPositionUs; private long lastSeekPositionUs;
@ -134,6 +135,8 @@ import java.util.LinkedList;
nextChunkHolder = new HlsChunkSource.HlsChunkHolder(); nextChunkHolder = new HlsChunkSource.HlsChunkHolder();
sampleQueueTrackIds = new int[0]; sampleQueueTrackIds = new int[0];
sampleQueues = new SampleQueue[0]; sampleQueues = new SampleQueue[0];
sampleQueueIsAudioVideoFlags = new boolean[0];
sampleQueuesEnabledStates = new boolean[0];
mediaChunks = new LinkedList<>(); mediaChunks = new LinkedList<>();
maybeFinishPrepareRunnable = new Runnable() { maybeFinishPrepareRunnable = new Runnable() {
@Override @Override
@ -190,12 +193,11 @@ import java.util.LinkedList;
public boolean selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags, public boolean selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags,
SampleStream[] streams, boolean[] streamResetFlags, long positionUs, boolean forceReset) { SampleStream[] streams, boolean[] streamResetFlags, long positionUs, boolean forceReset) {
Assertions.checkState(prepared); Assertions.checkState(prepared);
int oldEnabledTrackCount = enabledTrackCount; int oldEnabledSampleQueueCount = enabledSampleQueueCount;
// Deselect old tracks. // Deselect old tracks.
for (int i = 0; i < selections.length; i++) { for (int i = 0; i < selections.length; i++) {
if (streams[i] != null && (selections[i] == null || !mayRetainStreamFlags[i])) { if (streams[i] != null && (selections[i] == null || !mayRetainStreamFlags[i])) {
int group = ((HlsSampleStream) streams[i]).group; setSampleQueueEnabledState(((HlsSampleStream) streams[i]).sampleQueueIndex, false);
setTrackGroupEnabledState(group, false);
streams[i] = null; streams[i] = null;
} }
} }
@ -203,7 +205,8 @@ import java.util.LinkedList;
// a position other than the one we started preparing with, or if we're making a selection // a position other than the one we started preparing with, or if we're making a selection
// having previously disabled all tracks. // having previously disabled all tracks.
boolean seekRequired = forceReset boolean seekRequired = forceReset
|| (seenFirstTrackSelection ? oldEnabledTrackCount == 0 : positionUs != lastSeekPositionUs); || (seenFirstTrackSelection ? oldEnabledSampleQueueCount == 0
: positionUs != lastSeekPositionUs);
// Get the old (i.e. current before the loop below executes) primary track selection. The new // Get the old (i.e. current before the loop below executes) primary track selection. The new
// primary selection will equal the old one unless it's changed in the loop. // primary selection will equal the old one unless it's changed in the loop.
TrackSelection oldPrimaryTrackSelection = chunkSource.getTrackSelection(); TrackSelection oldPrimaryTrackSelection = chunkSource.getTrackSelection();
@ -213,16 +216,17 @@ import java.util.LinkedList;
if (streams[i] == null && selections[i] != null) { if (streams[i] == null && selections[i] != null) {
TrackSelection selection = selections[i]; TrackSelection selection = selections[i];
int trackGroupIndex = trackGroups.indexOf(selection.getTrackGroup()); int trackGroupIndex = trackGroups.indexOf(selection.getTrackGroup());
setTrackGroupEnabledState(trackGroupIndex, true); int sampleQueueIndex = trackGroupToSampleQueueIndex[trackGroupIndex];
setSampleQueueEnabledState(sampleQueueIndex, true);
if (trackGroupIndex == primaryTrackGroupIndex) { if (trackGroupIndex == primaryTrackGroupIndex) {
primaryTrackSelection = selection; primaryTrackSelection = selection;
chunkSource.selectTracks(selection); chunkSource.selectTracks(selection);
} }
streams[i] = new HlsSampleStream(this, trackGroupIndex); streams[i] = new HlsSampleStream(this, sampleQueueIndex);
streamResetFlags[i] = true; streamResetFlags[i] = true;
// If there's still a chance of avoiding a seek, try and seek within the sample queue. // If there's still a chance of avoiding a seek, try and seek within the sample queue.
if (!seekRequired) { if (!seekRequired) {
SampleQueue sampleQueue = sampleQueues[trackGroupIndex]; SampleQueue sampleQueue = sampleQueues[sampleQueueIndex];
sampleQueue.rewind(); sampleQueue.rewind();
// A seek can be avoided if we're able to advance to the current playback position in the // A seek can be avoided if we're able to advance to the current playback position in the
// sample queue, or if we haven't read anything from the queue since the previous seek // sample queue, or if we haven't read anything from the queue since the previous seek
@ -234,7 +238,7 @@ import java.util.LinkedList;
} }
} }
if (enabledTrackCount == 0) { if (enabledSampleQueueCount == 0) {
chunkSource.reset(); chunkSource.reset();
downstreamTrackFormat = null; downstreamTrackFormat = null;
mediaChunks.clear(); mediaChunks.clear();
@ -290,7 +294,7 @@ import java.util.LinkedList;
public void discardBuffer(long positionUs) { public void discardBuffer(long positionUs) {
int sampleQueueCount = sampleQueues.length; int sampleQueueCount = sampleQueues.length;
for (int i = 0; i < sampleQueueCount; i++) { for (int i = 0; i < sampleQueueCount; i++) {
sampleQueues[i].discardTo(positionUs, false, trackGroupEnabledStates[i]); sampleQueues[i].discardTo(positionUs, false, sampleQueuesEnabledStates[i]);
} }
} }
@ -370,8 +374,8 @@ import java.util.LinkedList;
// SampleStream implementation. // SampleStream implementation.
public boolean isReady(int trackGroupIndex) { public boolean isReady(int sampleQueueIndex) {
return loadingFinished || (!isPendingReset() && sampleQueues[trackGroupIndex].hasNextSample()); return loadingFinished || (!isPendingReset() && sampleQueues[sampleQueueIndex].hasNextSample());
} }
public void maybeThrowError() throws IOException { public void maybeThrowError() throws IOException {
@ -379,8 +383,8 @@ import java.util.LinkedList;
chunkSource.maybeThrowError(); chunkSource.maybeThrowError();
} }
public int readData(int trackGroupIndex, FormatHolder formatHolder, public int readData(int sampleQueueIndex, FormatHolder formatHolder, DecoderInputBuffer buffer,
DecoderInputBuffer buffer, boolean requireFormat) { boolean requireFormat) {
if (isPendingReset()) { if (isPendingReset()) {
return C.RESULT_NOTHING_READ; return C.RESULT_NOTHING_READ;
} }
@ -399,12 +403,12 @@ import java.util.LinkedList;
downstreamTrackFormat = trackFormat; downstreamTrackFormat = trackFormat;
} }
return sampleQueues[trackGroupIndex].read(formatHolder, buffer, requireFormat, loadingFinished, return sampleQueues[sampleQueueIndex].read(formatHolder, buffer, requireFormat, loadingFinished,
lastSeekPositionUs); lastSeekPositionUs);
} }
public int skipData(int trackGroupIndex, long positionUs) { public int skipData(int sampleQueueIndex, long positionUs) {
SampleQueue sampleQueue = sampleQueues[trackGroupIndex]; SampleQueue sampleQueue = sampleQueues[sampleQueueIndex];
if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) { if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) {
return sampleQueue.advanceToEnd(); return sampleQueue.advanceToEnd();
} else { } else {
@ -415,8 +419,9 @@ import java.util.LinkedList;
private boolean finishedReadingChunk(HlsMediaChunk chunk) { private boolean finishedReadingChunk(HlsMediaChunk chunk) {
int chunkUid = chunk.uid; int chunkUid = chunk.uid;
for (int i = 0; i < sampleQueues.length; i++) { int sampleQueueCount = sampleQueues.length;
if (trackGroupEnabledStates[i] && sampleQueues[i].peekSourceId() == chunkUid) { for (int i = 0; i < sampleQueueCount; i++) {
if (sampleQueuesEnabledStates[i] && sampleQueues[i].peekSourceId() == chunkUid) {
return false; return false;
} }
} }
@ -511,7 +516,7 @@ import java.util.LinkedList;
loadable.endTimeUs, elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded()); loadable.endTimeUs, elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded());
if (!released) { if (!released) {
resetSampleQueues(); resetSampleQueues();
if (enabledTrackCount > 0) { if (enabledSampleQueueCount > 0) {
callback.onContinueLoadingRequested(this); callback.onContinueLoadingRequested(this);
} }
} }
@ -587,6 +592,11 @@ import java.util.LinkedList;
sampleQueueTrackIds[trackCount] = id; sampleQueueTrackIds[trackCount] = id;
sampleQueues = Arrays.copyOf(sampleQueues, trackCount + 1); sampleQueues = Arrays.copyOf(sampleQueues, trackCount + 1);
sampleQueues[trackCount] = trackOutput; sampleQueues[trackCount] = trackOutput;
sampleQueueIsAudioVideoFlags = Arrays.copyOf(sampleQueueIsAudioVideoFlags, trackCount + 1);
sampleQueueIsAudioVideoFlags[trackCount] = type == C.TRACK_TYPE_AUDIO
|| type == C.TRACK_TYPE_VIDEO;
haveAudioVideoSampleQueues |= sampleQueueIsAudioVideoFlags[trackCount];
sampleQueuesEnabledStates = Arrays.copyOf(sampleQueuesEnabledStates, trackCount + 1);
return trackOutput; return trackOutput;
} }
@ -605,7 +615,9 @@ import java.util.LinkedList;
@Override @Override
public void onUpstreamFormatChanged(Format format) { public void onUpstreamFormatChanged(Format format) {
handler.post(maybeFinishPrepareRunnable); if (!prepared) {
handler.post(maybeFinishPrepareRunnable);
}
} }
// Called by the loading thread. // Called by the loading thread.
@ -696,17 +708,15 @@ import java.util.LinkedList;
// Instantiate the necessary internal data-structures. // Instantiate the necessary internal data-structures.
primaryTrackGroupIndex = C.INDEX_UNSET; primaryTrackGroupIndex = C.INDEX_UNSET;
trackGroupEnabledStates = new boolean[extractorTrackCount]; trackGroupToSampleQueueIndex = new int[extractorTrackCount];
trackGroupIsAudioVideoFlags = new boolean[extractorTrackCount]; for (int i = 0; i < extractorTrackCount; i++) {
trackGroupToSampleQueueIndex[i] = i;
}
// Construct the set of exposed track groups. // Construct the set of exposed track groups.
TrackGroup[] trackGroups = new TrackGroup[extractorTrackCount]; TrackGroup[] trackGroups = new TrackGroup[extractorTrackCount];
for (int i = 0; i < extractorTrackCount; i++) { for (int i = 0; i < extractorTrackCount; i++) {
Format sampleFormat = sampleQueues[i].getUpstreamFormat(); Format sampleFormat = sampleQueues[i].getUpstreamFormat();
String mimeType = sampleFormat.sampleMimeType;
boolean isAudioVideo = MimeTypes.isVideo(mimeType) || MimeTypes.isAudio(mimeType);
trackGroupIsAudioVideoFlags[i] = isAudioVideo;
haveAudioVideoTrackGroups |= isAudioVideo;
if (i == primaryExtractorTrackIndex) { if (i == primaryExtractorTrackIndex) {
Format[] formats = new Format[chunkSourceTrackCount]; Format[] formats = new Format[chunkSourceTrackCount];
for (int j = 0; j < chunkSourceTrackCount; j++) { for (int j = 0; j < chunkSourceTrackCount; j++) {
@ -724,15 +734,15 @@ import java.util.LinkedList;
} }
/** /**
* Enables or disables a specified track group. * Enables or disables a specified sample queue.
* *
* @param trackGroupIndex The index of the track group. * @param sampleQueueIndex The index of the sample queue.
* @param enabledState True if the group is being enabled, or false if it's being disabled. * @param enabledState True if the sample queue is being enabled, or false if it's being disabled.
*/ */
private void setTrackGroupEnabledState(int trackGroupIndex, boolean enabledState) { private void setSampleQueueEnabledState(int sampleQueueIndex, boolean enabledState) {
Assertions.checkState(trackGroupEnabledStates[trackGroupIndex] != enabledState); Assertions.checkState(sampleQueuesEnabledStates[sampleQueueIndex] != enabledState);
trackGroupEnabledStates[trackGroupIndex] = enabledState; sampleQueuesEnabledStates[sampleQueueIndex] = enabledState;
enabledTrackCount = enabledTrackCount + (enabledState ? 1 : -1); enabledSampleQueueCount = enabledSampleQueueCount + (enabledState ? 1 : -1);
} }
/** /**
@ -769,8 +779,8 @@ import java.util.LinkedList;
* @return Whether the in-buffer seek was successful. * @return Whether the in-buffer seek was successful.
*/ */
private boolean seekInsideBufferUs(long positionUs) { private boolean seekInsideBufferUs(long positionUs) {
int trackCount = sampleQueues.length; int sampleQueueCount = sampleQueues.length;
for (int i = 0; i < trackCount; i++) { for (int i = 0; i < sampleQueueCount; i++) {
SampleQueue sampleQueue = sampleQueues[i]; SampleQueue sampleQueue = sampleQueues[i];
sampleQueue.rewind(); sampleQueue.rewind();
boolean seekInsideQueue = sampleQueue.advanceTo(positionUs, true, false) boolean seekInsideQueue = sampleQueue.advanceTo(positionUs, true, false)
@ -779,7 +789,7 @@ import java.util.LinkedList;
// is successful. We ignore whether seeks within non-AV queues are successful in this case, as // is successful. We ignore whether seeks within non-AV queues are successful in this case, as
// they may be sparse or poorly interleaved. If we only have non-AV tracks then a seek is // they may be sparse or poorly interleaved. If we only have non-AV tracks then a seek is
// successful only if the seek into every queue succeeds. // successful only if the seek into every queue succeeds.
if (!seekInsideQueue && (trackGroupIsAudioVideoFlags[i] || !haveAudioVideoTrackGroups)) { if (!seekInsideQueue && (sampleQueueIsAudioVideoFlags[i] || !haveAudioVideoSampleQueues)) {
return false; return false;
} }
sampleQueue.discardToRead(); sampleQueue.discardToRead();