Commit 32c35617 by aquilescanta Committed by Oliver Woodman

Extract a ChunkExtractor interface

A future implementation will depend on MediaParser.

PiperOrigin-RevId: 313367998
parent d88f5f47
...@@ -18,7 +18,7 @@ package com.google.android.exoplayer2.source.chunk; ...@@ -18,7 +18,7 @@ package com.google.android.exoplayer2.source.chunk;
import com.google.android.exoplayer2.extractor.DummyTrackOutput; import com.google.android.exoplayer2.extractor.DummyTrackOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.source.SampleQueue; import com.google.android.exoplayer2.source.SampleQueue;
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper.TrackOutputProvider; import com.google.android.exoplayer2.source.chunk.ChunkExtractor.TrackOutputProvider;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
/** /**
......
...@@ -23,7 +23,9 @@ import com.google.android.exoplayer2.C; ...@@ -23,7 +23,9 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.DummyTrackOutput; import com.google.android.exoplayer2.extractor.DummyTrackOutput;
import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.upstream.DataReader; import com.google.android.exoplayer2.upstream.DataReader;
...@@ -33,34 +35,14 @@ import java.io.IOException; ...@@ -33,34 +35,14 @@ import java.io.IOException;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** /**
* An {@link Extractor} wrapper for loading chunks that contain a single primary track, and possibly * {@link ChunkExtractor} implementation that uses ExoPlayer app-bundled {@link Extractor
* additional embedded tracks. * Extractors}.
* <p>
* The wrapper allows switching of the {@link TrackOutput}s that receive parsed data.
*/ */
public final class ChunkExtractorWrapper implements ExtractorOutput { public final class BundledChunkExtractor implements ExtractorOutput, ChunkExtractor {
/** private static final PositionHolder DUMMY_POSITION_HOLDER = new PositionHolder();
* Provides {@link TrackOutput} instances to be written to by the wrapper.
*/
public interface TrackOutputProvider {
/**
* Called to get the {@link TrackOutput} for a specific track.
* <p>
* The same {@link TrackOutput} is returned if multiple calls are made with the same {@code id}.
*
* @param id A track identifier.
* @param type The type of the track. Typically one of the
* {@link com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants.
* @return The {@link TrackOutput} for the given track identifier.
*/
TrackOutput track(int id, int type);
}
public final Extractor extractor;
private final Extractor extractor;
private final int primaryTrackType; private final int primaryTrackType;
private final Format primaryTrackManifestFormat; private final Format primaryTrackManifestFormat;
private final SparseArray<BindingTrackOutput> bindingTrackOutputs; private final SparseArray<BindingTrackOutput> bindingTrackOutputs;
...@@ -72,48 +54,37 @@ public final class ChunkExtractorWrapper implements ExtractorOutput { ...@@ -72,48 +54,37 @@ public final class ChunkExtractorWrapper implements ExtractorOutput {
private Format @MonotonicNonNull [] sampleFormats; private Format @MonotonicNonNull [] sampleFormats;
/** /**
* Creates an instance.
*
* @param extractor The extractor to wrap. * @param extractor The extractor to wrap.
* @param primaryTrackType The type of the primary track. Typically one of the * @param primaryTrackType The type of the primary track. Typically one of the {@link
* {@link com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants. * com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants.
* @param primaryTrackManifestFormat A manifest defined {@link Format} whose data should be merged * @param primaryTrackManifestFormat A manifest defined {@link Format} whose data should be merged
* into any sample {@link Format} output from the {@link Extractor} for the primary track. * into any sample {@link Format} output from the {@link Extractor} for the primary track.
*/ */
public ChunkExtractorWrapper(Extractor extractor, int primaryTrackType, public BundledChunkExtractor(
Format primaryTrackManifestFormat) { Extractor extractor, int primaryTrackType, Format primaryTrackManifestFormat) {
this.extractor = extractor; this.extractor = extractor;
this.primaryTrackType = primaryTrackType; this.primaryTrackType = primaryTrackType;
this.primaryTrackManifestFormat = primaryTrackManifestFormat; this.primaryTrackManifestFormat = primaryTrackManifestFormat;
bindingTrackOutputs = new SparseArray<>(); bindingTrackOutputs = new SparseArray<>();
} }
/** // ChunkExtractor implementation.
* Returns the {@link SeekMap} most recently output by the extractor, or null if the extractor has
* not output a {@link SeekMap}. @Override
*/
@Nullable @Nullable
public SeekMap getSeekMap() { public SeekMap getSeekMap() {
return seekMap; return seekMap;
} }
/** @Override
* Returns the sample {@link Format}s for the tracks identified by the extractor, or null if the
* extractor has not finished identifying tracks.
*/
@Nullable @Nullable
public Format[] getSampleFormats() { public Format[] getSampleFormats() {
return sampleFormats; return sampleFormats;
} }
/** @Override
* Initializes the wrapper to output to {@link TrackOutput}s provided by the specified {@link
* TrackOutputProvider}, and configures the extractor to receive data from a new chunk.
*
* @param trackOutputProvider The provider of {@link TrackOutput}s that will receive sample data.
* @param startTimeUs The start position in the new chunk, or {@link C#TIME_UNSET} to output
* samples from the start of the chunk.
* @param endTimeUs The end position in the new chunk, or {@link C#TIME_UNSET} to output samples
* to the end of the chunk.
*/
public void init( public void init(
@Nullable TrackOutputProvider trackOutputProvider, long startTimeUs, long endTimeUs) { @Nullable TrackOutputProvider trackOutputProvider, long startTimeUs, long endTimeUs) {
this.trackOutputProvider = trackOutputProvider; this.trackOutputProvider = trackOutputProvider;
...@@ -132,6 +103,11 @@ public final class ChunkExtractorWrapper implements ExtractorOutput { ...@@ -132,6 +103,11 @@ public final class ChunkExtractorWrapper implements ExtractorOutput {
} }
} }
@Override
public int read(ExtractorInput input) throws IOException {
return extractor.read(input, DUMMY_POSITION_HOLDER);
}
// ExtractorOutput implementation. // ExtractorOutput implementation.
@Override @Override
......
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.source.chunk;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.extractor.TrackOutput;
import java.io.IOException;
/**
* Extracts samples and track {@link Format Formats} from chunks.
*
* <p>The {@link TrackOutputProvider} passed to {@link #init} provides the {@link TrackOutput
* TrackOutputs} that receive the extracted data.
*/
public interface ChunkExtractor {
/** Provides {@link TrackOutput} instances to be written to during extraction. */
interface TrackOutputProvider {
/**
* Called to get the {@link TrackOutput} for a specific track.
*
* <p>The same {@link TrackOutput} is returned if multiple calls are made with the same {@code
* id}.
*
* @param id A track identifier.
* @param type The type of the track. Typically one of the {@link C} {@code TRACK_TYPE_*}
* constants.
* @return The {@link TrackOutput} for the given track identifier.
*/
TrackOutput track(int id, int type);
}
/**
* Returns the {@link SeekMap} most recently output by the extractor, or null if the extractor has
* not output a {@link SeekMap}.
*/
@Nullable
SeekMap getSeekMap();
/**
* Returns the sample {@link Format}s for the tracks identified by the extractor, or null if the
* extractor has not finished identifying tracks.
*/
@Nullable
Format[] getSampleFormats();
/**
* Initializes the wrapper to output to {@link TrackOutput}s provided by the specified {@link
* TrackOutputProvider}, and configures the extractor to receive data from a new chunk.
*
* @param trackOutputProvider The provider of {@link TrackOutput}s that will receive sample data.
* @param startTimeUs The start position in the new chunk, or {@link C#TIME_UNSET} to output
* samples from the start of the chunk.
* @param endTimeUs The end position in the new chunk, or {@link C#TIME_UNSET} to output samples
* to the end of the chunk.
*/
void init(@Nullable TrackOutputProvider trackOutputProvider, long startTimeUs, long endTimeUs);
/**
* Reads from the given {@link ExtractorInput}.
*
* @param input The input to read from.
* @return One of the {@link Extractor}{@code .RESULT_*} values.
* @throws IOException If an error occurred reading from or parsing the input.
*/
int read(ExtractorInput input) throws IOException;
}
...@@ -21,8 +21,7 @@ import com.google.android.exoplayer2.Format; ...@@ -21,8 +21,7 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.DefaultExtractorInput; import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.source.chunk.ChunkExtractor.TrackOutputProvider;
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper.TrackOutputProvider;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
...@@ -34,11 +33,9 @@ import java.io.IOException; ...@@ -34,11 +33,9 @@ import java.io.IOException;
*/ */
public class ContainerMediaChunk extends BaseMediaChunk { public class ContainerMediaChunk extends BaseMediaChunk {
private static final PositionHolder DUMMY_POSITION_HOLDER = new PositionHolder();
private final int chunkCount; private final int chunkCount;
private final long sampleOffsetUs; private final long sampleOffsetUs;
private final ChunkExtractorWrapper extractorWrapper; private final ChunkExtractor chunkExtractor;
private long nextLoadPosition; private long nextLoadPosition;
private volatile boolean loadCanceled; private volatile boolean loadCanceled;
...@@ -61,7 +58,7 @@ public class ContainerMediaChunk extends BaseMediaChunk { ...@@ -61,7 +58,7 @@ public class ContainerMediaChunk extends BaseMediaChunk {
* instance. Normally equal to one, but may be larger if multiple chunks as defined by the * instance. Normally equal to one, but may be larger if multiple chunks as defined by the
* underlying media are being merged into a single load. * underlying media are being merged into a single load.
* @param sampleOffsetUs An offset to add to the sample timestamps parsed by the extractor. * @param sampleOffsetUs An offset to add to the sample timestamps parsed by the extractor.
* @param extractorWrapper A wrapped extractor to use for parsing the data. * @param chunkExtractor A wrapped extractor to use for parsing the data.
*/ */
public ContainerMediaChunk( public ContainerMediaChunk(
DataSource dataSource, DataSource dataSource,
...@@ -76,7 +73,7 @@ public class ContainerMediaChunk extends BaseMediaChunk { ...@@ -76,7 +73,7 @@ public class ContainerMediaChunk extends BaseMediaChunk {
long chunkIndex, long chunkIndex,
int chunkCount, int chunkCount,
long sampleOffsetUs, long sampleOffsetUs,
ChunkExtractorWrapper extractorWrapper) { ChunkExtractor chunkExtractor) {
super( super(
dataSource, dataSource,
dataSpec, dataSpec,
...@@ -90,7 +87,7 @@ public class ContainerMediaChunk extends BaseMediaChunk { ...@@ -90,7 +87,7 @@ public class ContainerMediaChunk extends BaseMediaChunk {
chunkIndex); chunkIndex);
this.chunkCount = chunkCount; this.chunkCount = chunkCount;
this.sampleOffsetUs = sampleOffsetUs; this.sampleOffsetUs = sampleOffsetUs;
this.extractorWrapper = extractorWrapper; this.chunkExtractor = chunkExtractor;
} }
@Override @Override
...@@ -117,7 +114,7 @@ public class ContainerMediaChunk extends BaseMediaChunk { ...@@ -117,7 +114,7 @@ public class ContainerMediaChunk extends BaseMediaChunk {
// 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( chunkExtractor.init(
getTrackOutputProvider(output), getTrackOutputProvider(output),
clippedStartTimeUs == C.TIME_UNSET ? C.TIME_UNSET : (clippedStartTimeUs - sampleOffsetUs), clippedStartTimeUs == C.TIME_UNSET ? C.TIME_UNSET : (clippedStartTimeUs - sampleOffsetUs),
clippedEndTimeUs == C.TIME_UNSET ? C.TIME_UNSET : (clippedEndTimeUs - sampleOffsetUs)); clippedEndTimeUs == C.TIME_UNSET ? C.TIME_UNSET : (clippedEndTimeUs - sampleOffsetUs));
...@@ -130,10 +127,9 @@ public class ContainerMediaChunk extends BaseMediaChunk { ...@@ -130,10 +127,9 @@ public class ContainerMediaChunk extends BaseMediaChunk {
dataSource, loadDataSpec.position, dataSource.open(loadDataSpec)); dataSource, loadDataSpec.position, dataSource.open(loadDataSpec));
// Load and decode the sample data. // Load and decode the sample data.
try { try {
Extractor extractor = extractorWrapper.extractor;
int result = Extractor.RESULT_CONTINUE; int result = Extractor.RESULT_CONTINUE;
while (result == Extractor.RESULT_CONTINUE && !loadCanceled) { while (result == Extractor.RESULT_CONTINUE && !loadCanceled) {
result = extractor.read(input, DUMMY_POSITION_HOLDER); result = chunkExtractor.read(input);
} }
Assertions.checkState(result != Extractor.RESULT_SEEK); Assertions.checkState(result != Extractor.RESULT_SEEK);
} finally { } finally {
......
...@@ -21,8 +21,7 @@ import com.google.android.exoplayer2.Format; ...@@ -21,8 +21,7 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.DefaultExtractorInput; import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.source.chunk.ChunkExtractor.TrackOutputProvider;
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper.TrackOutputProvider;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
...@@ -35,9 +34,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ...@@ -35,9 +34,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
*/ */
public final class InitializationChunk extends Chunk { public final class InitializationChunk extends Chunk {
private static final PositionHolder DUMMY_POSITION_HOLDER = new PositionHolder(); private final ChunkExtractor chunkExtractor;
private final ChunkExtractorWrapper extractorWrapper;
private @MonotonicNonNull TrackOutputProvider trackOutputProvider; private @MonotonicNonNull TrackOutputProvider trackOutputProvider;
private long nextLoadPosition; private long nextLoadPosition;
...@@ -49,7 +46,7 @@ public final class InitializationChunk extends Chunk { ...@@ -49,7 +46,7 @@ public final class InitializationChunk extends Chunk {
* @param trackFormat See {@link #trackFormat}. * @param trackFormat See {@link #trackFormat}.
* @param trackSelectionReason See {@link #trackSelectionReason}. * @param trackSelectionReason See {@link #trackSelectionReason}.
* @param trackSelectionData See {@link #trackSelectionData}. * @param trackSelectionData See {@link #trackSelectionData}.
* @param extractorWrapper A wrapped extractor to use for parsing the initialization data. * @param chunkExtractor A wrapped extractor to use for parsing the initialization data.
*/ */
public InitializationChunk( public InitializationChunk(
DataSource dataSource, DataSource dataSource,
...@@ -57,10 +54,10 @@ public final class InitializationChunk extends Chunk { ...@@ -57,10 +54,10 @@ public final class InitializationChunk extends Chunk {
Format trackFormat, Format trackFormat,
int trackSelectionReason, int trackSelectionReason,
@Nullable Object trackSelectionData, @Nullable Object trackSelectionData,
ChunkExtractorWrapper extractorWrapper) { ChunkExtractor chunkExtractor) {
super(dataSource, dataSpec, C.DATA_TYPE_MEDIA_INITIALIZATION, trackFormat, trackSelectionReason, super(dataSource, dataSpec, C.DATA_TYPE_MEDIA_INITIALIZATION, trackFormat, trackSelectionReason,
trackSelectionData, C.TIME_UNSET, C.TIME_UNSET); trackSelectionData, C.TIME_UNSET, C.TIME_UNSET);
this.extractorWrapper = extractorWrapper; this.chunkExtractor = chunkExtractor;
} }
/** /**
...@@ -85,7 +82,7 @@ public final class InitializationChunk extends Chunk { ...@@ -85,7 +82,7 @@ public final class InitializationChunk extends Chunk {
@Override @Override
public void load() throws IOException { public void load() throws IOException {
if (nextLoadPosition == 0) { if (nextLoadPosition == 0) {
extractorWrapper.init( chunkExtractor.init(
trackOutputProvider, /* startTimeUs= */ C.TIME_UNSET, /* endTimeUs= */ C.TIME_UNSET); trackOutputProvider, /* startTimeUs= */ C.TIME_UNSET, /* endTimeUs= */ C.TIME_UNSET);
} }
try { try {
...@@ -96,10 +93,9 @@ public final class InitializationChunk extends Chunk { ...@@ -96,10 +93,9 @@ public final class InitializationChunk extends Chunk {
dataSource, loadDataSpec.position, dataSource.open(loadDataSpec)); dataSource, loadDataSpec.position, dataSource.open(loadDataSpec));
// Load and decode the initialization data. // Load and decode the initialization data.
try { try {
Extractor extractor = extractorWrapper.extractor;
int result = Extractor.RESULT_CONTINUE; int result = Extractor.RESULT_CONTINUE;
while (result == Extractor.RESULT_CONTINUE && !loadCanceled) { while (result == Extractor.RESULT_CONTINUE && !loadCanceled) {
result = extractor.read(input, DUMMY_POSITION_HOLDER); result = chunkExtractor.read(input);
} }
Assertions.checkState(result != Extractor.RESULT_SEEK); Assertions.checkState(result != Extractor.RESULT_SEEK);
} finally { } finally {
......
...@@ -24,7 +24,8 @@ import com.google.android.exoplayer2.extractor.ChunkIndex; ...@@ -24,7 +24,8 @@ import com.google.android.exoplayer2.extractor.ChunkIndex;
import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor; import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor;
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper; import com.google.android.exoplayer2.source.chunk.BundledChunkExtractor;
import com.google.android.exoplayer2.source.chunk.ChunkExtractor;
import com.google.android.exoplayer2.source.chunk.InitializationChunk; import com.google.android.exoplayer2.source.chunk.InitializationChunk;
import com.google.android.exoplayer2.source.dash.manifest.DashManifest; import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser; import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser;
...@@ -113,11 +114,11 @@ public final class DashUtil { ...@@ -113,11 +114,11 @@ public final class DashUtil {
@Nullable @Nullable
public static Format loadSampleFormat( public static Format loadSampleFormat(
DataSource dataSource, int trackType, Representation representation) throws IOException { DataSource dataSource, int trackType, Representation representation) throws IOException {
ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, trackType, ChunkExtractor chunkExtractor =
representation, false); loadInitializationData(dataSource, trackType, representation, false);
return extractorWrapper == null return chunkExtractor == null
? null ? null
: Assertions.checkStateNotNull(extractorWrapper.getSampleFormats())[0]; : Assertions.checkStateNotNull(chunkExtractor.getSampleFormats())[0];
} }
/** /**
...@@ -135,33 +136,33 @@ public final class DashUtil { ...@@ -135,33 +136,33 @@ public final class DashUtil {
@Nullable @Nullable
public static ChunkIndex loadChunkIndex( public static ChunkIndex loadChunkIndex(
DataSource dataSource, int trackType, Representation representation) throws IOException { DataSource dataSource, int trackType, Representation representation) throws IOException {
ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, trackType, ChunkExtractor chunkExtractor =
representation, true); loadInitializationData(dataSource, trackType, representation, true);
return extractorWrapper == null ? null : (ChunkIndex) extractorWrapper.getSeekMap(); return chunkExtractor == null ? null : (ChunkIndex) chunkExtractor.getSeekMap();
} }
/** /**
* Loads initialization data for the {@code representation} and optionally index data then returns * Loads initialization data for the {@code representation} and optionally index data then returns
* a {@link ChunkExtractorWrapper} which contains the output. * a {@link BundledChunkExtractor} which contains the output.
* *
* @param dataSource The source from which the data should be loaded. * @param dataSource The source from which the data should be loaded.
* @param trackType The type of the representation. Typically one of the {@link * @param trackType The type of the representation. Typically one of the {@link
* com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants. * com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants.
* @param representation The representation which initialization chunk belongs to. * @param representation The representation which initialization chunk belongs to.
* @param loadIndex Whether to load index data too. * @param loadIndex Whether to load index data too.
* @return A {@link ChunkExtractorWrapper} for the {@code representation}, or null if no * @return A {@link BundledChunkExtractor} for the {@code representation}, or null if no
* initialization or (if requested) index data exists. * initialization or (if requested) index data exists.
* @throws IOException Thrown when there is an error while loading. * @throws IOException Thrown when there is an error while loading.
*/ */
@Nullable @Nullable
private static ChunkExtractorWrapper loadInitializationData( private static ChunkExtractor loadInitializationData(
DataSource dataSource, int trackType, Representation representation, boolean loadIndex) DataSource dataSource, int trackType, Representation representation, boolean loadIndex)
throws IOException { throws IOException {
RangedUri initializationUri = representation.getInitializationUri(); RangedUri initializationUri = representation.getInitializationUri();
if (initializationUri == null) { if (initializationUri == null) {
return null; return null;
} }
ChunkExtractorWrapper extractorWrapper = newWrappedExtractor(trackType, representation.format); ChunkExtractor chunkExtractor = newChunkExtractor(trackType, representation.format);
RangedUri requestUri; RangedUri requestUri;
if (loadIndex) { if (loadIndex) {
RangedUri indexUri = representation.getIndexUri(); RangedUri indexUri = representation.getIndexUri();
...@@ -172,37 +173,42 @@ public final class DashUtil { ...@@ -172,37 +173,42 @@ public final class DashUtil {
// the two requests together to request both at once. // the two requests together to request both at once.
requestUri = initializationUri.attemptMerge(indexUri, representation.baseUrl); requestUri = initializationUri.attemptMerge(indexUri, representation.baseUrl);
if (requestUri == null) { if (requestUri == null) {
loadInitializationData(dataSource, representation, extractorWrapper, initializationUri); loadInitializationData(dataSource, representation, chunkExtractor, initializationUri);
requestUri = indexUri; requestUri = indexUri;
} }
} else { } else {
requestUri = initializationUri; requestUri = initializationUri;
} }
loadInitializationData(dataSource, representation, extractorWrapper, requestUri); loadInitializationData(dataSource, representation, chunkExtractor, requestUri);
return extractorWrapper; return chunkExtractor;
} }
private static void loadInitializationData( private static void loadInitializationData(
DataSource dataSource, DataSource dataSource,
Representation representation, Representation representation,
ChunkExtractorWrapper extractorWrapper, ChunkExtractor chunkExtractor,
RangedUri requestUri) RangedUri requestUri)
throws IOException { throws IOException {
DataSpec dataSpec = DashUtil.buildDataSpec(representation, requestUri); DataSpec dataSpec = DashUtil.buildDataSpec(representation, requestUri);
InitializationChunk initializationChunk = new InitializationChunk(dataSource, dataSpec, InitializationChunk initializationChunk =
representation.format, C.SELECTION_REASON_UNKNOWN, null /* trackSelectionData */, new InitializationChunk(
extractorWrapper); dataSource,
dataSpec,
representation.format,
C.SELECTION_REASON_UNKNOWN,
null /* trackSelectionData */,
chunkExtractor);
initializationChunk.load(); initializationChunk.load();
} }
private static ChunkExtractorWrapper newWrappedExtractor(int trackType, Format format) { private static ChunkExtractor newChunkExtractor(int trackType, Format format) {
String mimeType = format.containerMimeType; String mimeType = format.containerMimeType;
boolean isWebm = boolean isWebm =
mimeType != null mimeType != null
&& (mimeType.startsWith(MimeTypes.VIDEO_WEBM) && (mimeType.startsWith(MimeTypes.VIDEO_WEBM)
|| mimeType.startsWith(MimeTypes.AUDIO_WEBM)); || mimeType.startsWith(MimeTypes.AUDIO_WEBM));
Extractor extractor = isWebm ? new MatroskaExtractor() : new FragmentedMp4Extractor(); Extractor extractor = isWebm ? new MatroskaExtractor() : new FragmentedMp4Extractor();
return new ChunkExtractorWrapper(extractor, trackType, format); return new BundledChunkExtractor(extractor, trackType, format);
} }
@Nullable @Nullable
......
...@@ -31,8 +31,9 @@ import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor; ...@@ -31,8 +31,9 @@ import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor;
import com.google.android.exoplayer2.extractor.rawcc.RawCcExtractor; import com.google.android.exoplayer2.extractor.rawcc.RawCcExtractor;
import com.google.android.exoplayer2.source.BehindLiveWindowException; import com.google.android.exoplayer2.source.BehindLiveWindowException;
import com.google.android.exoplayer2.source.chunk.BaseMediaChunkIterator; import com.google.android.exoplayer2.source.chunk.BaseMediaChunkIterator;
import com.google.android.exoplayer2.source.chunk.BundledChunkExtractor;
import com.google.android.exoplayer2.source.chunk.Chunk; import com.google.android.exoplayer2.source.chunk.Chunk;
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper; import com.google.android.exoplayer2.source.chunk.ChunkExtractor;
import com.google.android.exoplayer2.source.chunk.ChunkHolder; import com.google.android.exoplayer2.source.chunk.ChunkHolder;
import com.google.android.exoplayer2.source.chunk.ContainerMediaChunk; import com.google.android.exoplayer2.source.chunk.ContainerMediaChunk;
import com.google.android.exoplayer2.source.chunk.InitializationChunk; import com.google.android.exoplayer2.source.chunk.InitializationChunk;
...@@ -302,11 +303,11 @@ public class DefaultDashChunkSource implements DashChunkSource { ...@@ -302,11 +303,11 @@ public class DefaultDashChunkSource implements DashChunkSource {
RepresentationHolder representationHolder = RepresentationHolder representationHolder =
representationHolders[trackSelection.getSelectedIndex()]; representationHolders[trackSelection.getSelectedIndex()];
if (representationHolder.extractorWrapper != null) { if (representationHolder.chunkExtractor != null) {
Representation selectedRepresentation = representationHolder.representation; Representation selectedRepresentation = representationHolder.representation;
RangedUri pendingInitializationUri = null; RangedUri pendingInitializationUri = null;
RangedUri pendingIndexUri = null; RangedUri pendingIndexUri = null;
if (representationHolder.extractorWrapper.getSampleFormats() == null) { if (representationHolder.chunkExtractor.getSampleFormats() == null) {
pendingInitializationUri = selectedRepresentation.getInitializationUri(); pendingInitializationUri = selectedRepresentation.getInitializationUri();
} }
if (representationHolder.segmentIndex == null) { if (representationHolder.segmentIndex == null) {
...@@ -399,7 +400,7 @@ public class DefaultDashChunkSource implements DashChunkSource { ...@@ -399,7 +400,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
// from the stream. If the manifest defines an index then the stream shouldn't, but in cases // from the stream. If the manifest defines an index then the stream shouldn't, but in cases
// where it does we should ignore it. // where it does we should ignore it.
if (representationHolder.segmentIndex == null) { if (representationHolder.segmentIndex == null) {
SeekMap seekMap = representationHolder.extractorWrapper.getSeekMap(); SeekMap seekMap = representationHolder.chunkExtractor.getSeekMap();
if (seekMap != null) { if (seekMap != null) {
representationHolders[trackIndex] = representationHolders[trackIndex] =
representationHolder.copyWithNewSegmentIndex( representationHolder.copyWithNewSegmentIndex(
...@@ -500,8 +501,13 @@ public class DefaultDashChunkSource implements DashChunkSource { ...@@ -500,8 +501,13 @@ public class DefaultDashChunkSource implements DashChunkSource {
requestUri = indexUri; requestUri = indexUri;
} }
DataSpec dataSpec = DashUtil.buildDataSpec(representation, requestUri); DataSpec dataSpec = DashUtil.buildDataSpec(representation, requestUri);
return new InitializationChunk(dataSource, dataSpec, trackFormat, return new InitializationChunk(
trackSelectionReason, trackSelectionData, representationHolder.extractorWrapper); dataSource,
dataSpec,
trackFormat,
trackSelectionReason,
trackSelectionData,
representationHolder.chunkExtractor);
} }
protected Chunk newMediaChunk( protected Chunk newMediaChunk(
...@@ -518,7 +524,7 @@ public class DefaultDashChunkSource implements DashChunkSource { ...@@ -518,7 +524,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
long startTimeUs = representationHolder.getSegmentStartTimeUs(firstSegmentNum); long startTimeUs = representationHolder.getSegmentStartTimeUs(firstSegmentNum);
RangedUri segmentUri = representationHolder.getSegmentUrl(firstSegmentNum); RangedUri segmentUri = representationHolder.getSegmentUrl(firstSegmentNum);
String baseUrl = representation.baseUrl; String baseUrl = representation.baseUrl;
if (representationHolder.extractorWrapper == null) { if (representationHolder.chunkExtractor == null) {
long endTimeUs = representationHolder.getSegmentEndTimeUs(firstSegmentNum); long endTimeUs = representationHolder.getSegmentEndTimeUs(firstSegmentNum);
DataSpec dataSpec = DashUtil.buildDataSpec(representation, segmentUri); DataSpec dataSpec = DashUtil.buildDataSpec(representation, segmentUri);
return new SingleSampleMediaChunk(dataSource, dataSpec, trackFormat, trackSelectionReason, return new SingleSampleMediaChunk(dataSource, dataSpec, trackFormat, trackSelectionReason,
...@@ -556,7 +562,7 @@ public class DefaultDashChunkSource implements DashChunkSource { ...@@ -556,7 +562,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
firstSegmentNum, firstSegmentNum,
segmentCount, segmentCount,
sampleOffsetUs, sampleOffsetUs,
representationHolder.extractorWrapper); representationHolder.chunkExtractor);
} }
} }
...@@ -605,7 +611,7 @@ public class DefaultDashChunkSource implements DashChunkSource { ...@@ -605,7 +611,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
/** Holds information about a snapshot of a single {@link Representation}. */ /** Holds information about a snapshot of a single {@link Representation}. */
protected static final class RepresentationHolder { protected static final class RepresentationHolder {
/* package */ final @Nullable ChunkExtractorWrapper extractorWrapper; @Nullable /* package */ final ChunkExtractor chunkExtractor;
public final Representation representation; public final Representation representation;
@Nullable public final DashSegmentIndex segmentIndex; @Nullable public final DashSegmentIndex segmentIndex;
...@@ -623,7 +629,7 @@ public class DefaultDashChunkSource implements DashChunkSource { ...@@ -623,7 +629,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
this( this(
periodDurationUs, periodDurationUs,
representation, representation,
createExtractorWrapper( createChunkExtractor(
trackType, trackType,
representation, representation,
enableEventMessageTrack, enableEventMessageTrack,
...@@ -636,13 +642,13 @@ public class DefaultDashChunkSource implements DashChunkSource { ...@@ -636,13 +642,13 @@ public class DefaultDashChunkSource implements DashChunkSource {
private RepresentationHolder( private RepresentationHolder(
long periodDurationUs, long periodDurationUs,
Representation representation, Representation representation,
@Nullable ChunkExtractorWrapper extractorWrapper, @Nullable ChunkExtractor chunkExtractor,
long segmentNumShift, long segmentNumShift,
@Nullable DashSegmentIndex segmentIndex) { @Nullable DashSegmentIndex segmentIndex) {
this.periodDurationUs = periodDurationUs; this.periodDurationUs = periodDurationUs;
this.representation = representation; this.representation = representation;
this.segmentNumShift = segmentNumShift; this.segmentNumShift = segmentNumShift;
this.extractorWrapper = extractorWrapper; this.chunkExtractor = chunkExtractor;
this.segmentIndex = segmentIndex; this.segmentIndex = segmentIndex;
} }
...@@ -656,20 +662,20 @@ public class DefaultDashChunkSource implements DashChunkSource { ...@@ -656,20 +662,20 @@ public class DefaultDashChunkSource implements DashChunkSource {
if (oldIndex == null) { if (oldIndex == null) {
// Segment numbers cannot shift if the index isn't defined by the manifest. // Segment numbers cannot shift if the index isn't defined by the manifest.
return new RepresentationHolder( return new RepresentationHolder(
newPeriodDurationUs, newRepresentation, extractorWrapper, segmentNumShift, oldIndex); newPeriodDurationUs, newRepresentation, chunkExtractor, segmentNumShift, oldIndex);
} }
if (!oldIndex.isExplicit()) { if (!oldIndex.isExplicit()) {
// Segment numbers cannot shift if the index isn't explicit. // Segment numbers cannot shift if the index isn't explicit.
return new RepresentationHolder( return new RepresentationHolder(
newPeriodDurationUs, newRepresentation, extractorWrapper, segmentNumShift, newIndex); newPeriodDurationUs, newRepresentation, chunkExtractor, segmentNumShift, newIndex);
} }
int oldIndexSegmentCount = oldIndex.getSegmentCount(newPeriodDurationUs); int oldIndexSegmentCount = oldIndex.getSegmentCount(newPeriodDurationUs);
if (oldIndexSegmentCount == 0) { if (oldIndexSegmentCount == 0) {
// Segment numbers cannot shift if the old index was empty. // Segment numbers cannot shift if the old index was empty.
return new RepresentationHolder( return new RepresentationHolder(
newPeriodDurationUs, newRepresentation, extractorWrapper, segmentNumShift, newIndex); newPeriodDurationUs, newRepresentation, chunkExtractor, segmentNumShift, newIndex);
} }
long oldIndexFirstSegmentNum = oldIndex.getFirstSegmentNum(); long oldIndexFirstSegmentNum = oldIndex.getFirstSegmentNum();
...@@ -701,13 +707,13 @@ public class DefaultDashChunkSource implements DashChunkSource { ...@@ -701,13 +707,13 @@ public class DefaultDashChunkSource implements DashChunkSource {
- newIndexFirstSegmentNum; - newIndexFirstSegmentNum;
} }
return new RepresentationHolder( return new RepresentationHolder(
newPeriodDurationUs, newRepresentation, extractorWrapper, newSegmentNumShift, newIndex); newPeriodDurationUs, newRepresentation, chunkExtractor, newSegmentNumShift, newIndex);
} }
@CheckResult @CheckResult
/* package */ RepresentationHolder copyWithNewSegmentIndex(DashSegmentIndex segmentIndex) { /* package */ RepresentationHolder copyWithNewSegmentIndex(DashSegmentIndex segmentIndex) {
return new RepresentationHolder( return new RepresentationHolder(
periodDurationUs, representation, extractorWrapper, segmentNumShift, segmentIndex); periodDurationUs, representation, chunkExtractor, segmentNumShift, segmentIndex);
} }
public long getFirstSegmentNum() { public long getFirstSegmentNum() {
...@@ -772,7 +778,8 @@ public class DefaultDashChunkSource implements DashChunkSource { ...@@ -772,7 +778,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
|| mimeType.startsWith(MimeTypes.APPLICATION_WEBM); || mimeType.startsWith(MimeTypes.APPLICATION_WEBM);
} }
private static @Nullable ChunkExtractorWrapper createExtractorWrapper( @Nullable
private static ChunkExtractor createChunkExtractor(
int trackType, int trackType,
Representation representation, Representation representation,
boolean enableEventMessageTrack, boolean enableEventMessageTrack,
...@@ -803,7 +810,7 @@ public class DefaultDashChunkSource implements DashChunkSource { ...@@ -803,7 +810,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
closedCaptionFormats, closedCaptionFormats,
playerEmsgTrackOutput); playerEmsgTrackOutput);
} }
return new ChunkExtractorWrapper(extractor, trackType, representation.format); return new BundledChunkExtractor(extractor, trackType, representation.format);
} }
} }
} }
...@@ -25,8 +25,9 @@ import com.google.android.exoplayer2.extractor.mp4.Track; ...@@ -25,8 +25,9 @@ import com.google.android.exoplayer2.extractor.mp4.Track;
import com.google.android.exoplayer2.extractor.mp4.TrackEncryptionBox; import com.google.android.exoplayer2.extractor.mp4.TrackEncryptionBox;
import com.google.android.exoplayer2.source.BehindLiveWindowException; import com.google.android.exoplayer2.source.BehindLiveWindowException;
import com.google.android.exoplayer2.source.chunk.BaseMediaChunkIterator; import com.google.android.exoplayer2.source.chunk.BaseMediaChunkIterator;
import com.google.android.exoplayer2.source.chunk.BundledChunkExtractor;
import com.google.android.exoplayer2.source.chunk.Chunk; import com.google.android.exoplayer2.source.chunk.Chunk;
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper; import com.google.android.exoplayer2.source.chunk.ChunkExtractor;
import com.google.android.exoplayer2.source.chunk.ChunkHolder; import com.google.android.exoplayer2.source.chunk.ChunkHolder;
import com.google.android.exoplayer2.source.chunk.ContainerMediaChunk; import com.google.android.exoplayer2.source.chunk.ContainerMediaChunk;
import com.google.android.exoplayer2.source.chunk.MediaChunk; import com.google.android.exoplayer2.source.chunk.MediaChunk;
...@@ -74,7 +75,7 @@ public class DefaultSsChunkSource implements SsChunkSource { ...@@ -74,7 +75,7 @@ public class DefaultSsChunkSource implements SsChunkSource {
private final LoaderErrorThrower manifestLoaderErrorThrower; private final LoaderErrorThrower manifestLoaderErrorThrower;
private final int streamElementIndex; private final int streamElementIndex;
private final ChunkExtractorWrapper[] extractorWrappers; private final ChunkExtractor[] chunkExtractors;
private final DataSource dataSource; private final DataSource dataSource;
private TrackSelection trackSelection; private TrackSelection trackSelection;
...@@ -103,8 +104,8 @@ public class DefaultSsChunkSource implements SsChunkSource { ...@@ -103,8 +104,8 @@ public class DefaultSsChunkSource implements SsChunkSource {
this.dataSource = dataSource; this.dataSource = dataSource;
StreamElement streamElement = manifest.streamElements[streamElementIndex]; StreamElement streamElement = manifest.streamElements[streamElementIndex];
extractorWrappers = new ChunkExtractorWrapper[trackSelection.length()]; chunkExtractors = new ChunkExtractor[trackSelection.length()];
for (int i = 0; i < extractorWrappers.length; i++) { for (int i = 0; i < chunkExtractors.length; i++) {
int manifestTrackIndex = trackSelection.getIndexInTrackGroup(i); int manifestTrackIndex = trackSelection.getIndexInTrackGroup(i);
Format format = streamElement.formats[manifestTrackIndex]; Format format = streamElement.formats[manifestTrackIndex];
@Nullable @Nullable
...@@ -122,7 +123,7 @@ public class DefaultSsChunkSource implements SsChunkSource { ...@@ -122,7 +123,7 @@ public class DefaultSsChunkSource implements SsChunkSource {
| FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_TFDT_BOX, | FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_TFDT_BOX,
/* timestampAdjuster= */ null, /* timestampAdjuster= */ null,
track); track);
extractorWrappers[i] = new ChunkExtractorWrapper(extractor, streamElement.type, format); chunkExtractors[i] = new BundledChunkExtractor(extractor, streamElement.type, format);
} }
} }
...@@ -238,7 +239,7 @@ public class DefaultSsChunkSource implements SsChunkSource { ...@@ -238,7 +239,7 @@ public class DefaultSsChunkSource implements SsChunkSource {
int currentAbsoluteChunkIndex = chunkIndex + currentManifestChunkOffset; int currentAbsoluteChunkIndex = chunkIndex + currentManifestChunkOffset;
int trackSelectionIndex = trackSelection.getSelectedIndex(); int trackSelectionIndex = trackSelection.getSelectedIndex();
ChunkExtractorWrapper extractorWrapper = extractorWrappers[trackSelectionIndex]; ChunkExtractor chunkExtractor = chunkExtractors[trackSelectionIndex];
int manifestTrackIndex = trackSelection.getIndexInTrackGroup(trackSelectionIndex); int manifestTrackIndex = trackSelection.getIndexInTrackGroup(trackSelectionIndex);
Uri uri = streamElement.buildRequestUri(manifestTrackIndex, chunkIndex); Uri uri = streamElement.buildRequestUri(manifestTrackIndex, chunkIndex);
...@@ -254,7 +255,7 @@ public class DefaultSsChunkSource implements SsChunkSource { ...@@ -254,7 +255,7 @@ public class DefaultSsChunkSource implements SsChunkSource {
chunkSeekTimeUs, chunkSeekTimeUs,
trackSelection.getSelectionReason(), trackSelection.getSelectionReason(),
trackSelection.getSelectionData(), trackSelection.getSelectionData(),
extractorWrapper); chunkExtractor);
} }
@Override @Override
...@@ -282,7 +283,7 @@ public class DefaultSsChunkSource implements SsChunkSource { ...@@ -282,7 +283,7 @@ public class DefaultSsChunkSource implements SsChunkSource {
long chunkSeekTimeUs, long chunkSeekTimeUs,
int trackSelectionReason, int trackSelectionReason,
@Nullable Object trackSelectionData, @Nullable Object trackSelectionData,
ChunkExtractorWrapper extractorWrapper) { ChunkExtractor chunkExtractor) {
DataSpec dataSpec = new DataSpec(uri); DataSpec dataSpec = new DataSpec(uri);
// In SmoothStreaming each chunk contains sample timestamps relative to the start of the chunk. // In SmoothStreaming each chunk contains sample timestamps relative to the start of the chunk.
// To convert them the absolute timestamps, we need to set sampleOffsetUs to chunkStartTimeUs. // To convert them the absolute timestamps, we need to set sampleOffsetUs to chunkStartTimeUs.
...@@ -300,7 +301,7 @@ public class DefaultSsChunkSource implements SsChunkSource { ...@@ -300,7 +301,7 @@ public class DefaultSsChunkSource implements SsChunkSource {
chunkIndex, chunkIndex,
/* chunkCount= */ 1, /* chunkCount= */ 1,
sampleOffsetUs, sampleOffsetUs,
extractorWrapper); chunkExtractor);
} }
private long resolveTimeToLiveEdgeUs(long playbackPositionUs) { private long resolveTimeToLiveEdgeUs(long playbackPositionUs) {
......
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