Enable HLS sample queues as soon as possible.

Currently, the sample queues are lazily enabled when they are first read from.
This causes problems when the player tries to discard buffer and the
HlsSampleStreamWrapper assumes the sample queue is disabled even though it's
actually enabled but hasn't been read from.

This change moves setting the sample queue index of the sample stream back into
HlsSampleStreamWrapper. It enables the sample queues at track selection if the
queues are already built, or immediately after they have been built for
chunkless preparation.

Issue:#4241

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=197415741
This commit is contained in:
tonihei 2018-05-21 10:34:40 -07:00 committed by Andrew Lewis
parent e162d35689
commit 7b855e45e6
4 changed files with 35 additions and 12 deletions

View File

@ -22,6 +22,8 @@
* HLS: * HLS:
* Fix playback of livestreams with EXT-X-PROGRAM-DATE-TIME tags * Fix playback of livestreams with EXT-X-PROGRAM-DATE-TIME tags
([#4239](https://github.com/google/ExoPlayer/issues/4239)). ([#4239](https://github.com/google/ExoPlayer/issues/4239)).
* Fix playback of clipped streams starting from non-keyframe positions
([#4241](https://github.com/google/ExoPlayer/issues/4241)).
* Caption: * Caption:
* TTML: * TTML:
* Fix a styling issue when there are multiple regions displayed at the same * Fix a styling issue when there are multiple regions displayed at the same

View File

@ -19,6 +19,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.util.Assertions;
import java.io.IOException; import java.io.IOException;
/** /**
@ -36,6 +37,11 @@ import java.io.IOException;
sampleQueueIndex = HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_PENDING; sampleQueueIndex = HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_PENDING;
} }
public void bindSampleQueue() {
Assertions.checkArgument(sampleQueueIndex == HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_PENDING);
sampleQueueIndex = sampleStreamWrapper.bindSampleQueueToSampleStream(trackGroupIndex);
}
public void unbindSampleQueue() { public void unbindSampleQueue() {
if (sampleQueueIndex != HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_PENDING) { if (sampleQueueIndex != HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_PENDING) {
sampleStreamWrapper.unbindSampleQueue(trackGroupIndex); sampleStreamWrapper.unbindSampleQueue(trackGroupIndex);
@ -48,12 +54,11 @@ import java.io.IOException;
@Override @Override
public boolean isReady() { public boolean isReady() {
return sampleQueueIndex == HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_NO_MAPPING_NON_FATAL return sampleQueueIndex == HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_NO_MAPPING_NON_FATAL
|| (maybeMapToSampleQueue() && sampleStreamWrapper.isReady(sampleQueueIndex)); || (hasValidSampleQueueIndex() && sampleStreamWrapper.isReady(sampleQueueIndex));
} }
@Override @Override
public void maybeThrowError() throws IOException { public void maybeThrowError() throws IOException {
maybeMapToSampleQueue();
if (sampleQueueIndex == HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_NO_MAPPING_FATAL) { if (sampleQueueIndex == HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_NO_MAPPING_FATAL) {
throw new SampleQueueMappingException( throw new SampleQueueMappingException(
sampleStreamWrapper.getTrackGroups().get(trackGroupIndex).getFormat(0).sampleMimeType); sampleStreamWrapper.getTrackGroups().get(trackGroupIndex).getFormat(0).sampleMimeType);
@ -63,22 +68,21 @@ 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 maybeMapToSampleQueue() return hasValidSampleQueueIndex()
? sampleStreamWrapper.readData(sampleQueueIndex, formatHolder, buffer, requireFormat) ? sampleStreamWrapper.readData(sampleQueueIndex, formatHolder, buffer, requireFormat)
: C.RESULT_NOTHING_READ; : C.RESULT_NOTHING_READ;
} }
@Override @Override
public int skipData(long positionUs) { public int skipData(long positionUs) {
return maybeMapToSampleQueue() ? sampleStreamWrapper.skipData(sampleQueueIndex, positionUs) : 0; return hasValidSampleQueueIndex()
? sampleStreamWrapper.skipData(sampleQueueIndex, positionUs)
: 0;
} }
// Internal methods. // Internal methods.
private boolean maybeMapToSampleQueue() { private boolean hasValidSampleQueueIndex() {
if (sampleQueueIndex == HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_PENDING) {
sampleQueueIndex = sampleStreamWrapper.bindSampleQueueToSampleStream(trackGroupIndex);
}
return sampleQueueIndex != HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_PENDING return sampleQueueIndex != HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_PENDING
&& sampleQueueIndex != HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_NO_MAPPING_NON_FATAL && sampleQueueIndex != HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_NO_MAPPING_NON_FATAL
&& sampleQueueIndex != HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_NO_MAPPING_FATAL; && sampleQueueIndex != HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_NO_MAPPING_FATAL;

View File

@ -102,6 +102,7 @@ import java.util.Arrays;
private final Runnable maybeFinishPrepareRunnable; private final Runnable maybeFinishPrepareRunnable;
private final Runnable onTracksEndedRunnable; private final Runnable onTracksEndedRunnable;
private final Handler handler; private final Handler handler;
private final ArrayList<HlsSampleStream> hlsSampleStreams;
private SampleQueue[] sampleQueues; private SampleQueue[] sampleQueues;
private int[] sampleQueueTrackIds; private int[] sampleQueueTrackIds;
@ -166,6 +167,7 @@ import java.util.Arrays;
sampleQueueIsAudioVideoFlags = new boolean[0]; sampleQueueIsAudioVideoFlags = new boolean[0];
sampleQueuesEnabledStates = new boolean[0]; sampleQueuesEnabledStates = new boolean[0];
mediaChunks = new ArrayList<>(); mediaChunks = new ArrayList<>();
hlsSampleStreams = new ArrayList<>();
maybeFinishPrepareRunnable = maybeFinishPrepareRunnable =
new Runnable() { new Runnable() {
@Override @Override
@ -219,9 +221,6 @@ import java.util.Arrays;
} }
public int bindSampleQueueToSampleStream(int trackGroupIndex) { public int bindSampleQueueToSampleStream(int trackGroupIndex) {
if (trackGroupToSampleQueueIndex == null) {
return SAMPLE_QUEUE_INDEX_PENDING;
}
int sampleQueueIndex = trackGroupToSampleQueueIndex[trackGroupIndex]; int sampleQueueIndex = trackGroupToSampleQueueIndex[trackGroupIndex];
if (sampleQueueIndex == C.INDEX_UNSET) { if (sampleQueueIndex == C.INDEX_UNSET) {
return optionalTrackGroups.indexOf(trackGroups.get(trackGroupIndex)) == C.INDEX_UNSET return optionalTrackGroups.indexOf(trackGroups.get(trackGroupIndex)) == C.INDEX_UNSET
@ -295,6 +294,9 @@ import java.util.Arrays;
} }
streams[i] = new HlsSampleStream(this, trackGroupIndex); streams[i] = new HlsSampleStream(this, trackGroupIndex);
streamResetFlags[i] = true; streamResetFlags[i] = true;
if (trackGroupToSampleQueueIndex != null) {
((HlsSampleStream) streams[i]).bindSampleQueue();
}
// 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 (sampleQueuesBuilt && !seekRequired) { if (sampleQueuesBuilt && !seekRequired) {
SampleQueue sampleQueue = sampleQueues[trackGroupToSampleQueueIndex[trackGroupIndex]]; SampleQueue sampleQueue = sampleQueues[trackGroupToSampleQueueIndex[trackGroupIndex]];
@ -360,6 +362,7 @@ import java.util.Arrays;
} }
} }
updateSampleStreams(streams);
seenFirstTrackSelection = true; seenFirstTrackSelection = true;
return seekRequired; return seekRequired;
} }
@ -411,6 +414,7 @@ import java.util.Arrays;
loader.release(this); loader.release(this);
handler.removeCallbacksAndMessages(null); handler.removeCallbacksAndMessages(null);
released = true; released = true;
hlsSampleStreams.clear();
} }
@Override @Override
@ -750,6 +754,15 @@ import java.util.Arrays;
// Internal methods. // Internal methods.
private void updateSampleStreams(SampleStream[] streams) {
hlsSampleStreams.clear();
for (SampleStream stream : streams) {
if (stream != null) {
hlsSampleStreams.add((HlsSampleStream) stream);
}
}
}
private boolean finishedReadingChunk(HlsMediaChunk chunk) { private boolean finishedReadingChunk(HlsMediaChunk chunk) {
int chunkUid = chunk.uid; int chunkUid = chunk.uid;
int sampleQueueCount = sampleQueues.length; int sampleQueueCount = sampleQueues.length;
@ -807,6 +820,9 @@ import java.util.Arrays;
} }
} }
} }
for (HlsSampleStream sampleStream : hlsSampleStreams) {
sampleStream.bindSampleQueue();
}
} }
/** /**

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.source.hls; package com.google.android.exoplayer2.source.hls;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.source.SampleQueue; import com.google.android.exoplayer2.source.SampleQueue;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import java.io.IOException; import java.io.IOException;
@ -23,7 +24,7 @@ import java.io.IOException;
public final class SampleQueueMappingException extends IOException { public final class SampleQueueMappingException extends IOException {
/** @param mimeType The mime type of the track group whose mapping failed. */ /** @param mimeType The mime type of the track group whose mapping failed. */
public SampleQueueMappingException(String mimeType) { public SampleQueueMappingException(@Nullable String mimeType) {
super("Unable to bind a sample queue to TrackGroup with mime type " + mimeType + "."); super("Unable to bind a sample queue to TrackGroup with mime type " + mimeType + ".");
} }
} }