mirror of
https://github.com/samsonjs/media.git
synced 2026-03-29 10:05:48 +00:00
Extract a ChunkExtractor interface
A future implementation will depend on MediaParser. PiperOrigin-RevId: 313367998
This commit is contained in:
parent
d88f5f47e6
commit
32c356177f
8 changed files with 187 additions and 119 deletions
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue