From 7b855e45e60a2932105c1ffbf091c937cf3b7679 Mon Sep 17 00:00:00 2001 From: tonihei Date: Mon, 21 May 2018 10:34:40 -0700 Subject: [PATCH] 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 --- RELEASENOTES.md | 2 ++ .../source/hls/HlsSampleStream.java | 20 ++++++++++------- .../source/hls/HlsSampleStreamWrapper.java | 22 ++++++++++++++++--- .../hls/SampleQueueMappingException.java | 3 ++- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index fd20664692..018da272f6 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -22,6 +22,8 @@ * HLS: * Fix playback of livestreams with EXT-X-PROGRAM-DATE-TIME tags ([#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: * TTML: * Fix a styling issue when there are multiple regions displayed at the same diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStream.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStream.java index 5d4d953372..f43d119018 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStream.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStream.java @@ -19,6 +19,7 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.source.SampleStream; +import com.google.android.exoplayer2.util.Assertions; import java.io.IOException; /** @@ -36,6 +37,11 @@ import java.io.IOException; sampleQueueIndex = HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_PENDING; } + public void bindSampleQueue() { + Assertions.checkArgument(sampleQueueIndex == HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_PENDING); + sampleQueueIndex = sampleStreamWrapper.bindSampleQueueToSampleStream(trackGroupIndex); + } + public void unbindSampleQueue() { if (sampleQueueIndex != HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_PENDING) { sampleStreamWrapper.unbindSampleQueue(trackGroupIndex); @@ -48,12 +54,11 @@ import java.io.IOException; @Override public boolean isReady() { return sampleQueueIndex == HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_NO_MAPPING_NON_FATAL - || (maybeMapToSampleQueue() && sampleStreamWrapper.isReady(sampleQueueIndex)); + || (hasValidSampleQueueIndex() && sampleStreamWrapper.isReady(sampleQueueIndex)); } @Override public void maybeThrowError() throws IOException { - maybeMapToSampleQueue(); if (sampleQueueIndex == HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_NO_MAPPING_FATAL) { throw new SampleQueueMappingException( sampleStreamWrapper.getTrackGroups().get(trackGroupIndex).getFormat(0).sampleMimeType); @@ -63,22 +68,21 @@ import java.io.IOException; @Override public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean requireFormat) { - return maybeMapToSampleQueue() + return hasValidSampleQueueIndex() ? sampleStreamWrapper.readData(sampleQueueIndex, formatHolder, buffer, requireFormat) : C.RESULT_NOTHING_READ; } @Override public int skipData(long positionUs) { - return maybeMapToSampleQueue() ? sampleStreamWrapper.skipData(sampleQueueIndex, positionUs) : 0; + return hasValidSampleQueueIndex() + ? sampleStreamWrapper.skipData(sampleQueueIndex, positionUs) + : 0; } // Internal methods. - private boolean maybeMapToSampleQueue() { - if (sampleQueueIndex == HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_PENDING) { - sampleQueueIndex = sampleStreamWrapper.bindSampleQueueToSampleStream(trackGroupIndex); - } + private boolean hasValidSampleQueueIndex() { return sampleQueueIndex != HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_PENDING && sampleQueueIndex != HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_NO_MAPPING_NON_FATAL && sampleQueueIndex != HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_NO_MAPPING_FATAL; diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java index 0de4faa9c0..705320bdad 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java @@ -102,6 +102,7 @@ import java.util.Arrays; private final Runnable maybeFinishPrepareRunnable; private final Runnable onTracksEndedRunnable; private final Handler handler; + private final ArrayList hlsSampleStreams; private SampleQueue[] sampleQueues; private int[] sampleQueueTrackIds; @@ -166,6 +167,7 @@ import java.util.Arrays; sampleQueueIsAudioVideoFlags = new boolean[0]; sampleQueuesEnabledStates = new boolean[0]; mediaChunks = new ArrayList<>(); + hlsSampleStreams = new ArrayList<>(); maybeFinishPrepareRunnable = new Runnable() { @Override @@ -219,9 +221,6 @@ import java.util.Arrays; } public int bindSampleQueueToSampleStream(int trackGroupIndex) { - if (trackGroupToSampleQueueIndex == null) { - return SAMPLE_QUEUE_INDEX_PENDING; - } int sampleQueueIndex = trackGroupToSampleQueueIndex[trackGroupIndex]; if (sampleQueueIndex == 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); 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 (sampleQueuesBuilt && !seekRequired) { SampleQueue sampleQueue = sampleQueues[trackGroupToSampleQueueIndex[trackGroupIndex]]; @@ -360,6 +362,7 @@ import java.util.Arrays; } } + updateSampleStreams(streams); seenFirstTrackSelection = true; return seekRequired; } @@ -411,6 +414,7 @@ import java.util.Arrays; loader.release(this); handler.removeCallbacksAndMessages(null); released = true; + hlsSampleStreams.clear(); } @Override @@ -750,6 +754,15 @@ import java.util.Arrays; // 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) { int chunkUid = chunk.uid; int sampleQueueCount = sampleQueues.length; @@ -807,6 +820,9 @@ import java.util.Arrays; } } } + for (HlsSampleStream sampleStream : hlsSampleStreams) { + sampleStream.bindSampleQueue(); + } } /** diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/SampleQueueMappingException.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/SampleQueueMappingException.java index 2d430d2c79..9c9cb532a6 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/SampleQueueMappingException.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/SampleQueueMappingException.java @@ -15,6 +15,7 @@ */ 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.TrackGroup; import java.io.IOException; @@ -23,7 +24,7 @@ import java.io.IOException; public final class SampleQueueMappingException extends IOException { /** @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 + "."); } }