Extract a ChunkExtractor interface

A future implementation will depend on MediaParser.

PiperOrigin-RevId: 313367998
This commit is contained in:
aquilescanta 2020-05-27 12:57:11 +01:00 committed by Oliver Woodman
parent d88f5f47e6
commit 32c356177f
8 changed files with 187 additions and 119 deletions

View file

@ -18,7 +18,7 @@ package com.google.android.exoplayer2.source.chunk;
import com.google.android.exoplayer2.extractor.DummyTrackOutput;
import com.google.android.exoplayer2.extractor.TrackOutput;
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;
/**

View file

@ -23,7 +23,9 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.DummyTrackOutput;
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.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.upstream.DataReader;
@ -33,34 +35,14 @@ import java.io.IOException;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/**
* An {@link Extractor} wrapper for loading chunks that contain a single primary track, and possibly
* additional embedded tracks.
* <p>
* The wrapper allows switching of the {@link TrackOutput}s that receive parsed data.
* {@link ChunkExtractor} implementation that uses ExoPlayer app-bundled {@link Extractor
* Extractors}.
*/
public final class ChunkExtractorWrapper implements ExtractorOutput {
public final class BundledChunkExtractor implements ExtractorOutput, ChunkExtractor {
/**
* 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 static final PositionHolder DUMMY_POSITION_HOLDER = new PositionHolder();
private final Extractor extractor;
private final int primaryTrackType;
private final Format primaryTrackManifestFormat;
private final SparseArray<BindingTrackOutput> bindingTrackOutputs;
@ -72,48 +54,37 @@ public final class ChunkExtractorWrapper implements ExtractorOutput {
private Format @MonotonicNonNull [] sampleFormats;
/**
* Creates an instance.
*
* @param extractor The extractor to wrap.
* @param primaryTrackType The type of the primary track. Typically one of the
* {@link com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants.
* @param primaryTrackType The type of the primary track. Typically one of the {@link
* com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants.
* @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.
*/
public ChunkExtractorWrapper(Extractor extractor, int primaryTrackType,
Format primaryTrackManifestFormat) {
public BundledChunkExtractor(
Extractor extractor, int primaryTrackType, Format primaryTrackManifestFormat) {
this.extractor = extractor;
this.primaryTrackType = primaryTrackType;
this.primaryTrackManifestFormat = primaryTrackManifestFormat;
bindingTrackOutputs = new SparseArray<>();
}
/**
* Returns the {@link SeekMap} most recently output by the extractor, or null if the extractor has
* not output a {@link SeekMap}.
*/
// ChunkExtractor implementation.
@Override
@Nullable
public SeekMap getSeekMap() {
return seekMap;
}
/**
* Returns the sample {@link Format}s for the tracks identified by the extractor, or null if the
* extractor has not finished identifying tracks.
*/
@Override
@Nullable
public Format[] getSampleFormats() {
return sampleFormats;
}
/**
* 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.
*/
@Override
public void init(
@Nullable TrackOutputProvider trackOutputProvider, long startTimeUs, long endTimeUs) {
this.trackOutputProvider = trackOutputProvider;
@ -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.
@Override

View file

@ -0,0 +1,86 @@
/*
* 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;
}

View file

@ -21,8 +21,7 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper.TrackOutputProvider;
import com.google.android.exoplayer2.source.chunk.ChunkExtractor.TrackOutputProvider;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.util.Assertions;
@ -34,11 +33,9 @@ import java.io.IOException;
*/
public class ContainerMediaChunk extends BaseMediaChunk {
private static final PositionHolder DUMMY_POSITION_HOLDER = new PositionHolder();
private final int chunkCount;
private final long sampleOffsetUs;
private final ChunkExtractorWrapper extractorWrapper;
private final ChunkExtractor chunkExtractor;
private long nextLoadPosition;
private volatile boolean loadCanceled;
@ -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
* underlying media are being merged into a single load.
* @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(
DataSource dataSource,
@ -76,7 +73,7 @@ public class ContainerMediaChunk extends BaseMediaChunk {
long chunkIndex,
int chunkCount,
long sampleOffsetUs,
ChunkExtractorWrapper extractorWrapper) {
ChunkExtractor chunkExtractor) {
super(
dataSource,
dataSpec,
@ -90,7 +87,7 @@ public class ContainerMediaChunk extends BaseMediaChunk {
chunkIndex);
this.chunkCount = chunkCount;
this.sampleOffsetUs = sampleOffsetUs;
this.extractorWrapper = extractorWrapper;
this.chunkExtractor = chunkExtractor;
}
@Override
@ -117,7 +114,7 @@ public class ContainerMediaChunk extends BaseMediaChunk {
// Configure the output and set it as the target for the extractor wrapper.
BaseMediaChunkOutput output = getOutput();
output.setSampleOffsetUs(sampleOffsetUs);
extractorWrapper.init(
chunkExtractor.init(
getTrackOutputProvider(output),
clippedStartTimeUs == C.TIME_UNSET ? C.TIME_UNSET : (clippedStartTimeUs - sampleOffsetUs),
clippedEndTimeUs == C.TIME_UNSET ? C.TIME_UNSET : (clippedEndTimeUs - sampleOffsetUs));
@ -130,10 +127,9 @@ public class ContainerMediaChunk extends BaseMediaChunk {
dataSource, loadDataSpec.position, dataSource.open(loadDataSpec));
// Load and decode the sample data.
try {
Extractor extractor = extractorWrapper.extractor;
int result = Extractor.RESULT_CONTINUE;
while (result == Extractor.RESULT_CONTINUE && !loadCanceled) {
result = extractor.read(input, DUMMY_POSITION_HOLDER);
result = chunkExtractor.read(input);
}
Assertions.checkState(result != Extractor.RESULT_SEEK);
} finally {

View file

@ -21,8 +21,7 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper.TrackOutputProvider;
import com.google.android.exoplayer2.source.chunk.ChunkExtractor.TrackOutputProvider;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.util.Assertions;
@ -35,9 +34,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
*/
public final class InitializationChunk extends Chunk {
private static final PositionHolder DUMMY_POSITION_HOLDER = new PositionHolder();
private final ChunkExtractorWrapper extractorWrapper;
private final ChunkExtractor chunkExtractor;
private @MonotonicNonNull TrackOutputProvider trackOutputProvider;
private long nextLoadPosition;
@ -49,7 +46,7 @@ public final class InitializationChunk extends Chunk {
* @param trackFormat See {@link #trackFormat}.
* @param trackSelectionReason See {@link #trackSelectionReason}.
* @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(
DataSource dataSource,
@ -57,10 +54,10 @@ public final class InitializationChunk extends Chunk {
Format trackFormat,
int trackSelectionReason,
@Nullable Object trackSelectionData,
ChunkExtractorWrapper extractorWrapper) {
ChunkExtractor chunkExtractor) {
super(dataSource, dataSpec, C.DATA_TYPE_MEDIA_INITIALIZATION, trackFormat, trackSelectionReason,
trackSelectionData, C.TIME_UNSET, C.TIME_UNSET);
this.extractorWrapper = extractorWrapper;
this.chunkExtractor = chunkExtractor;
}
/**
@ -85,7 +82,7 @@ public final class InitializationChunk extends Chunk {
@Override
public void load() throws IOException {
if (nextLoadPosition == 0) {
extractorWrapper.init(
chunkExtractor.init(
trackOutputProvider, /* startTimeUs= */ C.TIME_UNSET, /* endTimeUs= */ C.TIME_UNSET);
}
try {
@ -96,10 +93,9 @@ public final class InitializationChunk extends Chunk {
dataSource, loadDataSpec.position, dataSource.open(loadDataSpec));
// Load and decode the initialization data.
try {
Extractor extractor = extractorWrapper.extractor;
int result = Extractor.RESULT_CONTINUE;
while (result == Extractor.RESULT_CONTINUE && !loadCanceled) {
result = extractor.read(input, DUMMY_POSITION_HOLDER);
result = chunkExtractor.read(input);
}
Assertions.checkState(result != Extractor.RESULT_SEEK);
} finally {

View file

@ -24,7 +24,8 @@ import com.google.android.exoplayer2.extractor.ChunkIndex;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
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.dash.manifest.DashManifest;
import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser;
@ -113,11 +114,11 @@ public final class DashUtil {
@Nullable
public static Format loadSampleFormat(
DataSource dataSource, int trackType, Representation representation) throws IOException {
ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, trackType,
representation, false);
return extractorWrapper == null
ChunkExtractor chunkExtractor =
loadInitializationData(dataSource, trackType, representation, false);
return chunkExtractor == null
? null
: Assertions.checkStateNotNull(extractorWrapper.getSampleFormats())[0];
: Assertions.checkStateNotNull(chunkExtractor.getSampleFormats())[0];
}
/**
@ -135,33 +136,33 @@ public final class DashUtil {
@Nullable
public static ChunkIndex loadChunkIndex(
DataSource dataSource, int trackType, Representation representation) throws IOException {
ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, trackType,
representation, true);
return extractorWrapper == null ? null : (ChunkIndex) extractorWrapper.getSeekMap();
ChunkExtractor chunkExtractor =
loadInitializationData(dataSource, trackType, representation, true);
return chunkExtractor == null ? null : (ChunkIndex) chunkExtractor.getSeekMap();
}
/**
* 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 trackType The type of the representation. Typically one of the {@link
* com.google.android.exoplayer2.C} {@code TRACK_TYPE_*} constants.
* @param representation The representation which initialization chunk belongs to.
* @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.
* @throws IOException Thrown when there is an error while loading.
*/
@Nullable
private static ChunkExtractorWrapper loadInitializationData(
private static ChunkExtractor loadInitializationData(
DataSource dataSource, int trackType, Representation representation, boolean loadIndex)
throws IOException {
RangedUri initializationUri = representation.getInitializationUri();
if (initializationUri == null) {
return null;
}
ChunkExtractorWrapper extractorWrapper = newWrappedExtractor(trackType, representation.format);
ChunkExtractor chunkExtractor = newChunkExtractor(trackType, representation.format);
RangedUri requestUri;
if (loadIndex) {
RangedUri indexUri = representation.getIndexUri();
@ -172,37 +173,42 @@ public final class DashUtil {
// the two requests together to request both at once.
requestUri = initializationUri.attemptMerge(indexUri, representation.baseUrl);
if (requestUri == null) {
loadInitializationData(dataSource, representation, extractorWrapper, initializationUri);
loadInitializationData(dataSource, representation, chunkExtractor, initializationUri);
requestUri = indexUri;
}
} else {
requestUri = initializationUri;
}
loadInitializationData(dataSource, representation, extractorWrapper, requestUri);
return extractorWrapper;
loadInitializationData(dataSource, representation, chunkExtractor, requestUri);
return chunkExtractor;
}
private static void loadInitializationData(
DataSource dataSource,
Representation representation,
ChunkExtractorWrapper extractorWrapper,
ChunkExtractor chunkExtractor,
RangedUri requestUri)
throws IOException {
DataSpec dataSpec = DashUtil.buildDataSpec(representation, requestUri);
InitializationChunk initializationChunk = new InitializationChunk(dataSource, dataSpec,
representation.format, C.SELECTION_REASON_UNKNOWN, null /* trackSelectionData */,
extractorWrapper);
InitializationChunk initializationChunk =
new InitializationChunk(
dataSource,
dataSpec,
representation.format,
C.SELECTION_REASON_UNKNOWN,
null /* trackSelectionData */,
chunkExtractor);
initializationChunk.load();
}
private static ChunkExtractorWrapper newWrappedExtractor(int trackType, Format format) {
private static ChunkExtractor newChunkExtractor(int trackType, Format format) {
String mimeType = format.containerMimeType;
boolean isWebm =
mimeType != null
&& (mimeType.startsWith(MimeTypes.VIDEO_WEBM)
|| mimeType.startsWith(MimeTypes.AUDIO_WEBM));
Extractor extractor = isWebm ? new MatroskaExtractor() : new FragmentedMp4Extractor();
return new ChunkExtractorWrapper(extractor, trackType, format);
return new BundledChunkExtractor(extractor, trackType, format);
}
@Nullable

View file

@ -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.source.BehindLiveWindowException;
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.ChunkExtractorWrapper;
import com.google.android.exoplayer2.source.chunk.ChunkExtractor;
import com.google.android.exoplayer2.source.chunk.ChunkHolder;
import com.google.android.exoplayer2.source.chunk.ContainerMediaChunk;
import com.google.android.exoplayer2.source.chunk.InitializationChunk;
@ -302,11 +303,11 @@ public class DefaultDashChunkSource implements DashChunkSource {
RepresentationHolder representationHolder =
representationHolders[trackSelection.getSelectedIndex()];
if (representationHolder.extractorWrapper != null) {
if (representationHolder.chunkExtractor != null) {
Representation selectedRepresentation = representationHolder.representation;
RangedUri pendingInitializationUri = null;
RangedUri pendingIndexUri = null;
if (representationHolder.extractorWrapper.getSampleFormats() == null) {
if (representationHolder.chunkExtractor.getSampleFormats() == null) {
pendingInitializationUri = selectedRepresentation.getInitializationUri();
}
if (representationHolder.segmentIndex == null) {
@ -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
// where it does we should ignore it.
if (representationHolder.segmentIndex == null) {
SeekMap seekMap = representationHolder.extractorWrapper.getSeekMap();
SeekMap seekMap = representationHolder.chunkExtractor.getSeekMap();
if (seekMap != null) {
representationHolders[trackIndex] =
representationHolder.copyWithNewSegmentIndex(
@ -500,8 +501,13 @@ public class DefaultDashChunkSource implements DashChunkSource {
requestUri = indexUri;
}
DataSpec dataSpec = DashUtil.buildDataSpec(representation, requestUri);
return new InitializationChunk(dataSource, dataSpec, trackFormat,
trackSelectionReason, trackSelectionData, representationHolder.extractorWrapper);
return new InitializationChunk(
dataSource,
dataSpec,
trackFormat,
trackSelectionReason,
trackSelectionData,
representationHolder.chunkExtractor);
}
protected Chunk newMediaChunk(
@ -518,7 +524,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
long startTimeUs = representationHolder.getSegmentStartTimeUs(firstSegmentNum);
RangedUri segmentUri = representationHolder.getSegmentUrl(firstSegmentNum);
String baseUrl = representation.baseUrl;
if (representationHolder.extractorWrapper == null) {
if (representationHolder.chunkExtractor == null) {
long endTimeUs = representationHolder.getSegmentEndTimeUs(firstSegmentNum);
DataSpec dataSpec = DashUtil.buildDataSpec(representation, segmentUri);
return new SingleSampleMediaChunk(dataSource, dataSpec, trackFormat, trackSelectionReason,
@ -556,7 +562,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
firstSegmentNum,
segmentCount,
sampleOffsetUs,
representationHolder.extractorWrapper);
representationHolder.chunkExtractor);
}
}
@ -605,7 +611,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
/** Holds information about a snapshot of a single {@link Representation}. */
protected static final class RepresentationHolder {
/* package */ final @Nullable ChunkExtractorWrapper extractorWrapper;
@Nullable /* package */ final ChunkExtractor chunkExtractor;
public final Representation representation;
@Nullable public final DashSegmentIndex segmentIndex;
@ -623,7 +629,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
this(
periodDurationUs,
representation,
createExtractorWrapper(
createChunkExtractor(
trackType,
representation,
enableEventMessageTrack,
@ -636,13 +642,13 @@ public class DefaultDashChunkSource implements DashChunkSource {
private RepresentationHolder(
long periodDurationUs,
Representation representation,
@Nullable ChunkExtractorWrapper extractorWrapper,
@Nullable ChunkExtractor chunkExtractor,
long segmentNumShift,
@Nullable DashSegmentIndex segmentIndex) {
this.periodDurationUs = periodDurationUs;
this.representation = representation;
this.segmentNumShift = segmentNumShift;
this.extractorWrapper = extractorWrapper;
this.chunkExtractor = chunkExtractor;
this.segmentIndex = segmentIndex;
}
@ -656,20 +662,20 @@ public class DefaultDashChunkSource implements DashChunkSource {
if (oldIndex == null) {
// Segment numbers cannot shift if the index isn't defined by the manifest.
return new RepresentationHolder(
newPeriodDurationUs, newRepresentation, extractorWrapper, segmentNumShift, oldIndex);
newPeriodDurationUs, newRepresentation, chunkExtractor, segmentNumShift, oldIndex);
}
if (!oldIndex.isExplicit()) {
// Segment numbers cannot shift if the index isn't explicit.
return new RepresentationHolder(
newPeriodDurationUs, newRepresentation, extractorWrapper, segmentNumShift, newIndex);
newPeriodDurationUs, newRepresentation, chunkExtractor, segmentNumShift, newIndex);
}
int oldIndexSegmentCount = oldIndex.getSegmentCount(newPeriodDurationUs);
if (oldIndexSegmentCount == 0) {
// Segment numbers cannot shift if the old index was empty.
return new RepresentationHolder(
newPeriodDurationUs, newRepresentation, extractorWrapper, segmentNumShift, newIndex);
newPeriodDurationUs, newRepresentation, chunkExtractor, segmentNumShift, newIndex);
}
long oldIndexFirstSegmentNum = oldIndex.getFirstSegmentNum();
@ -701,13 +707,13 @@ public class DefaultDashChunkSource implements DashChunkSource {
- newIndexFirstSegmentNum;
}
return new RepresentationHolder(
newPeriodDurationUs, newRepresentation, extractorWrapper, newSegmentNumShift, newIndex);
newPeriodDurationUs, newRepresentation, chunkExtractor, newSegmentNumShift, newIndex);
}
@CheckResult
/* package */ RepresentationHolder copyWithNewSegmentIndex(DashSegmentIndex segmentIndex) {
return new RepresentationHolder(
periodDurationUs, representation, extractorWrapper, segmentNumShift, segmentIndex);
periodDurationUs, representation, chunkExtractor, segmentNumShift, segmentIndex);
}
public long getFirstSegmentNum() {
@ -772,7 +778,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
|| mimeType.startsWith(MimeTypes.APPLICATION_WEBM);
}
private static @Nullable ChunkExtractorWrapper createExtractorWrapper(
@Nullable
private static ChunkExtractor createChunkExtractor(
int trackType,
Representation representation,
boolean enableEventMessageTrack,
@ -803,7 +810,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
closedCaptionFormats,
playerEmsgTrackOutput);
}
return new ChunkExtractorWrapper(extractor, trackType, representation.format);
return new BundledChunkExtractor(extractor, trackType, representation.format);
}
}
}

View file

@ -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.source.BehindLiveWindowException;
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.ChunkExtractorWrapper;
import com.google.android.exoplayer2.source.chunk.ChunkExtractor;
import com.google.android.exoplayer2.source.chunk.ChunkHolder;
import com.google.android.exoplayer2.source.chunk.ContainerMediaChunk;
import com.google.android.exoplayer2.source.chunk.MediaChunk;
@ -74,7 +75,7 @@ public class DefaultSsChunkSource implements SsChunkSource {
private final LoaderErrorThrower manifestLoaderErrorThrower;
private final int streamElementIndex;
private final ChunkExtractorWrapper[] extractorWrappers;
private final ChunkExtractor[] chunkExtractors;
private final DataSource dataSource;
private TrackSelection trackSelection;
@ -103,8 +104,8 @@ public class DefaultSsChunkSource implements SsChunkSource {
this.dataSource = dataSource;
StreamElement streamElement = manifest.streamElements[streamElementIndex];
extractorWrappers = new ChunkExtractorWrapper[trackSelection.length()];
for (int i = 0; i < extractorWrappers.length; i++) {
chunkExtractors = new ChunkExtractor[trackSelection.length()];
for (int i = 0; i < chunkExtractors.length; i++) {
int manifestTrackIndex = trackSelection.getIndexInTrackGroup(i);
Format format = streamElement.formats[manifestTrackIndex];
@Nullable
@ -122,7 +123,7 @@ public class DefaultSsChunkSource implements SsChunkSource {
| FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_TFDT_BOX,
/* timestampAdjuster= */ null,
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 {
int currentAbsoluteChunkIndex = chunkIndex + currentManifestChunkOffset;
int trackSelectionIndex = trackSelection.getSelectedIndex();
ChunkExtractorWrapper extractorWrapper = extractorWrappers[trackSelectionIndex];
ChunkExtractor chunkExtractor = chunkExtractors[trackSelectionIndex];
int manifestTrackIndex = trackSelection.getIndexInTrackGroup(trackSelectionIndex);
Uri uri = streamElement.buildRequestUri(manifestTrackIndex, chunkIndex);
@ -254,7 +255,7 @@ public class DefaultSsChunkSource implements SsChunkSource {
chunkSeekTimeUs,
trackSelection.getSelectionReason(),
trackSelection.getSelectionData(),
extractorWrapper);
chunkExtractor);
}
@Override
@ -282,7 +283,7 @@ public class DefaultSsChunkSource implements SsChunkSource {
long chunkSeekTimeUs,
int trackSelectionReason,
@Nullable Object trackSelectionData,
ChunkExtractorWrapper extractorWrapper) {
ChunkExtractor chunkExtractor) {
DataSpec dataSpec = new DataSpec(uri);
// 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.
@ -300,7 +301,7 @@ public class DefaultSsChunkSource implements SsChunkSource {
chunkIndex,
/* chunkCount= */ 1,
sampleOffsetUs,
extractorWrapper);
chunkExtractor);
}
private long resolveTimeToLiveEdgeUs(long playbackPositionUs) {