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
This commit is contained in:
olly 2020-01-15 13:44:13 +00:00 committed by Oliver Woodman
parent 4b234388c8
commit 7d0f0b69a3
4 changed files with 33 additions and 30 deletions

View File

@ -65,6 +65,11 @@
Matroska or MP4. Matroska or MP4.
* Javadocs: Add favicon for easier identification in browser tabs * Javadocs: Add favicon for easier identification in browser tabs
* FMP4: Add support for encrypted AC-4 tracks. * 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) ### ### 2.11.1 (2019-12-20) ###

View File

@ -111,22 +111,21 @@ public class ContainerMediaChunk extends BaseMediaChunk {
@SuppressWarnings("NonAtomicVolatileUpdate") @SuppressWarnings("NonAtomicVolatileUpdate")
@Override @Override
public final void load() throws IOException, InterruptedException { public final void load() throws IOException, InterruptedException {
DataSpec loadDataSpec = dataSpec.subrange(nextLoadPosition);
try {
// Create and open the input.
ExtractorInput input = new DefaultExtractorInput(dataSource,
loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec));
if (nextLoadPosition == 0) { if (nextLoadPosition == 0) {
// Configure the output and set it as the target for the extractor wrapper. // Configure the output and set it as the target for the extractor wrapper.
BaseMediaChunkOutput output = getOutput(); BaseMediaChunkOutput output = getOutput();
output.setSampleOffsetUs(sampleOffsetUs); output.setSampleOffsetUs(sampleOffsetUs);
extractorWrapper.init( extractorWrapper.init(
getTrackOutputProvider(output), getTrackOutputProvider(output),
clippedStartTimeUs == C.TIME_UNSET clippedStartTimeUs == C.TIME_UNSET ? C.TIME_UNSET : (clippedStartTimeUs - sampleOffsetUs),
? C.TIME_UNSET
: (clippedStartTimeUs - sampleOffsetUs),
clippedEndTimeUs == C.TIME_UNSET ? C.TIME_UNSET : (clippedEndTimeUs - sampleOffsetUs)); clippedEndTimeUs == C.TIME_UNSET ? C.TIME_UNSET : (clippedEndTimeUs - sampleOffsetUs));
} }
try {
// Create and open the input.
DataSpec loadDataSpec = dataSpec.subrange(nextLoadPosition);
ExtractorInput input =
new DefaultExtractorInput(
dataSource, loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec));
// Load and decode the sample data. // Load and decode the sample data.
try { try {
Extractor extractor = extractorWrapper.extractor; Extractor extractor = extractorWrapper.extractor;

View File

@ -70,17 +70,18 @@ public final class InitializationChunk extends Chunk {
@SuppressWarnings("NonAtomicVolatileUpdate") @SuppressWarnings("NonAtomicVolatileUpdate")
@Override @Override
public void load() throws IOException, InterruptedException { public void load() throws IOException, InterruptedException {
DataSpec loadDataSpec = dataSpec.subrange(nextLoadPosition);
try {
// Create and open the input.
ExtractorInput input = new DefaultExtractorInput(dataSource,
loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec));
if (nextLoadPosition == 0) { if (nextLoadPosition == 0) {
extractorWrapper.init( extractorWrapper.init(
/* trackOutputProvider= */ null, /* trackOutputProvider= */ null,
/* startTimeUs= */ C.TIME_UNSET, /* startTimeUs= */ C.TIME_UNSET,
/* endTimeUs= */ C.TIME_UNSET); /* endTimeUs= */ C.TIME_UNSET);
} }
try {
// Create and open the input.
DataSpec loadDataSpec = dataSpec.subrange(nextLoadPosition);
ExtractorInput input =
new DefaultExtractorInput(
dataSource, loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec));
// Load and decode the initialization data. // Load and decode the initialization data.
try { try {
Extractor extractor = extractorWrapper.extractor; Extractor extractor = extractorWrapper.extractor;
@ -96,5 +97,4 @@ public final class InitializationChunk extends Chunk {
Util.closeQuietly(dataSource); Util.closeQuietly(dataSource);
} }
} }
} }

View File

@ -91,19 +91,19 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
@SuppressWarnings("NonAtomicVolatileUpdate") @SuppressWarnings("NonAtomicVolatileUpdate")
@Override @Override
public void load() throws IOException, InterruptedException { 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 { try {
// Create and open the input. // Create and open the input.
DataSpec loadDataSpec = dataSpec.subrange(nextLoadPosition);
long length = dataSource.open(loadDataSpec); long length = dataSource.open(loadDataSpec);
if (length != C.LENGTH_UNSET) { if (length != C.LENGTH_UNSET) {
length += nextLoadPosition; length += nextLoadPosition;
} }
ExtractorInput extractorInput = ExtractorInput extractorInput =
new DefaultExtractorInput(dataSource, nextLoadPosition, length); new DefaultExtractorInput(dataSource, nextLoadPosition, length);
BaseMediaChunkOutput output = getOutput();
output.setSampleOffsetUs(0);
TrackOutput trackOutput = output.track(0, trackType);
trackOutput.format(sampleFormat);
// Load the sample data. // Load the sample data.
int result = 0; int result = 0;
while (result != C.RESULT_END_OF_INPUT) { while (result != C.RESULT_END_OF_INPUT) {
@ -117,5 +117,4 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
} }
loadCompleted = true; loadCompleted = true;
} }
} }