Make DefaultWebmExtractor handle cues and format independently.
* Remove concept of being prepared by simply reporting if format and/or cues are known. * Allow replacement of format and/or cues later in the stream. * Initialization and index segments can be parsed independently of one another but must be in order due to internal WebM dependencies. * Let seekTo() work even when cues are unknown.
This commit is contained in:
parent
32464e6de4
commit
2a82ff353b
@ -83,7 +83,6 @@ public final class DefaultWebmExtractor implements WebmExtractor {
|
|||||||
private SampleHolder tempSampleHolder;
|
private SampleHolder tempSampleHolder;
|
||||||
private boolean sampleRead;
|
private boolean sampleRead;
|
||||||
|
|
||||||
private boolean prepared = false;
|
|
||||||
private long segmentStartOffsetBytes = UNKNOWN;
|
private long segmentStartOffsetBytes = UNKNOWN;
|
||||||
private long segmentEndOffsetBytes = UNKNOWN;
|
private long segmentEndOffsetBytes = UNKNOWN;
|
||||||
private long timecodeScale = 1000000L;
|
private long timecodeScale = 1000000L;
|
||||||
@ -105,13 +104,11 @@ public final class DefaultWebmExtractor implements WebmExtractor {
|
|||||||
/* package */ DefaultWebmExtractor(EbmlReader reader) {
|
/* package */ DefaultWebmExtractor(EbmlReader reader) {
|
||||||
this.reader = reader;
|
this.reader = reader;
|
||||||
this.reader.setEventHandler(new InnerEbmlEventHandler());
|
this.reader.setEventHandler(new InnerEbmlEventHandler());
|
||||||
this.cueTimesUs = new LongArray();
|
|
||||||
this.cueClusterPositions = new LongArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPrepared() {
|
public boolean isPrepared() {
|
||||||
return prepared;
|
return format != null && cues != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -125,8 +122,9 @@ public final class DefaultWebmExtractor implements WebmExtractor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean seekTo(long seekTimeUs, boolean allowNoop) {
|
public boolean seekTo(long seekTimeUs, boolean allowNoop) {
|
||||||
checkPrepared();
|
|
||||||
if (allowNoop
|
if (allowNoop
|
||||||
|
&& cues != null
|
||||||
|
&& clusterTimecodeUs != UNKNOWN
|
||||||
&& simpleBlockTimecodeUs != UNKNOWN
|
&& simpleBlockTimecodeUs != UNKNOWN
|
||||||
&& seekTimeUs >= simpleBlockTimecodeUs) {
|
&& seekTimeUs >= simpleBlockTimecodeUs) {
|
||||||
int clusterIndex = Arrays.binarySearch(cues.timesUs, clusterTimecodeUs);
|
int clusterIndex = Arrays.binarySearch(cues.timesUs, clusterTimecodeUs);
|
||||||
@ -134,19 +132,19 @@ public final class DefaultWebmExtractor implements WebmExtractor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
clusterTimecodeUs = UNKNOWN;
|
||||||
|
simpleBlockTimecodeUs = UNKNOWN;
|
||||||
reader.reset();
|
reader.reset();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SegmentIndex getCues() {
|
public SegmentIndex getCues() {
|
||||||
checkPrepared();
|
|
||||||
return cues;
|
return cues;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MediaFormat getFormat() {
|
public MediaFormat getFormat() {
|
||||||
checkPrepared();
|
|
||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,6 +194,8 @@ public final class DefaultWebmExtractor implements WebmExtractor {
|
|||||||
break;
|
break;
|
||||||
case ID_CUES:
|
case ID_CUES:
|
||||||
cuesSizeBytes = headerSizeBytes + contentsSizeBytes;
|
cuesSizeBytes = headerSizeBytes + contentsSizeBytes;
|
||||||
|
cueTimesUs = new LongArray();
|
||||||
|
cueClusterPositions = new LongArray();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// pass
|
// pass
|
||||||
@ -204,11 +204,16 @@ public final class DefaultWebmExtractor implements WebmExtractor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* package */ boolean onMasterElementEnd(int id) {
|
/* package */ boolean onMasterElementEnd(int id) {
|
||||||
if (id == ID_CUES) {
|
switch (id) {
|
||||||
finishPreparing();
|
case ID_CUES:
|
||||||
return false;
|
buildCues();
|
||||||
|
return false;
|
||||||
|
case ID_VIDEO:
|
||||||
|
buildFormat();
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ boolean onIntegerElement(int id, long value) {
|
/* package */ boolean onIntegerElement(int id, long value) {
|
||||||
@ -330,30 +335,40 @@ public final class DefaultWebmExtractor implements WebmExtractor {
|
|||||||
return TimeUnit.NANOSECONDS.toMicros(unscaledTimecode * timecodeScale);
|
return TimeUnit.NANOSECONDS.toMicros(unscaledTimecode * timecodeScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkPrepared() {
|
/**
|
||||||
if (!prepared) {
|
* Build a video {@link MediaFormat} containing recently gathered Video information, if needed.
|
||||||
throw new IllegalStateException("Parser not yet prepared");
|
*
|
||||||
|
* <p>Replaces the previous {@link #format} only if video width/height have changed.
|
||||||
|
* {@link #format} is guaranteed to not be null after calling this method. In
|
||||||
|
* the event that it can't be built, an {@link IllegalStateException} will be thrown.
|
||||||
|
*/
|
||||||
|
private void buildFormat() {
|
||||||
|
if (pixelWidth != UNKNOWN && pixelHeight != UNKNOWN
|
||||||
|
&& (format == null || format.width != pixelWidth || format.height != pixelHeight)) {
|
||||||
|
format = MediaFormat.createVideoFormat(
|
||||||
|
MimeTypes.VIDEO_VP9, MediaFormat.NO_VALUE, pixelWidth, pixelHeight, null);
|
||||||
|
} else if (format == null) {
|
||||||
|
throw new IllegalStateException("Unable to build format");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void finishPreparing() {
|
/**
|
||||||
if (prepared) {
|
* Build a {@link SegmentIndex} containing recently gathered Cues information.
|
||||||
throw new IllegalStateException("Already prepared");
|
*
|
||||||
} else if (segmentStartOffsetBytes == UNKNOWN) {
|
* <p>{@link #cues} is guaranteed to not be null after calling this method. In
|
||||||
|
* the event that it can't be built, an {@link IllegalStateException} will be thrown.
|
||||||
|
*/
|
||||||
|
private void buildCues() {
|
||||||
|
if (segmentStartOffsetBytes == UNKNOWN) {
|
||||||
throw new IllegalStateException("Segment start/end offsets unknown");
|
throw new IllegalStateException("Segment start/end offsets unknown");
|
||||||
} else if (durationUs == UNKNOWN) {
|
} else if (durationUs == UNKNOWN) {
|
||||||
throw new IllegalStateException("Duration unknown");
|
throw new IllegalStateException("Duration unknown");
|
||||||
} else if (pixelWidth == UNKNOWN || pixelHeight == UNKNOWN) {
|
|
||||||
throw new IllegalStateException("Pixel width/height unknown");
|
|
||||||
} else if (cuesSizeBytes == UNKNOWN) {
|
} else if (cuesSizeBytes == UNKNOWN) {
|
||||||
throw new IllegalStateException("Cues size unknown");
|
throw new IllegalStateException("Cues size unknown");
|
||||||
} else if (cueTimesUs.size() == 0 || cueTimesUs.size() != cueClusterPositions.size()) {
|
} else if (cueTimesUs == null || cueClusterPositions == null
|
||||||
|
|| cueTimesUs.size() == 0 || cueTimesUs.size() != cueClusterPositions.size()) {
|
||||||
throw new IllegalStateException("Invalid/missing cue points");
|
throw new IllegalStateException("Invalid/missing cue points");
|
||||||
}
|
}
|
||||||
|
|
||||||
format = MediaFormat.createVideoFormat(
|
|
||||||
MimeTypes.VIDEO_VP9, MediaFormat.NO_VALUE, pixelWidth, pixelHeight, null);
|
|
||||||
|
|
||||||
int cuePointsSize = cueTimesUs.size();
|
int cuePointsSize = cueTimesUs.size();
|
||||||
int[] sizes = new int[cuePointsSize];
|
int[] sizes = new int[cuePointsSize];
|
||||||
long[] offsets = new long[cuePointsSize];
|
long[] offsets = new long[cuePointsSize];
|
||||||
@ -372,8 +387,6 @@ public final class DefaultWebmExtractor implements WebmExtractor {
|
|||||||
cues = new SegmentIndex((int) cuesSizeBytes, sizes, offsets, durationsUs, timesUs);
|
cues = new SegmentIndex((int) cuesSizeBytes, sizes, offsets, durationsUs, timesUs);
|
||||||
cueTimesUs = null;
|
cueTimesUs = null;
|
||||||
cueClusterPositions = null;
|
cueClusterPositions = null;
|
||||||
|
|
||||||
prepared = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user