/* * Copyright (C) 2014 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. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.android.exoplayer.extractor.webm; import com.google.android.exoplayer.C; import com.google.android.exoplayer.extractor.Extractor; import com.google.android.exoplayer.extractor.ExtractorInput; import com.google.android.exoplayer.util.ParsableByteArray; import java.io.IOException; /** * Utility class that peeks from the input stream in order to determine whether it appears to be * compatible input for this extractor. */ /* package */ final class Sniffer { /** * The number of bytes to search for a valid header in {@link #sniff(ExtractorInput)}. */ private static final int SEARCH_LENGTH = 1024; private static final int ID_EBML = 0x1A45DFA3; private final ParsableByteArray scratch; private int peekLength; public Sniffer() { scratch = new ParsableByteArray(8); } /** * @see Extractor#sniff */ public boolean sniff(ExtractorInput input) throws IOException, InterruptedException { long inputLength = input.getLength(); int bytesToSearch = (int) (inputLength == C.LENGTH_UNBOUNDED || inputLength > SEARCH_LENGTH ? SEARCH_LENGTH : inputLength); // Find four bytes equal to ID_EBML near the start of the input. input.peekFully(scratch.data, 0, 4); long tag = scratch.readUnsignedInt(); peekLength = 4; while (tag != ID_EBML) { if (++peekLength == bytesToSearch) { return false; } input.peekFully(scratch.data, 0, 1); tag = (tag << 8) & 0xFFFFFF00; tag |= scratch.data[0] & 0xFF; } // Read the size of the EBML header and make sure it is within the stream. long headerSize = readUint(input); long headerStart = peekLength; if (headerSize == Long.MIN_VALUE || (inputLength != C.LENGTH_UNBOUNDED && headerStart + headerSize >= inputLength)) { return false; } // Read the payload elements in the EBML header. while (peekLength < headerStart + headerSize) { long id = readUint(input); if (id == Long.MIN_VALUE) { return false; } long size = readUint(input); if (size < 0 || size > Integer.MAX_VALUE) { return false; } if (size != 0) { input.advancePeekPosition((int) size); peekLength += size; } } return peekLength == headerStart + headerSize; } /** * Peeks a variable-length unsigned EBML integer from the input. */ private long readUint(ExtractorInput input) throws IOException, InterruptedException { input.peekFully(scratch.data, 0, 1); int value = scratch.data[0] & 0xFF; if (value == 0) { return Long.MIN_VALUE; } int mask = 0x80; int length = 0; while ((value & mask) == 0) { mask >>= 1; length++; } value &= ~mask; input.peekFully(scratch.data, 1, length); for (int i = 0; i < length; i++) { value <<= 8; value += scratch.data[i + 1] & 0xFF; } peekLength += length + 1; return value; } }