Support multiple track outputs from BaseMediaChunk

Issue: #2362
Issue: #2176

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=148764237
This commit is contained in:
olly 2017-02-28 06:54:27 -08:00 committed by Oliver Woodman
parent d58008eeb7
commit ab8fd14724
10 changed files with 229 additions and 100 deletions

View File

@ -177,8 +177,7 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
Representation representation = adaptationSet.representations.get(0); Representation representation = adaptationSet.representations.get(0);
DrmInitData drmInitData = representation.format.drmInitData; DrmInitData drmInitData = representation.format.drmInitData;
if (drmInitData == null) { if (drmInitData == null) {
Format sampleFormat = DashUtil.loadSampleFormat(dataSource, representation, Format sampleFormat = DashUtil.loadSampleFormat(dataSource, representation);
adaptationSet.type);
if (sampleFormat != null) { if (sampleFormat != null) {
drmInitData = sampleFormat.drmInitData; drmInitData = sampleFormat.drmInitData;
} }

View File

@ -21,14 +21,12 @@ import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DataSpec;
/** /**
* A base implementation of {@link MediaChunk}, for chunks that contain a single track. * A base implementation of {@link MediaChunk} that outputs to a {@link BaseMediaChunkOutput}.
* <p>
* Loaded samples are output to a {@link DefaultTrackOutput}.
*/ */
public abstract class BaseMediaChunk extends MediaChunk { public abstract class BaseMediaChunk extends MediaChunk {
private DefaultTrackOutput trackOutput; private BaseMediaChunkOutput output;
private int firstSampleIndex; private int[] firstSampleIndices;
/** /**
* @param dataSource The source from which the data should be loaded. * @param dataSource The source from which the data should be loaded.
@ -48,29 +46,29 @@ public abstract class BaseMediaChunk extends MediaChunk {
} }
/** /**
* Initializes the chunk for loading, setting the {@link DefaultTrackOutput} that will receive * Initializes the chunk for loading, setting the {@link BaseMediaChunkOutput} that will receive
* samples as they are loaded. * samples as they are loaded.
* *
* @param trackOutput The output that will receive the loaded samples. * @param output The output that will receive the loaded media samples.
*/ */
public void init(DefaultTrackOutput trackOutput) { public void init(BaseMediaChunkOutput output) {
this.trackOutput = trackOutput; this.output = output;
this.firstSampleIndex = trackOutput.getWriteIndex(); firstSampleIndices = output.getWriteIndices();
} }
/** /**
* Returns the index of the first sample in the output that was passed to * Returns the index of the first sample in the specified track of the output that will originate
* {@link #init(DefaultTrackOutput)} that will originate from this chunk. * from this chunk.
*/ */
public final int getFirstSampleIndex() { public final int getFirstSampleIndex(int trackIndex) {
return firstSampleIndex; return firstSampleIndices[trackIndex];
} }
/** /**
* Returns the track output most recently passed to {@link #init(DefaultTrackOutput)}. * Returns the output most recently passed to {@link #init(BaseMediaChunkOutput)}.
*/ */
protected final DefaultTrackOutput getTrackOutput() { protected final BaseMediaChunkOutput getOutput() {
return trackOutput; return output;
} }
} }

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2017 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 android.util.Log;
import com.google.android.exoplayer2.extractor.DefaultTrackOutput;
import com.google.android.exoplayer2.extractor.DummyTrackOutput;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper.TrackOutputProvider;
/**
* An output for {@link BaseMediaChunk}s.
*/
/* package */ final class BaseMediaChunkOutput implements TrackOutputProvider {
private static final String TAG = "BaseMediaChunkOutput";
private final int[] trackTypes;
private final DefaultTrackOutput[] trackOutputs;
/**
* @param trackTypes The track types of the individual track outputs.
* @param trackOutputs The individual track outputs.
*/
public BaseMediaChunkOutput(int[] trackTypes, DefaultTrackOutput... trackOutputs) {
this.trackTypes = trackTypes;
this.trackOutputs = trackOutputs;
}
@Override
public TrackOutput track(int id, int type) {
for (int i = 0; i < trackTypes.length; i++) {
if (type == trackTypes[i]) {
return trackOutputs[i];
}
}
Log.e(TAG, "Unmatched track of type: " + type);
return new DummyTrackOutput();
}
/**
* Returns the current absolute write indices of the individual track outputs.
*/
public int[] getWriteIndices() {
int[] writeIndices = new int[trackOutputs.length];
for (int i = 0; i < trackOutputs.length; i++) {
if (trackOutputs[i] != null) {
writeIndices[i] = trackOutputs[i].getWriteIndex();
}
}
return writeIndices;
}
/**
* Sets an offset that will be added to the timestamps (and sub-sample timestamps) of samples
* subsequently written to the track outputs.
*/
public void setSampleOffsetUs(long sampleOffsetUs) {
for (DefaultTrackOutput trackOutput : trackOutputs) {
if (trackOutput != null) {
trackOutput.setSampleOffsetUs(sampleOffsetUs);
}
}
}
}

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.source.chunk; package com.google.android.exoplayer2.source.chunk;
import android.util.SparseArray;
import com.google.android.exoplayer2.C; 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;
@ -32,33 +33,46 @@ import java.io.IOException;
* <p> * <p>
* The wrapper allows switching of the {@link TrackOutput} that receives parsed data. * The wrapper allows switching of the {@link TrackOutput} that receives parsed data.
*/ */
public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput { public final class ChunkExtractorWrapper implements ExtractorOutput {
/**
* 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; public final Extractor extractor;
private final Format manifestFormat; private final Format manifestFormat;
private final int primaryTrackType; private final SparseArray<BindingTrackOutput> bindingTrackOutputs;
private boolean extractorInitialized; private boolean extractorInitialized;
private TrackOutput trackOutput; private TrackOutputProvider trackOutputProvider;
private SeekMap seekMap; private SeekMap seekMap;
private Format sampleFormat; private Format[] sampleFormats;
// Accessed only on the loader thread.
private boolean seenTrack;
private int seenTrackId;
/** /**
* @param extractor The extractor to wrap. * @param extractor The extractor to wrap.
* @param manifestFormat A manifest defined {@link Format} whose data should be merged into any * @param manifestFormat A manifest defined {@link Format} whose data should be merged into any
* sample {@link Format} output from the {@link Extractor}. * sample {@link Format} output from the {@link Extractor}.
* @param primaryTrackType The type of the primary track. Typically one of the {@link C}
* {@code TRACK_TYPE_*} constants.
*/ */
public ChunkExtractorWrapper(Extractor extractor, Format manifestFormat, int primaryTrackType) { public ChunkExtractorWrapper(Extractor extractor, Format manifestFormat) {
this.extractor = extractor; this.extractor = extractor;
this.manifestFormat = manifestFormat; this.manifestFormat = manifestFormat;
this.primaryTrackType = primaryTrackType; bindingTrackOutputs = new SparseArray<>();
} }
/** /**
@ -69,27 +83,27 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput
} }
/** /**
* Returns the sample {@link Format} most recently output by the extractor, or null. * Returns the sample {@link Format}s most recently output by the extractor, or null.
*/ */
public Format getSampleFormat() { public Format[] getSampleFormats() {
return sampleFormat; return sampleFormats;
} }
/** /**
* Initializes the extractor to output to the provided {@link TrackOutput}, and configures it to * Initializes the extractor to output to the provided {@link TrackOutput}, and configures it to
* receive data from a new chunk. * receive data from a new chunk.
* *
* @param trackOutput The {@link TrackOutput} that will receive sample data. * @param trackOutputProvider The provider of {@link TrackOutput}s that will receive sample data.
*/ */
public void init(TrackOutput trackOutput) { public void init(TrackOutputProvider trackOutputProvider) {
this.trackOutput = trackOutput; this.trackOutputProvider = trackOutputProvider;
if (!extractorInitialized) { if (!extractorInitialized) {
extractor.init(this); extractor.init(this);
extractorInitialized = true; extractorInitialized = true;
} else { } else {
extractor.seek(0, 0); extractor.seek(0, 0);
if (sampleFormat != null && trackOutput != null) { for (int i = 0; i < bindingTrackOutputs.size(); i++) {
trackOutput.format(sampleFormat); bindingTrackOutputs.valueAt(i).bind(trackOutputProvider);
} }
} }
} }
@ -98,18 +112,24 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput
@Override @Override
public TrackOutput track(int id, int type) { public TrackOutput track(int id, int type) {
if (primaryTrackType != C.TRACK_TYPE_UNKNOWN && primaryTrackType != type) { BindingTrackOutput bindingTrackOutput = bindingTrackOutputs.get(id);
return new DummyTrackOutput(); if (bindingTrackOutput == null) {
// Assert that if we're seeing a new track we have not seen endTracks.
Assertions.checkState(sampleFormats == null);
bindingTrackOutput = new BindingTrackOutput(id, type, manifestFormat);
bindingTrackOutput.bind(trackOutputProvider);
bindingTrackOutputs.put(id, bindingTrackOutput);
} }
Assertions.checkState(!seenTrack || seenTrackId == id); return bindingTrackOutput;
seenTrack = true;
seenTrackId = id;
return this;
} }
@Override @Override
public void endTracks() { public void endTracks() {
Assertions.checkState(seenTrack); Format[] sampleFormats = new Format[bindingTrackOutputs.size()];
for (int i = 0; i < bindingTrackOutputs.size(); i++) {
sampleFormats[i] = bindingTrackOutputs.valueAt(i).sampleFormat;
}
this.sampleFormats = sampleFormats;
} }
@Override @Override
@ -117,31 +137,59 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput
this.seekMap = seekMap; this.seekMap = seekMap;
} }
// TrackOutput implementation. // Internal logic.
@Override private static final class BindingTrackOutput implements TrackOutput {
public void format(Format format) {
sampleFormat = format.copyWithManifestFormatInfo(manifestFormat); private final int id;
if (trackOutput != null) { private final int type;
private final Format manifestFormat;
public Format sampleFormat;
private TrackOutput trackOutput;
public BindingTrackOutput(int id, int type, Format manifestFormat) {
this.id = id;
this.type = type;
this.manifestFormat = manifestFormat;
}
public void bind(TrackOutputProvider trackOutputProvider) {
if (trackOutputProvider == null) {
trackOutput = new DummyTrackOutput();
return;
}
trackOutput = trackOutputProvider.track(id, type);
if (trackOutput != null) {
trackOutput.format(sampleFormat);
}
}
@Override
public void format(Format format) {
// TODO: This should only happen for the primary track. Additional metadata/text tracks need
// to be copied with different manifest derived formats.
sampleFormat = format.copyWithManifestFormatInfo(manifestFormat);
trackOutput.format(sampleFormat); trackOutput.format(sampleFormat);
} }
}
@Override @Override
public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput) public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
throws IOException, InterruptedException { throws IOException, InterruptedException {
return trackOutput.sampleData(input, length, allowEndOfInput); return trackOutput.sampleData(input, length, allowEndOfInput);
} }
@Override @Override
public void sampleData(ParsableByteArray data, int length) { public void sampleData(ParsableByteArray data, int length) {
trackOutput.sampleData(data, length); trackOutput.sampleData(data, length);
} }
@Override
public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset,
byte[] encryptionKey) {
trackOutput.sampleMetadata(timeUs, flags, size, offset, encryptionKey);
}
@Override
public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset,
byte[] encryptionKey) {
trackOutput.sampleMetadata(timeUs, flags, size, offset, encryptionKey);
} }
} }

