Remove layer of indirection when piping sample data.

For DASH + SS, sample data is currently plumbed through:

Extractor
->ChunkExtractorWrapper
->ContainerMediaChunk
->DefaultTrackOutput
->RollingSampleNBuffer

This change bypasses the ContainerMediaChunk layer. It
should be possible to completely delete DefaultTrackOutput
in the future, but such a change may well be tied to
changes in HLS format splicing + how we buffer (or don't
buffer) disabled tracks.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=119853857
This commit is contained in:
olly 2016-04-14 08:32:26 -07:00 committed by Oliver Woodman
parent 628a209b9b
commit 28e926602f
6 changed files with 68 additions and 51 deletions

View File

@ -35,7 +35,7 @@ public abstract class BaseMediaChunk extends MediaChunk {
*/
public final boolean isSampleFormatFinal;
private DefaultTrackOutput output;
private DefaultTrackOutput trackOutput;
private int firstSampleIndex;
/**
@ -61,11 +61,11 @@ public abstract class BaseMediaChunk extends MediaChunk {
* Initializes the chunk for loading, setting the {@link DefaultTrackOutput} that will receive
* samples as they are loaded.
*
* @param output The output that will receive the loaded samples.
* @param trackOutput The output that will receive the loaded samples.
*/
public void init(DefaultTrackOutput output) {
this.output = output;
this.firstSampleIndex = output.getWriteIndex();
public void init(DefaultTrackOutput trackOutput) {
this.trackOutput = trackOutput;
this.firstSampleIndex = trackOutput.getWriteIndex();
}
/**
@ -97,10 +97,10 @@ public abstract class BaseMediaChunk extends MediaChunk {
public abstract DrmInitData getDrmInitData();
/**
* Returns the output most recently passed to {@link #init(DefaultTrackOutput)}.
* Returns the track output most recently passed to {@link #init(DefaultTrackOutput)}.
*/
protected final DefaultTrackOutput getOutput() {
return output;
protected final DefaultTrackOutput getTrackOutput() {
return trackOutput;
}
}

View File

@ -30,14 +30,15 @@ import java.io.IOException;
/**
* An {@link Extractor} wrapper for loading chunks containing a single track.
* <p>
* The wrapper allows switching of the {@link SingleTrackOutput} that receives parsed data.
* The wrapper allows switching of the {@link SingleTrackMetadataOutput} and {@link TrackOutput}
* which receive parsed data.
*/
public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput {
/**
* Receives stream level data extracted by the wrapped {@link Extractor}.
* Receives metadata associated with the track as extracted by the wrapped {@link Extractor}.
*/
public interface SingleTrackOutput extends TrackOutput {
public interface SingleTrackMetadataOutput {
/**
* @see ExtractorOutput#seekMap(SeekMap)
@ -49,11 +50,17 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput
*/
void drmInitData(DrmInitData drmInitData);
/**
* @see TrackOutput#format(Format)
*/
void format(Format format);
}
private final Extractor extractor;
private boolean extractorInitialized;
private SingleTrackOutput output;
private SingleTrackMetadataOutput metadataOutput;
private TrackOutput trackOutput;
// Accessed only on the loader thread.
private boolean seenTrack;
@ -66,13 +73,15 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput
}
/**
* Initializes the extractor to output to the provided {@link SingleTrackOutput}, and configures
* it to receive data from a new chunk.
* Initializes the extractor to output to the provided {@link SingleTrackMetadataOutput} and
* {@link TrackOutput} instances, and configures it to receive data from a new chunk.
*
* @param output The {@link SingleTrackOutput} that will receive the parsed data.
* @param metadataOutput The {@link SingleTrackMetadataOutput} that will receive metadata.
* @param trackOutput The {@link TrackOutput} that will receive sample data.
*/
public void init(SingleTrackOutput output) {
this.output = output;
public void init(SingleTrackMetadataOutput metadataOutput, TrackOutput trackOutput) {
this.metadataOutput = metadataOutput;
this.trackOutput = trackOutput;
if (!extractorInitialized) {
extractor.init(this);
extractorInitialized = true;
@ -111,35 +120,36 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput
@Override
public void seekMap(SeekMap seekMap) {
output.seekMap(seekMap);
metadataOutput.seekMap(seekMap);
}
@Override
public void drmInitData(DrmInitData drmInitData) {
output.drmInitData(drmInitData);
metadataOutput.drmInitData(drmInitData);
}
// TrackOutput implementation.
@Override
public void format(Format format) {
output.format(format);
// Redirect the format to the metadata output. The track output doesn't need it.
metadataOutput.format(format);
}
@Override
public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
throws IOException, InterruptedException {
return output.sampleData(input, length, allowEndOfInput);
return trackOutput.sampleData(input, length, allowEndOfInput);
}
@Override
public void sampleData(ParsableByteArray data, int length) {
output.sampleData(data, length);
trackOutput.sampleData(data, length);
}
@Override
public void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey) {
output.sampleMetadata(timeUs, flags, size, offset, encryptionKey);
trackOutput.sampleMetadata(timeUs, flags, size, offset, encryptionKey);
}
}

View File

@ -16,15 +16,15 @@
package com.google.android.exoplayer.chunk;
import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.chunk.ChunkExtractorWrapper.SingleTrackOutput;
import com.google.android.exoplayer.chunk.ChunkExtractorWrapper.SingleTrackMetadataOutput;
import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.extractor.DefaultExtractorInput;
import com.google.android.exoplayer.extractor.DefaultTrackOutput;
import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ExtractorInput;
import com.google.android.exoplayer.extractor.SeekMap;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.util.ParsableByteArray;
import com.google.android.exoplayer.util.Util;
import java.io.IOException;
@ -32,7 +32,7 @@ import java.io.IOException;
/**
* A {@link BaseMediaChunk} that uses an {@link Extractor} to parse sample data.
*/
public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackOutput {
public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMetadataOutput {
private final ChunkExtractorWrapper extractorWrapper;
private final long sampleOffsetUs;
@ -88,7 +88,7 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackOu
return drmInitData;
}
// SingleTrackOutput implementation.
// SingleTrackMetadataOutput implementation.
@Override
public final void seekMap(SeekMap seekMap) {
@ -105,23 +105,6 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackOu
this.sampleFormat = getAdjustedSampleFormat(format, sampleOffsetUs);
}
@Override
public final int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
throws IOException, InterruptedException {
return getOutput().sampleData(input, length, allowEndOfInput);
}
@Override
public final void sampleData(ParsableByteArray data, int length) {
getOutput().sampleData(data, length);
}
@Override
public final void sampleMetadata(long timeUs, int flags, int size, int offset,
byte[] encryptionKey) {
getOutput().sampleMetadata(timeUs + sampleOffsetUs, flags, size, offset, encryptionKey);
}
// Loadable implementation.
@Override
@ -144,7 +127,9 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackOu
loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec));
if (bytesLoaded == 0) {
// Set the target to ourselves.
extractorWrapper.init(this);
DefaultTrackOutput trackOutput = getTrackOutput();
trackOutput.setSampleOffsetUs(sampleOffsetUs);
extractorWrapper.init(this, trackOutput);
}
// Load and parse the initialization data.
try {

View File

@ -16,12 +16,13 @@
package com.google.android.exoplayer.chunk;
import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.chunk.ChunkExtractorWrapper.SingleTrackOutput;
import com.google.android.exoplayer.chunk.ChunkExtractorWrapper.SingleTrackMetadataOutput;
import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.extractor.DefaultExtractorInput;
import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ExtractorInput;
import com.google.android.exoplayer.extractor.SeekMap;
import com.google.android.exoplayer.extractor.TrackOutput;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.util.ParsableByteArray;
@ -32,7 +33,8 @@ import java.io.IOException;
/**
* A {@link Chunk} that uses an {@link Extractor} to parse initialization data for single track.
*/
public final class InitializationChunk extends Chunk implements SingleTrackOutput {
public final class InitializationChunk extends Chunk implements SingleTrackMetadataOutput,
TrackOutput {
private final ChunkExtractorWrapper extractorWrapper;
@ -120,7 +122,7 @@ public final class InitializationChunk extends Chunk implements SingleTrackOutpu
return seekMap;
}
// SingleTrackOutput implementation.
// SingleTrackMetadataOutput implementation.
@Override
public void seekMap(SeekMap seekMap) {
@ -137,6 +139,8 @@ public final class InitializationChunk extends Chunk implements SingleTrackOutpu
this.sampleFormat = format;
}
// TrackOutput implementation.
@Override
public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
throws IOException, InterruptedException {
@ -175,7 +179,7 @@ public final class InitializationChunk extends Chunk implements SingleTrackOutpu
loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec));
if (bytesLoaded == 0) {
// Set the target to ourselves.
extractorWrapper.init(this);
extractorWrapper.init(this, this);
}
// Load and parse the initialization data.
try {

View File

@ -20,6 +20,7 @@ import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.extractor.DefaultExtractorInput;
import com.google.android.exoplayer.extractor.ExtractorInput;
import com.google.android.exoplayer.extractor.TrackOutput;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.util.Util;
@ -95,14 +96,15 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
length += bytesLoaded;
}
ExtractorInput extractorInput = new DefaultExtractorInput(dataSource, bytesLoaded, length);
TrackOutput trackOutput = getTrackOutput();
// Load the sample data.
int result = 0;
while (result != C.RESULT_END_OF_INPUT) {
bytesLoaded += result;
result = getOutput().sampleData(extractorInput, Integer.MAX_VALUE, true);
result = trackOutput.sampleData(extractorInput, Integer.MAX_VALUE, true);
}
int sampleSize = bytesLoaded;
getOutput().sampleMetadata(startTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleSize, 0, null);
trackOutput.sampleMetadata(startTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleSize, 0, null);
} finally {
dataSource.close();
}

View File

@ -31,6 +31,9 @@ public class DefaultTrackOutput implements TrackOutput {
private final RollingSampleBuffer rollingBuffer;
private final DecoderInputBuffer sampleBuffer;
// Accessed only by the loading thread.
private long sampleOffsetUs;
// Accessed only by the consuming thread.
private boolean needKeyframe;
private long lastReadTimeUs;
@ -222,6 +225,18 @@ public class DefaultTrackOutput implements TrackOutput {
return true;
}
// Called by the loading thread.
/**
* Sets an offset that will be added to the timestamps passed to
* {@link #sampleMetadata(long, int, int, int, byte[])}.
*
* @param sampleOffsetUs The offset in microseconds.
*/
public void setSampleOffsetUs(long sampleOffsetUs) {
this.sampleOffsetUs = sampleOffsetUs;
}
// TrackOutput implementation. Called by the loading thread.
@Override
@ -242,6 +257,7 @@ public class DefaultTrackOutput implements TrackOutput {
@Override
public void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey) {
timeUs += sampleOffsetUs;
largestParsedTimestampUs = Math.max(largestParsedTimestampUs, timeUs);
rollingBuffer.commitSample(timeUs, flags, size, offset, encryptionKey);
}