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
This commit is contained in:
olly 2020-01-15 14:09:06 +00:00 committed by Oliver Woodman
parent 7d0f0b69a3
commit 4a5d788fa6
5 changed files with 34 additions and 18 deletions

View File

@ -65,11 +65,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) ###

View File

@ -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";

View File

@ -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(

View File

@ -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;
}
}

View File

@ -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.