View File

@ -44,6 +44,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
private final int minLoadableRetryCount; private final int minLoadableRetryCount;
private final LinkedList<BaseMediaChunk> mediaChunks; private final LinkedList<BaseMediaChunk> mediaChunks;
private final List<BaseMediaChunk> readOnlyMediaChunks; private final List<BaseMediaChunk> readOnlyMediaChunks;
private final BaseMediaChunkOutput mediaChunkOutput;
private final DefaultTrackOutput sampleQueue; private final DefaultTrackOutput sampleQueue;
private final ChunkHolder nextChunkHolder; private final ChunkHolder nextChunkHolder;
private final Loader loader; private final Loader loader;
@ -78,6 +79,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
mediaChunks = new LinkedList<>(); mediaChunks = new LinkedList<>();
readOnlyMediaChunks = Collections.unmodifiableList(mediaChunks); readOnlyMediaChunks = Collections.unmodifiableList(mediaChunks);
sampleQueue = new DefaultTrackOutput(allocator); sampleQueue = new DefaultTrackOutput(allocator);
mediaChunkOutput = new BaseMediaChunkOutput(new int[] {trackType}, sampleQueue);
lastSeekPositionUs = positionUs; lastSeekPositionUs = positionUs;
pendingResetPositionUs = positionUs; pendingResetPositionUs = positionUs;
} }
@ -127,7 +129,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
if (seekInsideBuffer) { if (seekInsideBuffer) {
// We succeeded. All we need to do is discard any chunks that we've moved past. // We succeeded. All we need to do is discard any chunks that we've moved past.
while (mediaChunks.size() > 1 while (mediaChunks.size() > 1
&& mediaChunks.get(1).getFirstSampleIndex() <= sampleQueue.getReadIndex()) { && mediaChunks.get(1).getFirstSampleIndex(0) <= sampleQueue.getReadIndex()) {
mediaChunks.removeFirst(); mediaChunks.removeFirst();
} }
} else { } else {
@ -176,7 +178,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
} }
while (mediaChunks.size() > 1 while (mediaChunks.size() > 1
&& mediaChunks.get(1).getFirstSampleIndex() <= sampleQueue.getReadIndex()) { && mediaChunks.get(1).getFirstSampleIndex(0) <= sampleQueue.getReadIndex()) {
mediaChunks.removeFirst(); mediaChunks.removeFirst();
} }
BaseMediaChunk currentChunk = mediaChunks.getFirst(); BaseMediaChunk currentChunk = mediaChunks.getFirst();
@ -232,7 +234,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
if (isMediaChunk) { if (isMediaChunk) {
BaseMediaChunk removed = mediaChunks.removeLast(); BaseMediaChunk removed = mediaChunks.removeLast();
Assertions.checkState(removed == loadable); Assertions.checkState(removed == loadable);
sampleQueue.discardUpstreamSamples(removed.getFirstSampleIndex()); sampleQueue.discardUpstreamSamples(removed.getFirstSampleIndex(0));
if (mediaChunks.isEmpty()) { if (mediaChunks.isEmpty()) {
pendingResetPositionUs = lastSeekPositionUs; pendingResetPositionUs = lastSeekPositionUs;
} }
@ -277,7 +279,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
if (isMediaChunk(loadable)) { if (isMediaChunk(loadable)) {
pendingResetPositionUs = C.TIME_UNSET; pendingResetPositionUs = C.TIME_UNSET;
BaseMediaChunk mediaChunk = (BaseMediaChunk) loadable; BaseMediaChunk mediaChunk = (BaseMediaChunk) loadable;
mediaChunk.init(sampleQueue); mediaChunk.init(mediaChunkOutput);
mediaChunks.add(mediaChunk); mediaChunks.add(mediaChunk);
} }
long elapsedRealtimeMs = loader.startLoading(loadable, this, minLoadableRetryCount); long elapsedRealtimeMs = loader.startLoading(loadable, this, minLoadableRetryCount);
@ -337,7 +339,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
startTimeUs = removed.startTimeUs; startTimeUs = removed.startTimeUs;
loadingFinished = false; loadingFinished = false;
} }
sampleQueue.discardUpstreamSamples(removed.getFirstSampleIndex()); sampleQueue.discardUpstreamSamples(removed.getFirstSampleIndex(0));
eventDispatcher.upstreamDiscarded(trackType, startTimeUs, endTimeUs); eventDispatcher.upstreamDiscarded(trackType, startTimeUs, endTimeUs);
return true; return true;
} }

