1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.android.exoplayer2.extractor.mkv; 17 18 19 import androidx.annotation.IntDef; 20 import com.google.android.exoplayer2.C; 21 import com.google.android.exoplayer2.ParserException; 22 import com.google.android.exoplayer2.extractor.ExtractorInput; 23 import com.google.android.exoplayer2.util.Assertions; 24 import java.io.EOFException; 25 import java.io.IOException; 26 import java.lang.annotation.Documented; 27 import java.lang.annotation.Retention; 28 import java.lang.annotation.RetentionPolicy; 29 import java.util.ArrayDeque; 30 import org.checkerframework.checker.nullness.qual.MonotonicNonNull; 31 import org.checkerframework.checker.nullness.qual.RequiresNonNull; 32 33 /** 34 * Default implementation of {@link EbmlReader}. 35 */ 36 /* package */ final class DefaultEbmlReader implements EbmlReader { 37 38 @Documented 39 @Retention(RetentionPolicy.SOURCE) 40 @IntDef({ELEMENT_STATE_READ_ID, ELEMENT_STATE_READ_CONTENT_SIZE, ELEMENT_STATE_READ_CONTENT}) 41 private @interface ElementState {} 42 43 private static final int ELEMENT_STATE_READ_ID = 0; 44 private static final int ELEMENT_STATE_READ_CONTENT_SIZE = 1; 45 private static final int ELEMENT_STATE_READ_CONTENT = 2; 46 47 private static final int MAX_ID_BYTES = 4; 48 private static final int MAX_LENGTH_BYTES = 8; 49 50 private static final int MAX_INTEGER_ELEMENT_SIZE_BYTES = 8; 51 private static final int VALID_FLOAT32_ELEMENT_SIZE_BYTES = 4; 52 private static final int VALID_FLOAT64_ELEMENT_SIZE_BYTES = 8; 53 54 private final byte[] scratch; 55 private final ArrayDeque<MasterElement> masterElementsStack; 56 private final VarintReader varintReader; 57 58 private @MonotonicNonNull EbmlProcessor processor; 59 private @ElementState int elementState; 60 private int elementId; 61 private long elementContentSize; 62 DefaultEbmlReader()63 public DefaultEbmlReader() { 64 scratch = new byte[8]; 65 masterElementsStack = new ArrayDeque<>(); 66 varintReader = new VarintReader(); 67 } 68 69 @Override init(EbmlProcessor processor)70 public void init(EbmlProcessor processor) { 71 this.processor = processor; 72 } 73 74 @Override reset()75 public void reset() { 76 elementState = ELEMENT_STATE_READ_ID; 77 masterElementsStack.clear(); 78 varintReader.reset(); 79 } 80 81 @Override read(ExtractorInput input)82 public boolean read(ExtractorInput input) throws IOException { 83 Assertions.checkStateNotNull(processor); 84 while (true) { 85 MasterElement head = masterElementsStack.peek(); 86 if (head != null && input.getPosition() >= head.elementEndPosition) { 87 processor.endMasterElement(masterElementsStack.pop().elementId); 88 return true; 89 } 90 91 if (elementState == ELEMENT_STATE_READ_ID) { 92 long result = varintReader.readUnsignedVarint(input, true, false, MAX_ID_BYTES); 93 if (result == C.RESULT_MAX_LENGTH_EXCEEDED) { 94 result = maybeResyncToNextLevel1Element(input); 95 } 96 if (result == C.RESULT_END_OF_INPUT) { 97 return false; 98 } 99 // Element IDs are at most 4 bytes, so we can cast to integers. 100 elementId = (int) result; 101 elementState = ELEMENT_STATE_READ_CONTENT_SIZE; 102 } 103 104 if (elementState == ELEMENT_STATE_READ_CONTENT_SIZE) { 105 elementContentSize = varintReader.readUnsignedVarint(input, false, true, MAX_LENGTH_BYTES); 106 elementState = ELEMENT_STATE_READ_CONTENT; 107 } 108 109 @EbmlProcessor.ElementType int type = processor.getElementType(elementId); 110 switch (type) { 111 case EbmlProcessor.ELEMENT_TYPE_MASTER: 112 long elementContentPosition = input.getPosition(); 113 long elementEndPosition = elementContentPosition + elementContentSize; 114 masterElementsStack.push(new MasterElement(elementId, elementEndPosition)); 115 processor.startMasterElement(elementId, elementContentPosition, elementContentSize); 116 elementState = ELEMENT_STATE_READ_ID; 117 return true; 118 case EbmlProcessor.ELEMENT_TYPE_UNSIGNED_INT: 119 if (elementContentSize > MAX_INTEGER_ELEMENT_SIZE_BYTES) { 120 throw new ParserException("Invalid integer size: " + elementContentSize); 121 } 122 processor.integerElement(elementId, readInteger(input, (int) elementContentSize)); 123 elementState = ELEMENT_STATE_READ_ID; 124 return true; 125 case EbmlProcessor.ELEMENT_TYPE_FLOAT: 126 if (elementContentSize != VALID_FLOAT32_ELEMENT_SIZE_BYTES 127 && elementContentSize != VALID_FLOAT64_ELEMENT_SIZE_BYTES) { 128 throw new ParserException("Invalid float size: " + elementContentSize); 129 } 130 processor.floatElement(elementId, readFloat(input, (int) elementContentSize)); 131 elementState = ELEMENT_STATE_READ_ID; 132 return true; 133 case EbmlProcessor.ELEMENT_TYPE_STRING: 134 if (elementContentSize > Integer.MAX_VALUE) { 135 throw new ParserException("String element size: " + elementContentSize); 136 } 137 processor.stringElement(elementId, readString(input, (int) elementContentSize)); 138 elementState = ELEMENT_STATE_READ_ID; 139 return true; 140 case EbmlProcessor.ELEMENT_TYPE_BINARY: 141 processor.binaryElement(elementId, (int) elementContentSize, input); 142 elementState = ELEMENT_STATE_READ_ID; 143 return true; 144 case EbmlProcessor.ELEMENT_TYPE_UNKNOWN: 145 input.skipFully((int) elementContentSize); 146 elementState = ELEMENT_STATE_READ_ID; 147 break; 148 default: 149 throw new ParserException("Invalid element type " + type); 150 } 151 } 152 } 153 154 /** 155 * Does a byte by byte search to try and find the next level 1 element. This method is called if 156 * some invalid data is encountered in the parser. 157 * 158 * @param input The {@link ExtractorInput} from which data has to be read. 159 * @return id of the next level 1 element that has been found. 160 * @throws EOFException If the end of input was encountered when searching for the next level 1 161 * element. 162 * @throws IOException If an error occurs reading from the input. 163 */ 164 @RequiresNonNull("processor") maybeResyncToNextLevel1Element(ExtractorInput input)165 private long maybeResyncToNextLevel1Element(ExtractorInput input) throws IOException { 166 input.resetPeekPosition(); 167 while (true) { 168 input.peekFully(scratch, 0, MAX_ID_BYTES); 169 int varintLength = VarintReader.parseUnsignedVarintLength(scratch[0]); 170 if (varintLength != C.LENGTH_UNSET && varintLength <= MAX_ID_BYTES) { 171 int potentialId = (int) VarintReader.assembleVarint(scratch, varintLength, false); 172 if (processor.isLevel1Element(potentialId)) { 173 input.skipFully(varintLength); 174 return potentialId; 175 } 176 } 177 input.skipFully(1); 178 } 179 } 180 181 /** 182 * Reads and returns an integer of length {@code byteLength} from the {@link ExtractorInput}. 183 * 184 * @param input The {@link ExtractorInput} from which to read. 185 * @param byteLength The length of the integer being read. 186 * @return The read integer value. 187 * @throws IOException If an error occurs reading from the input. 188 */ readInteger(ExtractorInput input, int byteLength)189 private long readInteger(ExtractorInput input, int byteLength) throws IOException { 190 input.readFully(scratch, 0, byteLength); 191 long value = 0; 192 for (int i = 0; i < byteLength; i++) { 193 value = (value << 8) | (scratch[i] & 0xFF); 194 } 195 return value; 196 } 197 198 /** 199 * Reads and returns a float of length {@code byteLength} from the {@link ExtractorInput}. 200 * 201 * @param input The {@link ExtractorInput} from which to read. 202 * @param byteLength The length of the float being read. 203 * @return The read float value. 204 * @throws IOException If an error occurs reading from the input. 205 */ readFloat(ExtractorInput input, int byteLength)206 private double readFloat(ExtractorInput input, int byteLength) throws IOException { 207 long integerValue = readInteger(input, byteLength); 208 double floatValue; 209 if (byteLength == VALID_FLOAT32_ELEMENT_SIZE_BYTES) { 210 floatValue = Float.intBitsToFloat((int) integerValue); 211 } else { 212 floatValue = Double.longBitsToDouble(integerValue); 213 } 214 return floatValue; 215 } 216 217 /** 218 * Reads a string of length {@code byteLength} from the {@link ExtractorInput}. Zero padding is 219 * removed, so the returned string may be shorter than {@code byteLength}. 220 * 221 * @param input The {@link ExtractorInput} from which to read. 222 * @param byteLength The length of the string being read, including zero padding. 223 * @return The read string value. 224 * @throws IOException If an error occurs reading from the input. 225 */ readString(ExtractorInput input, int byteLength)226 private static String readString(ExtractorInput input, int byteLength) throws IOException { 227 if (byteLength == 0) { 228 return ""; 229 } 230 byte[] stringBytes = new byte[byteLength]; 231 input.readFully(stringBytes, 0, byteLength); 232 // Remove zero padding. 233 int trimmedLength = byteLength; 234 while (trimmedLength > 0 && stringBytes[trimmedLength - 1] == 0) { 235 trimmedLength--; 236 } 237 return new String(stringBytes, 0, trimmedLength); 238 } 239 240 /** 241 * Used in {@link #masterElementsStack} to track when the current master element ends, so that 242 * {@link EbmlProcessor#endMasterElement(int)} can be called. 243 */ 244 private static final class MasterElement { 245 246 private final int elementId; 247 private final long elementEndPosition; 248 MasterElement(int elementId, long elementEndPosition)249 private MasterElement(int elementId, long elementEndPosition) { 250 this.elementId = elementId; 251 this.elementEndPosition = elementEndPosition; 252 } 253 254 } 255 256 } 257