mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +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.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;
|
||||||
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;
|
||||||
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 {
|
||||||
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 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(ExtractorInput input) throws IOException {
|
||||||
|
return extractor.read(input, DUMMY_POSITION_HOLDER);
|
||||||
|
}
|
||||||
|
|
||||||
// ExtractorOutput implementation.
|
// ExtractorOutput implementation.
|
||||||
|
|
||||||
@Override
|
@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.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;
|
||||||
*/
|
*/
|
||||||
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 {
|
||||||
* 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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
// 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 {
|
||||||
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;
|
||||||
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;
|
||||||
*/
|
*/
|
||||||
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 {
|
||||||
* @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 {
|
||||||
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 {
|
||||||
@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 {
|
||||||
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;
|
||||||
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 {
|
||||||
@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 {
|
||||||
@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 {
|
||||||
// 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;
|
||||||
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 {
|
||||||
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 {
|
||||||
// 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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
firstSegmentNum,
|
firstSegmentNum,
|
||||||
segmentCount,
|
segmentCount,
|
||||||
sampleOffsetUs,
|
sampleOffsetUs,
|
||||||
representationHolder.extractorWrapper);
|
representationHolder.chunkExtractor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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 {
|
||||||
this(
|
this(
|
||||||
periodDurationUs,
|
periodDurationUs,
|
||||||
representation,
|
representation,
|
||||||
createExtractorWrapper(
|
createChunkExtractor(
|
||||||
trackType,
|
trackType,
|
||||||
representation,
|
representation,
|
||||||
enableEventMessageTrack,
|
enableEventMessageTrack,
|
||||||
|
|
@ -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 {
|
||||||
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 {
|
||||||
- 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 {
|
||||||
|| 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 {
|
||||||
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;
|
||||||
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 {
|
||||||
|
|
||||||
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 {
|
||||||
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 {
|
||||||
| 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 {
|
||||||
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 {
|
||||||
chunkSeekTimeUs,
|
chunkSeekTimeUs,
|
||||||
trackSelection.getSelectionReason(),
|
trackSelection.getSelectionReason(),
|
||||||
trackSelection.getSelectionData(),
|
trackSelection.getSelectionData(),
|
||||||
extractorWrapper);
|
chunkExtractor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -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 {
|
||||||
chunkIndex,
|
chunkIndex,
|
||||||
/* chunkCount= */ 1,
|
/* chunkCount= */ 1,
|
||||||
sampleOffsetUs,
|
sampleOffsetUs,
|
||||||
extractorWrapper);
|
chunkExtractor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long resolveTimeToLiveEdgeUs(long playbackPositionUs) {
|
private long resolveTimeToLiveEdgeUs(long playbackPositionUs) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue