mirror of
https://github.com/androidx/media.git
synced 2025-05-12 01:59:50 +08:00
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:
parent
628a209b9b
commit
28e926602f
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user