View File

@ -17,7 +17,6 @@ package com.google.android.exoplayer2.source.chunk;
import com.google.android.exoplayer2.Format; 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.DefaultTrackOutput;
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.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
@ -100,10 +99,10 @@ public class ContainerMediaChunk extends BaseMediaChunk {
ExtractorInput input = new DefaultExtractorInput(dataSource, ExtractorInput input = new DefaultExtractorInput(dataSource,
loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec)); loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec));
if (bytesLoaded == 0) { if (bytesLoaded == 0) {
// Set the target to ourselves. // Configure the output and set it as the target for the extractor wrapper.
DefaultTrackOutput trackOutput = getTrackOutput(); BaseMediaChunkOutput output = getOutput();
trackOutput.setSampleOffsetUs(sampleOffsetUs); output.setSampleOffsetUs(sampleOffsetUs);
extractorWrapper.init(trackOutput); extractorWrapper.init(output);
} }
// Load and decode the sample data. // Load and decode the sample data.
try { try {

View File

@ -18,8 +18,8 @@ package com.google.android.exoplayer2.source.chunk;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; 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.DefaultTrackOutput;
import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.TrackOutput;
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.Util; import com.google.android.exoplayer2.util.Util;
@ -30,6 +30,7 @@ import java.io.IOException;
*/ */
public final class SingleSampleMediaChunk extends BaseMediaChunk { public final class SingleSampleMediaChunk extends BaseMediaChunk {
private final int trackType;
private final Format sampleFormat; private final Format sampleFormat;
private volatile int bytesLoaded; private volatile int bytesLoaded;
@ -45,15 +46,20 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
* @param startTimeUs The start time of the media contained by the chunk, in microseconds. * @param startTimeUs The start time of the media contained by the chunk, in microseconds.
* @param endTimeUs The end time of the media contained by the chunk, in microseconds. * @param endTimeUs The end time of the media contained by the chunk, in microseconds.
* @param chunkIndex The index of the chunk. * @param chunkIndex The index of the chunk.
* @param trackType The type of the chunk. Typically one of the {@link C} {@code TRACK_TYPE_*}
* constants.
* @param sampleFormat The {@link Format} of the sample in the chunk.
*/ */
public SingleSampleMediaChunk(DataSource dataSource, DataSpec dataSpec, Format trackFormat, public SingleSampleMediaChunk(DataSource dataSource, DataSpec dataSpec, Format trackFormat,
int trackSelectionReason, Object trackSelectionData, long startTimeUs, long endTimeUs, int trackSelectionReason, Object trackSelectionData, long startTimeUs, long endTimeUs,
int chunkIndex, Format sampleFormat) { int chunkIndex, int trackType, Format sampleFormat) {
super(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, startTimeUs, super(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, startTimeUs,
endTimeUs, chunkIndex); endTimeUs, chunkIndex);
this.trackType = trackType;
this.sampleFormat = sampleFormat; this.sampleFormat = sampleFormat;
} }
@Override @Override
public boolean isLoadCompleted() { public boolean isLoadCompleted() {
return loadCompleted; return loadCompleted;
@ -87,8 +93,9 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
length += bytesLoaded; length += bytesLoaded;
} }
ExtractorInput extractorInput = new DefaultExtractorInput(dataSource, bytesLoaded, length); ExtractorInput extractorInput = new DefaultExtractorInput(dataSource, bytesLoaded, length);
DefaultTrackOutput trackOutput = getTrackOutput(); BaseMediaChunkOutput output = getOutput();
trackOutput.setSampleOffsetUs(0); output.setSampleOffsetUs(0);
TrackOutput trackOutput = output.track(0, trackType);
trackOutput.format(sampleFormat); trackOutput.format(sampleFormat);
// Load the sample data. // Load the sample data.
int result = 0; int result = 0;

View File

@ -68,17 +68,15 @@ public final class DashUtil {
* *
* @param dataSource The source from which the data should be loaded. * @param dataSource The source from which the data should be loaded.
* @param representation The representation which initialization chunk belongs to. * @param representation The representation which initialization chunk belongs to.
* @param type The type of the primary track. Typically one of the {@link C} {@code TRACK_TYPE_*}
* constants.
* @return the sample {@link Format} of the given representation. * @return the sample {@link Format} of the given representation.
* @throws IOException Thrown when there is an error while loading. * @throws IOException Thrown when there is an error while loading.
* @throws InterruptedException Thrown if the thread was interrupted. * @throws InterruptedException Thrown if the thread was interrupted.
*/ */
public static Format loadSampleFormat(DataSource dataSource, Representation representation, public static Format loadSampleFormat(DataSource dataSource, Representation representation)
int type) throws IOException, InterruptedException { throws IOException, InterruptedException {
ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, representation, ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, representation,
type, false); false);
return extractorWrapper == null ? null : extractorWrapper.getSampleFormat(); return extractorWrapper == null ? null : extractorWrapper.getSampleFormats()[0];
} }
/** /**
@ -87,16 +85,14 @@ public final class DashUtil {
* *
* @param dataSource The source from which the data should be loaded. * @param dataSource The source from which the data should be loaded.
* @param representation The representation which initialization chunk belongs to. * @param representation The representation which initialization chunk belongs to.
* @param type The type of the primary track. Typically one of the {@link C} {@code TRACK_TYPE_*}
* constants.
* @return {@link ChunkIndex} of the given representation. * @return {@link ChunkIndex} of the given representation.
* @throws IOException Thrown when there is an error while loading. * @throws IOException Thrown when there is an error while loading.
* @throws InterruptedException Thrown if the thread was interrupted. * @throws InterruptedException Thrown if the thread was interrupted.
*/ */
public static ChunkIndex loadChunkIndex(DataSource dataSource, Representation representation, public static ChunkIndex loadChunkIndex(DataSource dataSource, Representation representation)
int type) throws IOException, InterruptedException { throws IOException, InterruptedException {
ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, representation, ChunkExtractorWrapper extractorWrapper = loadInitializationData(dataSource, representation,
type, true); true);
return extractorWrapper == null ? null : (ChunkIndex) extractorWrapper.getSeekMap(); return extractorWrapper == null ? null : (ChunkIndex) extractorWrapper.getSeekMap();
} }
@ -106,8 +102,6 @@ public final class DashUtil {
* *
* @param dataSource The source from which the data should be loaded. * @param dataSource The source from which the data should be loaded.
* @param representation The representation which initialization chunk belongs to. * @param representation The representation which initialization chunk belongs to.
* @param type The type of the primary track. Typically one of the {@link C} {@code TRACK_TYPE_*}
* constants.
* @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 ChunkExtractorWrapper} for the {@code representation}, or null if no
* initialization or (if requested) index data exists. * initialization or (if requested) index data exists.
@ -115,13 +109,13 @@ public final class DashUtil {
* @throws InterruptedException Thrown if the thread was interrupted. * @throws InterruptedException Thrown if the thread was interrupted.
*/ */
private static ChunkExtractorWrapper loadInitializationData(DataSource dataSource, private static ChunkExtractorWrapper loadInitializationData(DataSource dataSource,
Representation representation, int type, boolean loadIndex) Representation representation, boolean loadIndex)
throws IOException, InterruptedException { throws IOException, InterruptedException {
RangedUri initializationUri = representation.getInitializationUri(); RangedUri initializationUri = representation.getInitializationUri();
if (initializationUri == null) { if (initializationUri == null) {
return null; return null;
} }
ChunkExtractorWrapper extractorWrapper = newWrappedExtractor(representation.format, type); ChunkExtractorWrapper extractorWrapper = newWrappedExtractor(representation.format);
RangedUri requestUri; RangedUri requestUri;
if (loadIndex) { if (loadIndex) {
RangedUri indexUri = representation.getIndexUri(); RangedUri indexUri = representation.getIndexUri();
@ -153,12 +147,12 @@ public final class DashUtil {
initializationChunk.load(); initializationChunk.load();
} }
private static ChunkExtractorWrapper newWrappedExtractor(Format format, int trackType) { private static ChunkExtractorWrapper newWrappedExtractor(Format format) {
String mimeType = format.containerMimeType; String mimeType = format.containerMimeType;
boolean isWebm = mimeType.startsWith(MimeTypes.VIDEO_WEBM) boolean isWebm = 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, format, trackType); return new ChunkExtractorWrapper(extractor, format);
} }
private DashUtil() {} private DashUtil() {}

View File

@ -185,7 +185,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
RangedUri pendingInitializationUri = null; RangedUri pendingInitializationUri = null;
RangedUri pendingIndexUri = null; RangedUri pendingIndexUri = null;
if (representationHolder.extractorWrapper.getSampleFormat() == null) { if (representationHolder.extractorWrapper.getSampleFormats() == null) {
pendingInitializationUri = selectedRepresentation.getInitializationUri(); pendingInitializationUri = selectedRepresentation.getInitializationUri();
} }
if (segmentIndex == null) { if (segmentIndex == null) {
@ -343,7 +343,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
DataSpec dataSpec = new DataSpec(segmentUri.resolveUri(baseUrl), DataSpec dataSpec = new DataSpec(segmentUri.resolveUri(baseUrl),
segmentUri.start, segmentUri.length, representation.getCacheKey()); segmentUri.start, segmentUri.length, representation.getCacheKey());
return new SingleSampleMediaChunk(dataSource, dataSpec, trackFormat, trackSelectionReason, return new SingleSampleMediaChunk(dataSource, dataSpec, trackFormat, trackSelectionReason,
trackSelectionData, startTimeUs, endTimeUs, firstSegmentNum, trackFormat); trackSelectionData, startTimeUs, endTimeUs, firstSegmentNum,
representationHolder.trackType, trackFormat);
} else { } else {
int segmentCount = 1; int segmentCount = 1;
for (int i = 1; i < maxSegmentCount; i++) { for (int i = 1; i < maxSegmentCount; i++) {
@ -370,6 +371,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
protected static final class RepresentationHolder { protected static final class RepresentationHolder {
public final int trackType;
public final ChunkExtractorWrapper extractorWrapper; public final ChunkExtractorWrapper extractorWrapper;
public Representation representation; public Representation representation;
@ -382,6 +384,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
boolean enableEventMessageTrack, boolean enableCea608Track, int trackType) { boolean enableEventMessageTrack, boolean enableCea608Track, int trackType) {
this.periodDurationUs = periodDurationUs; this.periodDurationUs = periodDurationUs;
this.representation = representation; this.representation = representation;
this.trackType = trackType;
String containerMimeType = representation.format.containerMimeType; String containerMimeType = representation.format.containerMimeType;
if (mimeTypeIsRawText(containerMimeType)) { if (mimeTypeIsRawText(containerMimeType)) {
extractorWrapper = null; extractorWrapper = null;
@ -403,7 +406,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
} }
// Prefer drmInitData obtained from the manifest over drmInitData obtained from the stream, // Prefer drmInitData obtained from the manifest over drmInitData obtained from the stream,
// as per DASH IF Interoperability Recommendations V3.0, 7.5.3. // as per DASH IF Interoperability Recommendations V3.0, 7.5.3.
extractorWrapper = new ChunkExtractorWrapper(extractor, representation.format, trackType); extractorWrapper = new ChunkExtractorWrapper(extractor, representation.format);
} }
segmentIndex = representation.getIndex(); segmentIndex = representation.getIndex();
} }

View File

@ -102,7 +102,7 @@ public class DefaultSsChunkSource implements SsChunkSource {
FragmentedMp4Extractor extractor = new FragmentedMp4Extractor( FragmentedMp4Extractor extractor = new FragmentedMp4Extractor(
FragmentedMp4Extractor.FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME FragmentedMp4Extractor.FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME
| FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_TFDT_BOX, null, track); | FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_TFDT_BOX, null, track);
extractorWrappers[i] = new ChunkExtractorWrapper(extractor, format, streamElement.type); extractorWrappers[i] = new ChunkExtractorWrapper(extractor, format);
} }
} }