mirror of
https://github.com/androidx/media.git
synced 2025-05-10 09:12:16 +08:00
Simplify chunk package ahead of EMSG/608 piping
Issue: #2362 Issue: #2176 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=146243681
This commit is contained in:
parent
025a67cae9
commit
ee3c5f875f
@ -32,6 +32,7 @@ import com.google.android.exoplayer2.source.dash.manifest.Representation;
|
|||||||
import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegmentBase;
|
import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegmentBase;
|
||||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
@ -217,7 +218,11 @@ public class OfflineLicenseHelperTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Representation newRepresentations(DrmInitData drmInitData) {
|
private static Representation newRepresentations(DrmInitData drmInitData) {
|
||||||
Format format = Format.createVideoSampleFormat("", "", "", 0, 0, 0, 0, 0, null, drmInitData);
|
Format format = Format.createVideoContainerFormat("id", MimeTypes.VIDEO_MP4,
|
||||||
|
MimeTypes.VIDEO_H264, "", Format.NO_VALUE, 1024, 768, Format.NO_VALUE, null, 0);
|
||||||
|
if (drmInitData != null) {
|
||||||
|
format = format.copyWithDrmInitData(drmInitData);
|
||||||
|
}
|
||||||
return Representation.newInstance("", 0, format, "", new SingleSegmentBase());
|
return Representation.newInstance("", 0, format, "", new SingleSegmentBase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,11 +210,13 @@ 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) {
|
||||||
InitializationChunk initializationChunk = loadInitializationChunk(dataSource, representation);
|
ChunkExtractorWrapper extractorWrapper = newWrappedExtractor(representation.format);
|
||||||
|
InitializationChunk initializationChunk = loadInitializationChunk(dataSource, representation,
|
||||||
|
extractorWrapper);
|
||||||
if (initializationChunk == null) {
|
if (initializationChunk == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Format sampleFormat = initializationChunk.getSampleFormat();
|
Format sampleFormat = extractorWrapper.getSampleFormat();
|
||||||
if (sampleFormat != null) {
|
if (sampleFormat != null) {
|
||||||
drmInitData = sampleFormat.drmInitData;
|
drmInitData = sampleFormat.drmInitData;
|
||||||
}
|
}
|
||||||
@ -288,8 +290,9 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
|
|||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InitializationChunk loadInitializationChunk(final DataSource dataSource,
|
private static InitializationChunk loadInitializationChunk(DataSource dataSource,
|
||||||
final Representation representation) throws IOException, InterruptedException {
|
Representation representation, ChunkExtractorWrapper extractorWrapper)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
RangedUri rangedUri = representation.getInitializationUri();
|
RangedUri rangedUri = representation.getInitializationUri();
|
||||||
if (rangedUri == null) {
|
if (rangedUri == null) {
|
||||||
return null;
|
return null;
|
||||||
@ -298,7 +301,7 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
|
|||||||
rangedUri.length, representation.getCacheKey());
|
rangedUri.length, representation.getCacheKey());
|
||||||
InitializationChunk initializationChunk = new InitializationChunk(dataSource, dataSpec,
|
InitializationChunk initializationChunk = new InitializationChunk(dataSource, dataSpec,
|
||||||
representation.format, C.SELECTION_REASON_UNKNOWN, null /* trackSelectionData */,
|
representation.format, C.SELECTION_REASON_UNKNOWN, null /* trackSelectionData */,
|
||||||
newWrappedExtractor(representation.format));
|
extractorWrapper);
|
||||||
initializationChunk.load();
|
initializationChunk.load();
|
||||||
return initializationChunk;
|
return initializationChunk;
|
||||||
}
|
}
|
||||||
@ -308,8 +311,7 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
|
|||||||
final boolean isWebm = mimeType.startsWith(MimeTypes.VIDEO_WEBM)
|
final boolean isWebm = mimeType.startsWith(MimeTypes.VIDEO_WEBM)
|
||||||
|| mimeType.startsWith(MimeTypes.AUDIO_WEBM);
|
|| mimeType.startsWith(MimeTypes.AUDIO_WEBM);
|
||||||
final Extractor extractor = isWebm ? new MatroskaExtractor() : new FragmentedMp4Extractor();
|
final Extractor extractor = isWebm ? new MatroskaExtractor() : new FragmentedMp4Extractor();
|
||||||
return new ChunkExtractorWrapper(extractor, format, false /* preferManifestDrmInitData */,
|
return new ChunkExtractorWrapper(extractor, format, false /* preferManifestDrmInitData */);
|
||||||
false /* resendFormatOnInit */);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,8 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
private Format downstreamFormat;
|
private Format downstreamFormat;
|
||||||
|
|
||||||
// Accessed only by the loading thread (or the consuming thread when there is no loading thread).
|
// Accessed only by the loading thread (or the consuming thread when there is no loading thread).
|
||||||
|
private boolean pendingFormatAdjustment;
|
||||||
|
private Format lastUnadjustedFormat;
|
||||||
private long sampleOffsetUs;
|
private long sampleOffsetUs;
|
||||||
private long totalBytesWritten;
|
private long totalBytesWritten;
|
||||||
private Allocation lastAllocation;
|
private Allocation lastAllocation;
|
||||||
@ -445,23 +447,24 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like {@link #format(Format)}, but with an offset that will be added to the timestamps of
|
* Sets an offset that will be added to the timestamps (and sub-sample timestamps) of samples
|
||||||
* samples subsequently queued to the buffer. The offset is also used to adjust
|
* subsequently queued to the buffer.
|
||||||
* {@link Format#subsampleOffsetUs} for both the {@link Format} passed and those subsequently
|
|
||||||
* passed to {@link #format(Format)}.
|
|
||||||
*
|
*
|
||||||
* @param format The format.
|
|
||||||
* @param sampleOffsetUs The timestamp offset in microseconds.
|
* @param sampleOffsetUs The timestamp offset in microseconds.
|
||||||
*/
|
*/
|
||||||
public void formatWithOffset(Format format, long sampleOffsetUs) {
|
public void setSampleOffsetUs(long sampleOffsetUs) {
|
||||||
this.sampleOffsetUs = sampleOffsetUs;
|
if (this.sampleOffsetUs != sampleOffsetUs) {
|
||||||
format(format);
|
this.sampleOffsetUs = sampleOffsetUs;
|
||||||
|
pendingFormatAdjustment = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void format(Format format) {
|
public void format(Format format) {
|
||||||
Format adjustedFormat = getAdjustedSampleFormat(format, sampleOffsetUs);
|
Format adjustedFormat = getAdjustedSampleFormat(format, sampleOffsetUs);
|
||||||
boolean formatChanged = infoQueue.format(adjustedFormat);
|
boolean formatChanged = infoQueue.format(adjustedFormat);
|
||||||
|
lastUnadjustedFormat = format;
|
||||||
|
pendingFormatAdjustment = false;
|
||||||
if (upstreamFormatChangeListener != null && formatChanged) {
|
if (upstreamFormatChangeListener != null && formatChanged) {
|
||||||
upstreamFormatChangeListener.onUpstreamFormatChanged(adjustedFormat);
|
upstreamFormatChangeListener.onUpstreamFormatChanged(adjustedFormat);
|
||||||
}
|
}
|
||||||
@ -518,6 +521,9 @@ public final class DefaultTrackOutput implements TrackOutput {
|
|||||||
@Override
|
@Override
|
||||||
public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset,
|
public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset,
|
||||||
byte[] encryptionKey) {
|
byte[] encryptionKey) {
|
||||||
|
if (pendingFormatAdjustment) {
|
||||||
|
format(lastUnadjustedFormat);
|
||||||
|
}
|
||||||
if (!startWriteOperation()) {
|
if (!startWriteOperation()) {
|
||||||
infoQueue.commitSampleTimestamp(timeUs);
|
infoQueue.commitSampleTimestamp(timeUs);
|
||||||
return;
|
return;
|
||||||
|
@ -30,33 +30,19 @@ import java.io.IOException;
|
|||||||
/**
|
/**
|
||||||
* An {@link Extractor} wrapper for loading chunks containing a single track.
|
* An {@link Extractor} wrapper for loading chunks containing a single track.
|
||||||
* <p>
|
* <p>
|
||||||
* The wrapper allows switching of the {@link SeekMapOutput} and {@link TrackOutput} that receive
|
* The wrapper allows switching of the {@link TrackOutput} that receives parsed data.
|
||||||
* parsed data.
|
|
||||||
*/
|
*/
|
||||||
public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput {
|
public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput {
|
||||||
|
|
||||||
/**
|
|
||||||
* Receives {@link SeekMap}s extracted by the wrapped {@link Extractor}.
|
|
||||||
*/
|
|
||||||
public interface SeekMapOutput {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ExtractorOutput#seekMap(SeekMap)
|
|
||||||
*/
|
|
||||||
void seekMap(SeekMap seekMap);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Extractor extractor;
|
public final Extractor extractor;
|
||||||
|
|
||||||
private final Format manifestFormat;
|
private final Format manifestFormat;
|
||||||
private final boolean preferManifestDrmInitData;
|
private final boolean preferManifestDrmInitData;
|
||||||
private final boolean resendFormatOnInit;
|
|
||||||
|
|
||||||
private boolean extractorInitialized;
|
private boolean extractorInitialized;
|
||||||
private SeekMapOutput seekMapOutput;
|
|
||||||
private TrackOutput trackOutput;
|
private TrackOutput trackOutput;
|
||||||
private Format sentFormat;
|
private SeekMap seekMap;
|
||||||
|
private Format sampleFormat;
|
||||||
|
|
||||||
// Accessed only on the loader thread.
|
// Accessed only on the loader thread.
|
||||||
private boolean seenTrack;
|
private boolean seenTrack;
|
||||||
@ -68,34 +54,43 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput
|
|||||||
* sample {@link Format} output from the {@link Extractor}.
|
* sample {@link Format} output from the {@link Extractor}.
|
||||||
* @param preferManifestDrmInitData Whether {@link DrmInitData} defined in {@code manifestFormat}
|
* @param preferManifestDrmInitData Whether {@link DrmInitData} defined in {@code manifestFormat}
|
||||||
* should be preferred when the sample and manifest {@link Format}s are merged.
|
* should be preferred when the sample and manifest {@link Format}s are merged.
|
||||||
* @param resendFormatOnInit Whether the extractor should resend the previous {@link Format} when
|
|
||||||
* it is initialized via {@link #init(SeekMapOutput, TrackOutput)}.
|
|
||||||
*/
|
*/
|
||||||
public ChunkExtractorWrapper(Extractor extractor, Format manifestFormat,
|
public ChunkExtractorWrapper(Extractor extractor, Format manifestFormat,
|
||||||
boolean preferManifestDrmInitData, boolean resendFormatOnInit) {
|
boolean preferManifestDrmInitData) {
|
||||||
this.extractor = extractor;
|
this.extractor = extractor;
|
||||||
this.manifestFormat = manifestFormat;
|
this.manifestFormat = manifestFormat;
|
||||||
this.preferManifestDrmInitData = preferManifestDrmInitData;
|
this.preferManifestDrmInitData = preferManifestDrmInitData;
|
||||||
this.resendFormatOnInit = resendFormatOnInit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the extractor to output to the provided {@link SeekMapOutput} and
|
* Returns the {@link SeekMap} most recently output by the extractor, or null.
|
||||||
* {@link TrackOutput} instances, and configures it to receive data from a new chunk.
|
*/
|
||||||
|
public SeekMap getSeekMap() {
|
||||||
|
return seekMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the sample {@link Format} most recently output by the extractor, or null.
|
||||||
|
*/
|
||||||
|
public Format getSampleFormat() {
|
||||||
|
return sampleFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the extractor to output to the provided {@link TrackOutput}, and configures it to
|
||||||
|
* receive data from a new chunk.
|
||||||
*
|
*
|
||||||
* @param seekMapOutput The {@link SeekMapOutput} that will receive extracted {@link SeekMap}s.
|
|
||||||
* @param trackOutput The {@link TrackOutput} that will receive sample data.
|
* @param trackOutput The {@link TrackOutput} that will receive sample data.
|
||||||
*/
|
*/
|
||||||
public void init(SeekMapOutput seekMapOutput, TrackOutput trackOutput) {
|
public void init(TrackOutput trackOutput) {
|
||||||
this.seekMapOutput = seekMapOutput;
|
|
||||||
this.trackOutput = trackOutput;
|
this.trackOutput = trackOutput;
|
||||||
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 (resendFormatOnInit && sentFormat != null) {
|
if (sampleFormat != null) {
|
||||||
trackOutput.format(sentFormat);
|
trackOutput.format(sampleFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,15 +112,17 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void seekMap(SeekMap seekMap) {
|
public void seekMap(SeekMap seekMap) {
|
||||||
seekMapOutput.seekMap(seekMap);
|
this.seekMap = seekMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrackOutput implementation.
|
// TrackOutput implementation.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void format(Format format) {
|
public void format(Format format) {
|
||||||
sentFormat = format.copyWithManifestFormatInfo(manifestFormat, preferManifestDrmInitData);
|
sampleFormat = format.copyWithManifestFormatInfo(manifestFormat, preferManifestDrmInitData);
|
||||||
trackOutput.format(sentFormat);
|
if (trackOutput != null) {
|
||||||
|
trackOutput.format(sampleFormat);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -20,8 +20,6 @@ import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
|
|||||||
import com.google.android.exoplayer2.extractor.DefaultTrackOutput;
|
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.extractor.SeekMap;
|
|
||||||
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper.SeekMapOutput;
|
|
||||||
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;
|
||||||
@ -31,12 +29,11 @@ import java.io.IOException;
|
|||||||
/**
|
/**
|
||||||
* A {@link BaseMediaChunk} that uses an {@link Extractor} to decode sample data.
|
* A {@link BaseMediaChunk} that uses an {@link Extractor} to decode sample data.
|
||||||
*/
|
*/
|
||||||
public class ContainerMediaChunk extends BaseMediaChunk implements SeekMapOutput {
|
public class ContainerMediaChunk extends BaseMediaChunk {
|
||||||
|
|
||||||
private final int chunkCount;
|
private final int chunkCount;
|
||||||
private final long sampleOffsetUs;
|
private final long sampleOffsetUs;
|
||||||
private final ChunkExtractorWrapper extractorWrapper;
|
private final ChunkExtractorWrapper extractorWrapper;
|
||||||
private final Format sampleFormat;
|
|
||||||
|
|
||||||
private volatile int bytesLoaded;
|
private volatile int bytesLoaded;
|
||||||
private volatile boolean loadCanceled;
|
private volatile boolean loadCanceled;
|
||||||
@ -56,19 +53,15 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SeekMapOutput
|
|||||||
* 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 extractorWrapper A wrapped extractor to use for parsing the data.
|
||||||
* @param sampleFormat The {@link Format} of the samples in the chunk, if known. May be null if
|
|
||||||
* the data is known to define its own sample format.
|
|
||||||
*/
|
*/
|
||||||
public ContainerMediaChunk(DataSource dataSource, DataSpec dataSpec, Format trackFormat,
|
public ContainerMediaChunk(DataSource dataSource, DataSpec dataSpec, Format trackFormat,
|
||||||
int trackSelectionReason, Object trackSelectionData, long startTimeUs, long endTimeUs,
|
int trackSelectionReason, Object trackSelectionData, long startTimeUs, long endTimeUs,
|
||||||
int chunkIndex, int chunkCount, long sampleOffsetUs, ChunkExtractorWrapper extractorWrapper,
|
int chunkIndex, int chunkCount, long sampleOffsetUs, ChunkExtractorWrapper extractorWrapper) {
|
||||||
Format sampleFormat) {
|
|
||||||
super(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, startTimeUs,
|
super(dataSource, dataSpec, trackFormat, trackSelectionReason, trackSelectionData, startTimeUs,
|
||||||
endTimeUs, chunkIndex);
|
endTimeUs, chunkIndex);
|
||||||
this.chunkCount = chunkCount;
|
this.chunkCount = chunkCount;
|
||||||
this.sampleOffsetUs = sampleOffsetUs;
|
this.sampleOffsetUs = sampleOffsetUs;
|
||||||
this.extractorWrapper = extractorWrapper;
|
this.extractorWrapper = extractorWrapper;
|
||||||
this.sampleFormat = sampleFormat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -86,13 +79,6 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SeekMapOutput
|
|||||||
return bytesLoaded;
|
return bytesLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SeekMapOutput implementation.
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void seekMap(SeekMap seekMap) {
|
|
||||||
// Do nothing.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loadable implementation.
|
// Loadable implementation.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -116,8 +102,8 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SeekMapOutput
|
|||||||
if (bytesLoaded == 0) {
|
if (bytesLoaded == 0) {
|
||||||
// Set the target to ourselves.
|
// Set the target to ourselves.
|
||||||
DefaultTrackOutput trackOutput = getTrackOutput();
|
DefaultTrackOutput trackOutput = getTrackOutput();
|
||||||
trackOutput.formatWithOffset(sampleFormat, sampleOffsetUs);
|
trackOutput.setSampleOffsetUs(sampleOffsetUs);
|
||||||
extractorWrapper.init(this, trackOutput);
|
extractorWrapper.init(trackOutput);
|
||||||
}
|
}
|
||||||
// Load and decode the sample data.
|
// Load and decode the sample data.
|
||||||
try {
|
try {
|
||||||
|
@ -20,30 +20,19 @@ 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.SeekMap;
|
|
||||||
import com.google.android.exoplayer2.extractor.TrackOutput;
|
|
||||||
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper.SeekMapOutput;
|
|
||||||
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;
|
||||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link Chunk} that uses an {@link Extractor} to decode initialization data for single track.
|
* A {@link Chunk} that uses an {@link Extractor} to decode initialization data for single track.
|
||||||
*/
|
*/
|
||||||
public final class InitializationChunk extends Chunk implements SeekMapOutput,
|
public final class InitializationChunk extends Chunk {
|
||||||
TrackOutput {
|
|
||||||
|
|
||||||
private final ChunkExtractorWrapper extractorWrapper;
|
private final ChunkExtractorWrapper extractorWrapper;
|
||||||
|
|
||||||
// Initialization results. Set by the loader thread and read by any thread that knows loading
|
|
||||||
// has completed. These variables do not need to be volatile, since a memory barrier must occur
|
|
||||||
// for the reading thread to know that loading has completed.
|
|
||||||
private Format sampleFormat;
|
|
||||||
private SeekMap seekMap;
|
|
||||||
|
|
||||||
private volatile int bytesLoaded;
|
private volatile int bytesLoaded;
|
||||||
private volatile boolean loadCanceled;
|
private volatile boolean loadCanceled;
|
||||||
|
|
||||||
@ -68,55 +57,6 @@ public final class InitializationChunk extends Chunk implements SeekMapOutput,
|
|||||||
return bytesLoaded;
|
return bytesLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a {@link Format} parsed from the chunk, or null.
|
|
||||||
* <p>
|
|
||||||
* Should be called after loading has completed.
|
|
||||||
*/
|
|
||||||
public Format getSampleFormat() {
|
|
||||||
return sampleFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a {@link SeekMap} parsed from the chunk, or null.
|
|
||||||
* <p>
|
|
||||||
* Should be called after loading has completed.
|
|
||||||
*/
|
|
||||||
public SeekMap getSeekMap() {
|
|
||||||
return seekMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SeekMapOutput implementation.
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void seekMap(SeekMap seekMap) {
|
|
||||||
this.seekMap = seekMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TrackOutput implementation.
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void format(Format format) {
|
|
||||||
this.sampleFormat = format;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
|
|
||||||
throws IOException, InterruptedException {
|
|
||||||
throw new IllegalStateException("Unexpected sample data in initialization chunk");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sampleData(ParsableByteArray data, int length) {
|
|
||||||
throw new IllegalStateException("Unexpected sample data in initialization chunk");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset,
|
|
||||||
byte[] encryptionKey) {
|
|
||||||
throw new IllegalStateException("Unexpected sample data in initialization chunk");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loadable implementation.
|
// Loadable implementation.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -138,8 +78,7 @@ public final class InitializationChunk extends Chunk implements SeekMapOutput,
|
|||||||
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.
|
extractorWrapper.init(null);
|
||||||
extractorWrapper.init(this, this);
|
|
||||||
}
|
}
|
||||||
// Load and decode the initialization data.
|
// Load and decode the initialization data.
|
||||||
try {
|
try {
|
||||||
|
@ -88,7 +88,8 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
|
|||||||
}
|
}
|
||||||
ExtractorInput extractorInput = new DefaultExtractorInput(dataSource, bytesLoaded, length);
|
ExtractorInput extractorInput = new DefaultExtractorInput(dataSource, bytesLoaded, length);
|
||||||
DefaultTrackOutput trackOutput = getTrackOutput();
|
DefaultTrackOutput trackOutput = getTrackOutput();
|
||||||
trackOutput.formatWithOffset(sampleFormat, 0);
|
trackOutput.setSampleOffsetUs(0);
|
||||||
|
trackOutput.format(sampleFormat);
|
||||||
// Load the sample data.
|
// Load the sample data.
|
||||||
int result = 0;
|
int result = 0;
|
||||||
while (result != C.RESULT_END_OF_INPUT) {
|
while (result != C.RESULT_END_OF_INPUT) {
|
||||||
|
@ -176,8 +176,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||||||
|
|
||||||
RangedUri pendingInitializationUri = null;
|
RangedUri pendingInitializationUri = null;
|
||||||
RangedUri pendingIndexUri = null;
|
RangedUri pendingIndexUri = null;
|
||||||
Format sampleFormat = representationHolder.sampleFormat;
|
if (representationHolder.extractorWrapper.getSampleFormat() == null) {
|
||||||
if (sampleFormat == null) {
|
|
||||||
pendingInitializationUri = selectedRepresentation.getInitializationUri();
|
pendingInitializationUri = selectedRepresentation.getInitializationUri();
|
||||||
}
|
}
|
||||||
if (segmentIndex == null) {
|
if (segmentIndex == null) {
|
||||||
@ -233,8 +232,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||||||
|
|
||||||
int maxSegmentCount = Math.min(maxSegmentsPerLoad, lastAvailableSegmentNum - segmentNum + 1);
|
int maxSegmentCount = Math.min(maxSegmentsPerLoad, lastAvailableSegmentNum - segmentNum + 1);
|
||||||
out.chunk = newMediaChunk(representationHolder, dataSource, trackSelection.getSelectedFormat(),
|
out.chunk = newMediaChunk(representationHolder, dataSource, trackSelection.getSelectedFormat(),
|
||||||
trackSelection.getSelectionReason(), trackSelection.getSelectionData(), sampleFormat,
|
trackSelection.getSelectionReason(), trackSelection.getSelectionData(), segmentNum,
|
||||||
segmentNum, maxSegmentCount);
|
maxSegmentCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -243,15 +242,11 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||||||
InitializationChunk initializationChunk = (InitializationChunk) chunk;
|
InitializationChunk initializationChunk = (InitializationChunk) chunk;
|
||||||
RepresentationHolder representationHolder =
|
RepresentationHolder representationHolder =
|
||||||
representationHolders[trackSelection.indexOf(initializationChunk.trackFormat)];
|
representationHolders[trackSelection.indexOf(initializationChunk.trackFormat)];
|
||||||
Format sampleFormat = initializationChunk.getSampleFormat();
|
|
||||||
if (sampleFormat != null) {
|
|
||||||
representationHolder.setSampleFormat(sampleFormat);
|
|
||||||
}
|
|
||||||
// The null check avoids overwriting an index obtained from the manifest with one obtained
|
// The null check avoids overwriting an index obtained from the manifest with one obtained
|
||||||
// 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 = initializationChunk.getSeekMap();
|
SeekMap seekMap = representationHolder.extractorWrapper.getSeekMap();
|
||||||
if (seekMap != null) {
|
if (seekMap != null) {
|
||||||
representationHolder.segmentIndex = new DashWrappingSegmentIndex((ChunkIndex) seekMap);
|
representationHolder.segmentIndex = new DashWrappingSegmentIndex((ChunkIndex) seekMap);
|
||||||
}
|
}
|
||||||
@ -318,7 +313,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||||||
|
|
||||||
private static Chunk newMediaChunk(RepresentationHolder representationHolder,
|
private static Chunk newMediaChunk(RepresentationHolder representationHolder,
|
||||||
DataSource dataSource, Format trackFormat, int trackSelectionReason,
|
DataSource dataSource, Format trackFormat, int trackSelectionReason,
|
||||||
Object trackSelectionData, Format sampleFormat, int firstSegmentNum, int maxSegmentCount) {
|
Object trackSelectionData, int firstSegmentNum, int maxSegmentCount) {
|
||||||
Representation representation = representationHolder.representation;
|
Representation representation = representationHolder.representation;
|
||||||
long startTimeUs = representationHolder.getSegmentStartTimeUs(firstSegmentNum);
|
long startTimeUs = representationHolder.getSegmentStartTimeUs(firstSegmentNum);
|
||||||
RangedUri segmentUri = representationHolder.getSegmentUrl(firstSegmentNum);
|
RangedUri segmentUri = representationHolder.getSegmentUrl(firstSegmentNum);
|
||||||
@ -347,7 +342,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||||||
long sampleOffsetUs = -representation.presentationTimeOffsetUs;
|
long sampleOffsetUs = -representation.presentationTimeOffsetUs;
|
||||||
return new ContainerMediaChunk(dataSource, dataSpec, trackFormat, trackSelectionReason,
|
return new ContainerMediaChunk(dataSource, dataSpec, trackFormat, trackSelectionReason,
|
||||||
trackSelectionData, startTimeUs, endTimeUs, firstSegmentNum, segmentCount,
|
trackSelectionData, startTimeUs, endTimeUs, firstSegmentNum, segmentCount,
|
||||||
sampleOffsetUs, representationHolder.extractorWrapper, sampleFormat);
|
sampleOffsetUs, representationHolder.extractorWrapper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,7 +354,6 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||||||
|
|
||||||
public Representation representation;
|
public Representation representation;
|
||||||
public DashSegmentIndex segmentIndex;
|
public DashSegmentIndex segmentIndex;
|
||||||
public Format sampleFormat;
|
|
||||||
|
|
||||||
private long periodDurationUs;
|
private long periodDurationUs;
|
||||||
private int segmentNumShift;
|
private int segmentNumShift;
|
||||||
@ -371,11 +365,9 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||||||
if (mimeTypeIsRawText(containerMimeType)) {
|
if (mimeTypeIsRawText(containerMimeType)) {
|
||||||
extractorWrapper = null;
|
extractorWrapper = null;
|
||||||
} else {
|
} else {
|
||||||
boolean resendFormatOnInit = false;
|
|
||||||
Extractor extractor;
|
Extractor extractor;
|
||||||
if (MimeTypes.APPLICATION_RAWCC.equals(containerMimeType)) {
|
if (MimeTypes.APPLICATION_RAWCC.equals(containerMimeType)) {
|
||||||
extractor = new RawCcExtractor(representation.format);
|
extractor = new RawCcExtractor(representation.format);
|
||||||
resendFormatOnInit = true;
|
|
||||||
} else if (mimeTypeIsWebm(containerMimeType)) {
|
} else if (mimeTypeIsWebm(containerMimeType)) {
|
||||||
extractor = new MatroskaExtractor();
|
extractor = new MatroskaExtractor();
|
||||||
} else {
|
} else {
|
||||||
@ -383,17 +375,12 @@ 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,
|
extractorWrapper = new ChunkExtractorWrapper(extractor, representation.format,
|
||||||
representation.format, true /* preferManifestDrmInitData */,
|
true /* preferManifestDrmInitData */);
|
||||||
resendFormatOnInit);
|
|
||||||
}
|
}
|
||||||
segmentIndex = representation.getIndex();
|
segmentIndex = representation.getIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSampleFormat(Format sampleFormat) {
|
|
||||||
this.sampleFormat = sampleFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateRepresentation(long newPeriodDurationUs, Representation newRepresentation)
|
public void updateRepresentation(long newPeriodDurationUs, Representation newRepresentation)
|
||||||
throws BehindLiveWindowException{
|
throws BehindLiveWindowException{
|
||||||
DashSegmentIndex oldIndex = representation.getIndex();
|
DashSegmentIndex oldIndex = representation.getIndex();
|
||||||
|
@ -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, track, null);
|
| FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_TFDT_BOX, track, null);
|
||||||
extractorWrappers[i] = new ChunkExtractorWrapper(extractor, format, false, false);
|
extractorWrappers[i] = new ChunkExtractorWrapper(extractor, format, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,7 +219,7 @@ public class DefaultSsChunkSource implements SsChunkSource {
|
|||||||
long sampleOffsetUs = chunkStartTimeUs;
|
long sampleOffsetUs = chunkStartTimeUs;
|
||||||
return new ContainerMediaChunk(dataSource, dataSpec, format, trackSelectionReason,
|
return new ContainerMediaChunk(dataSource, dataSpec, format, trackSelectionReason,
|
||||||
trackSelectionData, chunkStartTimeUs, chunkEndTimeUs, chunkIndex, 1, sampleOffsetUs,
|
trackSelectionData, chunkStartTimeUs, chunkEndTimeUs, chunkIndex, 1, sampleOffsetUs,
|
||||||
extractorWrapper, format);
|
extractorWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user