From 7d0f0b69a31596738cee74b51db9e7b5418e0f24 Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 15 Jan 2020 13:44:13 +0000 Subject: [PATCH] Optimize chunks to init their outputs before opening the DataSource The current order of operations means that the Format is only passed to the chunk's output after the DataSource has been opened. This means that establishing the network connection and the downstream renderers initializing their codecs are effectively serialized to occur one after the other. In the new order, the Format is passed to the chunk's output before the DataSource has been opened. This allows the downstream renderers to initialize their codecs in parallel with the network connection being established, and hence latency at the start of playback is reduced. PiperOrigin-RevId: 289841854 --- RELEASENOTES.md | 5 ++++ .../source/chunk/ContainerMediaChunk.java | 27 +++++++++---------- .../source/chunk/InitializationChunk.java | 20 +++++++------- .../source/chunk/SingleSampleMediaChunk.java | 11 ++++---- 4 files changed, 33 insertions(+), 30 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4edc5a5de1..05ff1b9535 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -65,6 +65,11 @@ Matroska or MP4. * Javadocs: Add favicon for easier identification in browser tabs * FMP4: Add support for encrypted AC-4 tracks. +* Reduce startup latency for DASH and SmoothStreaming adaptive playbacks. + In previous versions, codec initialization would only occur after the network + connection for requesting the first media segment had been established. Codec + initialization can now occur before this network connection being established, + reducing startup latency. ### 2.11.1 (2019-12-20) ### diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ContainerMediaChunk.java b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ContainerMediaChunk.java index ba7c0d0d5b..5003208f7b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ContainerMediaChunk.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ContainerMediaChunk.java @@ -111,22 +111,21 @@ public class ContainerMediaChunk extends BaseMediaChunk { @SuppressWarnings("NonAtomicVolatileUpdate") @Override public final void load() throws IOException, InterruptedException { - DataSpec loadDataSpec = dataSpec.subrange(nextLoadPosition); + if (nextLoadPosition == 0) { + // Configure the output and set it as the target for the extractor wrapper. + BaseMediaChunkOutput output = getOutput(); + output.setSampleOffsetUs(sampleOffsetUs); + extractorWrapper.init( + getTrackOutputProvider(output), + clippedStartTimeUs == C.TIME_UNSET ? C.TIME_UNSET : (clippedStartTimeUs - sampleOffsetUs), + clippedEndTimeUs == C.TIME_UNSET ? C.TIME_UNSET : (clippedEndTimeUs - sampleOffsetUs)); + } try { // Create and open the input. - ExtractorInput input = new DefaultExtractorInput(dataSource, - loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec)); - if (nextLoadPosition == 0) { - // Configure the output and set it as the target for the extractor wrapper. - BaseMediaChunkOutput output = getOutput(); - output.setSampleOffsetUs(sampleOffsetUs); - extractorWrapper.init( - getTrackOutputProvider(output), - clippedStartTimeUs == C.TIME_UNSET - ? C.TIME_UNSET - : (clippedStartTimeUs - sampleOffsetUs), - clippedEndTimeUs == C.TIME_UNSET ? C.TIME_UNSET : (clippedEndTimeUs - sampleOffsetUs)); - } + DataSpec loadDataSpec = dataSpec.subrange(nextLoadPosition); + ExtractorInput input = + new DefaultExtractorInput( + dataSource, loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec)); // Load and decode the sample data. try { Extractor extractor = extractorWrapper.extractor; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/InitializationChunk.java b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/InitializationChunk.java index 37c70d5498..892a4f1e32 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/InitializationChunk.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/InitializationChunk.java @@ -70,17 +70,18 @@ public final class InitializationChunk extends Chunk { @SuppressWarnings("NonAtomicVolatileUpdate") @Override public void load() throws IOException, InterruptedException { - DataSpec loadDataSpec = dataSpec.subrange(nextLoadPosition); + if (nextLoadPosition == 0) { + extractorWrapper.init( + /* trackOutputProvider= */ null, + /* startTimeUs= */ C.TIME_UNSET, + /* endTimeUs= */ C.TIME_UNSET); + } try { // Create and open the input. - ExtractorInput input = new DefaultExtractorInput(dataSource, - loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec)); - if (nextLoadPosition == 0) { - extractorWrapper.init( - /* trackOutputProvider= */ null, - /* startTimeUs= */ C.TIME_UNSET, - /* endTimeUs= */ C.TIME_UNSET); - } + DataSpec loadDataSpec = dataSpec.subrange(nextLoadPosition); + ExtractorInput input = + new DefaultExtractorInput( + dataSource, loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec)); // Load and decode the initialization data. try { Extractor extractor = extractorWrapper.extractor; @@ -96,5 +97,4 @@ public final class InitializationChunk extends Chunk { Util.closeQuietly(dataSource); } } - } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/SingleSampleMediaChunk.java b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/SingleSampleMediaChunk.java index d53caf8e10..00d841eee0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/SingleSampleMediaChunk.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/SingleSampleMediaChunk.java @@ -91,19 +91,19 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk { @SuppressWarnings("NonAtomicVolatileUpdate") @Override public void load() throws IOException, InterruptedException { - DataSpec loadDataSpec = dataSpec.subrange(nextLoadPosition); + BaseMediaChunkOutput output = getOutput(); + output.setSampleOffsetUs(0); + TrackOutput trackOutput = output.track(0, trackType); + trackOutput.format(sampleFormat); try { // Create and open the input. + DataSpec loadDataSpec = dataSpec.subrange(nextLoadPosition); long length = dataSource.open(loadDataSpec); if (length != C.LENGTH_UNSET) { length += nextLoadPosition; } ExtractorInput extractorInput = new DefaultExtractorInput(dataSource, nextLoadPosition, length); - BaseMediaChunkOutput output = getOutput(); - output.setSampleOffsetUs(0); - TrackOutput trackOutput = output.track(0, trackType); - trackOutput.format(sampleFormat); // Load the sample data. int result = 0; while (result != C.RESULT_END_OF_INPUT) { @@ -117,5 +117,4 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk { } loadCompleted = true; } - }