mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Support self-contained media chunks.
- Support parsing of moov atoms contained within each chunk. - Also do a small cleanup to WebM parser.
This commit is contained in:
parent
16fe6a809e
commit
4366afc273
@ -54,8 +54,8 @@ public interface SampleSource {
|
|||||||
* Prepares the source.
|
* Prepares the source.
|
||||||
* <p>
|
* <p>
|
||||||
* Preparation may require reading from the data source (e.g. to determine the available tracks
|
* Preparation may require reading from the data source (e.g. to determine the available tracks
|
||||||
* and formats). If insufficient data is available then the call will return rather than block.
|
* and formats). If insufficient data is available then the call will return {@code false} rather
|
||||||
* The method can be called repeatedly until the return value indicates success.
|
* than block. The method can be called repeatedly until the return value indicates success.
|
||||||
*
|
*
|
||||||
* @return True if the source was prepared successfully, false otherwise.
|
* @return True if the source was prepared successfully, false otherwise.
|
||||||
* @throws IOException If an error occurred preparing the source.
|
* @throws IOException If an error occurred preparing the source.
|
||||||
|
@ -160,6 +160,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Listener {
|
|||||||
private int currentLoadableExceptionCount;
|
private int currentLoadableExceptionCount;
|
||||||
private long currentLoadableExceptionTimestamp;
|
private long currentLoadableExceptionTimestamp;
|
||||||
|
|
||||||
|
private MediaFormat downstreamMediaFormat;
|
||||||
private volatile Format downstreamFormat;
|
private volatile Format downstreamFormat;
|
||||||
|
|
||||||
public ChunkSampleSource(ChunkSource chunkSource, LoadControl loadControl,
|
public ChunkSampleSource(ChunkSource chunkSource, LoadControl loadControl,
|
||||||
@ -221,6 +222,7 @@ public class ChunkSampleSource implements SampleSource, Loader.Listener {
|
|||||||
chunkSource.enable();
|
chunkSource.enable();
|
||||||
loadControl.register(this, bufferSizeContribution);
|
loadControl.register(this, bufferSizeContribution);
|
||||||
downstreamFormat = null;
|
downstreamFormat = null;
|
||||||
|
downstreamMediaFormat = null;
|
||||||
downstreamPositionUs = timeUs;
|
downstreamPositionUs = timeUs;
|
||||||
lastSeekPositionUs = timeUs;
|
lastSeekPositionUs = timeUs;
|
||||||
restartFrom(timeUs);
|
restartFrom(timeUs);
|
||||||
@ -288,21 +290,30 @@ public class ChunkSampleSource implements SampleSource, Loader.Listener {
|
|||||||
return readData(track, playbackPositionUs, formatHolder, sampleHolder, false);
|
return readData(track, playbackPositionUs, formatHolder, sampleHolder, false);
|
||||||
} else if (mediaChunk.isLastChunk()) {
|
} else if (mediaChunk.isLastChunk()) {
|
||||||
return END_OF_STREAM;
|
return END_OF_STREAM;
|
||||||
} else {
|
}
|
||||||
IOException chunkSourceException = chunkSource.getError();
|
IOException chunkSourceException = chunkSource.getError();
|
||||||
if (chunkSourceException != null) {
|
if (chunkSourceException != null) {
|
||||||
throw chunkSourceException;
|
throw chunkSourceException;
|
||||||
}
|
}
|
||||||
return NOTHING_READ;
|
return NOTHING_READ;
|
||||||
}
|
}
|
||||||
} else if (downstreamFormat == null || !downstreamFormat.id.equals(mediaChunk.format.id)) {
|
|
||||||
|
if (downstreamFormat == null || !downstreamFormat.equals(mediaChunk.format)) {
|
||||||
notifyDownstreamFormatChanged(mediaChunk.format.id, mediaChunk.trigger,
|
notifyDownstreamFormatChanged(mediaChunk.format.id, mediaChunk.trigger,
|
||||||
mediaChunk.startTimeUs);
|
mediaChunk.startTimeUs);
|
||||||
MediaFormat format = mediaChunk.getMediaFormat();
|
|
||||||
chunkSource.getMaxVideoDimensions(format);
|
|
||||||
formatHolder.format = format;
|
|
||||||
formatHolder.drmInitData = mediaChunk.getPsshInfo();
|
|
||||||
downstreamFormat = mediaChunk.format;
|
downstreamFormat = mediaChunk.format;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mediaChunk.prepare()) {
|
||||||
|
return NOTHING_READ;
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaFormat mediaFormat = mediaChunk.getMediaFormat();
|
||||||
|
if (downstreamMediaFormat == null || !downstreamMediaFormat.equals(mediaFormat)) {
|
||||||
|
chunkSource.getMaxVideoDimensions(mediaFormat);
|
||||||
|
formatHolder.format = mediaFormat;
|
||||||
|
formatHolder.drmInitData = mediaChunk.getPsshInfo();
|
||||||
|
downstreamMediaFormat = mediaFormat;
|
||||||
return FORMAT_READ;
|
return FORMAT_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer.chunk;
|
package com.google.android.exoplayer.chunk;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,7 +99,7 @@ public class Format {
|
|||||||
*/
|
*/
|
||||||
public Format(String id, String mimeType, int width, int height, int numChannels,
|
public Format(String id, String mimeType, int width, int height, int numChannels,
|
||||||
int audioSamplingRate, int bandwidth) {
|
int audioSamplingRate, int bandwidth) {
|
||||||
this.id = id;
|
this.id = Assertions.checkNotNull(id);
|
||||||
this.mimeType = mimeType;
|
this.mimeType = mimeType;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
@ -106,4 +108,19 @@ public class Format {
|
|||||||
this.bandwidth = bandwidth;
|
this.bandwidth = bandwidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements equality based on {@link #id} only.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null || getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Format other = (Format) obj;
|
||||||
|
return other.id.equals(id);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -87,8 +87,22 @@ public abstract class MediaChunk extends Chunk {
|
|||||||
*/
|
*/
|
||||||
public abstract boolean seekTo(long positionUs, boolean allowNoop);
|
public abstract boolean seekTo(long positionUs, boolean allowNoop);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares the chunk for reading. Does nothing if the chunk is already prepared.
|
||||||
|
* <p>
|
||||||
|
* Preparation may require consuming some of the chunk. If the data is not yet available then
|
||||||
|
* this method will return {@code false} rather than block. The method can be called repeatedly
|
||||||
|
* until the return value indicates success.
|
||||||
|
*
|
||||||
|
* @return True if the chunk was prepared. False otherwise.
|
||||||
|
* @throws ParserException If an error occurs parsing the media data.
|
||||||
|
*/
|
||||||
|
public abstract boolean prepare() throws ParserException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the next media sample from the chunk.
|
* Reads the next media sample from the chunk.
|
||||||
|
* <p>
|
||||||
|
* Should only be called after the chunk has been successfully prepared.
|
||||||
*
|
*
|
||||||
* @param holder A holder to store the read sample.
|
* @param holder A holder to store the read sample.
|
||||||
* @return True if a sample was read. False if more data is still required.
|
* @return True if a sample was read. False if more data is still required.
|
||||||
@ -99,6 +113,8 @@ public abstract class MediaChunk extends Chunk {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the media format of the samples contained within this chunk.
|
* Returns the media format of the samples contained within this chunk.
|
||||||
|
* <p>
|
||||||
|
* Should only be called after the chunk has been successfully prepared.
|
||||||
*
|
*
|
||||||
* @return The sample media format.
|
* @return The sample media format.
|
||||||
*/
|
*/
|
||||||
@ -106,6 +122,8 @@ public abstract class MediaChunk extends Chunk {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the pssh information associated with the chunk.
|
* Returns the pssh information associated with the chunk.
|
||||||
|
* <p>
|
||||||
|
* Should only be called after the chunk has been successfully prepared.
|
||||||
*
|
*
|
||||||
* @return The pssh information.
|
* @return The pssh information.
|
||||||
*/
|
*/
|
||||||
|
@ -33,24 +33,34 @@ import java.util.UUID;
|
|||||||
public final class Mp4MediaChunk extends MediaChunk {
|
public final class Mp4MediaChunk extends MediaChunk {
|
||||||
|
|
||||||
private final FragmentedMp4Extractor extractor;
|
private final FragmentedMp4Extractor extractor;
|
||||||
|
private final boolean maybeSelfContained;
|
||||||
private final long sampleOffsetUs;
|
private final long sampleOffsetUs;
|
||||||
|
|
||||||
|
private boolean prepared;
|
||||||
|
private MediaFormat mediaFormat;
|
||||||
|
private Map<UUID, byte[]> psshInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param dataSource A {@link DataSource} for loading the data.
|
* @param dataSource A {@link DataSource} for loading the data.
|
||||||
* @param dataSpec Defines the data to be loaded.
|
* @param dataSpec Defines the data to be loaded.
|
||||||
* @param format The format of the stream to which this chunk belongs.
|
* @param format The format of the stream to which this chunk belongs.
|
||||||
* @param extractor The extractor that will be used to extract the samples.
|
|
||||||
* @param trigger The reason for this chunk being selected.
|
* @param trigger The reason for this chunk being selected.
|
||||||
* @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 sampleOffsetUs An offset to subtract from the sample timestamps parsed by the extractor.
|
|
||||||
* @param nextChunkIndex The index of the next chunk, or -1 if this is the last chunk.
|
* @param nextChunkIndex The index of the next chunk, or -1 if this is the last chunk.
|
||||||
|
* @param extractor The extractor that will be used to extract the samples.
|
||||||
|
* @param maybeSelfContained Set to true if this chunk might be self contained, meaning it might
|
||||||
|
* contain a moov atom defining the media format of the chunk. This parameter can always be
|
||||||
|
* safely set to true. Setting to false where the chunk is known to not be self contained may
|
||||||
|
* improve startup latency.
|
||||||
|
* @param sampleOffsetUs An offset to subtract from the sample timestamps parsed by the extractor.
|
||||||
*/
|
*/
|
||||||
public Mp4MediaChunk(DataSource dataSource, DataSpec dataSpec, Format format,
|
public Mp4MediaChunk(DataSource dataSource, DataSpec dataSpec, Format format,
|
||||||
int trigger, FragmentedMp4Extractor extractor, long startTimeUs, long endTimeUs,
|
int trigger, long startTimeUs, long endTimeUs, int nextChunkIndex,
|
||||||
long sampleOffsetUs, int nextChunkIndex) {
|
FragmentedMp4Extractor extractor, boolean maybeSelfContained, long sampleOffsetUs) {
|
||||||
super(dataSource, dataSpec, format, trigger, startTimeUs, endTimeUs, nextChunkIndex);
|
super(dataSource, dataSpec, format, trigger, startTimeUs, endTimeUs, nextChunkIndex);
|
||||||
this.extractor = extractor;
|
this.extractor = extractor;
|
||||||
|
this.maybeSelfContained = maybeSelfContained;
|
||||||
this.sampleOffsetUs = sampleOffsetUs;
|
this.sampleOffsetUs = sampleOffsetUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,6 +80,29 @@ public final class Mp4MediaChunk extends MediaChunk {
|
|||||||
return isDiscontinuous;
|
return isDiscontinuous;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean prepare() throws ParserException {
|
||||||
|
if (!prepared) {
|
||||||
|
if (maybeSelfContained) {
|
||||||
|
// Read up to the first sample. Once we're there, we know that the extractor must have
|
||||||
|
// parsed a moov atom if the chunk contains one.
|
||||||
|
NonBlockingInputStream inputStream = getNonBlockingInputStream();
|
||||||
|
Assertions.checkState(inputStream != null);
|
||||||
|
int result = extractor.read(inputStream, null);
|
||||||
|
prepared = (result & FragmentedMp4Extractor.RESULT_NEED_SAMPLE_HOLDER) != 0;
|
||||||
|
} else {
|
||||||
|
// We know there isn't a moov atom. The extractor must have parsed one from a separate
|
||||||
|
// initialization chunk.
|
||||||
|
prepared = true;
|
||||||
|
}
|
||||||
|
if (prepared) {
|
||||||
|
mediaFormat = Assertions.checkNotNull(extractor.getFormat());
|
||||||
|
psshInfo = extractor.getPsshInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prepared;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean read(SampleHolder holder) throws ParserException {
|
public boolean read(SampleHolder holder) throws ParserException {
|
||||||
NonBlockingInputStream inputStream = getNonBlockingInputStream();
|
NonBlockingInputStream inputStream = getNonBlockingInputStream();
|
||||||
@ -84,12 +117,12 @@ public final class Mp4MediaChunk extends MediaChunk {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MediaFormat getMediaFormat() {
|
public MediaFormat getMediaFormat() {
|
||||||
return extractor.getFormat();
|
return mediaFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<UUID, byte[]> getPsshInfo() {
|
public Map<UUID, byte[]> getPsshInfo() {
|
||||||
return extractor.getPsshInfo();
|
return psshInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,11 @@ public class SingleSampleMediaChunk extends MediaChunk {
|
|||||||
this.headerData = headerData;
|
this.headerData = headerData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean prepare() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean read(SampleHolder holder) {
|
public boolean read(SampleHolder holder) {
|
||||||
NonBlockingInputStream inputStream = getNonBlockingInputStream();
|
NonBlockingInputStream inputStream = getNonBlockingInputStream();
|
||||||
|
@ -64,6 +64,11 @@ public final class WebmMediaChunk extends MediaChunk {
|
|||||||
return isDiscontinuous;
|
return isDiscontinuous;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean prepare() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean read(SampleHolder holder) {
|
public boolean read(SampleHolder holder) {
|
||||||
NonBlockingInputStream inputStream = getNonBlockingInputStream();
|
NonBlockingInputStream inputStream = getNonBlockingInputStream();
|
||||||
|
@ -231,8 +231,8 @@ public class DashMp4ChunkSource implements ChunkSource {
|
|||||||
|
|
||||||
DataSpec dataSpec = new DataSpec(representation.uri, offset, size,
|
DataSpec dataSpec = new DataSpec(representation.uri, offset, size,
|
||||||
representation.getCacheKey());
|
representation.getCacheKey());
|
||||||
return new Mp4MediaChunk(dataSource, dataSpec, representation.format, trigger, extractor,
|
return new Mp4MediaChunk(dataSource, dataSpec, representation.format, trigger, startTimeUs,
|
||||||
startTimeUs, endTimeUs, 0, nextIndex);
|
endTimeUs, nextIndex, extractor, false, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class InitializationMp4Loadable extends Chunk {
|
private static class InitializationMp4Loadable extends Chunk {
|
||||||
|
@ -83,9 +83,13 @@ public final class FragmentedMp4Extractor {
|
|||||||
* A sidx atom was read. The parsed data can be read using {@link #getSegmentIndex()}.
|
* A sidx atom was read. The parsed data can be read using {@link #getSegmentIndex()}.
|
||||||
*/
|
*/
|
||||||
public static final int RESULT_READ_SIDX = 32;
|
public static final int RESULT_READ_SIDX = 32;
|
||||||
|
/**
|
||||||
|
* The next thing to be read is a sample, but a {@link SampleHolder} was not supplied.
|
||||||
|
*/
|
||||||
|
public static final int RESULT_NEED_SAMPLE_HOLDER = 64;
|
||||||
|
|
||||||
private static final int READ_TERMINATING_RESULTS = RESULT_NEED_MORE_DATA | RESULT_END_OF_STREAM
|
private static final int READ_TERMINATING_RESULTS = RESULT_NEED_MORE_DATA | RESULT_END_OF_STREAM
|
||||||
| RESULT_READ_SAMPLE_FULL;
|
| RESULT_READ_SAMPLE_FULL | RESULT_NEED_SAMPLE_HOLDER;
|
||||||
private static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1};
|
private static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1};
|
||||||
private static final byte[] PIFF_SAMPLE_ENCRYPTION_BOX_EXTENDED_TYPE =
|
private static final byte[] PIFF_SAMPLE_ENCRYPTION_BOX_EXTENDED_TYPE =
|
||||||
new byte[] {-94, 57, 79, 82, 90, -101, 79, 20, -94, 68, 108, 66, 124, 100, -115, -12};
|
new byte[] {-94, 57, 79, 82, 90, -101, 79, 20, -94, 68, 108, 66, 124, 100, -115, -12};
|
||||||
@ -272,7 +276,8 @@ public final class FragmentedMp4Extractor {
|
|||||||
* in subsequent calls until the whole sample has been read.
|
* in subsequent calls until the whole sample has been read.
|
||||||
*
|
*
|
||||||
* @param inputStream The input stream from which data should be read.
|
* @param inputStream The input stream from which data should be read.
|
||||||
* @param out A {@link SampleHolder} into which the sample should be read.
|
* @param out A {@link SampleHolder} into which the next sample should be read. If null then
|
||||||
|
* {@link #RESULT_NEED_SAMPLE_HOLDER} will be returned once a sample has been reached.
|
||||||
* @return One or more of the {@code RESULT_*} flags defined in this class.
|
* @return One or more of the {@code RESULT_*} flags defined in this class.
|
||||||
* @throws ParserException If an error occurs parsing the media data.
|
* @throws ParserException If an error occurs parsing the media data.
|
||||||
*/
|
*/
|
||||||
@ -1142,6 +1147,9 @@ public final class FragmentedMp4Extractor {
|
|||||||
|
|
||||||
@SuppressLint("InlinedApi")
|
@SuppressLint("InlinedApi")
|
||||||
private int readSample(NonBlockingInputStream inputStream, SampleHolder out) {
|
private int readSample(NonBlockingInputStream inputStream, SampleHolder out) {
|
||||||
|
if (out == null) {
|
||||||
|
return RESULT_NEED_SAMPLE_HOLDER;
|
||||||
|
}
|
||||||
int sampleSize = fragmentRun.sampleSizeTable[sampleIndex];
|
int sampleSize = fragmentRun.sampleSizeTable[sampleIndex];
|
||||||
ByteBuffer outputData = out.data;
|
ByteBuffer outputData = out.data;
|
||||||
if (parserState == STATE_READING_SAMPLE_START) {
|
if (parserState == STATE_READING_SAMPLE_START) {
|
||||||
|
@ -36,7 +36,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
* More info about WebM is <a href="http://www.webmproject.org/code/specs/container/">here</a>.
|
* More info about WebM is <a href="http://www.webmproject.org/code/specs/container/">here</a>.
|
||||||
*/
|
*/
|
||||||
@TargetApi(16)
|
@TargetApi(16)
|
||||||
public final class DefaultWebmExtractor implements WebmExtractor, EbmlEventHandler {
|
public final class DefaultWebmExtractor implements WebmExtractor {
|
||||||
|
|
||||||
private static final String DOC_TYPE_WEBM = "webm";
|
private static final String DOC_TYPE_WEBM = "webm";
|
||||||
private static final String CODEC_ID_VP9 = "V_VP9";
|
private static final String CODEC_ID_VP9 = "V_VP9";
|
||||||
@ -104,7 +104,7 @@ public final class DefaultWebmExtractor implements WebmExtractor, EbmlEventHandl
|
|||||||
|
|
||||||
/* package */ DefaultWebmExtractor(EbmlReader reader) {
|
/* package */ DefaultWebmExtractor(EbmlReader reader) {
|
||||||
this.reader = reader;
|
this.reader = reader;
|
||||||
this.reader.setEventHandler(this);
|
this.reader.setEventHandler(new InnerEbmlEventHandler());
|
||||||
this.cueTimesUs = new LongArray();
|
this.cueTimesUs = new LongArray();
|
||||||
this.cueClusterPositions = new LongArray();
|
this.cueClusterPositions = new LongArray();
|
||||||
}
|
}
|
||||||
@ -150,8 +150,7 @@ public final class DefaultWebmExtractor implements WebmExtractor, EbmlEventHandl
|
|||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/* package */ int getElementType(int id) {
|
||||||
public int getElementType(int id) {
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case ID_EBML:
|
case ID_EBML:
|
||||||
case ID_SEGMENT:
|
case ID_SEGMENT:
|
||||||
@ -185,8 +184,7 @@ public final class DefaultWebmExtractor implements WebmExtractor, EbmlEventHandl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/* package */ boolean onMasterElementStart(
|
||||||
public boolean onMasterElementStart(
|
|
||||||
int id, long elementOffsetBytes, int headerSizeBytes, long contentsSizeBytes) {
|
int id, long elementOffsetBytes, int headerSizeBytes, long contentsSizeBytes) {
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case ID_SEGMENT:
|
case ID_SEGMENT:
|
||||||
@ -205,8 +203,7 @@ public final class DefaultWebmExtractor implements WebmExtractor, EbmlEventHandl
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/* package */ boolean onMasterElementEnd(int id) {
|
||||||
public boolean onMasterElementEnd(int id) {
|
|
||||||
if (id == ID_CUES) {
|
if (id == ID_CUES) {
|
||||||
finishPreparing();
|
finishPreparing();
|
||||||
return false;
|
return false;
|
||||||
@ -214,8 +211,7 @@ public final class DefaultWebmExtractor implements WebmExtractor, EbmlEventHandl
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/* package */ boolean onIntegerElement(int id, long value) {
|
||||||
public boolean onIntegerElement(int id, long value) {
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case ID_EBML_READ_VERSION:
|
case ID_EBML_READ_VERSION:
|
||||||
// Validate that EBMLReadVersion is supported. This extractor only supports v1.
|
// Validate that EBMLReadVersion is supported. This extractor only supports v1.
|
||||||
@ -253,16 +249,14 @@ public final class DefaultWebmExtractor implements WebmExtractor, EbmlEventHandl
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/* package */ boolean onFloatElement(int id, double value) {
|
||||||
public boolean onFloatElement(int id, double value) {
|
|
||||||
if (id == ID_DURATION) {
|
if (id == ID_DURATION) {
|
||||||
durationUs = scaleTimecodeToUs((long) value);
|
durationUs = scaleTimecodeToUs((long) value);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/* package */ boolean onStringElement(int id, String value) {
|
||||||
public boolean onStringElement(int id, String value) {
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case ID_DOC_TYPE:
|
case ID_DOC_TYPE:
|
||||||
// Validate that DocType is supported. This extractor only supports "webm".
|
// Validate that DocType is supported. This extractor only supports "webm".
|
||||||
@ -282,8 +276,7 @@ public final class DefaultWebmExtractor implements WebmExtractor, EbmlEventHandl
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/* package */ boolean onBinaryElement(
|
||||||
public boolean onBinaryElement(
|
|
||||||
int id, long elementOffsetBytes, int headerSizeBytes, int contentsSizeBytes,
|
int id, long elementOffsetBytes, int headerSizeBytes, int contentsSizeBytes,
|
||||||
NonBlockingInputStream inputStream) {
|
NonBlockingInputStream inputStream) {
|
||||||
if (id == ID_SIMPLE_BLOCK) {
|
if (id == ID_SIMPLE_BLOCK) {
|
||||||
@ -383,4 +376,52 @@ public final class DefaultWebmExtractor implements WebmExtractor, EbmlEventHandl
|
|||||||
prepared = true;
|
prepared = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Passes events through to {@link DefaultWebmExtractor} as
|
||||||
|
* callbacks from {@link EbmlReader} are received.
|
||||||
|
*/
|
||||||
|
private final class InnerEbmlEventHandler implements EbmlEventHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getElementType(int id) {
|
||||||
|
return DefaultWebmExtractor.this.getElementType(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMasterElementStart(
|
||||||
|
int id, long elementOffsetBytes, int headerSizeBytes, long contentsSizeBytes) {
|
||||||
|
return DefaultWebmExtractor.this.onMasterElementStart(
|
||||||
|
id, elementOffsetBytes, headerSizeBytes, contentsSizeBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMasterElementEnd(int id) {
|
||||||
|
return DefaultWebmExtractor.this.onMasterElementEnd(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onIntegerElement(int id, long value) {
|
||||||
|
return DefaultWebmExtractor.this.onIntegerElement(id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onFloatElement(int id, double value) {
|
||||||
|
return DefaultWebmExtractor.this.onFloatElement(id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onStringElement(int id, String value) {
|
||||||
|
return DefaultWebmExtractor.this.onStringElement(id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onBinaryElement(
|
||||||
|
int id, long elementOffsetBytes, int headerSizeBytes, int contentsSizeBytes,
|
||||||
|
NonBlockingInputStream inputStream) {
|
||||||
|
return DefaultWebmExtractor.this.onBinaryElement(
|
||||||
|
id, elementOffsetBytes, headerSizeBytes, contentsSizeBytes, inputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -235,8 +235,8 @@ public class SmoothStreamingChunkSource implements ChunkSource {
|
|||||||
DataSpec dataSpec = new DataSpec(uri, offset, -1, cacheKey);
|
DataSpec dataSpec = new DataSpec(uri, offset, -1, cacheKey);
|
||||||
// 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.
|
||||||
return new Mp4MediaChunk(dataSource, dataSpec, formatInfo, trigger, extractor,
|
return new Mp4MediaChunk(dataSource, dataSpec, formatInfo, trigger, chunkStartTimeUs,
|
||||||
chunkStartTimeUs, nextStartTimeUs, -chunkStartTimeUs, nextChunkIndex);
|
nextStartTimeUs, nextChunkIndex, extractor, false, -chunkStartTimeUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] getKeyId(byte[] initData) {
|
private static byte[] getKeyId(byte[] initData) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user