Commit 12451027 by olly Committed by Oliver Woodman

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
parent 771aa328
...@@ -38,6 +38,11 @@ ...@@ -38,6 +38,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) ###
......
...@@ -111,22 +111,21 @@ public class ContainerMediaChunk extends BaseMediaChunk { ...@@ -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;
......
...@@ -70,17 +70,18 @@ public final class InitializationChunk extends Chunk { ...@@ -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 { ...@@ -96,5 +97,4 @@ public final class InitializationChunk extends Chunk {
Util.closeQuietly(dataSource); Util.closeQuietly(dataSource);
} }
} }
} }
...@@ -91,19 +91,19 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk { ...@@ -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 { ...@@ -117,5 +117,4 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
} }
loadCompleted = true; loadCompleted = true;
} }
} }
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