Commit 5bdcb5fd by olly Committed by Oliver Woodman

DASH: Output Format before InitializationChunk load completes

This optimization allows a ChunkSampleStream to output track
formats as soon as they're parsed during an InitializationChunk
load, rather than waiting until after the InitializationChunk
load is completed.

In DASH VOD, a single InitializationChunk typically loads the
moov and sidx atoms in that order. Hence for long form content
where the sidx is a non-trivial size, this may result in the
track formats being output a non-negligible period of time
sooner than was previously the case. This allows downstream
renderers to start codec initialization sooner, potentially
decreasing startup latency.

For a single test stream on a fast & stable network, this
pretty consistently reduced elapsed time until both audio and
video codecs have been initialized from ~0.5s to ~0.3 seconds
on a Galaxy S8. For 5 test runs without and with these patches,
the eventTime logged by EventLogger for the second decoder
init were:

Without (secs): 0.47 0.47 0.45 0.48 0.46
With (secs)   : 0.32 0.33 0.34 0.31 0.40

PiperOrigin-RevId: 289845089
parent 12451027
......@@ -38,11 +38,12 @@
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.
* Startup latency optimizations:
* Reduce startup latency for DASH and SmoothStreaming playbacks by allowing
codec initialization to occur before the network connection for the first
media segment has been established.
* Reduce startup latency for on-demand DASH playbacks by allowing codec
initialization to occur before the sidx box has been loaded.
### 2.11.1 (2019-12-20) ###
......
......@@ -21,7 +21,10 @@ import com.google.android.exoplayer2.source.SampleQueue;
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper.TrackOutputProvider;
import com.google.android.exoplayer2.util.Log;
/** An output for {@link BaseMediaChunk}s. */
/**
* A {@link TrackOutputProvider} that provides {@link TrackOutput TrackOutputs} based on a
* predefined mapping from track type to output.
*/
public final class BaseMediaChunkOutput implements TrackOutputProvider {
private static final String TAG = "BaseMediaChunkOutput";
......
......@@ -74,7 +74,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
private final List<BaseMediaChunk> readOnlyMediaChunks;
private final SampleQueue primarySampleQueue;
private final SampleQueue[] embeddedSampleQueues;
private final BaseMediaChunkOutput mediaChunkOutput;
private final BaseMediaChunkOutput chunkOutput;
private Format primaryDownstreamTrackFormat;
@Nullable private ReleaseCallback<T> releaseCallback;
......@@ -142,7 +142,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
trackTypes[i + 1] = embeddedTrackTypes[i];
}
mediaChunkOutput = new BaseMediaChunkOutput(trackTypes, sampleQueues);
chunkOutput = new BaseMediaChunkOutput(trackTypes, sampleQueues);
pendingResetPositionUs = positionUs;
lastSeekPositionUs = positionUs;
}
......@@ -554,8 +554,10 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
decodeOnlyUntilPositionUs = resetToMediaChunk ? 0 : pendingResetPositionUs;
pendingResetPositionUs = C.TIME_UNSET;
}
mediaChunk.init(mediaChunkOutput);
mediaChunk.init(chunkOutput);
mediaChunks.add(mediaChunk);
} else if (loadable instanceof InitializationChunk) {
((InitializationChunk) loadable).init(chunkOutput);
}
long elapsedRealtimeMs =
loader.startLoading(
......
......@@ -21,6 +21,7 @@ import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper.TrackOutputProvider;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.util.Assertions;
......@@ -144,16 +145,13 @@ public class ContainerMediaChunk extends BaseMediaChunk {
}
/**
* Returns the {@link ChunkExtractorWrapper.TrackOutputProvider} to be used by the wrapped
* extractor.
* Returns the {@link TrackOutputProvider} to be used by the wrapped extractor.
*
* @param baseMediaChunkOutput The {@link BaseMediaChunkOutput} most recently passed to {@link
* #init(BaseMediaChunkOutput)}.
* @return A {@link ChunkExtractorWrapper.TrackOutputProvider} to be used by the wrapped
* extractor.
* @return A {@link TrackOutputProvider} to be used by the wrapped extractor.
*/
protected ChunkExtractorWrapper.TrackOutputProvider getTrackOutputProvider(
BaseMediaChunkOutput baseMediaChunkOutput) {
protected TrackOutputProvider getTrackOutputProvider(BaseMediaChunkOutput baseMediaChunkOutput) {
return baseMediaChunkOutput;
}
}
......@@ -22,11 +22,13 @@ import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper.TrackOutputProvider;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/**
* A {@link Chunk} that uses an {@link Extractor} to decode initialization data for single track.
......@@ -37,6 +39,7 @@ public final class InitializationChunk extends Chunk {
private final ChunkExtractorWrapper extractorWrapper;
@MonotonicNonNull private TrackOutputProvider trackOutputProvider;
private long nextLoadPosition;
private volatile boolean loadCanceled;
......@@ -60,6 +63,17 @@ public final class InitializationChunk extends Chunk {
this.extractorWrapper = extractorWrapper;
}
/**
* Initializes the chunk for loading, setting a {@link TrackOutputProvider} for track outputs to
* which formats will be written as they are loaded.
*
* @param trackOutputProvider The {@link TrackOutputProvider} for track outputs to which formats
* will be written as they are loaded.
*/
public void init(TrackOutputProvider trackOutputProvider) {
this.trackOutputProvider = trackOutputProvider;
}
// Loadable implementation.
@Override
......@@ -72,9 +86,7 @@ public final class InitializationChunk extends Chunk {
public void load() throws IOException, InterruptedException {
if (nextLoadPosition == 0) {
extractorWrapper.init(
/* trackOutputProvider= */ null,
/* startTimeUs= */ C.TIME_UNSET,
/* endTimeUs= */ C.TIME_UNSET);
trackOutputProvider, /* startTimeUs= */ C.TIME_UNSET, /* endTimeUs= */ C.TIME_UNSET);
}
try {
// Create and open the input.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment