Skip to content

Commit

Permalink
Decouple TrackGroups from SampleQueues in HlsSampleStreamWrapper
Browse files Browse the repository at this point in the history
This CL does not aim to introduce any functionality changes.

Issue:#3149

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=174864875
  • Loading branch information
AquilesCanta authored and ojw28 committed Nov 13, 2017
1 parent 9830146 commit 15543f1
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,22 @@
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 {

public final int group;
public final int sampleQueueIndex;

private final HlsSampleStreamWrapper sampleStreamWrapper;

public HlsSampleStream(HlsSampleStreamWrapper sampleStreamWrapper, int group) {
public HlsSampleStream(HlsSampleStreamWrapper sampleStreamWrapper, int sampleQueueIndex) {
this.sampleStreamWrapper = sampleStreamWrapper;
this.group = group;
this.sampleQueueIndex = sampleQueueIndex;
}

@Override
public boolean isReady() {
return sampleStreamWrapper.isReady(group);
return sampleStreamWrapper.isReady(sampleQueueIndex);
}

@Override
Expand All @@ -46,12 +46,12 @@ public void maybeThrowError() throws IOException {

@Override
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean requireFormat) {
return sampleStreamWrapper.readData(group, formatHolder, buffer, requireFormat);
return sampleStreamWrapper.readData(sampleQueueIndex, formatHolder, buffer, requireFormat);
}

@Override
public int skipData(long positionUs) {
return sampleStreamWrapper.skipData(group, positionUs);
return sampleStreamWrapper.skipData(sampleQueueIndex, positionUs);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -89,18 +89,19 @@ public interface Callback extends SequenceableLoader.Callback<HlsSampleStreamWra
private int[] sampleQueueTrackIds;
private boolean sampleQueuesBuilt;
private boolean prepared;
private int enabledTrackCount;
private int enabledSampleQueueCount;
private Format downstreamTrackFormat;
private boolean released;

// Tracks are complicated in HLS. See documentation of buildTracks for details.
// Indexed by track (as exposed by this source).
private TrackGroupArray trackGroups;
private int primaryTrackGroupIndex;
private boolean haveAudioVideoTrackGroups;
// Indexed by track group.
private boolean[] trackGroupEnabledStates;
private boolean[] trackGroupIsAudioVideoFlags;
private int[] trackGroupToSampleQueueIndex;
private int primaryTrackGroupIndex;
private boolean haveAudioVideoSampleQueues;
private boolean[] sampleQueuesEnabledStates;
private boolean[] sampleQueueIsAudioVideoFlags;

private long sampleOffsetUs;
private long lastSeekPositionUs;
Expand Down Expand Up @@ -134,6 +135,8 @@ public HlsSampleStreamWrapper(int trackType, Callback callback, HlsChunkSource c
nextChunkHolder = new HlsChunkSource.HlsChunkHolder();
sampleQueueTrackIds = new int[0];
sampleQueues = new SampleQueue[0];
sampleQueueIsAudioVideoFlags = new boolean[0];
sampleQueuesEnabledStates = new boolean[0];
mediaChunks = new LinkedList<>();
maybeFinishPrepareRunnable = new Runnable() {
@Override
Expand Down Expand Up @@ -190,20 +193,20 @@ public TrackGroupArray getTrackGroups() {
public boolean selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags,
SampleStream[] streams, boolean[] streamResetFlags, long positionUs, boolean forceReset) {
Assertions.checkState(prepared);
int oldEnabledTrackCount = enabledTrackCount;
int oldEnabledSampleQueueCount = enabledSampleQueueCount;
// Deselect old tracks.
for (int i = 0; i < selections.length; i++) {
if (streams[i] != null && (selections[i] == null || !mayRetainStreamFlags[i])) {
int group = ((HlsSampleStream) streams[i]).group;
setTrackGroupEnabledState(group, false);
setSampleQueueEnabledState(((HlsSampleStream) streams[i]).sampleQueueIndex, false);
streams[i] = null;
}
}
// We'll always need to seek if we're being forced to reset, or if this is a first selection to
// a position other than the one we started preparing with, or if we're making a selection
// having previously disabled all tracks.
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
// primary selection will equal the old one unless it's changed in the loop.
TrackSelection oldPrimaryTrackSelection = chunkSource.getTrackSelection();
Expand All @@ -213,16 +216,17 @@ public boolean selectTracks(TrackSelection[] selections, boolean[] mayRetainStre
if (streams[i] == null && selections[i] != null) {
TrackSelection selection = selections[i];
int trackGroupIndex = trackGroups.indexOf(selection.getTrackGroup());
setTrackGroupEnabledState(trackGroupIndex, true);
int sampleQueueIndex = trackGroupToSampleQueueIndex[trackGroupIndex];
setSampleQueueEnabledState(sampleQueueIndex, true);
if (trackGroupIndex == primaryTrackGroupIndex) {
primaryTrackSelection = selection;
chunkSource.selectTracks(selection);
}
streams[i] = new HlsSampleStream(this, trackGroupIndex);
streams[i] = new HlsSampleStream(this, sampleQueueIndex);
streamResetFlags[i] = true;
// If there's still a chance of avoiding a seek, try and seek within the sample queue.
if (!seekRequired) {
SampleQueue sampleQueue = sampleQueues[trackGroupIndex];
SampleQueue sampleQueue = sampleQueues[sampleQueueIndex];
sampleQueue.rewind();
// 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
Expand All @@ -234,7 +238,7 @@ public boolean selectTracks(TrackSelection[] selections, boolean[] mayRetainStre
}
}

if (enabledTrackCount == 0) {
if (enabledSampleQueueCount == 0) {
chunkSource.reset();
downstreamTrackFormat = null;
mediaChunks.clear();
Expand Down Expand Up @@ -290,7 +294,7 @@ public boolean selectTracks(TrackSelection[] selections, boolean[] mayRetainStre
public void discardBuffer(long positionUs) {
int sampleQueueCount = sampleQueues.length;
for (int i = 0; i < sampleQueueCount; i++) {
sampleQueues[i].discardTo(positionUs, false, trackGroupEnabledStates[i]);
sampleQueues[i].discardTo(positionUs, false, sampleQueuesEnabledStates[i]);
}
}

Expand Down Expand Up @@ -370,17 +374,17 @@ public void onPlaylistBlacklisted(HlsUrl url, long blacklistMs) {

// SampleStream implementation.

public boolean isReady(int trackGroupIndex) {
return loadingFinished || (!isPendingReset() && sampleQueues[trackGroupIndex].hasNextSample());
public boolean isReady(int sampleQueueIndex) {
return loadingFinished || (!isPendingReset() && sampleQueues[sampleQueueIndex].hasNextSample());
}

public void maybeThrowError() throws IOException {
loader.maybeThrowError();
chunkSource.maybeThrowError();
}

public int readData(int trackGroupIndex, FormatHolder formatHolder,
DecoderInputBuffer buffer, boolean requireFormat) {
public int readData(int sampleQueueIndex, FormatHolder formatHolder, DecoderInputBuffer buffer,
boolean requireFormat) {
if (isPendingReset()) {
return C.RESULT_NOTHING_READ;
}
Expand All @@ -399,12 +403,12 @@ public int readData(int trackGroupIndex, FormatHolder formatHolder,
downstreamTrackFormat = trackFormat;
}

return sampleQueues[trackGroupIndex].read(formatHolder, buffer, requireFormat, loadingFinished,
return sampleQueues[sampleQueueIndex].read(formatHolder, buffer, requireFormat, loadingFinished,
lastSeekPositionUs);
}

public int skipData(int trackGroupIndex, long positionUs) {
SampleQueue sampleQueue = sampleQueues[trackGroupIndex];
public int skipData(int sampleQueueIndex, long positionUs) {
SampleQueue sampleQueue = sampleQueues[sampleQueueIndex];
if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) {
return sampleQueue.advanceToEnd();
} else {
Expand All @@ -415,8 +419,9 @@ public int skipData(int trackGroupIndex, long positionUs) {

private boolean finishedReadingChunk(HlsMediaChunk chunk) {
int chunkUid = chunk.uid;
for (int i = 0; i < sampleQueues.length; i++) {
if (trackGroupEnabledStates[i] && sampleQueues[i].peekSourceId() == chunkUid) {
int sampleQueueCount = sampleQueues.length;
for (int i = 0; i < sampleQueueCount; i++) {
if (sampleQueuesEnabledStates[i] && sampleQueues[i].peekSourceId() == chunkUid) {
return false;
}
}
Expand Down Expand Up @@ -511,7 +516,7 @@ public void onLoadCanceled(Chunk loadable, long elapsedRealtimeMs, long loadDura
loadable.endTimeUs, elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded());
if (!released) {
resetSampleQueues();
if (enabledTrackCount > 0) {
if (enabledSampleQueueCount > 0) {
callback.onContinueLoadingRequested(this);
}
}
Expand Down Expand Up @@ -587,6 +592,11 @@ public SampleQueue track(int id, int type) {
sampleQueueTrackIds[trackCount] = id;
sampleQueues = Arrays.copyOf(sampleQueues, trackCount + 1);
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;
}

Expand All @@ -605,7 +615,9 @@ public void seekMap(SeekMap seekMap) {

@Override
public void onUpstreamFormatChanged(Format format) {
handler.post(maybeFinishPrepareRunnable);
if (!prepared) {
handler.post(maybeFinishPrepareRunnable);
}
}

// Called by the loading thread.
Expand Down Expand Up @@ -696,17 +708,15 @@ private void buildTracks() {

// Instantiate the necessary internal data-structures.
primaryTrackGroupIndex = C.INDEX_UNSET;
trackGroupEnabledStates = new boolean[extractorTrackCount];
trackGroupIsAudioVideoFlags = new boolean[extractorTrackCount];
trackGroupToSampleQueueIndex = new int[extractorTrackCount];
for (int i = 0; i < extractorTrackCount; i++) {
trackGroupToSampleQueueIndex[i] = i;
}

// Construct the set of exposed track groups.
TrackGroup[] trackGroups = new TrackGroup[extractorTrackCount];
for (int i = 0; i < extractorTrackCount; i++) {
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) {
Format[] formats = new Format[chunkSourceTrackCount];
for (int j = 0; j < chunkSourceTrackCount; j++) {
Expand All @@ -724,15 +734,15 @@ private void buildTracks() {
}

/**
* Enables or disables a specified track group.
* Enables or disables a specified sample queue.
*
* @param trackGroupIndex The index of the track group.
* @param enabledState True if the group is being enabled, or false if it's being disabled.
* @param sampleQueueIndex The index of the sample queue.
* @param enabledState True if the sample queue is being enabled, or false if it's being disabled.
*/
private void setTrackGroupEnabledState(int trackGroupIndex, boolean enabledState) {
Assertions.checkState(trackGroupEnabledStates[trackGroupIndex] != enabledState);
trackGroupEnabledStates[trackGroupIndex] = enabledState;
enabledTrackCount = enabledTrackCount + (enabledState ? 1 : -1);
private void setSampleQueueEnabledState(int sampleQueueIndex, boolean enabledState) {
Assertions.checkState(sampleQueuesEnabledStates[sampleQueueIndex] != enabledState);
sampleQueuesEnabledStates[sampleQueueIndex] = enabledState;
enabledSampleQueueCount = enabledSampleQueueCount + (enabledState ? 1 : -1);
}

/**
Expand Down Expand Up @@ -769,8 +779,8 @@ private boolean isPendingReset() {
* @return Whether the in-buffer seek was successful.
*/
private boolean seekInsideBufferUs(long positionUs) {
int trackCount = sampleQueues.length;
for (int i = 0; i < trackCount; i++) {
int sampleQueueCount = sampleQueues.length;
for (int i = 0; i < sampleQueueCount; i++) {
SampleQueue sampleQueue = sampleQueues[i];
sampleQueue.rewind();
boolean seekInsideQueue = sampleQueue.advanceTo(positionUs, true, false)
Expand All @@ -779,7 +789,7 @@ private boolean seekInsideBufferUs(long positionUs) {
// 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
// successful only if the seek into every queue succeeds.
if (!seekInsideQueue && (trackGroupIsAudioVideoFlags[i] || !haveAudioVideoTrackGroups)) {
if (!seekInsideQueue && (sampleQueueIsAudioVideoFlags[i] || !haveAudioVideoSampleQueues)) {
return false;
}
sampleQueue.discardToRead();
Expand Down

0 comments on commit 15543f1

Please sign in to comment.