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; public final boolean isSampleFormatFinal;
private DefaultTrackOutput output; private DefaultTrackOutput trackOutput;
private int firstSampleIndex; 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 * Initializes the chunk for loading, setting the {@link DefaultTrackOutput} that will receive
* samples as they are loaded. * 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) { public void init(DefaultTrackOutput trackOutput) {
this.output = output; this.trackOutput = trackOutput;
this.firstSampleIndex = output.getWriteIndex(); this.firstSampleIndex = trackOutput.getWriteIndex();
} }
/** /**
@ -97,10 +97,10 @@ public abstract class BaseMediaChunk extends MediaChunk {
public abstract DrmInitData getDrmInitData(); 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() { protected final DefaultTrackOutput getTrackOutput() {
return output; return trackOutput;
} }
} }

View File

@ -30,14 +30,15 @@ import java.io.IOException;
/** /**
* An {@link Extractor} wrapper for loading chunks containing a single track. * An {@link Extractor} wrapper for loading chunks containing a single track.
* <p> * <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 { 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) * @see ExtractorOutput#seekMap(SeekMap)
@ -49,11 +50,17 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput
*/ */
void drmInitData(DrmInitData drmInitData); void drmInitData(DrmInitData drmInitData);
/**
* @see TrackOutput#format(Format)
*/
void format(Format format);
} }
private final Extractor extractor; private final Extractor extractor;
private boolean extractorInitialized; private boolean extractorInitialized;
private SingleTrackOutput output; private SingleTrackMetadataOutput metadataOutput;
private TrackOutput trackOutput;
// Accessed only on the loader thread. // Accessed only on the loader thread.
private boolean seenTrack; 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 * Initializes the extractor to output to the provided {@link SingleTrackMetadataOutput} and
* it to receive data from a new chunk. * {@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) { public void init(SingleTrackMetadataOutput metadataOutput, TrackOutput trackOutput) {
this.output = output; this.metadataOutput = metadataOutput;
this.trackOutput = trackOutput;
if (!extractorInitialized) { if (!extractorInitialized) {
extractor.init(this); extractor.init(this);
extractorInitialized = true; extractorInitialized = true;
@ -111,35 +120,36 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput
@Override @Override
public void seekMap(SeekMap seekMap) { public void seekMap(SeekMap seekMap) {
output.seekMap(seekMap); metadataOutput.seekMap(seekMap);
} }
@Override @Override
public void drmInitData(DrmInitData drmInitData) { public void drmInitData(DrmInitData drmInitData) {
output.drmInitData(drmInitData); metadataOutput.drmInitData(drmInitData);
} }
// TrackOutput implementation. // TrackOutput implementation.
@Override @Override
public void format(Format format) { 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 @Override
public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput) public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
throws IOException, InterruptedException { throws IOException, InterruptedException {
return output.sampleData(input, length, allowEndOfInput); return trackOutput.sampleData(input, length, allowEndOfInput);
} }
@Override @Override
public void sampleData(ParsableByteArray data, int length) { public void sampleData(ParsableByteArray data, int length) {
output.sampleData(data, length); trackOutput.sampleData(data, length);
} }
@Override @Override
public void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey) { 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; package com.google.android.exoplayer.chunk;
import com.google.android.exoplayer.Format; 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.drm.DrmInitData;
import com.google.android.exoplayer.extractor.DefaultExtractorInput; 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.Extractor;
import com.google.android.exoplayer.extractor.ExtractorInput; import com.google.android.exoplayer.extractor.ExtractorInput;
import com.google.android.exoplayer.extractor.SeekMap; import com.google.android.exoplayer.extractor.SeekMap;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSpec; import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.util.ParsableByteArray;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
import java.io.IOException; import java.io.IOException;
@ -32,7 +32,7 @@ import java.io.IOException;
/** /**
* A {@link BaseMediaChunk} that uses an {@link Extractor} to parse sample data. * 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 ChunkExtractorWrapper extractorWrapper;
private final long sampleOffsetUs; private final long sampleOffsetUs;
@ -88,7 +88,7 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackOu
return drmInitData; return drmInitData;
} }
// SingleTrackOutput implementation. // SingleTrackMetadataOutput implementation.
@Override @Override
public final void seekMap(SeekMap seekMap) { public final void seekMap(SeekMap seekMap) {
@ -105,23 +105,6 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackOu
this.sampleFormat = getAdjustedSampleFormat(format, sampleOffsetUs); 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. // Loadable implementation.
@Override @Override
@ -144,7 +127,9 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackOu
loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec)); loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec));
if (bytesLoaded == 0) { if (bytesLoaded == 0) {
// Set the target to ourselves. // Set the target to ourselves.
extractorWrapper.init(this); DefaultTrackOutput trackOutput = getTrackOutput();
trackOutput.setSampleOffsetUs(sampleOffsetUs);
extractorWrapper.init(this, trackOutput);
} }
// Load and parse the initialization data. // Load and parse the initialization data.
try { try {

View File

@ -16,12 +16,13 @@
package com.google.android.exoplayer.chunk; package com.google.android.exoplayer.chunk;
import com.google.android.exoplayer.Format; 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.drm.DrmInitData;
import com.google.android.exoplayer.extractor.DefaultExtractorInput; import com.google.android.exoplayer.extractor.DefaultExtractorInput;
import com.google.android.exoplayer.extractor.Extractor; import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ExtractorInput; import com.google.android.exoplayer.extractor.ExtractorInput;
import com.google.android.exoplayer.extractor.SeekMap; 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.DataSource;
import com.google.android.exoplayer.upstream.DataSpec; import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.util.ParsableByteArray; 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. * 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; private final ChunkExtractorWrapper extractorWrapper;
@ -120,7 +122,7 @@ public final class InitializationChunk extends Chunk implements SingleTrackOutpu
return seekMap; return seekMap;
} }
// SingleTrackOutput implementation. // SingleTrackMetadataOutput implementation.
@Override @Override
public void seekMap(SeekMap seekMap) { public void seekMap(SeekMap seekMap) {
@ -137,6 +139,8 @@ public final class InitializationChunk extends Chunk implements SingleTrackOutpu
this.sampleFormat = format; this.sampleFormat = format;
} }
// TrackOutput implementation.
@Override @Override
public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput) public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
throws IOException, InterruptedException { throws IOException, InterruptedException {
@ -175,7 +179,7 @@ public final class InitializationChunk extends Chunk implements SingleTrackOutpu
loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec)); loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec));
if (bytesLoaded == 0) { if (bytesLoaded == 0) {
// Set the target to ourselves. // Set the target to ourselves.
extractorWrapper.init(this); extractorWrapper.init(this, this);
} }
// Load and parse the initialization data. // Load and parse the initialization data.
try { 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.drm.DrmInitData;
import com.google.android.exoplayer.extractor.DefaultExtractorInput; import com.google.android.exoplayer.extractor.DefaultExtractorInput;
import com.google.android.exoplayer.extractor.ExtractorInput; 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.DataSource;
import com.google.android.exoplayer.upstream.DataSpec; import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
@ -95,14 +96,15 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
length += bytesLoaded; length += bytesLoaded;
} }
ExtractorInput extractorInput = new DefaultExtractorInput(dataSource, bytesLoaded, length); ExtractorInput extractorInput = new DefaultExtractorInput(dataSource, bytesLoaded, length);
TrackOutput trackOutput = getTrackOutput();
// 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) {
bytesLoaded += result; bytesLoaded += result;
result = getOutput().sampleData(extractorInput, Integer.MAX_VALUE, true); result = trackOutput.sampleData(extractorInput, Integer.MAX_VALUE, true);
} }
int sampleSize = bytesLoaded; 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 { } finally {
dataSource.close(); dataSource.close();
} }

View File

@ -31,6 +31,9 @@ public class DefaultTrackOutput implements TrackOutput {
private final RollingSampleBuffer rollingBuffer; private final RollingSampleBuffer rollingBuffer;
private final DecoderInputBuffer sampleBuffer; private final DecoderInputBuffer sampleBuffer;
// Accessed only by the loading thread.
private long sampleOffsetUs;
// Accessed only by the consuming thread. // Accessed only by the consuming thread.
private boolean needKeyframe; private boolean needKeyframe;
private long lastReadTimeUs; private long lastReadTimeUs;
@ -222,6 +225,18 @@ public class DefaultTrackOutput implements TrackOutput {
return true; 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. // TrackOutput implementation. Called by the loading thread.
@Override @Override
@ -242,6 +257,7 @@ public class DefaultTrackOutput implements TrackOutput {
@Override @Override
public void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey) { public void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey) {
timeUs += sampleOffsetUs;
largestParsedTimestampUs = Math.max(largestParsedTimestampUs, timeUs); largestParsedTimestampUs = Math.max(largestParsedTimestampUs, timeUs);
rollingBuffer.commitSample(timeUs, flags, size, offset, encryptionKey); rollingBuffer.commitSample(timeUs, flags, size, offset, encryptionKey);
} }