mirror of
https://github.com/samsonjs/media.git
synced 2026-03-27 09:45:47 +00:00
Allow extension of MatroskaExtractor
Subclasses can handle arbitrary elements. PiperOrigin-RevId: 233057241
This commit is contained in:
parent
36da5fb511
commit
9c3ac92ae8
5 changed files with 261 additions and 198 deletions
|
|
@ -52,7 +52,7 @@ import java.util.ArrayDeque;
|
|||
private final ArrayDeque<MasterElement> masterElementsStack;
|
||||
private final VarintReader varintReader;
|
||||
|
||||
private EbmlReaderOutput output;
|
||||
private EbmlProcessor processor;
|
||||
private @ElementState int elementState;
|
||||
private int elementId;
|
||||
private long elementContentSize;
|
||||
|
|
@ -64,8 +64,8 @@ import java.util.ArrayDeque;
|
|||
}
|
||||
|
||||
@Override
|
||||
public void init(EbmlReaderOutput eventHandler) {
|
||||
this.output = eventHandler;
|
||||
public void init(EbmlProcessor processor) {
|
||||
this.processor = processor;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -77,11 +77,11 @@ import java.util.ArrayDeque;
|
|||
|
||||
@Override
|
||||
public boolean read(ExtractorInput input) throws IOException, InterruptedException {
|
||||
Assertions.checkState(output != null);
|
||||
Assertions.checkNotNull(processor);
|
||||
while (true) {
|
||||
if (!masterElementsStack.isEmpty()
|
||||
&& input.getPosition() >= masterElementsStack.peek().elementEndPosition) {
|
||||
output.endMasterElement(masterElementsStack.pop().elementId);
|
||||
processor.endMasterElement(masterElementsStack.pop().elementId);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -103,42 +103,42 @@ import java.util.ArrayDeque;
|
|||
elementState = ELEMENT_STATE_READ_CONTENT;
|
||||
}
|
||||
|
||||
@EbmlReaderOutput.ElementType int type = output.getElementType(elementId);
|
||||
@EbmlProcessor.ElementType int type = processor.getElementType(elementId);
|
||||
switch (type) {
|
||||
case EbmlReaderOutput.TYPE_MASTER:
|
||||
case EbmlProcessor.ELEMENT_TYPE_MASTER:
|
||||
long elementContentPosition = input.getPosition();
|
||||
long elementEndPosition = elementContentPosition + elementContentSize;
|
||||
masterElementsStack.push(new MasterElement(elementId, elementEndPosition));
|
||||
output.startMasterElement(elementId, elementContentPosition, elementContentSize);
|
||||
processor.startMasterElement(elementId, elementContentPosition, elementContentSize);
|
||||
elementState = ELEMENT_STATE_READ_ID;
|
||||
return true;
|
||||
case EbmlReaderOutput.TYPE_UNSIGNED_INT:
|
||||
case EbmlProcessor.ELEMENT_TYPE_UNSIGNED_INT:
|
||||
if (elementContentSize > MAX_INTEGER_ELEMENT_SIZE_BYTES) {
|
||||
throw new ParserException("Invalid integer size: " + elementContentSize);
|
||||
}
|
||||
output.integerElement(elementId, readInteger(input, (int) elementContentSize));
|
||||
processor.integerElement(elementId, readInteger(input, (int) elementContentSize));
|
||||
elementState = ELEMENT_STATE_READ_ID;
|
||||
return true;
|
||||
case EbmlReaderOutput.TYPE_FLOAT:
|
||||
case EbmlProcessor.ELEMENT_TYPE_FLOAT:
|
||||
if (elementContentSize != VALID_FLOAT32_ELEMENT_SIZE_BYTES
|
||||
&& elementContentSize != VALID_FLOAT64_ELEMENT_SIZE_BYTES) {
|
||||
throw new ParserException("Invalid float size: " + elementContentSize);
|
||||
}
|
||||
output.floatElement(elementId, readFloat(input, (int) elementContentSize));
|
||||
processor.floatElement(elementId, readFloat(input, (int) elementContentSize));
|
||||
elementState = ELEMENT_STATE_READ_ID;
|
||||
return true;
|
||||
case EbmlReaderOutput.TYPE_STRING:
|
||||
case EbmlProcessor.ELEMENT_TYPE_STRING:
|
||||
if (elementContentSize > Integer.MAX_VALUE) {
|
||||
throw new ParserException("String element size: " + elementContentSize);
|
||||
}
|
||||
output.stringElement(elementId, readString(input, (int) elementContentSize));
|
||||
processor.stringElement(elementId, readString(input, (int) elementContentSize));
|
||||
elementState = ELEMENT_STATE_READ_ID;
|
||||
return true;
|
||||
case EbmlReaderOutput.TYPE_BINARY:
|
||||
output.binaryElement(elementId, (int) elementContentSize, input);
|
||||
case EbmlProcessor.ELEMENT_TYPE_BINARY:
|
||||
processor.binaryElement(elementId, (int) elementContentSize, input);
|
||||
elementState = ELEMENT_STATE_READ_ID;
|
||||
return true;
|
||||
case EbmlReaderOutput.TYPE_UNKNOWN:
|
||||
case EbmlProcessor.ELEMENT_TYPE_UNKNOWN:
|
||||
input.skipFully((int) elementContentSize);
|
||||
elementState = ELEMENT_STATE_READ_ID;
|
||||
break;
|
||||
|
|
@ -167,7 +167,7 @@ import java.util.ArrayDeque;
|
|||
int varintLength = VarintReader.parseUnsignedVarintLength(scratch[0]);
|
||||
if (varintLength != C.LENGTH_UNSET && varintLength <= MAX_ID_BYTES) {
|
||||
int potentialId = (int) VarintReader.assembleVarint(scratch, varintLength, false);
|
||||
if (output.isLevel1Element(potentialId)) {
|
||||
if (processor.isLevel1Element(potentialId)) {
|
||||
input.skipFully(varintLength);
|
||||
return potentialId;
|
||||
}
|
||||
|
|
@ -243,7 +243,7 @@ import java.util.ArrayDeque;
|
|||
|
||||
/**
|
||||
* Used in {@link #masterElementsStack} to track when the current master element ends, so that
|
||||
* {@link EbmlReaderOutput#endMasterElement(int)} can be called.
|
||||
* {@link EbmlProcessor#endMasterElement(int)} can be called.
|
||||
*/
|
||||
private static final class MasterElement {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
* Copyright (C) 2019 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.
|
||||
|
|
@ -23,41 +23,48 @@ import java.lang.annotation.Documented;
|
|||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Defines EBML element IDs/types and reacts to events.
|
||||
*/
|
||||
/* package */ interface EbmlReaderOutput {
|
||||
/** Defines EBML element IDs/types and processes events. */
|
||||
public interface EbmlProcessor {
|
||||
|
||||
/**
|
||||
* EBML element types. One of {@link #TYPE_UNKNOWN}, {@link #TYPE_MASTER}, {@link
|
||||
* #TYPE_UNSIGNED_INT}, {@link #TYPE_STRING}, {@link #TYPE_BINARY} or {@link #TYPE_FLOAT}.
|
||||
* EBML element types. One of {@link #ELEMENT_TYPE_UNKNOWN}, {@link #ELEMENT_TYPE_MASTER}, {@link
|
||||
* #ELEMENT_TYPE_UNSIGNED_INT}, {@link #ELEMENT_TYPE_STRING}, {@link #ELEMENT_TYPE_BINARY} or
|
||||
* {@link #ELEMENT_TYPE_FLOAT}.
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({TYPE_UNKNOWN, TYPE_MASTER, TYPE_UNSIGNED_INT, TYPE_STRING, TYPE_BINARY, TYPE_FLOAT})
|
||||
@IntDef({
|
||||
ELEMENT_TYPE_UNKNOWN,
|
||||
ELEMENT_TYPE_MASTER,
|
||||
ELEMENT_TYPE_UNSIGNED_INT,
|
||||
ELEMENT_TYPE_STRING,
|
||||
ELEMENT_TYPE_BINARY,
|
||||
ELEMENT_TYPE_FLOAT
|
||||
})
|
||||
@interface ElementType {}
|
||||
/** Type for unknown elements. */
|
||||
int TYPE_UNKNOWN = 0;
|
||||
int ELEMENT_TYPE_UNKNOWN = 0;
|
||||
/** Type for elements that contain child elements. */
|
||||
int TYPE_MASTER = 1;
|
||||
int ELEMENT_TYPE_MASTER = 1;
|
||||
/** Type for integer value elements of up to 8 bytes. */
|
||||
int TYPE_UNSIGNED_INT = 2;
|
||||
int ELEMENT_TYPE_UNSIGNED_INT = 2;
|
||||
/** Type for string elements. */
|
||||
int TYPE_STRING = 3;
|
||||
int ELEMENT_TYPE_STRING = 3;
|
||||
/** Type for binary elements. */
|
||||
int TYPE_BINARY = 4;
|
||||
int ELEMENT_TYPE_BINARY = 4;
|
||||
/** Type for IEEE floating point value elements of either 4 or 8 bytes. */
|
||||
int TYPE_FLOAT = 5;
|
||||
int ELEMENT_TYPE_FLOAT = 5;
|
||||
|
||||
/**
|
||||
* Maps an element ID to a corresponding type.
|
||||
*
|
||||
* <p>If {@link #TYPE_UNKNOWN} is returned then the element is skipped. Note that all children of
|
||||
* a skipped element are also skipped.
|
||||
* <p>If {@link #ELEMENT_TYPE_UNKNOWN} is returned then the element is skipped. Note that all
|
||||
* children of a skipped element are also skipped.
|
||||
*
|
||||
* @param id The element ID to map.
|
||||
* @return One of {@link #TYPE_UNKNOWN}, {@link #TYPE_MASTER}, {@link #TYPE_UNSIGNED_INT}, {@link
|
||||
* #TYPE_STRING}, {@link #TYPE_BINARY} and {@link #TYPE_FLOAT}.
|
||||
* @return One of {@link #ELEMENT_TYPE_UNKNOWN}, {@link #ELEMENT_TYPE_MASTER}, {@link
|
||||
* #ELEMENT_TYPE_UNSIGNED_INT}, {@link #ELEMENT_TYPE_STRING}, {@link #ELEMENT_TYPE_BINARY} and
|
||||
* {@link #ELEMENT_TYPE_FLOAT}.
|
||||
*/
|
||||
@ElementType
|
||||
int getElementType(int id);
|
||||
|
|
@ -20,20 +20,20 @@ import com.google.android.exoplayer2.extractor.ExtractorInput;
|
|||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Event-driven EBML reader that delivers events to an {@link EbmlReaderOutput}.
|
||||
* <p>
|
||||
* EBML can be summarized as a binary XML format somewhat similar to Protocol Buffers. It was
|
||||
* originally designed for the Matroska container format. More information about EBML and
|
||||
* Matroska is available <a href="http://www.matroska.org/technical/specs/index.html">here</a>.
|
||||
* Event-driven EBML reader that delivers events to an {@link EbmlProcessor}.
|
||||
*
|
||||
* <p>EBML can be summarized as a binary XML format somewhat similar to Protocol Buffers. It was
|
||||
* originally designed for the Matroska container format. More information about EBML and Matroska
|
||||
* is available <a href="http://www.matroska.org/technical/specs/index.html">here</a>.
|
||||
*/
|
||||
/* package */ interface EbmlReader {
|
||||
|
||||
/**
|
||||
* Initializes the extractor with an {@link EbmlReaderOutput}.
|
||||
* Initializes the extractor with an {@link EbmlProcessor}.
|
||||
*
|
||||
* @param output An {@link EbmlReaderOutput} to receive events.
|
||||
* @param processor An {@link EbmlProcessor} to process events.
|
||||
*/
|
||||
void init(EbmlReaderOutput output);
|
||||
void init(EbmlProcessor processor);
|
||||
|
||||
/**
|
||||
* Resets the state of the reader.
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.extractor.mkv;
|
||||
|
||||
import android.support.annotation.CallSuper;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Pair;
|
||||
|
|
@ -57,10 +58,8 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Extracts data from the Matroska and WebM container formats.
|
||||
*/
|
||||
public final class MatroskaExtractor implements Extractor {
|
||||
/** Extracts data from the Matroska and WebM container formats. */
|
||||
public class MatroskaExtractor implements Extractor {
|
||||
|
||||
/** Factory for {@link MatroskaExtractor} instances. */
|
||||
public static final ExtractorsFactory FACTORY = () -> new Extractor[] {new MatroskaExtractor()};
|
||||
|
|
@ -387,7 +386,7 @@ public final class MatroskaExtractor implements Extractor {
|
|||
|
||||
/* package */ MatroskaExtractor(EbmlReader reader, @Flags int flags) {
|
||||
this.reader = reader;
|
||||
this.reader.init(new InnerEbmlReaderOutput());
|
||||
this.reader.init(new InnerEbmlProcessor());
|
||||
seekForCuesEnabled = (flags & FLAG_DISABLE_SEEK_FOR_CUES) == 0;
|
||||
varintReader = new VarintReader();
|
||||
tracks = new SparseArray<>();
|
||||
|
|
@ -403,17 +402,17 @@ public final class MatroskaExtractor implements Extractor {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean sniff(ExtractorInput input) throws IOException, InterruptedException {
|
||||
public final boolean sniff(ExtractorInput input) throws IOException, InterruptedException {
|
||||
return new Sniffer().sniff(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(ExtractorOutput output) {
|
||||
public final void init(ExtractorOutput output) {
|
||||
extractorOutput = output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long position, long timeUs) {
|
||||
public final void seek(long position, long timeUs) {
|
||||
clusterTimecodeUs = C.TIME_UNSET;
|
||||
blockState = BLOCK_STATE_START;
|
||||
reader.reset();
|
||||
|
|
@ -425,13 +424,13 @@ public final class MatroskaExtractor implements Extractor {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
public final void release() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException,
|
||||
InterruptedException {
|
||||
public final int read(ExtractorInput input, PositionHolder seekPosition)
|
||||
throws IOException, InterruptedException {
|
||||
sampleRead = false;
|
||||
boolean continueReading = true;
|
||||
while (continueReading && !sampleRead) {
|
||||
|
|
@ -449,7 +448,125 @@ public final class MatroskaExtractor implements Extractor {
|
|||
return Extractor.RESULT_CONTINUE;
|
||||
}
|
||||
|
||||
/* package */ void startMasterElement(int id, long contentPosition, long contentSize)
|
||||
/**
|
||||
* Maps an element ID to a corresponding type.
|
||||
*
|
||||
* @see EbmlProcessor#getElementType(int)
|
||||
*/
|
||||
@CallSuper
|
||||
@EbmlProcessor.ElementType
|
||||
protected int getElementType(int id) {
|
||||
switch (id) {
|
||||
case ID_EBML:
|
||||
case ID_SEGMENT:
|
||||
case ID_SEEK_HEAD:
|
||||
case ID_SEEK:
|
||||
case ID_INFO:
|
||||
case ID_CLUSTER:
|
||||
case ID_TRACKS:
|
||||
case ID_TRACK_ENTRY:
|
||||
case ID_AUDIO:
|
||||
case ID_VIDEO:
|
||||
case ID_CONTENT_ENCODINGS:
|
||||
case ID_CONTENT_ENCODING:
|
||||
case ID_CONTENT_COMPRESSION:
|
||||
case ID_CONTENT_ENCRYPTION:
|
||||
case ID_CONTENT_ENCRYPTION_AES_SETTINGS:
|
||||
case ID_CUES:
|
||||
case ID_CUE_POINT:
|
||||
case ID_CUE_TRACK_POSITIONS:
|
||||
case ID_BLOCK_GROUP:
|
||||
case ID_PROJECTION:
|
||||
case ID_COLOUR:
|
||||
case ID_MASTERING_METADATA:
|
||||
return EbmlProcessor.ELEMENT_TYPE_MASTER;
|
||||
case ID_EBML_READ_VERSION:
|
||||
case ID_DOC_TYPE_READ_VERSION:
|
||||
case ID_SEEK_POSITION:
|
||||
case ID_TIMECODE_SCALE:
|
||||
case ID_TIME_CODE:
|
||||
case ID_BLOCK_DURATION:
|
||||
case ID_PIXEL_WIDTH:
|
||||
case ID_PIXEL_HEIGHT:
|
||||
case ID_DISPLAY_WIDTH:
|
||||
case ID_DISPLAY_HEIGHT:
|
||||
case ID_DISPLAY_UNIT:
|
||||
case ID_TRACK_NUMBER:
|
||||
case ID_TRACK_TYPE:
|
||||
case ID_FLAG_DEFAULT:
|
||||
case ID_FLAG_FORCED:
|
||||
case ID_DEFAULT_DURATION:
|
||||
case ID_CODEC_DELAY:
|
||||
case ID_SEEK_PRE_ROLL:
|
||||
case ID_CHANNELS:
|
||||
case ID_AUDIO_BIT_DEPTH:
|
||||
case ID_CONTENT_ENCODING_ORDER:
|
||||
case ID_CONTENT_ENCODING_SCOPE:
|
||||
case ID_CONTENT_COMPRESSION_ALGORITHM:
|
||||
case ID_CONTENT_ENCRYPTION_ALGORITHM:
|
||||
case ID_CONTENT_ENCRYPTION_AES_SETTINGS_CIPHER_MODE:
|
||||
case ID_CUE_TIME:
|
||||
case ID_CUE_CLUSTER_POSITION:
|
||||
case ID_REFERENCE_BLOCK:
|
||||
case ID_STEREO_MODE:
|
||||
case ID_COLOUR_RANGE:
|
||||
case ID_COLOUR_TRANSFER:
|
||||
case ID_COLOUR_PRIMARIES:
|
||||
case ID_MAX_CLL:
|
||||
case ID_MAX_FALL:
|
||||
case ID_PROJECTION_TYPE:
|
||||
return EbmlProcessor.ELEMENT_TYPE_UNSIGNED_INT;
|
||||
case ID_DOC_TYPE:
|
||||
case ID_NAME:
|
||||
case ID_CODEC_ID:
|
||||
case ID_LANGUAGE:
|
||||
return EbmlProcessor.ELEMENT_TYPE_STRING;
|
||||
case ID_SEEK_ID:
|
||||
case ID_CONTENT_COMPRESSION_SETTINGS:
|
||||
case ID_CONTENT_ENCRYPTION_KEY_ID:
|
||||
case ID_SIMPLE_BLOCK:
|
||||
case ID_BLOCK:
|
||||
case ID_CODEC_PRIVATE:
|
||||
case ID_PROJECTION_PRIVATE:
|
||||
return EbmlProcessor.ELEMENT_TYPE_BINARY;
|
||||
case ID_DURATION:
|
||||
case ID_SAMPLING_FREQUENCY:
|
||||
case ID_PRIMARY_R_CHROMATICITY_X:
|
||||
case ID_PRIMARY_R_CHROMATICITY_Y:
|
||||
case ID_PRIMARY_G_CHROMATICITY_X:
|
||||
case ID_PRIMARY_G_CHROMATICITY_Y:
|
||||
case ID_PRIMARY_B_CHROMATICITY_X:
|
||||
case ID_PRIMARY_B_CHROMATICITY_Y:
|
||||
case ID_WHITE_POINT_CHROMATICITY_X:
|
||||
case ID_WHITE_POINT_CHROMATICITY_Y:
|
||||
case ID_LUMNINANCE_MAX:
|
||||
case ID_LUMNINANCE_MIN:
|
||||
case ID_PROJECTION_POSE_YAW:
|
||||
case ID_PROJECTION_POSE_PITCH:
|
||||
case ID_PROJECTION_POSE_ROLL:
|
||||
return EbmlProcessor.ELEMENT_TYPE_FLOAT;
|
||||
default:
|
||||
return EbmlProcessor.ELEMENT_TYPE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given id is that of a level 1 element.
|
||||
*
|
||||
* @see EbmlProcessor#isLevel1Element(int)
|
||||
*/
|
||||
@CallSuper
|
||||
protected boolean isLevel1Element(int id) {
|
||||
return id == ID_SEGMENT_INFO || id == ID_CLUSTER || id == ID_CUES || id == ID_TRACKS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the start of a master element is encountered.
|
||||
*
|
||||
* @see EbmlProcessor#startMasterElement(int, long, long)
|
||||
*/
|
||||
@CallSuper
|
||||
protected void startMasterElement(int id, long contentPosition, long contentSize)
|
||||
throws ParserException {
|
||||
switch (id) {
|
||||
case ID_SEGMENT:
|
||||
|
|
@ -505,7 +622,13 @@ public final class MatroskaExtractor implements Extractor {
|
|||
}
|
||||
}
|
||||
|
||||
/* package */ void endMasterElement(int id) throws ParserException {
|
||||
/**
|
||||
* Called when the end of a master element is encountered.
|
||||
*
|
||||
* @see EbmlProcessor#endMasterElement(int)
|
||||
*/
|
||||
@CallSuper
|
||||
protected void endMasterElement(int id) throws ParserException {
|
||||
switch (id) {
|
||||
case ID_SEGMENT_INFO:
|
||||
if (timecodeScale == C.TIME_UNSET) {
|
||||
|
|
@ -576,7 +699,13 @@ public final class MatroskaExtractor implements Extractor {
|
|||
}
|
||||
}
|
||||
|
||||
/* package */ void integerElement(int id, long value) throws ParserException {
|
||||
/**
|
||||
* Called when an integer element is encountered.
|
||||
*
|
||||
* @see EbmlProcessor#integerElement(int, long)
|
||||
*/
|
||||
@CallSuper
|
||||
protected void integerElement(int id, long value) throws ParserException {
|
||||
switch (id) {
|
||||
case ID_EBML_READ_VERSION:
|
||||
// Validate that EBMLReadVersion is supported. This extractor only supports v1.
|
||||
|
|
@ -787,7 +916,13 @@ public final class MatroskaExtractor implements Extractor {
|
|||
}
|
||||
}
|
||||
|
||||
/* package */ void floatElement(int id, double value) {
|
||||
/**
|
||||
* Called when a float element is encountered.
|
||||
*
|
||||
* @see EbmlProcessor#floatElement(int, double)
|
||||
*/
|
||||
@CallSuper
|
||||
protected void floatElement(int id, double value) throws ParserException {
|
||||
switch (id) {
|
||||
case ID_DURATION:
|
||||
durationTimecode = (long) value;
|
||||
|
|
@ -839,7 +974,13 @@ public final class MatroskaExtractor implements Extractor {
|
|||
}
|
||||
}
|
||||
|
||||
/* package */ void stringElement(int id, String value) throws ParserException {
|
||||
/**
|
||||
* Called when a string element is encountered.
|
||||
*
|
||||
* @see EbmlProcessor#stringElement(int, String)
|
||||
*/
|
||||
@CallSuper
|
||||
protected void stringElement(int id, String value) throws ParserException {
|
||||
switch (id) {
|
||||
case ID_DOC_TYPE:
|
||||
// Validate that DocType is supported.
|
||||
|
|
@ -861,7 +1002,13 @@ public final class MatroskaExtractor implements Extractor {
|
|||
}
|
||||
}
|
||||
|
||||
/* package */ void binaryElement(int id, int contentSize, ExtractorInput input)
|
||||
/**
|
||||
* Called when a binary element is encountered.
|
||||
*
|
||||
* @see EbmlProcessor#binaryElement(int, int, ExtractorInput)
|
||||
*/
|
||||
@CallSuper
|
||||
protected void binaryElement(int id, int contentSize, ExtractorInput input)
|
||||
throws IOException, InterruptedException {
|
||||
switch (id) {
|
||||
case ID_SEEK_ID:
|
||||
|
|
@ -1431,110 +1578,18 @@ public final class MatroskaExtractor implements Extractor {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes events through to the outer {@link MatroskaExtractor}.
|
||||
*/
|
||||
private final class InnerEbmlReaderOutput implements EbmlReaderOutput {
|
||||
/** Passes events through to the outer {@link MatroskaExtractor}. */
|
||||
private final class InnerEbmlProcessor implements EbmlProcessor {
|
||||
|
||||
@Override
|
||||
@ElementType
|
||||
public int getElementType(int id) {
|
||||
switch (id) {
|
||||
case ID_EBML:
|
||||
case ID_SEGMENT:
|
||||
case ID_SEEK_HEAD:
|
||||
case ID_SEEK:
|
||||
case ID_INFO:
|
||||
case ID_CLUSTER:
|
||||
case ID_TRACKS:
|
||||
case ID_TRACK_ENTRY:
|
||||
case ID_AUDIO:
|
||||
case ID_VIDEO:
|
||||
case ID_CONTENT_ENCODINGS:
|
||||
case ID_CONTENT_ENCODING:
|
||||
case ID_CONTENT_COMPRESSION:
|
||||
case ID_CONTENT_ENCRYPTION:
|
||||
case ID_CONTENT_ENCRYPTION_AES_SETTINGS:
|
||||
case ID_CUES:
|
||||
case ID_CUE_POINT:
|
||||
case ID_CUE_TRACK_POSITIONS:
|
||||
case ID_BLOCK_GROUP:
|
||||
case ID_PROJECTION:
|
||||
case ID_COLOUR:
|
||||
case ID_MASTERING_METADATA:
|
||||
return TYPE_MASTER;
|
||||
case ID_EBML_READ_VERSION:
|
||||
case ID_DOC_TYPE_READ_VERSION:
|
||||
case ID_SEEK_POSITION:
|
||||
case ID_TIMECODE_SCALE:
|
||||
case ID_TIME_CODE:
|
||||
case ID_BLOCK_DURATION:
|
||||
case ID_PIXEL_WIDTH:
|
||||
case ID_PIXEL_HEIGHT:
|
||||
case ID_DISPLAY_WIDTH:
|
||||
case ID_DISPLAY_HEIGHT:
|
||||
case ID_DISPLAY_UNIT:
|
||||
case ID_TRACK_NUMBER:
|
||||
case ID_TRACK_TYPE:
|
||||
case ID_FLAG_DEFAULT:
|
||||
case ID_FLAG_FORCED:
|
||||
case ID_DEFAULT_DURATION:
|
||||
case ID_CODEC_DELAY:
|
||||
case ID_SEEK_PRE_ROLL:
|
||||
case ID_CHANNELS:
|
||||
case ID_AUDIO_BIT_DEPTH:
|
||||
case ID_CONTENT_ENCODING_ORDER:
|
||||
case ID_CONTENT_ENCODING_SCOPE:
|
||||
case ID_CONTENT_COMPRESSION_ALGORITHM:
|
||||
case ID_CONTENT_ENCRYPTION_ALGORITHM:
|
||||
case ID_CONTENT_ENCRYPTION_AES_SETTINGS_CIPHER_MODE:
|
||||
case ID_CUE_TIME:
|
||||
case ID_CUE_CLUSTER_POSITION:
|
||||
case ID_REFERENCE_BLOCK:
|
||||
case ID_STEREO_MODE:
|
||||
case ID_COLOUR_RANGE:
|
||||
case ID_COLOUR_TRANSFER:
|
||||
case ID_COLOUR_PRIMARIES:
|
||||
case ID_MAX_CLL:
|
||||
case ID_MAX_FALL:
|
||||
case ID_PROJECTION_TYPE:
|
||||
return TYPE_UNSIGNED_INT;
|
||||
case ID_DOC_TYPE:
|
||||
case ID_NAME:
|
||||
case ID_CODEC_ID:
|
||||
case ID_LANGUAGE:
|
||||
return TYPE_STRING;
|
||||
case ID_SEEK_ID:
|
||||
case ID_CONTENT_COMPRESSION_SETTINGS:
|
||||
case ID_CONTENT_ENCRYPTION_KEY_ID:
|
||||
case ID_SIMPLE_BLOCK:
|
||||
case ID_BLOCK:
|
||||
case ID_CODEC_PRIVATE:
|
||||
case ID_PROJECTION_PRIVATE:
|
||||
return TYPE_BINARY;
|
||||
case ID_DURATION:
|
||||
case ID_SAMPLING_FREQUENCY:
|
||||
case ID_PRIMARY_R_CHROMATICITY_X:
|
||||
case ID_PRIMARY_R_CHROMATICITY_Y:
|
||||
case ID_PRIMARY_G_CHROMATICITY_X:
|
||||
case ID_PRIMARY_G_CHROMATICITY_Y:
|
||||
case ID_PRIMARY_B_CHROMATICITY_X:
|
||||
case ID_PRIMARY_B_CHROMATICITY_Y:
|
||||
case ID_WHITE_POINT_CHROMATICITY_X:
|
||||
case ID_WHITE_POINT_CHROMATICITY_Y:
|
||||
case ID_LUMNINANCE_MAX:
|
||||
case ID_LUMNINANCE_MIN:
|
||||
case ID_PROJECTION_POSE_YAW:
|
||||
case ID_PROJECTION_POSE_PITCH:
|
||||
case ID_PROJECTION_POSE_ROLL:
|
||||
return TYPE_FLOAT;
|
||||
default:
|
||||
return TYPE_UNKNOWN;
|
||||
}
|
||||
return MatroskaExtractor.this.getElementType(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLevel1Element(int id) {
|
||||
return id == ID_SEGMENT_INFO || id == ID_CLUSTER || id == ID_CUES || id == ID_TRACKS;
|
||||
return MatroskaExtractor.this.isLevel1Element(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -37,19 +37,19 @@ public class DefaultEbmlReaderTest {
|
|||
@Test
|
||||
public void testMasterElement() throws IOException, InterruptedException {
|
||||
ExtractorInput input = createTestInput(0x1A, 0x45, 0xDF, 0xA3, 0x84, 0x42, 0x85, 0x81, 0x01);
|
||||
TestOutput expected = new TestOutput();
|
||||
expected.startMasterElement(TestOutput.ID_EBML, 5, 4);
|
||||
expected.integerElement(TestOutput.ID_DOC_TYPE_READ_VERSION, 1);
|
||||
expected.endMasterElement(TestOutput.ID_EBML);
|
||||
TestProcessor expected = new TestProcessor();
|
||||
expected.startMasterElement(TestProcessor.ID_EBML, 5, 4);
|
||||
expected.integerElement(TestProcessor.ID_DOC_TYPE_READ_VERSION, 1);
|
||||
expected.endMasterElement(TestProcessor.ID_EBML);
|
||||
assertEvents(input, expected.events);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMasterElementEmpty() throws IOException, InterruptedException {
|
||||
ExtractorInput input = createTestInput(0x18, 0x53, 0x80, 0x67, 0x80);
|
||||
TestOutput expected = new TestOutput();
|
||||
expected.startMasterElement(TestOutput.ID_SEGMENT, 5, 0);
|
||||
expected.endMasterElement(TestOutput.ID_SEGMENT);
|
||||
TestProcessor expected = new TestProcessor();
|
||||
expected.startMasterElement(TestProcessor.ID_SEGMENT, 5, 0);
|
||||
expected.endMasterElement(TestProcessor.ID_SEGMENT);
|
||||
assertEvents(input, expected.events);
|
||||
}
|
||||
|
||||
|
|
@ -57,8 +57,8 @@ public class DefaultEbmlReaderTest {
|
|||
public void testUnsignedIntegerElement() throws IOException, InterruptedException {
|
||||
// 0xFE is chosen because for signed integers it should be interpreted as -2
|
||||
ExtractorInput input = createTestInput(0x42, 0xF7, 0x81, 0xFE);
|
||||
TestOutput expected = new TestOutput();
|
||||
expected.integerElement(TestOutput.ID_EBML_READ_VERSION, 254);
|
||||
TestProcessor expected = new TestProcessor();
|
||||
expected.integerElement(TestProcessor.ID_EBML_READ_VERSION, 254);
|
||||
assertEvents(input, expected.events);
|
||||
}
|
||||
|
||||
|
|
@ -66,8 +66,8 @@ public class DefaultEbmlReaderTest {
|
|||
public void testUnsignedIntegerElementLarge() throws IOException, InterruptedException {
|
||||
ExtractorInput input =
|
||||
createTestInput(0x42, 0xF7, 0x88, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
|
||||
TestOutput expected = new TestOutput();
|
||||
expected.integerElement(TestOutput.ID_EBML_READ_VERSION, Long.MAX_VALUE);
|
||||
TestProcessor expected = new TestProcessor();
|
||||
expected.integerElement(TestProcessor.ID_EBML_READ_VERSION, Long.MAX_VALUE);
|
||||
assertEvents(input, expected.events);
|
||||
}
|
||||
|
||||
|
|
@ -76,32 +76,32 @@ public class DefaultEbmlReaderTest {
|
|||
throws IOException, InterruptedException {
|
||||
ExtractorInput input =
|
||||
createTestInput(0x42, 0xF7, 0x88, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
|
||||
TestOutput expected = new TestOutput();
|
||||
expected.integerElement(TestOutput.ID_EBML_READ_VERSION, -1);
|
||||
TestProcessor expected = new TestProcessor();
|
||||
expected.integerElement(TestProcessor.ID_EBML_READ_VERSION, -1);
|
||||
assertEvents(input, expected.events);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringElement() throws IOException, InterruptedException {
|
||||
ExtractorInput input = createTestInput(0x42, 0x82, 0x86, 0x41, 0x62, 0x63, 0x31, 0x32, 0x33);
|
||||
TestOutput expected = new TestOutput();
|
||||
expected.stringElement(TestOutput.ID_DOC_TYPE, "Abc123");
|
||||
TestProcessor expected = new TestProcessor();
|
||||
expected.stringElement(TestProcessor.ID_DOC_TYPE, "Abc123");
|
||||
assertEvents(input, expected.events);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringElementWithZeroPadding() throws IOException, InterruptedException {
|
||||
ExtractorInput input = createTestInput(0x42, 0x82, 0x86, 0x41, 0x62, 0x63, 0x00, 0x00, 0x00);
|
||||
TestOutput expected = new TestOutput();
|
||||
expected.stringElement(TestOutput.ID_DOC_TYPE, "Abc");
|
||||
TestProcessor expected = new TestProcessor();
|
||||
expected.stringElement(TestProcessor.ID_DOC_TYPE, "Abc");
|
||||
assertEvents(input, expected.events);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringElementEmpty() throws IOException, InterruptedException {
|
||||
ExtractorInput input = createTestInput(0x42, 0x82, 0x80);
|
||||
TestOutput expected = new TestOutput();
|
||||
expected.stringElement(TestOutput.ID_DOC_TYPE, "");
|
||||
TestProcessor expected = new TestProcessor();
|
||||
expected.stringElement(TestProcessor.ID_DOC_TYPE, "");
|
||||
assertEvents(input, expected.events);
|
||||
}
|
||||
|
||||
|
|
@ -109,8 +109,8 @@ public class DefaultEbmlReaderTest {
|
|||
public void testFloatElementFourBytes() throws IOException, InterruptedException {
|
||||
ExtractorInput input =
|
||||
createTestInput(0x44, 0x89, 0x84, 0x3F, 0x80, 0x00, 0x00);
|
||||
TestOutput expected = new TestOutput();
|
||||
expected.floatElement(TestOutput.ID_DURATION, 1.0);
|
||||
TestProcessor expected = new TestProcessor();
|
||||
expected.floatElement(TestProcessor.ID_DURATION, 1.0);
|
||||
assertEvents(input, expected.events);
|
||||
}
|
||||
|
||||
|
|
@ -118,8 +118,8 @@ public class DefaultEbmlReaderTest {
|
|||
public void testFloatElementEightBytes() throws IOException, InterruptedException {
|
||||
ExtractorInput input =
|
||||
createTestInput(0x44, 0x89, 0x88, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
|
||||
TestOutput expected = new TestOutput();
|
||||
expected.floatElement(TestOutput.ID_DURATION, -2.0);
|
||||
TestProcessor expected = new TestProcessor();
|
||||
expected.floatElement(TestProcessor.ID_DURATION, -2.0);
|
||||
assertEvents(input, expected.events);
|
||||
}
|
||||
|
||||
|
|
@ -127,8 +127,10 @@ public class DefaultEbmlReaderTest {
|
|||
public void testBinaryElement() throws IOException, InterruptedException {
|
||||
ExtractorInput input =
|
||||
createTestInput(0xA3, 0x88, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08);
|
||||
TestOutput expected = new TestOutput();
|
||||
expected.binaryElement(TestOutput.ID_SIMPLE_BLOCK, 8,
|
||||
TestProcessor expected = new TestProcessor();
|
||||
expected.binaryElement(
|
||||
TestProcessor.ID_SIMPLE_BLOCK,
|
||||
8,
|
||||
createTestInput(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08));
|
||||
assertEvents(input, expected.events);
|
||||
}
|
||||
|
|
@ -136,7 +138,7 @@ public class DefaultEbmlReaderTest {
|
|||
private static void assertEvents(ExtractorInput input, List<String> expectedEvents)
|
||||
throws IOException, InterruptedException {
|
||||
DefaultEbmlReader reader = new DefaultEbmlReader();
|
||||
TestOutput output = new TestOutput();
|
||||
TestProcessor output = new TestProcessor();
|
||||
reader.init(output);
|
||||
|
||||
// We expect the number of successful reads to equal the number of expected events.
|
||||
|
|
@ -164,10 +166,8 @@ public class DefaultEbmlReaderTest {
|
|||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link EbmlReaderOutput} that records each event callback.
|
||||
*/
|
||||
private static final class TestOutput implements EbmlReaderOutput {
|
||||
/** An {@link EbmlProcessor} that records each event callback. */
|
||||
private static final class TestProcessor implements EbmlProcessor {
|
||||
|
||||
// Element IDs
|
||||
private static final int ID_EBML = 0x1A45DFA3;
|
||||
|
|
@ -182,22 +182,23 @@ public class DefaultEbmlReaderTest {
|
|||
private final List<String> events = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public @ElementType int getElementType(int id) {
|
||||
@EbmlProcessor.ElementType
|
||||
public int getElementType(int id) {
|
||||
switch (id) {
|
||||
case ID_EBML:
|
||||
case ID_SEGMENT:
|
||||
return TYPE_MASTER;
|
||||
return EbmlProcessor.ELEMENT_TYPE_MASTER;
|
||||
case ID_EBML_READ_VERSION:
|
||||
case ID_DOC_TYPE_READ_VERSION:
|
||||
return TYPE_UNSIGNED_INT;
|
||||
return EbmlProcessor.ELEMENT_TYPE_UNSIGNED_INT;
|
||||
case ID_DOC_TYPE:
|
||||
return TYPE_STRING;
|
||||
return EbmlProcessor.ELEMENT_TYPE_STRING;
|
||||
case ID_SIMPLE_BLOCK:
|
||||
return TYPE_BINARY;
|
||||
return EbmlProcessor.ELEMENT_TYPE_BINARY;
|
||||
case ID_DURATION:
|
||||
return TYPE_FLOAT;
|
||||
return EbmlProcessor.ELEMENT_TYPE_FLOAT;
|
||||
default:
|
||||
return TYPE_UNKNOWN;
|
||||
return EbmlProcessor.ELEMENT_TYPE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -219,12 +220,12 @@ public class DefaultEbmlReaderTest {
|
|||
|
||||
@Override
|
||||
public void integerElement(int id, long value) {
|
||||
events.add(formatEvent(id, "integer=" + String.valueOf(value)));
|
||||
events.add(formatEvent(id, "integer=" + value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void floatElement(int id, double value) {
|
||||
events.add(formatEvent(id, "float=" + String.valueOf(value)));
|
||||
events.add(formatEvent(id, "float=" + value));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Reference in a new issue