com.google.android.exoplayer2.util.ParsableByteArray

Here are the examples of the java api com.google.android.exoplayer2.util.ParsableByteArray taken from open source projects. By voting up you can indicate which examples are most useful and appropriate.

564 Examples 7

19 Source : RtpH264PayloadReader.java
with GNU General Public License v2.0
from warren-bank

private int getNalUnitType(ParsableByteArray packet, int offset) {
    return packet.data[offset] & 0x1F;
}

19 Source : RtpG711PayloadReader.java
with GNU General Public License v2.0
from warren-bank

// read the G.711.1 payload header and return the mode
private int readG711V1Header(ParsableByteArray packet) {
    return packet.readUnsignedByte() & 0x07;
}

19 Source : RtpAc3PayloadReader.java
with GNU General Public License v2.0
from warren-bank

// read the frame type from AC3 packet
private int readFrameType(ParsableByteArray packet) {
    return packet.readUnsignedByte() & 0x03;
}

19 Source : RtpAc3PayloadReader.java
with GNU General Public License v2.0
from warren-bank

// read the number of frames from AC3 packet
private int readNumFrames(ParsableByteArray packet) {
    return packet.readUnsignedByte() & 0xFF;
}

19 Source : WebvttDecoder.java
with GNU General Public License v2.0
from warren-bank

private static void skipComment(ParsableByteArray parsableWebvttData) {
    while (!TextUtils.isEmpty(parsableWebvttData.readLine())) {
    }
}

19 Source : Mp4WebvttDecoder.java
with GNU General Public License v2.0
from warren-bank

/**
 * A {@link SimpleSubreplacedleDecoder} for Webvtt embedded in a Mp4 container file.
 */
public final clreplaced Mp4WebvttDecoder extends SimpleSubreplacedleDecoder {

    private static final int BOX_HEADER_SIZE = 8;

    private static final int TYPE_payl = Util.getIntegerCodeForString("payl");

    private static final int TYPE_sttg = Util.getIntegerCodeForString("sttg");

    private static final int TYPE_vttc = Util.getIntegerCodeForString("vttc");

    private final ParsableByteArray sampleData;

    private final WebvttCue.Builder builder;

    public Mp4WebvttDecoder() {
        super("Mp4WebvttDecoder");
        sampleData = new ParsableByteArray();
        builder = new WebvttCue.Builder();
    }

    @Override
    protected Mp4WebvttSubreplacedle decode(byte[] bytes, int length, boolean reset) throws SubreplacedleDecoderException {
        // Webvtt in Mp4 samples have boxes inside of them, so we have to do a traditional box parsing:
        // first 4 bytes size and then 4 bytes type.
        sampleData.reset(bytes, length);
        List<Cue> resultingCueList = new ArrayList<>();
        while (sampleData.bytesLeft() > 0) {
            if (sampleData.bytesLeft() < BOX_HEADER_SIZE) {
                throw new SubreplacedleDecoderException("Incomplete Mp4Webvtt Top Level box header found.");
            }
            int boxSize = sampleData.readInt();
            int boxType = sampleData.readInt();
            if (boxType == TYPE_vttc) {
                resultingCueList.add(parseVttCueBox(sampleData, builder, boxSize - BOX_HEADER_SIZE));
            } else {
                // Peers of the VTTCueBox are still not supported and are skipped.
                sampleData.skipBytes(boxSize - BOX_HEADER_SIZE);
            }
        }
        return new Mp4WebvttSubreplacedle(resultingCueList);
    }

    private static Cue parseVttCueBox(ParsableByteArray sampleData, WebvttCue.Builder builder, int remainingCueBoxBytes) throws SubreplacedleDecoderException {
        builder.reset();
        while (remainingCueBoxBytes > 0) {
            if (remainingCueBoxBytes < BOX_HEADER_SIZE) {
                throw new SubreplacedleDecoderException("Incomplete vtt cue box header found.");
            }
            int boxSize = sampleData.readInt();
            int boxType = sampleData.readInt();
            remainingCueBoxBytes -= BOX_HEADER_SIZE;
            int payloadLength = boxSize - BOX_HEADER_SIZE;
            String boxPayload = Util.fromUtf8Bytes(sampleData.data, sampleData.getPosition(), payloadLength);
            sampleData.skipBytes(payloadLength);
            remainingCueBoxBytes -= payloadLength;
            if (boxType == TYPE_sttg) {
                WebvttCueParser.parseCueSettingsList(boxPayload, builder);
            } else if (boxType == TYPE_payl) {
                WebvttCueParser.parseCueText(null, boxPayload.trim(), builder, Collections.emptyList());
            } else {
            // Other VTTCueBox children are still not supported and are ignored.
            }
        }
        return builder.build();
    }
}

19 Source : CssParser.java
with GNU General Public License v2.0
from warren-bank

private static char peekCharAtPosition(ParsableByteArray input, int position) {
    return (char) input.data[position];
}

19 Source : CssParser.java
with GNU General Public License v2.0
from warren-bank

// Visible for testing.
/* package */
static void skipStyleBlock(ParsableByteArray input) {
    // The style block cannot contain empty lines, so we replacedume the input ends when a empty line
    // is found.
    String line;
    do {
        line = input.readLine();
    } while (!TextUtils.isEmpty(line));
}

19 Source : CssParser.java
with GNU General Public License v2.0
from warren-bank

// Visible for testing.
/* package */
static String parseNextToken(ParsableByteArray input, StringBuilder stringBuilder) {
    skipWhitespaceAndComments(input);
    if (input.bytesLeft() == 0) {
        return null;
    }
    String identifier = parseIdentifier(input, stringBuilder);
    if (!"".equals(identifier)) {
        return identifier;
    }
    // We found a delimiter.
    return "" + (char) input.readUnsignedByte();
}

19 Source : CssParser.java
with GNU General Public License v2.0
from warren-bank

// Visible for testing.
/* package */
static void skipWhitespaceAndComments(ParsableByteArray input) {
    boolean skipping = true;
    while (input.bytesLeft() > 0 && skipping) {
        skipping = maybeSkipWhitespace(input) || maybeSkipComment(input);
    }
}

19 Source : Tx3gDecoder.java
with GNU General Public License v2.0
from warren-bank

/**
 * A {@link SimpleSubreplacedleDecoder} for tx3g.
 * <p>
 * Currently supports parsing of a single text track with embedded styles.
 */
public final clreplaced Tx3gDecoder extends SimpleSubreplacedleDecoder {

    private static final char BOM_UTF16_BE = '\uFEFF';

    private static final char BOM_UTF16_LE = '\uFFFE';

    private static final int TYPE_STYL = Util.getIntegerCodeForString("styl");

    private static final int TYPE_TBOX = Util.getIntegerCodeForString("tbox");

    private static final String TX3G_SERIF = "Serif";

    private static final int SIZE_ATOM_HEADER = 8;

    private static final int SIZE_SHORT = 2;

    private static final int SIZE_BOM_UTF16 = 2;

    private static final int SIZE_STYLE_RECORD = 12;

    private static final int FONT_FACE_BOLD = 0x0001;

    private static final int FONT_FACE_ITALIC = 0x0002;

    private static final int FONT_FACE_UNDERLINE = 0x0004;

    private static final int SPAN_PRIORITY_LOW = 0xFF << Spanned.SPAN_PRIORITY_SHIFT;

    private static final int SPAN_PRIORITY_HIGH = 0;

    private static final int DEFAULT_FONT_FACE = 0;

    private static final int DEFAULT_COLOR = Color.WHITE;

    private static final String DEFAULT_FONT_FAMILY = C.SANS_SERIF_NAME;

    private static final float DEFAULT_VERTICAL_PLACEMENT = 0.85f;

    private final ParsableByteArray parsableByteArray;

    private boolean customVerticalPlacement;

    private int defaultFontFace;

    private int defaultColorRgba;

    private String defaultFontFamily;

    private float defaultVerticalPlacement;

    private int calculatedVideoTrackHeight;

    /**
     * Sets up a new {@link Tx3gDecoder} with default values.
     *
     * @param initializationData Sample description atom ('stsd') data with default subreplacedle styles.
     */
    public Tx3gDecoder(List<byte[]> initializationData) {
        super("Tx3gDecoder");
        parsableByteArray = new ParsableByteArray();
        decodeInitializationData(initializationData);
    }

    private void decodeInitializationData(List<byte[]> initializationData) {
        if (initializationData != null && initializationData.size() == 1 && (initializationData.get(0).length == 48 || initializationData.get(0).length == 53)) {
            byte[] initializationBytes = initializationData.get(0);
            defaultFontFace = initializationBytes[24];
            defaultColorRgba = ((initializationBytes[26] & 0xFF) << 24) | ((initializationBytes[27] & 0xFF) << 16) | ((initializationBytes[28] & 0xFF) << 8) | (initializationBytes[29] & 0xFF);
            String fontFamily = Util.fromUtf8Bytes(initializationBytes, 43, initializationBytes.length - 43);
            defaultFontFamily = TX3G_SERIF.equals(fontFamily) ? C.SERIF_NAME : C.SANS_SERIF_NAME;
            // font size (initializationBytes[25]) is 5% of video height
            calculatedVideoTrackHeight = 20 * initializationBytes[25];
            customVerticalPlacement = (initializationBytes[0] & 0x20) != 0;
            if (customVerticalPlacement) {
                int requestedVerticalPlacement = ((initializationBytes[10] & 0xFF) << 8) | (initializationBytes[11] & 0xFF);
                defaultVerticalPlacement = (float) requestedVerticalPlacement / calculatedVideoTrackHeight;
                defaultVerticalPlacement = Util.constrainValue(defaultVerticalPlacement, 0.0f, 0.95f);
            } else {
                defaultVerticalPlacement = DEFAULT_VERTICAL_PLACEMENT;
            }
        } else {
            defaultFontFace = DEFAULT_FONT_FACE;
            defaultColorRgba = DEFAULT_COLOR;
            defaultFontFamily = DEFAULT_FONT_FAMILY;
            customVerticalPlacement = false;
            defaultVerticalPlacement = DEFAULT_VERTICAL_PLACEMENT;
        }
    }

    @Override
    protected Subreplacedle decode(byte[] bytes, int length, boolean reset) throws SubreplacedleDecoderException {
        parsableByteArray.reset(bytes, length);
        String cueTextString = readSubreplacedleText(parsableByteArray);
        if (cueTextString.isEmpty()) {
            return Tx3gSubreplacedle.EMPTY;
        }
        // Attach default styles.
        SpannableStringBuilder cueText = new SpannableStringBuilder(cueTextString);
        attachFontFace(cueText, defaultFontFace, DEFAULT_FONT_FACE, 0, cueText.length(), SPAN_PRIORITY_LOW);
        attachColor(cueText, defaultColorRgba, DEFAULT_COLOR, 0, cueText.length(), SPAN_PRIORITY_LOW);
        attachFontFamily(cueText, defaultFontFamily, DEFAULT_FONT_FAMILY, 0, cueText.length(), SPAN_PRIORITY_LOW);
        float verticalPlacement = defaultVerticalPlacement;
        // Find and attach additional styles.
        while (parsableByteArray.bytesLeft() >= SIZE_ATOM_HEADER) {
            int position = parsableByteArray.getPosition();
            int atomSize = parsableByteArray.readInt();
            int atomType = parsableByteArray.readInt();
            if (atomType == TYPE_STYL) {
                replacedertTrue(parsableByteArray.bytesLeft() >= SIZE_SHORT);
                int styleRecordCount = parsableByteArray.readUnsignedShort();
                for (int i = 0; i < styleRecordCount; i++) {
                    applyStyleRecord(parsableByteArray, cueText);
                }
            } else if (atomType == TYPE_TBOX && customVerticalPlacement) {
                replacedertTrue(parsableByteArray.bytesLeft() >= SIZE_SHORT);
                int requestedVerticalPlacement = parsableByteArray.readUnsignedShort();
                verticalPlacement = (float) requestedVerticalPlacement / calculatedVideoTrackHeight;
                verticalPlacement = Util.constrainValue(verticalPlacement, 0.0f, 0.95f);
            }
            parsableByteArray.setPosition(position + atomSize);
        }
        return new Tx3gSubreplacedle(new Cue(cueText, null, verticalPlacement, Cue.LINE_TYPE_FRACTION, Cue.ANCHOR_TYPE_START, Cue.DIMEN_UNSET, Cue.TYPE_UNSET, Cue.DIMEN_UNSET));
    }

    private static String readSubreplacedleText(ParsableByteArray parsableByteArray) throws SubreplacedleDecoderException {
        replacedertTrue(parsableByteArray.bytesLeft() >= SIZE_SHORT);
        int textLength = parsableByteArray.readUnsignedShort();
        if (textLength == 0) {
            return "";
        }
        if (parsableByteArray.bytesLeft() >= SIZE_BOM_UTF16) {
            char firstChar = parsableByteArray.peekChar();
            if (firstChar == BOM_UTF16_BE || firstChar == BOM_UTF16_LE) {
                return parsableByteArray.readString(textLength, Charset.forName(C.UTF16_NAME));
            }
        }
        return parsableByteArray.readString(textLength, Charset.forName(C.UTF8_NAME));
    }

    private void applyStyleRecord(ParsableByteArray parsableByteArray, SpannableStringBuilder cueText) throws SubreplacedleDecoderException {
        replacedertTrue(parsableByteArray.bytesLeft() >= SIZE_STYLE_RECORD);
        int start = parsableByteArray.readUnsignedShort();
        int end = parsableByteArray.readUnsignedShort();
        // font identifier
        parsableByteArray.skipBytes(2);
        int fontFace = parsableByteArray.readUnsignedByte();
        // font size
        parsableByteArray.skipBytes(1);
        int colorRgba = parsableByteArray.readInt();
        attachFontFace(cueText, fontFace, defaultFontFace, start, end, SPAN_PRIORITY_HIGH);
        attachColor(cueText, colorRgba, defaultColorRgba, start, end, SPAN_PRIORITY_HIGH);
    }

    private static void attachFontFace(SpannableStringBuilder cueText, int fontFace, int defaultFontFace, int start, int end, int spanPriority) {
        if (fontFace != defaultFontFace) {
            final int flags = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | spanPriority;
            boolean isBold = (fontFace & FONT_FACE_BOLD) != 0;
            boolean isItalic = (fontFace & FONT_FACE_ITALIC) != 0;
            if (isBold) {
                if (isItalic) {
                    cueText.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), start, end, flags);
                } else {
                    cueText.setSpan(new StyleSpan(Typeface.BOLD), start, end, flags);
                }
            } else if (isItalic) {
                cueText.setSpan(new StyleSpan(Typeface.ITALIC), start, end, flags);
            }
            boolean isUnderlined = (fontFace & FONT_FACE_UNDERLINE) != 0;
            if (isUnderlined) {
                cueText.setSpan(new UnderlineSpan(), start, end, flags);
            }
            if (!isUnderlined && !isBold && !isItalic) {
                cueText.setSpan(new StyleSpan(Typeface.NORMAL), start, end, flags);
            }
        }
    }

    private static void attachColor(SpannableStringBuilder cueText, int colorRgba, int defaultColorRgba, int start, int end, int spanPriority) {
        if (colorRgba != defaultColorRgba) {
            int colorArgb = ((colorRgba & 0xFF) << 24) | (colorRgba >>> 8);
            cueText.setSpan(new ForegroundColorSpan(colorArgb), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | spanPriority);
        }
    }

    @SuppressWarnings("ReferenceEquality")
    private static void attachFontFamily(SpannableStringBuilder cueText, String fontFamily, String defaultFontFamily, int start, int end, int spanPriority) {
        if (fontFamily != defaultFontFamily) {
            cueText.setSpan(new TypefaceSpan(fontFamily), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | spanPriority);
        }
    }

    private static void replacedertTrue(boolean checkValue) throws SubreplacedleDecoderException {
        if (!checkValue) {
            throw new SubreplacedleDecoderException("Unexpected subreplacedle format.");
        }
    }
}

19 Source : SsaDecoder.java
with GNU General Public License v2.0
from warren-bank

/**
 * Parses the header of the subreplacedle.
 *
 * @param data A {@link ParsableByteArray} from which the header should be read.
 */
private void parseHeader(ParsableByteArray data) {
    String currentLine;
    while ((currentLine = data.readLine()) != null) {
        // TODO: Parse useful data from the header.
        if (currentLine.startsWith("[Events]")) {
            // We've reached the event body.
            return;
        }
    }
}

19 Source : CeaUtil.java
with GNU General Public License v2.0
from warren-bank

/**
 * Reads a value from the provided buffer consisting of zero or more 0xFF bytes followed by a
 * terminating byte not equal to 0xFF. The returned value is ((0xFF * N) + T), where N is the
 * number of 0xFF bytes and T is the value of the terminating byte.
 *
 * @param buffer The buffer from which to read the value.
 * @return The read value, or -1 if the end of the buffer is reached before a value is read.
 */
private static int readNon255TerminatedValue(ParsableByteArray buffer) {
    int b;
    int value = 0;
    do {
        if (buffer.bytesLeft() == 0) {
            return -1;
        }
        b = buffer.readUnsignedByte();
        value += b;
    } while (b == 0xFF);
    return value;
}

19 Source : TimeSignalCommand.java
with GNU General Public License v2.0
from warren-bank

/**
 * Parses pts_time from splice_time(), defined in Section 9.4.1. Returns {@link C#TIME_UNSET}, if
 * time_specified_flag is false.
 *
 * @param sectionData The section data from which the pts_time is parsed.
 * @param ptsAdjustment The pts adjustment provided by the splice info section header.
 * @return The pts_time defined by splice_time(), or {@link C#TIME_UNSET}, if time_specified_flag
 *     is false.
 */
/* package */
static long parseSpliceTime(ParsableByteArray sectionData, long ptsAdjustment) {
    long firstByte = sectionData.readUnsignedByte();
    long ptsTime = C.TIME_UNSET;
    if ((firstByte & 0x80) != 0) /* time_specified_flag */
    {
        // See SCTE35 9.2.1 for more information about pts adjustment.
        ptsTime = (firstByte & 0x01) << 32 | sectionData.readUnsignedInt();
        ptsTime += ptsAdjustment;
        ptsTime &= 0x1FFFFFFFFL;
    }
    return ptsTime;
}

19 Source : SpliceScheduleCommand.java
with GNU General Public License v2.0
from warren-bank

/* package */
static SpliceScheduleCommand parseFromSection(ParsableByteArray sectionData) {
    int spliceCount = sectionData.readUnsignedByte();
    ArrayList<Event> events = new ArrayList<>(spliceCount);
    for (int i = 0; i < spliceCount; i++) {
        events.add(Event.parseFromSection(sectionData));
    }
    return new SpliceScheduleCommand(events);
}

19 Source : Id3Decoder.java
with GNU General Public License v2.0
from warren-bank

/**
 * Performs in-place removal of unsynchronization for {@code length} bytes starting from
 * {@link ParsableByteArray#getPosition()}
 *
 * @param data Contains the data to be processed.
 * @param length The length of the data to be processed.
 * @return The length of the data after processing.
 */
private static int removeUnsynchronization(ParsableByteArray data, int length) {
    byte[] bytes = data.data;
    for (int i = data.getPosition(); i + 1 < length; i++) {
        if ((bytes[i] & 0xFF) == 0xFF && bytes[i + 1] == 0x00) {
            System.arraycopy(bytes, i + 2, bytes, i + 1, length - i - 2);
            length--;
        }
    }
    return length;
}

19 Source : SectionReader.java
with GNU General Public License v2.0
from warren-bank

/**
 * Reads section data packets and feeds the whole sections to a given {@link SectionPayloadReader}.
 * Useful information on PSI sections can be found in ISO/IEC 13818-1, section 2.4.4.
 */
public final clreplaced SectionReader implements TsPayloadReader {

    private static final int SECTION_HEADER_LENGTH = 3;

    private static final int DEFAULT_SECTION_BUFFER_LENGTH = 32;

    private static final int MAX_SECTION_LENGTH = 4098;

    private final SectionPayloadReader reader;

    private final ParsableByteArray sectionData;

    private int totalSectionLength;

    private int bytesRead;

    private boolean sectionSyntaxIndicator;

    private boolean waitingForPayloadStart;

    public SectionReader(SectionPayloadReader reader) {
        this.reader = reader;
        sectionData = new ParsableByteArray(DEFAULT_SECTION_BUFFER_LENGTH);
    }

    @Override
    public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
        reader.init(timestampAdjuster, extractorOutput, idGenerator);
        waitingForPayloadStart = true;
    }

    @Override
    public void seek() {
        waitingForPayloadStart = true;
    }

    @Override
    public void consume(ParsableByteArray data, boolean payloadUnitStartIndicator) {
        int payloadStartPosition = C.POSITION_UNSET;
        if (payloadUnitStartIndicator) {
            int payloadStartOffset = data.readUnsignedByte();
            payloadStartPosition = data.getPosition() + payloadStartOffset;
        }
        if (waitingForPayloadStart) {
            if (!payloadUnitStartIndicator) {
                return;
            }
            waitingForPayloadStart = false;
            data.setPosition(payloadStartPosition);
            bytesRead = 0;
        }
        while (data.bytesLeft() > 0) {
            if (bytesRead < SECTION_HEADER_LENGTH) {
                // Note: see ISO/IEC 13818-1, section 2.4.4.3 for detailed information on the format of
                // the header.
                if (bytesRead == 0) {
                    int tableId = data.readUnsignedByte();
                    data.setPosition(data.getPosition() - 1);
                    if (tableId == 0xFF) /* forbidden value */
                    {
                        // No more sections in this ts packet.
                        waitingForPayloadStart = true;
                        return;
                    }
                }
                int headerBytesToRead = Math.min(data.bytesLeft(), SECTION_HEADER_LENGTH - bytesRead);
                data.readBytes(sectionData.data, bytesRead, headerBytesToRead);
                bytesRead += headerBytesToRead;
                if (bytesRead == SECTION_HEADER_LENGTH) {
                    sectionData.reset(SECTION_HEADER_LENGTH);
                    // Skip table id (8).
                    sectionData.skipBytes(1);
                    int secondHeaderByte = sectionData.readUnsignedByte();
                    int thirdHeaderByte = sectionData.readUnsignedByte();
                    sectionSyntaxIndicator = (secondHeaderByte & 0x80) != 0;
                    totalSectionLength = (((secondHeaderByte & 0x0F) << 8) | thirdHeaderByte) + SECTION_HEADER_LENGTH;
                    if (sectionData.capacity() < totalSectionLength) {
                        // Ensure there is enough space to keep the whole section.
                        byte[] bytes = sectionData.data;
                        sectionData.reset(Math.min(MAX_SECTION_LENGTH, Math.max(totalSectionLength, bytes.length * 2)));
                        System.arraycopy(bytes, 0, sectionData.data, 0, SECTION_HEADER_LENGTH);
                    }
                }
            } else {
                // Reading the body.
                int bodyBytesToRead = Math.min(data.bytesLeft(), totalSectionLength - bytesRead);
                data.readBytes(sectionData.data, bytesRead, bodyBytesToRead);
                bytesRead += bodyBytesToRead;
                if (bytesRead == totalSectionLength) {
                    if (sectionSyntaxIndicator) {
                        // This section has common syntax as defined in ISO/IEC 13818-1, section 2.4.4.11.
                        if (Util.crc(sectionData.data, 0, totalSectionLength, 0xFFFFFFFF) != 0) {
                            // The CRC is invalid so discard the section.
                            waitingForPayloadStart = true;
                            return;
                        }
                        // Exclude the CRC_32 field.
                        sectionData.reset(totalSectionLength - 4);
                    } else {
                        // This is a private section with private defined syntax.
                        sectionData.reset(totalSectionLength);
                    }
                    reader.consume(sectionData);
                    bytesRead = 0;
                }
            }
        }
    }
}

19 Source : MpegAudioReader.java
with GNU General Public License v2.0
from warren-bank

/**
 * Parses a continuous MPEG Audio byte stream and extracts individual frames.
 */
public final clreplaced MpegAudioReader implements ElementaryStreamReader {

    private static final int STATE_FINDING_HEADER = 0;

    private static final int STATE_READING_HEADER = 1;

    private static final int STATE_READING_FRAME = 2;

    private static final int HEADER_SIZE = 4;

    private final ParsableByteArray headerScratch;

    private final MpegAudioHeader header;

    private final String language;

    private String formatId;

    private TrackOutput output;

    private int state;

    private int frameBytesRead;

    private boolean hasOutputFormat;

    // Used when finding the frame header.
    private boolean lastByteWasFF;

    // Parsed from the frame header.
    private long frameDurationUs;

    private int frameSize;

    // The timestamp to attach to the next sample in the current packet.
    private long timeUs;

    public MpegAudioReader() {
        this(null);
    }

    public MpegAudioReader(String language) {
        state = STATE_FINDING_HEADER;
        // The first byte of an MPEG Audio frame header is always 0xFF.
        headerScratch = new ParsableByteArray(4);
        headerScratch.data[0] = (byte) 0xFF;
        header = new MpegAudioHeader();
        this.language = language;
    }

    @Override
    public void seek() {
        state = STATE_FINDING_HEADER;
        frameBytesRead = 0;
        lastByteWasFF = false;
    }

    @Override
    public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
        idGenerator.generateNewId();
        formatId = idGenerator.getFormatId();
        output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_AUDIO);
    }

    @Override
    public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) {
        timeUs = pesTimeUs;
    }

    @Override
    public void consume(ParsableByteArray data) {
        while (data.bytesLeft() > 0) {
            switch(state) {
                case STATE_FINDING_HEADER:
                    findHeader(data);
                    break;
                case STATE_READING_HEADER:
                    readHeaderRemainder(data);
                    break;
                case STATE_READING_FRAME:
                    readFrameRemainder(data);
                    break;
                default:
                    throw new IllegalStateException();
            }
        }
    }

    @Override
    public void packetFinished() {
    // Do nothing.
    }

    /**
     * Attempts to locate the start of the next frame header.
     * <p>
     * If a frame header is located then the state is changed to {@link #STATE_READING_HEADER}, the
     * first two bytes of the header are written into {@link #headerScratch}, and the position of the
     * source is advanced to the byte that immediately follows these two bytes.
     * <p>
     * If a frame header is not located then the position of the source is advanced to the limit, and
     * the method should be called again with the next source to continue the search.
     *
     * @param source The source from which to read.
     */
    private void findHeader(ParsableByteArray source) {
        byte[] data = source.data;
        int startOffset = source.getPosition();
        int endOffset = source.limit();
        for (int i = startOffset; i < endOffset; i++) {
            boolean byteIsFF = (data[i] & 0xFF) == 0xFF;
            boolean found = lastByteWasFF && (data[i] & 0xE0) == 0xE0;
            lastByteWasFF = byteIsFF;
            if (found) {
                source.setPosition(i + 1);
                // Reset lastByteWasFF for next time.
                lastByteWasFF = false;
                headerScratch.data[1] = data[i];
                frameBytesRead = 2;
                state = STATE_READING_HEADER;
                return;
            }
        }
        source.setPosition(endOffset);
    }

    /**
     * Attempts to read the remaining two bytes of the frame header.
     * <p>
     * If a frame header is read in full then the state is changed to {@link #STATE_READING_FRAME},
     * the media format is output if this has not previously occurred, the four header bytes are
     * output as sample data, and the position of the source is advanced to the byte that immediately
     * follows the header.
     * <p>
     * If a frame header is read in full but cannot be parsed then the state is changed to
     * {@link #STATE_READING_HEADER}.
     * <p>
     * If a frame header is not read in full then the position of the source is advanced to the limit,
     * and the method should be called again with the next source to continue the read.
     *
     * @param source The source from which to read.
     */
    private void readHeaderRemainder(ParsableByteArray source) {
        int bytesToRead = Math.min(source.bytesLeft(), HEADER_SIZE - frameBytesRead);
        source.readBytes(headerScratch.data, frameBytesRead, bytesToRead);
        frameBytesRead += bytesToRead;
        if (frameBytesRead < HEADER_SIZE) {
            // We haven't read the whole header yet.
            return;
        }
        headerScratch.setPosition(0);
        boolean parsedHeader = MpegAudioHeader.populateHeader(headerScratch.readInt(), header);
        if (!parsedHeader) {
            // We thought we'd located a frame header, but we hadn't.
            frameBytesRead = 0;
            state = STATE_READING_HEADER;
            return;
        }
        frameSize = header.frameSize;
        if (!hasOutputFormat) {
            frameDurationUs = (C.MICROS_PER_SECOND * header.samplesPerFrame) / header.sampleRate;
            Format format = Format.createAudioSampleFormat(formatId, header.mimeType, null, Format.NO_VALUE, MpegAudioHeader.MAX_FRAME_SIZE_BYTES, header.channels, header.sampleRate, null, null, 0, language);
            output.format(format);
            hasOutputFormat = true;
        }
        headerScratch.setPosition(0);
        output.sampleData(headerScratch, HEADER_SIZE);
        state = STATE_READING_FRAME;
    }

    /**
     * Attempts to read the remainder of the frame.
     * <p>
     * If a frame is read in full then true is returned. The frame will have been output, and the
     * position of the source will have been advanced to the byte that immediately follows the end of
     * the frame.
     * <p>
     * If a frame is not read in full then the position of the source will have been advanced to the
     * limit, and the method should be called again with the next source to continue the read.
     *
     * @param source The source from which to read.
     */
    private void readFrameRemainder(ParsableByteArray source) {
        int bytesToRead = Math.min(source.bytesLeft(), frameSize - frameBytesRead);
        output.sampleData(source, bytesToRead);
        frameBytesRead += bytesToRead;
        if (frameBytesRead < frameSize) {
            // We haven't read the whole of the frame yet.
            return;
        }
        output.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, frameSize, 0, null);
        timeUs += frameDurationUs;
        frameBytesRead = 0;
        state = STATE_FINDING_HEADER;
    }
}

19 Source : Id3Reader.java
with GNU General Public License v2.0
from warren-bank

/**
 * Parses ID3 data and extracts individual text information frames.
 */
public final clreplaced Id3Reader implements ElementaryStreamReader {

    private static final String TAG = "Id3Reader";

    private static final int ID3_HEADER_SIZE = 10;

    private final ParsableByteArray id3Header;

    private TrackOutput output;

    // State that should be reset on seek.
    private boolean writingSample;

    // Per sample state that gets reset at the start of each sample.
    private long sampleTimeUs;

    private int sampleSize;

    private int sampleBytesRead;

    public Id3Reader() {
        id3Header = new ParsableByteArray(ID3_HEADER_SIZE);
    }

    @Override
    public void seek() {
        writingSample = false;
    }

    @Override
    public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
        idGenerator.generateNewId();
        output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_METADATA);
        output.format(Format.createSampleFormat(idGenerator.getFormatId(), MimeTypes.APPLICATION_ID3, null, Format.NO_VALUE, null));
    }

    @Override
    public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) {
        if (!dataAlignmentIndicator) {
            return;
        }
        writingSample = true;
        sampleTimeUs = pesTimeUs;
        sampleSize = 0;
        sampleBytesRead = 0;
    }

    @Override
    public void consume(ParsableByteArray data) {
        if (!writingSample) {
            return;
        }
        int bytesAvailable = data.bytesLeft();
        if (sampleBytesRead < ID3_HEADER_SIZE) {
            // We're still reading the ID3 header.
            int headerBytesAvailable = Math.min(bytesAvailable, ID3_HEADER_SIZE - sampleBytesRead);
            System.arraycopy(data.data, data.getPosition(), id3Header.data, sampleBytesRead, headerBytesAvailable);
            if (sampleBytesRead + headerBytesAvailable == ID3_HEADER_SIZE) {
                // We've finished reading the ID3 header. Extract the sample size.
                id3Header.setPosition(0);
                if ('I' != id3Header.readUnsignedByte() || 'D' != id3Header.readUnsignedByte() || '3' != id3Header.readUnsignedByte()) {
                    Log.w(TAG, "Discarding invalid ID3 tag");
                    writingSample = false;
                    return;
                }
                // version (2) + flags (1)
                id3Header.skipBytes(3);
                sampleSize = ID3_HEADER_SIZE + id3Header.readSynchSafeInt();
            }
        }
        // Write data to the output.
        int bytesToWrite = Math.min(bytesAvailable, sampleSize - sampleBytesRead);
        output.sampleData(data, bytesToWrite);
        sampleBytesRead += bytesToWrite;
    }

    @Override
    public void packetFinished() {
        if (!writingSample || sampleSize == 0 || sampleBytesRead != sampleSize) {
            return;
        }
        output.sampleMetadata(sampleTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleSize, 0, null);
        writingSample = false;
    }
}

19 Source : DvbSubtitleReader.java
with GNU General Public License v2.0
from warren-bank

private boolean checkNextByte(ParsableByteArray data, int expectedValue) {
    if (data.bytesLeft() == 0) {
        return false;
    }
    if (data.readUnsignedByte() != expectedValue) {
        writingSample = false;
    }
    bytesToCheck--;
    return writingSample;
}

19 Source : Ac3Reader.java
with GNU General Public License v2.0
from warren-bank

/**
 * Locates the next syncword, advancing the position to the byte that immediately follows it. If a
 * syncword was not located, the position is advanced to the limit.
 *
 * @param pesBuffer The buffer whose position should be advanced.
 * @return Whether a syncword position was found.
 */
private boolean skipToNextSync(ParsableByteArray pesBuffer) {
    while (pesBuffer.bytesLeft() > 0) {
        if (!lastByteWas0B) {
            lastByteWas0B = pesBuffer.readUnsignedByte() == 0x0B;
            continue;
        }
        int secondByte = pesBuffer.readUnsignedByte();
        if (secondByte == 0x77) {
            lastByteWas0B = false;
            return true;
        } else {
            lastByteWas0B = secondByte == 0x0B;
        }
    }
    return false;
}

19 Source : VorbisUtil.java
with GNU General Public License v2.0
from warren-bank

/**
 * Verifies whether the next bytes in {@code header} are a vorbis header of the given
 * {@code headerType}.
 *
 * @param headerType the type of the header expected.
 * @param header the alleged header bytes.
 * @param quiet if {@code true} no exceptions are thrown. Instead {@code false} is returned.
 * @return the number of bytes read.
 * @throws ParserException thrown if header type or capture pattern is not as expected.
 */
public static boolean verifyVorbisHeaderCapturePattern(int headerType, ParsableByteArray header, boolean quiet) throws ParserException {
    if (header.bytesLeft() < 7) {
        if (quiet) {
            return false;
        } else {
            throw new ParserException("too short header: " + header.bytesLeft());
        }
    }
    if (header.readUnsignedByte() != headerType) {
        if (quiet) {
            return false;
        } else {
            throw new ParserException("expected header type " + Integer.toHexString(headerType));
        }
    }
    if (!(header.readUnsignedByte() == 'v' && header.readUnsignedByte() == 'o' && header.readUnsignedByte() == 'r' && header.readUnsignedByte() == 'b' && header.readUnsignedByte() == 'i' && header.readUnsignedByte() == 's')) {
        if (quiet) {
            return false;
        } else {
            throw new ParserException("expected characters 'vorbis'");
        }
    }
    return true;
}

19 Source : VorbisUtil.java
with GNU General Public License v2.0
from warren-bank

/**
 * Reads a vorbis identification header from {@code headerData}.
 *
 * @see <a href="https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-630004.2.2">Vorbis
 *     spec/Identification header</a>
 * @param headerData a {@link ParsableByteArray} wrapping the header data.
 * @return a {@link VorbisUtil.VorbisIdHeader} with meta data.
 * @throws ParserException thrown if invalid capture pattern is detected.
 */
public static VorbisIdHeader readVorbisIdentificationHeader(ParsableByteArray headerData) throws ParserException {
    verifyVorbisHeaderCapturePattern(0x01, headerData, false);
    long version = headerData.readLittleEndianUnsignedInt();
    int channels = headerData.readUnsignedByte();
    long sampleRate = headerData.readLittleEndianUnsignedInt();
    int bitrateMax = headerData.readLittleEndianInt();
    int bitrateNominal = headerData.readLittleEndianInt();
    int bitrateMin = headerData.readLittleEndianInt();
    int blockSize = headerData.readUnsignedByte();
    int blockSize0 = (int) Math.pow(2, blockSize & 0x0F);
    int blockSize1 = (int) Math.pow(2, (blockSize & 0xF0) >> 4);
    boolean framingFlag = (headerData.readUnsignedByte() & 0x01) > 0;
    // raw data of vorbis setup header has to be preplaceded to decoder as CSD buffer #1
    byte[] data = Arrays.copyOf(headerData.data, headerData.limit());
    return new VorbisIdHeader(version, channels, sampleRate, bitrateMax, bitrateNominal, bitrateMin, blockSize0, blockSize1, framingFlag, data);
}

19 Source : VorbisUtil.java
with GNU General Public License v2.0
from warren-bank

/**
 * This method reads the modes which are located at the very end of the vorbis setup header.
 * That's why we need to partially decode or at least read the entire setup header to know
 * where to start reading the modes.
 *
 * @see <a href="https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-650004.2.4">
 *     Vorbis spec/Setup header</a>
 * @param headerData a {@link ParsableByteArray} containing setup header data.
 * @param channels the number of channels.
 * @return an array of {@link Mode}s.
 * @throws ParserException thrown if bit stream is invalid.
 */
public static Mode[] readVorbisModes(ParsableByteArray headerData, int channels) throws ParserException {
    verifyVorbisHeaderCapturePattern(0x05, headerData, false);
    int numberOfBooks = headerData.readUnsignedByte() + 1;
    VorbisBitArray bitArray = new VorbisBitArray(headerData.data);
    bitArray.skipBits(headerData.getPosition() * 8);
    for (int i = 0; i < numberOfBooks; i++) {
        readBook(bitArray);
    }
    int timeCount = bitArray.readBits(6) + 1;
    for (int i = 0; i < timeCount; i++) {
        if (bitArray.readBits(16) != 0x00) {
            throw new ParserException("placeholder of time domain transforms not zeroed out");
        }
    }
    readFloors(bitArray);
    readResidues(bitArray);
    readMappings(channels, bitArray);
    Mode[] modes = readModes(bitArray);
    if (!bitArray.readBit()) {
        throw new ParserException("framing bit after modes not set as expected");
    }
    return modes;
}

19 Source : VorbisReader.java
with GNU General Public License v2.0
from warren-bank

public static boolean verifyBitstreamType(ParsableByteArray data) {
    try {
        return VorbisUtil.verifyVorbisHeaderCapturePattern(0x01, data, true);
    } catch (ParserException e) {
        return false;
    }
}

19 Source : OggPageHeader.java
with GNU General Public License v2.0
from warren-bank

/**
 * Data object to store header information.
 */
/* package */
final clreplaced OggPageHeader {

    public static final int EMPTY_PAGE_HEADER_SIZE = 27;

    public static final int MAX_SEGMENT_COUNT = 255;

    public static final int MAX_PAGE_PAYLOAD = 255 * 255;

    public static final int MAX_PAGE_SIZE = EMPTY_PAGE_HEADER_SIZE + MAX_SEGMENT_COUNT + MAX_PAGE_PAYLOAD;

    private static final int TYPE_OGGS = Util.getIntegerCodeForString("OggS");

    public int revision;

    public int type;

    public long granulePosition;

    public long streamSerialNumber;

    public long pageSequenceNumber;

    public long pageChecksum;

    public int pageSegmentCount;

    public int headerSize;

    public int bodySize;

    /**
     * Be aware that {@code laces.length} is always {@link #MAX_SEGMENT_COUNT}. Instead use
     * {@link #pageSegmentCount} to iterate.
     */
    public final int[] laces = new int[MAX_SEGMENT_COUNT];

    private final ParsableByteArray scratch = new ParsableByteArray(MAX_SEGMENT_COUNT);

    /**
     * Resets all primitive member fields to zero.
     */
    public void reset() {
        revision = 0;
        type = 0;
        granulePosition = 0;
        streamSerialNumber = 0;
        pageSequenceNumber = 0;
        pageChecksum = 0;
        pageSegmentCount = 0;
        headerSize = 0;
        bodySize = 0;
    }

    /**
     * Peeks an Ogg page header and updates this {@link OggPageHeader}.
     *
     * @param input The {@link ExtractorInput} to read from.
     * @param quiet If {@code true}, no exceptions are thrown but {@code false} is returned if
     *     something goes wrong.
     * @return {@code true} if the read was successful. The read fails if the end of the input is
     *     encountered without reading data.
     * @throws IOException If reading data fails or the stream is invalid.
     * @throws InterruptedException If the thread is interrupted.
     */
    public boolean populate(ExtractorInput input, boolean quiet) throws IOException, InterruptedException {
        scratch.reset();
        reset();
        boolean hasEnoughBytes = input.getLength() == C.LENGTH_UNSET || input.getLength() - input.getPeekPosition() >= EMPTY_PAGE_HEADER_SIZE;
        if (!hasEnoughBytes || !input.peekFully(scratch.data, 0, EMPTY_PAGE_HEADER_SIZE, true)) {
            if (quiet) {
                return false;
            } else {
                throw new EOFException();
            }
        }
        if (scratch.readUnsignedInt() != TYPE_OGGS) {
            if (quiet) {
                return false;
            } else {
                throw new ParserException("expected OggS capture pattern at begin of page");
            }
        }
        revision = scratch.readUnsignedByte();
        if (revision != 0x00) {
            if (quiet) {
                return false;
            } else {
                throw new ParserException("unsupported bit stream revision");
            }
        }
        type = scratch.readUnsignedByte();
        granulePosition = scratch.readLittleEndianLong();
        streamSerialNumber = scratch.readLittleEndianUnsignedInt();
        pageSequenceNumber = scratch.readLittleEndianUnsignedInt();
        pageChecksum = scratch.readLittleEndianUnsignedInt();
        pageSegmentCount = scratch.readUnsignedByte();
        headerSize = EMPTY_PAGE_HEADER_SIZE + pageSegmentCount;
        // calculate total size of header including laces
        scratch.reset();
        input.peekFully(scratch.data, 0, pageSegmentCount);
        for (int i = 0; i < pageSegmentCount; i++) {
            laces[i] = scratch.readUnsignedByte();
            bodySize += laces[i];
        }
        return true;
    }
}

19 Source : OggPacket.java
with GNU General Public License v2.0
from warren-bank

/**
 * OGG packet clreplaced.
 */
/* package */
final clreplaced OggPacket {

    private final OggPageHeader pageHeader = new OggPageHeader();

    private final ParsableByteArray packetArray = new ParsableByteArray(new byte[OggPageHeader.MAX_PAGE_PAYLOAD], 0);

    private int currentSegmentIndex = C.INDEX_UNSET;

    private int segmentCount;

    private boolean populated;

    /**
     * Resets this reader.
     */
    public void reset() {
        pageHeader.reset();
        packetArray.reset();
        currentSegmentIndex = C.INDEX_UNSET;
        populated = false;
    }

    /**
     * Reads the next packet of the ogg stream. In case of an {@code IOException} the caller must make
     * sure to preplaced the same instance of {@code ParsableByteArray} to this method again so this reader
     * can resume properly from an error while reading a continued packet spanned across multiple
     * pages.
     *
     * @param input The {@link ExtractorInput} to read data from.
     * @return {@code true} if the read was successful. The read fails if the end of the input is
     *     encountered without reading data.
     * @throws IOException If reading from the input fails.
     * @throws InterruptedException If the thread is interrupted.
     */
    public boolean populate(ExtractorInput input) throws IOException, InterruptedException {
        replacedertions.checkState(input != null);
        if (populated) {
            populated = false;
            packetArray.reset();
        }
        while (!populated) {
            if (currentSegmentIndex < 0) {
                // We're at the start of a page.
                if (!pageHeader.populate(input, true)) {
                    return false;
                }
                int segmentIndex = 0;
                int bytesToSkip = pageHeader.headerSize;
                if ((pageHeader.type & 0x01) == 0x01 && packetArray.limit() == 0) {
                    // After seeking, the first packet may be the remainder
                    // part of a continued packet which has to be discarded.
                    bytesToSkip += calculatePacketSize(segmentIndex);
                    segmentIndex += segmentCount;
                }
                input.skipFully(bytesToSkip);
                currentSegmentIndex = segmentIndex;
            }
            int size = calculatePacketSize(currentSegmentIndex);
            int segmentIndex = currentSegmentIndex + segmentCount;
            if (size > 0) {
                if (packetArray.capacity() < packetArray.limit() + size) {
                    packetArray.data = Arrays.copyOf(packetArray.data, packetArray.limit() + size);
                }
                input.readFully(packetArray.data, packetArray.limit(), size);
                packetArray.setLimit(packetArray.limit() + size);
                populated = pageHeader.laces[segmentIndex - 1] != 255;
            }
            // Advance now since we are sure reading didn't throw an exception.
            currentSegmentIndex = segmentIndex == pageHeader.pageSegmentCount ? C.INDEX_UNSET : segmentIndex;
        }
        return true;
    }

    /**
     * An OGG Packet may span multiple pages. Returns the {@link OggPageHeader} of the last page read,
     * or an empty header if the packet has yet to be populated.
     * <p>
     * Note that the returned {@link OggPageHeader} is mutable and may be updated during subsequent
     * calls to {@link #populate(ExtractorInput)}.
     *
     * @return the {@code PageHeader} of the last page read or an empty header if the packet has yet
     *     to be populated.
     */
    // @VisibleForTesting
    public OggPageHeader getPageHeader() {
        return pageHeader;
    }

    /**
     * Returns a {@link ParsableByteArray} containing the packet's payload.
     */
    public ParsableByteArray getPayload() {
        return packetArray;
    }

    /**
     * Trims the packet data array.
     */
    public void trimPayload() {
        if (packetArray.data.length == OggPageHeader.MAX_PAGE_PAYLOAD) {
            return;
        }
        packetArray.data = Arrays.copyOf(packetArray.data, Math.max(OggPageHeader.MAX_PAGE_PAYLOAD, packetArray.limit()));
    }

    /**
     * Calculates the size of the packet starting from {@code startSegmentIndex}.
     *
     * @param startSegmentIndex the index of the first segment of the packet.
     * @return Size of the packet.
     */
    private int calculatePacketSize(int startSegmentIndex) {
        segmentCount = 0;
        int size = 0;
        while (startSegmentIndex + segmentCount < pageHeader.pageSegmentCount) {
            int segmentLength = pageHeader.laces[startSegmentIndex + segmentCount++];
            size += segmentLength;
            if (segmentLength != 255) {
                // packets end at first lace < 255
                break;
            }
        }
        return size;
    }
}

19 Source : FlacReader.java
with GNU General Public License v2.0
from warren-bank

public static boolean verifyBitstreamType(ParsableByteArray data) {
    return // packet type
    data.bytesLeft() >= 5 && data.readUnsignedByte() == 0x7F && // ASCII signature "FLAC"
    data.readUnsignedInt() == 0x464C4143;
}

19 Source : TrackFragment.java
with GNU General Public License v2.0
from warren-bank

/**
 * A holder for information corresponding to a single fragment of an mp4 file.
 */
/* package */
final clreplaced TrackFragment {

    /**
     * The default values for samples from the track fragment header.
     */
    public DefaultSampleValues header;

    /**
     * The position (byte offset) of the start of fragment.
     */
    public long atomPosition;

    /**
     * The position (byte offset) of the start of data contained in the fragment.
     */
    public long dataPosition;

    /**
     * The position (byte offset) of the start of auxiliary data.
     */
    public long auxiliaryDataPosition;

    /**
     * The number of track runs of the fragment.
     */
    public int trunCount;

    /**
     * The total number of samples in the fragment.
     */
    public int sampleCount;

    /**
     * The position (byte offset) of the start of sample data of each track run in the fragment.
     */
    public long[] trunDataPosition;

    /**
     * The number of samples contained by each track run in the fragment.
     */
    public int[] trunLength;

    /**
     * The size of each sample in the fragment.
     */
    public int[] sampleSizeTable;

    /**
     * The composition time offset of each sample in the fragment.
     */
    public int[] sampleCompositionTimeOffsetTable;

    /**
     * The decoding time of each sample in the fragment.
     */
    public long[] sampleDecodingTimeTable;

    /**
     * Indicates which samples are sync frames.
     */
    public boolean[] sampleIsSyncFrameTable;

    /**
     * Whether the fragment defines encryption data.
     */
    public boolean definesEncryptionData;

    /**
     * If {@link #definesEncryptionData} is true, indicates which samples use sub-sample encryption.
     * Undefined otherwise.
     */
    public boolean[] sampleHreplacedubsampleEncryptionTable;

    /**
     * Fragment specific track encryption. May be null.
     */
    public TrackEncryptionBox trackEncryptionBox;

    /**
     * If {@link #definesEncryptionData} is true, indicates the length of the sample encryption data.
     * Undefined otherwise.
     */
    public int sampleEncryptionDataLength;

    /**
     * If {@link #definesEncryptionData} is true, contains binary sample encryption data. Undefined
     * otherwise.
     */
    public ParsableByteArray sampleEncryptionData;

    /**
     * Whether {@link #sampleEncryptionData} needs populating with the actual encryption data.
     */
    public boolean sampleEncryptionDataNeedsFill;

    /**
     * The absolute decode time of the start of the next fragment.
     */
    public long nextFragmentDecodeTime;

    /**
     * Resets the fragment.
     * <p>
     * {@link #sampleCount} and {@link #nextFragmentDecodeTime} are set to 0, and both
     * {@link #definesEncryptionData} and {@link #sampleEncryptionDataNeedsFill} is set to false,
     * and {@link #trackEncryptionBox} is set to null.
     */
    public void reset() {
        trunCount = 0;
        nextFragmentDecodeTime = 0;
        definesEncryptionData = false;
        sampleEncryptionDataNeedsFill = false;
        trackEncryptionBox = null;
    }

    /**
     * Configures the fragment for the specified number of samples.
     * <p>
     * The {@link #sampleCount} of the fragment is set to the specified sample count, and the
     * contained tables are resized if necessary such that they are at least this length.
     *
     * @param sampleCount The number of samples in the new run.
     */
    public void initTables(int trunCount, int sampleCount) {
        this.trunCount = trunCount;
        this.sampleCount = sampleCount;
        if (trunLength == null || trunLength.length < trunCount) {
            trunDataPosition = new long[trunCount];
            trunLength = new int[trunCount];
        }
        if (sampleSizeTable == null || sampleSizeTable.length < sampleCount) {
            // Size the tables 25% larger than needed, so as to make future resize operations less
            // likely. The choice of 25% is relatively arbitrary.
            int tableSize = (sampleCount * 125) / 100;
            sampleSizeTable = new int[tableSize];
            sampleCompositionTimeOffsetTable = new int[tableSize];
            sampleDecodingTimeTable = new long[tableSize];
            sampleIsSyncFrameTable = new boolean[tableSize];
            sampleHreplacedubsampleEncryptionTable = new boolean[tableSize];
        }
    }

    /**
     * Configures the fragment to be one that defines encryption data of the specified length.
     * <p>
     * {@link #definesEncryptionData} is set to true, {@link #sampleEncryptionDataLength} is set to
     * the specified length, and {@link #sampleEncryptionData} is resized if necessary such that it
     * is at least this length.
     *
     * @param length The length in bytes of the encryption data.
     */
    public void initEncryptionData(int length) {
        if (sampleEncryptionData == null || sampleEncryptionData.limit() < length) {
            sampleEncryptionData = new ParsableByteArray(length);
        }
        sampleEncryptionDataLength = length;
        definesEncryptionData = true;
        sampleEncryptionDataNeedsFill = true;
    }

    /**
     * Fills {@link #sampleEncryptionData} from the provided input.
     *
     * @param input An {@link ExtractorInput} from which to read the encryption data.
     */
    public void fillEncryptionData(ExtractorInput input) throws IOException, InterruptedException {
        input.readFully(sampleEncryptionData.data, 0, sampleEncryptionDataLength);
        sampleEncryptionData.setPosition(0);
        sampleEncryptionDataNeedsFill = false;
    }

    /**
     * Fills {@link #sampleEncryptionData} from the provided source.
     *
     * @param source A source from which to read the encryption data.
     */
    public void fillEncryptionData(ParsableByteArray source) {
        source.readBytes(sampleEncryptionData.data, 0, sampleEncryptionDataLength);
        sampleEncryptionData.setPosition(0);
        sampleEncryptionDataNeedsFill = false;
    }

    public long getSamplePresentationTime(int index) {
        return sampleDecodingTimeTable[index] + sampleCompositionTimeOffsetTable[index];
    }

    /**
     * Returns whether the sample at the given index has a subsample encryption table.
     */
    public boolean sampleHreplacedubsampleEncryptionTable(int index) {
        return definesEncryptionData && sampleHreplacedubsampleEncryptionTable[index];
    }
}

19 Source : Sniffer.java
with GNU General Public License v2.0
from warren-bank

/**
 * Utility clreplaced that peeks from the input stream in order to determine whether it appears to be
 * compatible input for this extractor.
 */
/* package */
final clreplaced 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 com.google.android.exoplayer2.extractor.Extractor#sniff(ExtractorInput)
     */
    public boolean sniff(ExtractorInput input) throws IOException, InterruptedException {
        long inputLength = input.getLength();
        int bytesToSearch = (int) (inputLength == C.LENGTH_UNSET || 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_UNSET && 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) {
                int sizeInt = (int) size;
                input.advancePeekPosition(sizeInt);
                peekLength += sizeInt;
            }
        }
        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;
    }
}

19 Source : Id3Peeker.java
with GNU General Public License v2.0
from warren-bank

/**
 * Peeks data from the beginning of an {@link ExtractorInput} to determine if there is any ID3 tag.
 */
public final clreplaced Id3Peeker {

    private final ParsableByteArray scratch;

    public Id3Peeker() {
        scratch = new ParsableByteArray(Id3Decoder.ID3_HEADER_LENGTH);
    }

    /**
     * Peeks ID3 data from the input and parses the first ID3 tag.
     *
     * @param input The {@link ExtractorInput} from which data should be peeked.
     * @param id3FramePredicate Determines which ID3 frames are decoded. May be null to decode all
     *     frames.
     * @return The first ID3 tag decoded into a {@link Metadata} object. May be null if ID3 tag is not
     *     present in the input.
     * @throws IOException If an error occurred peeking from the input.
     * @throws InterruptedException If the thread was interrupted.
     */
    @Nullable
    public Metadata peekId3Data(ExtractorInput input, @Nullable Id3Decoder.FramePredicate id3FramePredicate) throws IOException, InterruptedException {
        int peekedId3Bytes = 0;
        Metadata metadata = null;
        while (true) {
            try {
                input.peekFully(scratch.data, 0, Id3Decoder.ID3_HEADER_LENGTH);
            } catch (EOFException e) {
                // If input has less than ID3_HEADER_LENGTH, ignore the rest.
                break;
            }
            scratch.setPosition(0);
            if (scratch.readUnsignedInt24() != Id3Decoder.ID3_TAG) {
                // Not an ID3 tag.
                break;
            }
            // Skip major version, minor version and flags.
            scratch.skipBytes(3);
            int framesLength = scratch.readSynchSafeInt();
            int tagLength = Id3Decoder.ID3_HEADER_LENGTH + framesLength;
            if (metadata == null) {
                byte[] id3Data = new byte[tagLength];
                System.arraycopy(scratch.data, 0, id3Data, 0, Id3Decoder.ID3_HEADER_LENGTH);
                input.peekFully(id3Data, Id3Decoder.ID3_HEADER_LENGTH, framesLength);
                metadata = new Id3Decoder(id3FramePredicate).decode(id3Data, tagLength);
            } else {
                input.advancePeekPosition(framesLength);
            }
            peekedId3Bytes += tagLength;
        }
        input.resetPeekPosition();
        input.advancePeekPosition(peekedId3Bytes);
        return metadata;
    }
}

19 Source : VideoTagPayloadReader.java
with GNU General Public License v2.0
from warren-bank

/**
 * Parses video tags from an FLV stream and extracts H.264 nal units.
 */
/* package */
final clreplaced VideoTagPayloadReader extends TagPayloadReader {

    // Video codec.
    private static final int VIDEO_CODEC_AVC = 7;

    // Frame types.
    private static final int VIDEO_FRAME_KEYFRAME = 1;

    private static final int VIDEO_FRAME_VIDEO_INFO = 5;

    // Packet types.
    private static final int AVC_PACKET_TYPE_SEQUENCE_HEADER = 0;

    private static final int AVC_PACKET_TYPE_AVC_NALU = 1;

    // Temporary arrays.
    private final ParsableByteArray nalStartCode;

    private final ParsableByteArray nalLength;

    private int nalUnitLengthFieldLength;

    // State variables.
    private boolean hasOutputFormat;

    private int frameType;

    /**
     * @param output A {@link TrackOutput} to which samples should be written.
     */
    public VideoTagPayloadReader(TrackOutput output) {
        super(output);
        nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE);
        nalLength = new ParsableByteArray(4);
    }

    @Override
    public void seek() {
    // Do nothing.
    }

    @Override
    protected boolean parseHeader(ParsableByteArray data) throws UnsupportedFormatException {
        int header = data.readUnsignedByte();
        int frameType = (header >> 4) & 0x0F;
        int videoCodec = (header & 0x0F);
        // Support just H.264 encoded content.
        if (videoCodec != VIDEO_CODEC_AVC) {
            throw new UnsupportedFormatException("Video format not supported: " + videoCodec);
        }
        this.frameType = frameType;
        return (frameType != VIDEO_FRAME_VIDEO_INFO);
    }

    @Override
    protected void parsePayload(ParsableByteArray data, long timeUs) throws ParserException {
        int packetType = data.readUnsignedByte();
        int compositionTimeMs = data.readInt24();
        timeUs += compositionTimeMs * 1000L;
        // Parse avc sequence header in case this was not done before.
        if (packetType == AVC_PACKET_TYPE_SEQUENCE_HEADER && !hasOutputFormat) {
            ParsableByteArray videoSequence = new ParsableByteArray(new byte[data.bytesLeft()]);
            data.readBytes(videoSequence.data, 0, data.bytesLeft());
            AvcConfig avcConfig = AvcConfig.parse(videoSequence);
            nalUnitLengthFieldLength = avcConfig.nalUnitLengthFieldLength;
            // Construct and output the format.
            Format format = Format.createVideoSampleFormat(null, MimeTypes.VIDEO_H264, null, Format.NO_VALUE, Format.NO_VALUE, avcConfig.width, avcConfig.height, Format.NO_VALUE, avcConfig.initializationData, Format.NO_VALUE, avcConfig.pixelWidthAspectRatio, null);
            output.format(format);
            hasOutputFormat = true;
        } else if (packetType == AVC_PACKET_TYPE_AVC_NALU && hasOutputFormat) {
            // TODO: Deduplicate with Mp4Extractor.
            // Zero the top three bytes of the array that we'll use to decode nal unit lengths, in case
            // they're only 1 or 2 bytes long.
            byte[] nalLengthData = nalLength.data;
            nalLengthData[0] = 0;
            nalLengthData[1] = 0;
            nalLengthData[2] = 0;
            int nalUnitLengthFieldLengthDiff = 4 - nalUnitLengthFieldLength;
            // NAL units are length delimited, but the decoder requires start code delimited units.
            // Loop until we've written the sample to the track output, replacing length delimiters with
            // start codes as we encounter them.
            int bytesWritten = 0;
            int bytesToWrite;
            while (data.bytesLeft() > 0) {
                // Read the NAL length so that we know where we find the next one.
                data.readBytes(nalLength.data, nalUnitLengthFieldLengthDiff, nalUnitLengthFieldLength);
                nalLength.setPosition(0);
                bytesToWrite = nalLength.readUnsignedIntToInt();
                // Write a start code for the current NAL unit.
                nalStartCode.setPosition(0);
                output.sampleData(nalStartCode, 4);
                bytesWritten += 4;
                // Write the payload of the NAL unit.
                output.sampleData(data, bytesToWrite);
                bytesWritten += bytesToWrite;
            }
            output.sampleMetadata(timeUs, frameType == VIDEO_FRAME_KEYFRAME ? C.BUFFER_FLAG_KEY_FRAME : 0, bytesWritten, 0, null);
        }
    }
}

19 Source : TagPayloadReader.java
with GNU General Public License v2.0
from warren-bank

/**
 * Consumes payload data.
 *
 * @param data The payload data to consume.
 * @param timeUs The timestamp replacedociated with the payload.
 * @throws ParserException If an error occurs parsing the data.
 */
public final void consume(ParsableByteArray data, long timeUs) throws ParserException {
    if (parseHeader(data)) {
        parsePayload(data, timeUs);
    }
}

19 Source : ScriptTagPayloadReader.java
with GNU General Public License v2.0
from warren-bank

/**
 * Read a boolean from an AMF encoded buffer.
 *
 * @param data The buffer from which to read.
 * @return The value read from the buffer.
 */
private static Boolean readAmfBoolean(ParsableByteArray data) {
    return data.readUnsignedByte() == 1;
}

19 Source : ScriptTagPayloadReader.java
with GNU General Public License v2.0
from warren-bank

private static Object readAmfData(ParsableByteArray data, int type) {
    switch(type) {
        case AMF_TYPE_NUMBER:
            return readAmfDouble(data);
        case AMF_TYPE_BOOLEAN:
            return readAmfBoolean(data);
        case AMF_TYPE_STRING:
            return readAmfString(data);
        case AMF_TYPE_OBJECT:
            return readAmfObject(data);
        case AMF_TYPE_ECMA_ARRAY:
            return readAmfEcmaArray(data);
        case AMF_TYPE_STRICT_ARRAY:
            return readAmfStrictArray(data);
        case AMF_TYPE_DATE:
            return readAmfDate(data);
        default:
            return null;
    }
}

19 Source : ScriptTagPayloadReader.java
with GNU General Public License v2.0
from warren-bank

/**
 * Read an array from an AMF encoded buffer.
 *
 * @param data The buffer from which to read.
 * @return The value read from the buffer.
 */
private static ArrayList<Object> readAmfStrictArray(ParsableByteArray data) {
    int count = data.readUnsignedIntToInt();
    ArrayList<Object> list = new ArrayList<>(count);
    for (int i = 0; i < count; i++) {
        int type = readAmfType(data);
        list.add(readAmfData(data, type));
    }
    return list;
}

19 Source : Tx3gDecoder.java
with GNU General Public License v2.0
from TelePlusDev

/**
 * A {@link SimpleSubreplacedleDecoder} for tx3g.
 * <p>
 * Currently supports parsing of a single text track with embedded styles.
 */
public final clreplaced Tx3gDecoder extends SimpleSubreplacedleDecoder {

    private static final char BOM_UTF16_BE = '\uFEFF';

    private static final char BOM_UTF16_LE = '\uFFFE';

    private static final int TYPE_STYL = Util.getIntegerCodeForString("styl");

    private static final int TYPE_TBOX = Util.getIntegerCodeForString("tbox");

    private static final String TX3G_SERIF = "Serif";

    private static final int SIZE_ATOM_HEADER = 8;

    private static final int SIZE_SHORT = 2;

    private static final int SIZE_BOM_UTF16 = 2;

    private static final int SIZE_STYLE_RECORD = 12;

    private static final int FONT_FACE_BOLD = 0x0001;

    private static final int FONT_FACE_ITALIC = 0x0002;

    private static final int FONT_FACE_UNDERLINE = 0x0004;

    private static final int SPAN_PRIORITY_LOW = (0xFF << Spanned.SPAN_PRIORITY_SHIFT);

    private static final int SPAN_PRIORITY_HIGH = (0x00 << Spanned.SPAN_PRIORITY_SHIFT);

    private static final int DEFAULT_FONT_FACE = 0;

    private static final int DEFAULT_COLOR = Color.WHITE;

    private static final String DEFAULT_FONT_FAMILY = C.SANS_SERIF_NAME;

    private static final float DEFAULT_VERTICAL_PLACEMENT = 0.85f;

    private final ParsableByteArray parsableByteArray;

    private boolean customVerticalPlacement;

    private int defaultFontFace;

    private int defaultColorRgba;

    private String defaultFontFamily;

    private float defaultVerticalPlacement;

    private int calculatedVideoTrackHeight;

    /**
     * Sets up a new {@link Tx3gDecoder} with default values.
     *
     * @param initializationData Sample description atom ('stsd') data with default subreplacedle styles.
     */
    public Tx3gDecoder(List<byte[]> initializationData) {
        super("Tx3gDecoder");
        parsableByteArray = new ParsableByteArray();
        decodeInitializationData(initializationData);
    }

    private void decodeInitializationData(List<byte[]> initializationData) {
        if (initializationData != null && initializationData.size() == 1 && (initializationData.get(0).length == 48 || initializationData.get(0).length == 53)) {
            byte[] initializationBytes = initializationData.get(0);
            defaultFontFace = initializationBytes[24];
            defaultColorRgba = ((initializationBytes[26] & 0xFF) << 24) | ((initializationBytes[27] & 0xFF) << 16) | ((initializationBytes[28] & 0xFF) << 8) | (initializationBytes[29] & 0xFF);
            String fontFamily = Util.fromUtf8Bytes(initializationBytes, 43, initializationBytes.length - 43);
            defaultFontFamily = TX3G_SERIF.equals(fontFamily) ? C.SERIF_NAME : C.SANS_SERIF_NAME;
            // font size (initializationBytes[25]) is 5% of video height
            calculatedVideoTrackHeight = 20 * initializationBytes[25];
            customVerticalPlacement = (initializationBytes[0] & 0x20) != 0;
            if (customVerticalPlacement) {
                int requestedVerticalPlacement = ((initializationBytes[10] & 0xFF) << 8) | (initializationBytes[11] & 0xFF);
                defaultVerticalPlacement = (float) requestedVerticalPlacement / calculatedVideoTrackHeight;
                defaultVerticalPlacement = Util.constrainValue(defaultVerticalPlacement, 0.0f, 0.95f);
            } else {
                defaultVerticalPlacement = DEFAULT_VERTICAL_PLACEMENT;
            }
        } else {
            defaultFontFace = DEFAULT_FONT_FACE;
            defaultColorRgba = DEFAULT_COLOR;
            defaultFontFamily = DEFAULT_FONT_FAMILY;
            customVerticalPlacement = false;
            defaultVerticalPlacement = DEFAULT_VERTICAL_PLACEMENT;
        }
    }

    @Override
    protected Subreplacedle decode(byte[] bytes, int length, boolean reset) throws SubreplacedleDecoderException {
        parsableByteArray.reset(bytes, length);
        String cueTextString = readSubreplacedleText(parsableByteArray);
        if (cueTextString.isEmpty()) {
            return Tx3gSubreplacedle.EMPTY;
        }
        // Attach default styles.
        SpannableStringBuilder cueText = new SpannableStringBuilder(cueTextString);
        attachFontFace(cueText, defaultFontFace, DEFAULT_FONT_FACE, 0, cueText.length(), SPAN_PRIORITY_LOW);
        attachColor(cueText, defaultColorRgba, DEFAULT_COLOR, 0, cueText.length(), SPAN_PRIORITY_LOW);
        attachFontFamily(cueText, defaultFontFamily, DEFAULT_FONT_FAMILY, 0, cueText.length(), SPAN_PRIORITY_LOW);
        float verticalPlacement = defaultVerticalPlacement;
        // Find and attach additional styles.
        while (parsableByteArray.bytesLeft() >= SIZE_ATOM_HEADER) {
            int position = parsableByteArray.getPosition();
            int atomSize = parsableByteArray.readInt();
            int atomType = parsableByteArray.readInt();
            if (atomType == TYPE_STYL) {
                replacedertTrue(parsableByteArray.bytesLeft() >= SIZE_SHORT);
                int styleRecordCount = parsableByteArray.readUnsignedShort();
                for (int i = 0; i < styleRecordCount; i++) {
                    applyStyleRecord(parsableByteArray, cueText);
                }
            } else if (atomType == TYPE_TBOX && customVerticalPlacement) {
                replacedertTrue(parsableByteArray.bytesLeft() >= SIZE_SHORT);
                int requestedVerticalPlacement = parsableByteArray.readUnsignedShort();
                verticalPlacement = (float) requestedVerticalPlacement / calculatedVideoTrackHeight;
                verticalPlacement = Util.constrainValue(verticalPlacement, 0.0f, 0.95f);
            }
            parsableByteArray.setPosition(position + atomSize);
        }
        return new Tx3gSubreplacedle(new Cue(cueText, null, verticalPlacement, Cue.LINE_TYPE_FRACTION, Cue.ANCHOR_TYPE_START, Cue.DIMEN_UNSET, Cue.TYPE_UNSET, Cue.DIMEN_UNSET));
    }

    private static String readSubreplacedleText(ParsableByteArray parsableByteArray) throws SubreplacedleDecoderException {
        replacedertTrue(parsableByteArray.bytesLeft() >= SIZE_SHORT);
        int textLength = parsableByteArray.readUnsignedShort();
        if (textLength == 0) {
            return "";
        }
        if (parsableByteArray.bytesLeft() >= SIZE_BOM_UTF16) {
            char firstChar = parsableByteArray.peekChar();
            if (firstChar == BOM_UTF16_BE || firstChar == BOM_UTF16_LE) {
                return parsableByteArray.readString(textLength, Charset.forName(C.UTF16_NAME));
            }
        }
        return parsableByteArray.readString(textLength, Charset.forName(C.UTF8_NAME));
    }

    private void applyStyleRecord(ParsableByteArray parsableByteArray, SpannableStringBuilder cueText) throws SubreplacedleDecoderException {
        replacedertTrue(parsableByteArray.bytesLeft() >= SIZE_STYLE_RECORD);
        int start = parsableByteArray.readUnsignedShort();
        int end = parsableByteArray.readUnsignedShort();
        // font identifier
        parsableByteArray.skipBytes(2);
        int fontFace = parsableByteArray.readUnsignedByte();
        // font size
        parsableByteArray.skipBytes(1);
        int colorRgba = parsableByteArray.readInt();
        attachFontFace(cueText, fontFace, defaultFontFace, start, end, SPAN_PRIORITY_HIGH);
        attachColor(cueText, colorRgba, defaultColorRgba, start, end, SPAN_PRIORITY_HIGH);
    }

    private static void attachFontFace(SpannableStringBuilder cueText, int fontFace, int defaultFontFace, int start, int end, int spanPriority) {
        if (fontFace != defaultFontFace) {
            final int flags = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | spanPriority;
            boolean isBold = (fontFace & FONT_FACE_BOLD) != 0;
            boolean isItalic = (fontFace & FONT_FACE_ITALIC) != 0;
            if (isBold) {
                if (isItalic) {
                    cueText.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), start, end, flags);
                } else {
                    cueText.setSpan(new StyleSpan(Typeface.BOLD), start, end, flags);
                }
            } else if (isItalic) {
                cueText.setSpan(new StyleSpan(Typeface.ITALIC), start, end, flags);
            }
            boolean isUnderlined = (fontFace & FONT_FACE_UNDERLINE) != 0;
            if (isUnderlined) {
                cueText.setSpan(new UnderlineSpan(), start, end, flags);
            }
            if (!isUnderlined && !isBold && !isItalic) {
                cueText.setSpan(new StyleSpan(Typeface.NORMAL), start, end, flags);
            }
        }
    }

    private static void attachColor(SpannableStringBuilder cueText, int colorRgba, int defaultColorRgba, int start, int end, int spanPriority) {
        if (colorRgba != defaultColorRgba) {
            int colorArgb = ((colorRgba & 0xFF) << 24) | (colorRgba >>> 8);
            cueText.setSpan(new ForegroundColorSpan(colorArgb), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | spanPriority);
        }
    }

    @SuppressWarnings("ReferenceEquality")
    private static void attachFontFamily(SpannableStringBuilder cueText, String fontFamily, String defaultFontFamily, int start, int end, int spanPriority) {
        if (fontFamily != defaultFontFamily) {
            cueText.setSpan(new TypefaceSpan(fontFamily), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | spanPriority);
        }
    }

    private static void replacedertTrue(boolean checkValue) throws SubreplacedleDecoderException {
        if (!checkValue) {
            throw new SubreplacedleDecoderException("Unexpected subreplacedle format.");
        }
    }
}

19 Source : MpegAudioReader.java
with GNU General Public License v2.0
from TelePlusDev

/**
 * Parses a continuous MPEG Audio byte stream and extracts individual frames.
 */
public final clreplaced MpegAudioReader implements ElementaryStreamReader {

    private static final int STATE_FINDING_HEADER = 0;

    private static final int STATE_READING_HEADER = 1;

    private static final int STATE_READING_FRAME = 2;

    private static final int HEADER_SIZE = 4;

    private final ParsableByteArray headerScratch;

    private final MpegAudioHeader header;

    private final String language;

    private String formatId;

    private TrackOutput output;

    private int state;

    private int frameBytesRead;

    private boolean hasOutputFormat;

    // Used when finding the frame header.
    private boolean lastByteWasFF;

    // Parsed from the frame header.
    private long frameDurationUs;

    private int frameSize;

    // The timestamp to attach to the next sample in the current packet.
    private long timeUs;

    public MpegAudioReader() {
        this(null);
    }

    public MpegAudioReader(String language) {
        state = STATE_FINDING_HEADER;
        // The first byte of an MPEG Audio frame header is always 0xFF.
        headerScratch = new ParsableByteArray(4);
        headerScratch.data[0] = (byte) 0xFF;
        header = new MpegAudioHeader();
        this.language = language;
    }

    @Override
    public void seek() {
        state = STATE_FINDING_HEADER;
        frameBytesRead = 0;
        lastByteWasFF = false;
    }

    @Override
    public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
        idGenerator.generateNewId();
        formatId = idGenerator.getFormatId();
        output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_AUDIO);
    }

    @Override
    public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) {
        timeUs = pesTimeUs;
    }

    @Override
    public void consume(ParsableByteArray data) {
        while (data.bytesLeft() > 0) {
            switch(state) {
                case STATE_FINDING_HEADER:
                    findHeader(data);
                    break;
                case STATE_READING_HEADER:
                    readHeaderRemainder(data);
                    break;
                case STATE_READING_FRAME:
                    readFrameRemainder(data);
                    break;
            }
        }
    }

    @Override
    public void packetFinished() {
    // Do nothing.
    }

    /**
     * Attempts to locate the start of the next frame header.
     * <p>
     * If a frame header is located then the state is changed to {@link #STATE_READING_HEADER}, the
     * first two bytes of the header are written into {@link #headerScratch}, and the position of the
     * source is advanced to the byte that immediately follows these two bytes.
     * <p>
     * If a frame header is not located then the position of the source is advanced to the limit, and
     * the method should be called again with the next source to continue the search.
     *
     * @param source The source from which to read.
     */
    private void findHeader(ParsableByteArray source) {
        byte[] data = source.data;
        int startOffset = source.getPosition();
        int endOffset = source.limit();
        for (int i = startOffset; i < endOffset; i++) {
            boolean byteIsFF = (data[i] & 0xFF) == 0xFF;
            boolean found = lastByteWasFF && (data[i] & 0xE0) == 0xE0;
            lastByteWasFF = byteIsFF;
            if (found) {
                source.setPosition(i + 1);
                // Reset lastByteWasFF for next time.
                lastByteWasFF = false;
                headerScratch.data[1] = data[i];
                frameBytesRead = 2;
                state = STATE_READING_HEADER;
                return;
            }
        }
        source.setPosition(endOffset);
    }

    /**
     * Attempts to read the remaining two bytes of the frame header.
     * <p>
     * If a frame header is read in full then the state is changed to {@link #STATE_READING_FRAME},
     * the media format is output if this has not previously occurred, the four header bytes are
     * output as sample data, and the position of the source is advanced to the byte that immediately
     * follows the header.
     * <p>
     * If a frame header is read in full but cannot be parsed then the state is changed to
     * {@link #STATE_READING_HEADER}.
     * <p>
     * If a frame header is not read in full then the position of the source is advanced to the limit,
     * and the method should be called again with the next source to continue the read.
     *
     * @param source The source from which to read.
     */
    private void readHeaderRemainder(ParsableByteArray source) {
        int bytesToRead = Math.min(source.bytesLeft(), HEADER_SIZE - frameBytesRead);
        source.readBytes(headerScratch.data, frameBytesRead, bytesToRead);
        frameBytesRead += bytesToRead;
        if (frameBytesRead < HEADER_SIZE) {
            // We haven't read the whole header yet.
            return;
        }
        headerScratch.setPosition(0);
        boolean parsedHeader = MpegAudioHeader.populateHeader(headerScratch.readInt(), header);
        if (!parsedHeader) {
            // We thought we'd located a frame header, but we hadn't.
            frameBytesRead = 0;
            state = STATE_READING_HEADER;
            return;
        }
        frameSize = header.frameSize;
        if (!hasOutputFormat) {
            frameDurationUs = (C.MICROS_PER_SECOND * header.samplesPerFrame) / header.sampleRate;
            Format format = Format.createAudioSampleFormat(formatId, header.mimeType, null, Format.NO_VALUE, MpegAudioHeader.MAX_FRAME_SIZE_BYTES, header.channels, header.sampleRate, null, null, 0, language);
            output.format(format);
            hasOutputFormat = true;
        }
        headerScratch.setPosition(0);
        output.sampleData(headerScratch, HEADER_SIZE);
        state = STATE_READING_FRAME;
    }

    /**
     * Attempts to read the remainder of the frame.
     * <p>
     * If a frame is read in full then true is returned. The frame will have been output, and the
     * position of the source will have been advanced to the byte that immediately follows the end of
     * the frame.
     * <p>
     * If a frame is not read in full then the position of the source will have been advanced to the
     * limit, and the method should be called again with the next source to continue the read.
     *
     * @param source The source from which to read.
     */
    private void readFrameRemainder(ParsableByteArray source) {
        int bytesToRead = Math.min(source.bytesLeft(), frameSize - frameBytesRead);
        output.sampleData(source, bytesToRead);
        frameBytesRead += bytesToRead;
        if (frameBytesRead < frameSize) {
            // We haven't read the whole of the frame yet.
            return;
        }
        output.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, frameSize, 0, null);
        timeUs += frameDurationUs;
        frameBytesRead = 0;
        state = STATE_FINDING_HEADER;
    }
}

19 Source : OggPageHeader.java
with GNU General Public License v2.0
from TelePlusDev

/**
 * Data object to store header information.
 */
/* package */
final clreplaced OggPageHeader {

    public static final int EMPTY_PAGE_HEADER_SIZE = 27;

    public static final int MAX_SEGMENT_COUNT = 255;

    public static final int MAX_PAGE_PAYLOAD = 255 * 255;

    public static final int MAX_PAGE_SIZE = EMPTY_PAGE_HEADER_SIZE + MAX_SEGMENT_COUNT + MAX_PAGE_PAYLOAD;

    private static final int TYPE_OGGS = Util.getIntegerCodeForString("OggS");

    public int revision;

    public int type;

    public long granulePosition;

    public long streamSerialNumber;

    public long pageSequenceNumber;

    public long pageChecksum;

    public int pageSegmentCount;

    public int headerSize;

    public int bodySize;

    /**
     * Be aware that {@code laces.length} is always {@link #MAX_SEGMENT_COUNT}. Instead use
     * {@link #pageSegmentCount} to iterate.
     */
    public final int[] laces = new int[MAX_SEGMENT_COUNT];

    private final ParsableByteArray scratch = new ParsableByteArray(MAX_SEGMENT_COUNT);

    /**
     * Resets all primitive member fields to zero.
     */
    public void reset() {
        revision = 0;
        type = 0;
        granulePosition = 0;
        streamSerialNumber = 0;
        pageSequenceNumber = 0;
        pageChecksum = 0;
        pageSegmentCount = 0;
        headerSize = 0;
        bodySize = 0;
    }

    /**
     * Peeks an Ogg page header and updates this {@link OggPageHeader}.
     *
     * @param input the {@link ExtractorInput} to read from.
     * @param quiet if {@code true} no Exceptions are thrown but {@code false} is return if something
     *    goes wrong.
     * @return {@code true} if the read was successful. {@code false} if the end of the input was
     *    encountered having read no data.
     * @throws IOException thrown if reading data fails or the stream is invalid.
     * @throws InterruptedException thrown if thread is interrupted when reading/peeking.
     */
    public boolean populate(ExtractorInput input, boolean quiet) throws IOException, InterruptedException {
        scratch.reset();
        reset();
        boolean hasEnoughBytes = input.getLength() == C.LENGTH_UNSET || input.getLength() - input.getPeekPosition() >= EMPTY_PAGE_HEADER_SIZE;
        if (!hasEnoughBytes || !input.peekFully(scratch.data, 0, EMPTY_PAGE_HEADER_SIZE, true)) {
            if (quiet) {
                return false;
            } else {
                throw new EOFException();
            }
        }
        if (scratch.readUnsignedInt() != TYPE_OGGS) {
            if (quiet) {
                return false;
            } else {
                throw new ParserException("expected OggS capture pattern at begin of page");
            }
        }
        revision = scratch.readUnsignedByte();
        if (revision != 0x00) {
            if (quiet) {
                return false;
            } else {
                throw new ParserException("unsupported bit stream revision");
            }
        }
        type = scratch.readUnsignedByte();
        granulePosition = scratch.readLittleEndianLong();
        streamSerialNumber = scratch.readLittleEndianUnsignedInt();
        pageSequenceNumber = scratch.readLittleEndianUnsignedInt();
        pageChecksum = scratch.readLittleEndianUnsignedInt();
        pageSegmentCount = scratch.readUnsignedByte();
        headerSize = EMPTY_PAGE_HEADER_SIZE + pageSegmentCount;
        // calculate total size of header including laces
        scratch.reset();
        input.peekFully(scratch.data, 0, pageSegmentCount);
        for (int i = 0; i < pageSegmentCount; i++) {
            laces[i] = scratch.readUnsignedByte();
            bodySize += laces[i];
        }
        return true;
    }
}

19 Source : OggPacket.java
with GNU General Public License v2.0
from TelePlusDev

/**
 * OGG packet clreplaced.
 */
/* package */
final clreplaced OggPacket {

    private final OggPageHeader pageHeader = new OggPageHeader();

    private final ParsableByteArray packetArray = new ParsableByteArray(new byte[OggPageHeader.MAX_PAGE_PAYLOAD], 0);

    private int currentSegmentIndex = C.INDEX_UNSET;

    private int segmentCount;

    private boolean populated;

    /**
     * Resets this reader.
     */
    public void reset() {
        pageHeader.reset();
        packetArray.reset();
        currentSegmentIndex = C.INDEX_UNSET;
        populated = false;
    }

    /**
     * Reads the next packet of the ogg stream. In case of an {@code IOException} the caller must make
     * sure to preplaced the same instance of {@code ParsableByteArray} to this method again so this reader
     * can resume properly from an error while reading a continued packet spanned across multiple
     * pages.
     *
     * @param input the {@link ExtractorInput} to read data from.
     * @return {@code true} if the read was successful. {@code false} if the end of the input was
     *    encountered having read no data.
     * @throws IOException thrown if reading from the input fails.
     * @throws InterruptedException thrown if interrupted while reading from input.
     */
    public boolean populate(ExtractorInput input) throws IOException, InterruptedException {
        replacedertions.checkState(input != null);
        if (populated) {
            populated = false;
            packetArray.reset();
        }
        while (!populated) {
            if (currentSegmentIndex < 0) {
                // We're at the start of a page.
                if (!pageHeader.populate(input, true)) {
                    return false;
                }
                int segmentIndex = 0;
                int bytesToSkip = pageHeader.headerSize;
                if ((pageHeader.type & 0x01) == 0x01 && packetArray.limit() == 0) {
                    // After seeking, the first packet may be the remainder
                    // part of a continued packet which has to be discarded.
                    bytesToSkip += calculatePacketSize(segmentIndex);
                    segmentIndex += segmentCount;
                }
                input.skipFully(bytesToSkip);
                currentSegmentIndex = segmentIndex;
            }
            int size = calculatePacketSize(currentSegmentIndex);
            int segmentIndex = currentSegmentIndex + segmentCount;
            if (size > 0) {
                if (packetArray.capacity() < packetArray.limit() + size) {
                    packetArray.data = Arrays.copyOf(packetArray.data, packetArray.limit() + size);
                }
                input.readFully(packetArray.data, packetArray.limit(), size);
                packetArray.setLimit(packetArray.limit() + size);
                populated = pageHeader.laces[segmentIndex - 1] != 255;
            }
            // Advance now since we are sure reading didn't throw an exception.
            currentSegmentIndex = segmentIndex == pageHeader.pageSegmentCount ? C.INDEX_UNSET : segmentIndex;
        }
        return true;
    }

    /**
     * An OGG Packet may span multiple pages. Returns the {@link OggPageHeader} of the last page read,
     * or an empty header if the packet has yet to be populated.
     * <p>
     * Note that the returned {@link OggPageHeader} is mutable and may be updated during subsequent
     * calls to {@link #populate(ExtractorInput)}.
     *
     * @return the {@code PageHeader} of the last page read or an empty header if the packet has yet
     *     to be populated.
     */
    // @VisibleForTesting
    public OggPageHeader getPageHeader() {
        return pageHeader;
    }

    /**
     * Returns a {@link ParsableByteArray} containing the packet's payload.
     */
    public ParsableByteArray getPayload() {
        return packetArray;
    }

    /**
     * Trims the packet data array.
     */
    public void trimPayload() {
        if (packetArray.data.length == OggPageHeader.MAX_PAGE_PAYLOAD) {
            return;
        }
        packetArray.data = Arrays.copyOf(packetArray.data, Math.max(OggPageHeader.MAX_PAGE_PAYLOAD, packetArray.limit()));
    }

    /**
     * Calculates the size of the packet starting from {@code startSegmentIndex}.
     *
     * @param startSegmentIndex the index of the first segment of the packet.
     * @return Size of the packet.
     */
    private int calculatePacketSize(int startSegmentIndex) {
        segmentCount = 0;
        int size = 0;
        while (startSegmentIndex + segmentCount < pageHeader.pageSegmentCount) {
            int segmentLength = pageHeader.laces[startSegmentIndex + segmentCount++];
            size += segmentLength;
            if (segmentLength != 255) {
                // packets end at first lace < 255
                break;
            }
        }
        return size;
    }
}

19 Source : TrackFragment.java
with GNU General Public License v2.0
from TelePlusDev

/**
 * A holder for information corresponding to a single fragment of an mp4 file.
 */
/* package */
final clreplaced TrackFragment {

    /**
     * The default values for samples from the track fragment header.
     */
    public DefaultSampleValues header;

    /**
     * The position (byte offset) of the start of fragment.
     */
    public long atomPosition;

    /**
     * The position (byte offset) of the start of data contained in the fragment.
     */
    public long dataPosition;

    /**
     * The position (byte offset) of the start of auxiliary data.
     */
    public long auxiliaryDataPosition;

    /**
     * The number of track runs of the fragment.
     */
    public int trunCount;

    /**
     * The total number of samples in the fragment.
     */
    public int sampleCount;

    /**
     * The position (byte offset) of the start of sample data of each track run in the fragment.
     */
    public long[] trunDataPosition;

    /**
     * The number of samples contained by each track run in the fragment.
     */
    public int[] trunLength;

    /**
     * The size of each sample in the fragment.
     */
    public int[] sampleSizeTable;

    /**
     * The composition time offset of each sample in the fragment.
     */
    public int[] sampleCompositionTimeOffsetTable;

    /**
     * The decoding time of each sample in the fragment.
     */
    public long[] sampleDecodingTimeTable;

    /**
     * Indicates which samples are sync frames.
     */
    public boolean[] sampleIsSyncFrameTable;

    /**
     * Whether the fragment defines encryption data.
     */
    public boolean definesEncryptionData;

    /**
     * If {@link #definesEncryptionData} is true, indicates which samples use sub-sample encryption.
     * Undefined otherwise.
     */
    public boolean[] sampleHreplacedubsampleEncryptionTable;

    /**
     * Fragment specific track encryption. May be null.
     */
    public TrackEncryptionBox trackEncryptionBox;

    /**
     * If {@link #definesEncryptionData} is true, indicates the length of the sample encryption data.
     * Undefined otherwise.
     */
    public int sampleEncryptionDataLength;

    /**
     * If {@link #definesEncryptionData} is true, contains binary sample encryption data. Undefined
     * otherwise.
     */
    public ParsableByteArray sampleEncryptionData;

    /**
     * Whether {@link #sampleEncryptionData} needs populating with the actual encryption data.
     */
    public boolean sampleEncryptionDataNeedsFill;

    /**
     * The absolute decode time of the start of the next fragment.
     */
    public long nextFragmentDecodeTime;

    /**
     * Resets the fragment.
     * <p>
     * {@link #sampleCount} and {@link #nextFragmentDecodeTime} are set to 0, and both
     * {@link #definesEncryptionData} and {@link #sampleEncryptionDataNeedsFill} is set to false,
     * and {@link #trackEncryptionBox} is set to null.
     */
    public void reset() {
        trunCount = 0;
        nextFragmentDecodeTime = 0;
        definesEncryptionData = false;
        sampleEncryptionDataNeedsFill = false;
        trackEncryptionBox = null;
    }

    /**
     * Configures the fragment for the specified number of samples.
     * <p>
     * The {@link #sampleCount} of the fragment is set to the specified sample count, and the
     * contained tables are resized if necessary such that they are at least this length.
     *
     * @param sampleCount The number of samples in the new run.
     */
    public void initTables(int trunCount, int sampleCount) {
        this.trunCount = trunCount;
        this.sampleCount = sampleCount;
        if (trunLength == null || trunLength.length < trunCount) {
            trunDataPosition = new long[trunCount];
            trunLength = new int[trunCount];
        }
        if (sampleSizeTable == null || sampleSizeTable.length < sampleCount) {
            // Size the tables 25% larger than needed, so as to make future resize operations less
            // likely. The choice of 25% is relatively arbitrary.
            int tableSize = (sampleCount * 125) / 100;
            sampleSizeTable = new int[tableSize];
            sampleCompositionTimeOffsetTable = new int[tableSize];
            sampleDecodingTimeTable = new long[tableSize];
            sampleIsSyncFrameTable = new boolean[tableSize];
            sampleHreplacedubsampleEncryptionTable = new boolean[tableSize];
        }
    }

    /**
     * Configures the fragment to be one that defines encryption data of the specified length.
     * <p>
     * {@link #definesEncryptionData} is set to true, {@link #sampleEncryptionDataLength} is set to
     * the specified length, and {@link #sampleEncryptionData} is resized if necessary such that it
     * is at least this length.
     *
     * @param length The length in bytes of the encryption data.
     */
    public void initEncryptionData(int length) {
        if (sampleEncryptionData == null || sampleEncryptionData.limit() < length) {
            sampleEncryptionData = new ParsableByteArray(length);
        }
        sampleEncryptionDataLength = length;
        definesEncryptionData = true;
        sampleEncryptionDataNeedsFill = true;
    }

    /**
     * Fills {@link #sampleEncryptionData} from the provided input.
     *
     * @param input An {@link ExtractorInput} from which to read the encryption data.
     */
    public void fillEncryptionData(ExtractorInput input) throws IOException, InterruptedException {
        input.readFully(sampleEncryptionData.data, 0, sampleEncryptionDataLength);
        sampleEncryptionData.setPosition(0);
        sampleEncryptionDataNeedsFill = false;
    }

    /**
     * Fills {@link #sampleEncryptionData} from the provided source.
     *
     * @param source A source from which to read the encryption data.
     */
    public void fillEncryptionData(ParsableByteArray source) {
        source.readBytes(sampleEncryptionData.data, 0, sampleEncryptionDataLength);
        sampleEncryptionData.setPosition(0);
        sampleEncryptionDataNeedsFill = false;
    }

    public long getSamplePresentationTime(int index) {
        return sampleDecodingTimeTable[index] + sampleCompositionTimeOffsetTable[index];
    }
}

19 Source : CssParser.java
with Apache License 2.0
from PaulWoitaschek

// Visible for testing.
@Nullable
static /* package */
String parseNextToken(ParsableByteArray input, StringBuilder stringBuilder) {
    skipWhitespaceAndComments(input);
    if (input.bytesLeft() == 0) {
        return null;
    }
    String identifier = parseIdentifier(input, stringBuilder);
    if (!"".equals(identifier)) {
        return identifier;
    }
    // We found a delimiter.
    return "" + (char) input.readUnsignedByte();
}

19 Source : Tx3gDecoder.java
with Apache License 2.0
from PaulWoitaschek

/**
 * A {@link SimpleSubreplacedleDecoder} for tx3g.
 * <p>
 * Currently supports parsing of a single text track with embedded styles.
 */
public final clreplaced Tx3gDecoder extends SimpleSubreplacedleDecoder {

    private static final char BOM_UTF16_BE = '\uFEFF';

    private static final char BOM_UTF16_LE = '\uFFFE';

    private static final int TYPE_STYL = 0x7374796c;

    private static final int TYPE_TBOX = 0x74626f78;

    private static final String TX3G_SERIF = "Serif";

    private static final int SIZE_ATOM_HEADER = 8;

    private static final int SIZE_SHORT = 2;

    private static final int SIZE_BOM_UTF16 = 2;

    private static final int SIZE_STYLE_RECORD = 12;

    private static final int FONT_FACE_BOLD = 0x0001;

    private static final int FONT_FACE_ITALIC = 0x0002;

    private static final int FONT_FACE_UNDERLINE = 0x0004;

    private static final int SPAN_PRIORITY_LOW = 0xFF << Spanned.SPAN_PRIORITY_SHIFT;

    private static final int SPAN_PRIORITY_HIGH = 0;

    private static final int DEFAULT_FONT_FACE = 0;

    private static final int DEFAULT_COLOR = Color.WHITE;

    private static final String DEFAULT_FONT_FAMILY = C.SANS_SERIF_NAME;

    private static final float DEFAULT_VERTICAL_PLACEMENT = 0.85f;

    private final ParsableByteArray parsableByteArray;

    private boolean customVerticalPlacement;

    private int defaultFontFace;

    private int defaultColorRgba;

    private String defaultFontFamily;

    private float defaultVerticalPlacement;

    private int calculatedVideoTrackHeight;

    /**
     * Sets up a new {@link Tx3gDecoder} with default values.
     *
     * @param initializationData Sample description atom ('stsd') data with default subreplacedle styles.
     */
    public Tx3gDecoder(List<byte[]> initializationData) {
        super("Tx3gDecoder");
        parsableByteArray = new ParsableByteArray();
        if (initializationData != null && initializationData.size() == 1 && (initializationData.get(0).length == 48 || initializationData.get(0).length == 53)) {
            byte[] initializationBytes = initializationData.get(0);
            defaultFontFace = initializationBytes[24];
            defaultColorRgba = ((initializationBytes[26] & 0xFF) << 24) | ((initializationBytes[27] & 0xFF) << 16) | ((initializationBytes[28] & 0xFF) << 8) | (initializationBytes[29] & 0xFF);
            String fontFamily = Util.fromUtf8Bytes(initializationBytes, 43, initializationBytes.length - 43);
            defaultFontFamily = TX3G_SERIF.equals(fontFamily) ? C.SERIF_NAME : C.SANS_SERIF_NAME;
            // font size (initializationBytes[25]) is 5% of video height
            calculatedVideoTrackHeight = 20 * initializationBytes[25];
            customVerticalPlacement = (initializationBytes[0] & 0x20) != 0;
            if (customVerticalPlacement) {
                int requestedVerticalPlacement = ((initializationBytes[10] & 0xFF) << 8) | (initializationBytes[11] & 0xFF);
                defaultVerticalPlacement = (float) requestedVerticalPlacement / calculatedVideoTrackHeight;
                defaultVerticalPlacement = Util.constrainValue(defaultVerticalPlacement, 0.0f, 0.95f);
            } else {
                defaultVerticalPlacement = DEFAULT_VERTICAL_PLACEMENT;
            }
        } else {
            defaultFontFace = DEFAULT_FONT_FACE;
            defaultColorRgba = DEFAULT_COLOR;
            defaultFontFamily = DEFAULT_FONT_FAMILY;
            customVerticalPlacement = false;
            defaultVerticalPlacement = DEFAULT_VERTICAL_PLACEMENT;
        }
    }

    @Override
    protected Subreplacedle decode(byte[] bytes, int length, boolean reset) throws SubreplacedleDecoderException {
        parsableByteArray.reset(bytes, length);
        String cueTextString = readSubreplacedleText(parsableByteArray);
        if (cueTextString.isEmpty()) {
            return Tx3gSubreplacedle.EMPTY;
        }
        // Attach default styles.
        SpannableStringBuilder cueText = new SpannableStringBuilder(cueTextString);
        attachFontFace(cueText, defaultFontFace, DEFAULT_FONT_FACE, 0, cueText.length(), SPAN_PRIORITY_LOW);
        attachColor(cueText, defaultColorRgba, DEFAULT_COLOR, 0, cueText.length(), SPAN_PRIORITY_LOW);
        attachFontFamily(cueText, defaultFontFamily, DEFAULT_FONT_FAMILY, 0, cueText.length(), SPAN_PRIORITY_LOW);
        float verticalPlacement = defaultVerticalPlacement;
        // Find and attach additional styles.
        while (parsableByteArray.bytesLeft() >= SIZE_ATOM_HEADER) {
            int position = parsableByteArray.getPosition();
            int atomSize = parsableByteArray.readInt();
            int atomType = parsableByteArray.readInt();
            if (atomType == TYPE_STYL) {
                replacedertTrue(parsableByteArray.bytesLeft() >= SIZE_SHORT);
                int styleRecordCount = parsableByteArray.readUnsignedShort();
                for (int i = 0; i < styleRecordCount; i++) {
                    applyStyleRecord(parsableByteArray, cueText);
                }
            } else if (atomType == TYPE_TBOX && customVerticalPlacement) {
                replacedertTrue(parsableByteArray.bytesLeft() >= SIZE_SHORT);
                int requestedVerticalPlacement = parsableByteArray.readUnsignedShort();
                verticalPlacement = (float) requestedVerticalPlacement / calculatedVideoTrackHeight;
                verticalPlacement = Util.constrainValue(verticalPlacement, 0.0f, 0.95f);
            }
            parsableByteArray.setPosition(position + atomSize);
        }
        return new Tx3gSubreplacedle(new Cue(cueText, /* textAlignment= */
        null, verticalPlacement, Cue.LINE_TYPE_FRACTION, Cue.ANCHOR_TYPE_START, Cue.DIMEN_UNSET, Cue.TYPE_UNSET, Cue.DIMEN_UNSET));
    }

    private static String readSubreplacedleText(ParsableByteArray parsableByteArray) throws SubreplacedleDecoderException {
        replacedertTrue(parsableByteArray.bytesLeft() >= SIZE_SHORT);
        int textLength = parsableByteArray.readUnsignedShort();
        if (textLength == 0) {
            return "";
        }
        if (parsableByteArray.bytesLeft() >= SIZE_BOM_UTF16) {
            char firstChar = parsableByteArray.peekChar();
            if (firstChar == BOM_UTF16_BE || firstChar == BOM_UTF16_LE) {
                return parsableByteArray.readString(textLength, Charset.forName(C.UTF16_NAME));
            }
        }
        return parsableByteArray.readString(textLength, Charset.forName(C.UTF8_NAME));
    }

    private void applyStyleRecord(ParsableByteArray parsableByteArray, SpannableStringBuilder cueText) throws SubreplacedleDecoderException {
        replacedertTrue(parsableByteArray.bytesLeft() >= SIZE_STYLE_RECORD);
        int start = parsableByteArray.readUnsignedShort();
        int end = parsableByteArray.readUnsignedShort();
        // font identifier
        parsableByteArray.skipBytes(2);
        int fontFace = parsableByteArray.readUnsignedByte();
        // font size
        parsableByteArray.skipBytes(1);
        int colorRgba = parsableByteArray.readInt();
        attachFontFace(cueText, fontFace, defaultFontFace, start, end, SPAN_PRIORITY_HIGH);
        attachColor(cueText, colorRgba, defaultColorRgba, start, end, SPAN_PRIORITY_HIGH);
    }

    private static void attachFontFace(SpannableStringBuilder cueText, int fontFace, int defaultFontFace, int start, int end, int spanPriority) {
        if (fontFace != defaultFontFace) {
            final int flags = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | spanPriority;
            boolean isBold = (fontFace & FONT_FACE_BOLD) != 0;
            boolean isItalic = (fontFace & FONT_FACE_ITALIC) != 0;
            if (isBold) {
                if (isItalic) {
                    cueText.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), start, end, flags);
                } else {
                    cueText.setSpan(new StyleSpan(Typeface.BOLD), start, end, flags);
                }
            } else if (isItalic) {
                cueText.setSpan(new StyleSpan(Typeface.ITALIC), start, end, flags);
            }
            boolean isUnderlined = (fontFace & FONT_FACE_UNDERLINE) != 0;
            if (isUnderlined) {
                cueText.setSpan(new UnderlineSpan(), start, end, flags);
            }
            if (!isUnderlined && !isBold && !isItalic) {
                cueText.setSpan(new StyleSpan(Typeface.NORMAL), start, end, flags);
            }
        }
    }

    private static void attachColor(SpannableStringBuilder cueText, int colorRgba, int defaultColorRgba, int start, int end, int spanPriority) {
        if (colorRgba != defaultColorRgba) {
            int colorArgb = ((colorRgba & 0xFF) << 24) | (colorRgba >>> 8);
            cueText.setSpan(new ForegroundColorSpan(colorArgb), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | spanPriority);
        }
    }

    @SuppressWarnings("ReferenceEquality")
    private static void attachFontFamily(SpannableStringBuilder cueText, String fontFamily, String defaultFontFamily, int start, int end, int spanPriority) {
        if (fontFamily != defaultFontFamily) {
            cueText.setSpan(new TypefaceSpan(fontFamily), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | spanPriority);
        }
    }

    private static void replacedertTrue(boolean checkValue) throws SubreplacedleDecoderException {
        if (!checkValue) {
            throw new SubreplacedleDecoderException("Unexpected subreplacedle format.");
        }
    }
}

19 Source : SampleDataQueue.java
with Apache License 2.0
from PaulWoitaschek

/**
 * A queue of media sample data.
 */
/* package */
clreplaced SampleDataQueue {

    private static final int INITIAL_SCRATCH_SIZE = 32;

    private final Allocator allocator;

    private final int allocationLength;

    private final ParsableByteArray scratch;

    // References into the linked list of allocations.
    private AllocationNode firstAllocationNode;

    private AllocationNode readAllocationNode;

    private AllocationNode writeAllocationNode;

    // Accessed only by the loading thread (or the consuming thread when there is no loading thread).
    private long totalBytesWritten;

    public SampleDataQueue(Allocator allocator) {
        this.allocator = allocator;
        allocationLength = allocator.getIndividualAllocationLength();
        scratch = new ParsableByteArray(INITIAL_SCRATCH_SIZE);
        firstAllocationNode = new AllocationNode(/* startPosition= */
        0, allocationLength);
        readAllocationNode = firstAllocationNode;
        writeAllocationNode = firstAllocationNode;
    }

    // Called by the consuming thread, but only when there is no loading thread.
    /**
     * Clears all sample data.
     */
    public void reset() {
        clearAllocationNodes(firstAllocationNode);
        firstAllocationNode = new AllocationNode(0, allocationLength);
        readAllocationNode = firstAllocationNode;
        writeAllocationNode = firstAllocationNode;
        totalBytesWritten = 0;
        allocator.trim();
    }

    /**
     * Discards sample data bytes from the write side of the queue.
     *
     * @param totalBytesWritten The reduced total number of bytes written after the samples have been
     *     discarded, or 0 if the queue is now empty.
     */
    public void discardUpstreamSampleBytes(long totalBytesWritten) {
        this.totalBytesWritten = totalBytesWritten;
        if (this.totalBytesWritten == 0 || this.totalBytesWritten == firstAllocationNode.startPosition) {
            clearAllocationNodes(firstAllocationNode);
            firstAllocationNode = new AllocationNode(this.totalBytesWritten, allocationLength);
            readAllocationNode = firstAllocationNode;
            writeAllocationNode = firstAllocationNode;
        } else {
            // Find the last node containing at least 1 byte of data that we need to keep.
            AllocationNode lastNodeToKeep = firstAllocationNode;
            while (this.totalBytesWritten > lastNodeToKeep.endPosition) {
                lastNodeToKeep = lastNodeToKeep.next;
            }
            // Discard all subsequent nodes.
            AllocationNode firstNodeToDiscard = lastNodeToKeep.next;
            clearAllocationNodes(firstNodeToDiscard);
            // Reset the successor of the last node to be an uninitialized node.
            lastNodeToKeep.next = new AllocationNode(lastNodeToKeep.endPosition, allocationLength);
            // Update writeAllocationNode and readAllocationNode as necessary.
            writeAllocationNode = this.totalBytesWritten == lastNodeToKeep.endPosition ? lastNodeToKeep.next : lastNodeToKeep;
            if (readAllocationNode == firstNodeToDiscard) {
                readAllocationNode = lastNodeToKeep.next;
            }
        }
    }

    // Called by the consuming thread.
    /**
     * Rewinds the read position to the first sample in the queue.
     */
    public void rewind() {
        readAllocationNode = firstAllocationNode;
    }

    /**
     * Reads data from the rolling buffer to populate a decoder input buffer.
     *
     * @param buffer The buffer to populate.
     * @param extrasHolder The extras holder whose offset should be read and subsequently adjusted.
     */
    public void readToBuffer(DecoderInputBuffer buffer, SampleExtrasHolder extrasHolder) {
        // Read encryption data if the sample is encrypted.
        if (buffer.isEncrypted()) {
            readEncryptionData(buffer, extrasHolder);
        }
        // Read sample data, extracting supplemental data into a separate buffer if needed.
        if (buffer.hreplacedupplementalData()) {
            // If there is supplemental data, the sample data is prefixed by its size.
            scratch.reset(4);
            readData(extrasHolder.offset, scratch.data, 4);
            int sampleSize = scratch.readUnsignedIntToInt();
            extrasHolder.offset += 4;
            extrasHolder.size -= 4;
            // Write the sample data.
            buffer.ensureSpaceForWrite(sampleSize);
            readData(extrasHolder.offset, buffer.data, sampleSize);
            extrasHolder.offset += sampleSize;
            extrasHolder.size -= sampleSize;
            // Write the remaining data as supplemental data.
            buffer.resetSupplementalData(extrasHolder.size);
            readData(extrasHolder.offset, buffer.supplementalData, extrasHolder.size);
        } else {
            // Write the sample data.
            buffer.ensureSpaceForWrite(extrasHolder.size);
            readData(extrasHolder.offset, buffer.data, extrasHolder.size);
        }
    }

    /**
     * Advances the read position to the specified absolute position.
     *
     * @param absolutePosition The new absolute read position. May be {@link C#POSITION_UNSET}, in
     *     which case calling this method is a no-op.
     */
    public void discardDownstreamTo(long absolutePosition) {
        if (absolutePosition == C.POSITION_UNSET) {
            return;
        }
        while (absolutePosition >= firstAllocationNode.endPosition) {
            // Advance firstAllocationNode to the specified absolute position. Also clear nodes that are
            // advanced past, and return their underlying allocations to the allocator.
            allocator.release(firstAllocationNode.allocation);
            firstAllocationNode = firstAllocationNode.clear();
        }
        if (readAllocationNode.startPosition < firstAllocationNode.startPosition) {
            // We discarded the node referenced by readAllocationNode. We need to advance it to the first
            // remaining node.
            readAllocationNode = firstAllocationNode;
        }
    }

    // Called by the loading thread.
    public long getTotalBytesWritten() {
        return totalBytesWritten;
    }

    public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput) throws IOException, InterruptedException {
        length = preAppend(length);
        int bytesAppended = input.read(writeAllocationNode.allocation.data, writeAllocationNode.translateOffset(totalBytesWritten), length);
        if (bytesAppended == C.RESULT_END_OF_INPUT) {
            if (allowEndOfInput) {
                return C.RESULT_END_OF_INPUT;
            }
            throw new EOFException();
        }
        postAppend(bytesAppended);
        return bytesAppended;
    }

    public void sampleData(ParsableByteArray buffer, int length) {
        while (length > 0) {
            int bytesAppended = preAppend(length);
            buffer.readBytes(writeAllocationNode.allocation.data, writeAllocationNode.translateOffset(totalBytesWritten), bytesAppended);
            length -= bytesAppended;
            postAppend(bytesAppended);
        }
    }

    // Private methods.
    /**
     * Reads encryption data for the current sample.
     *
     * <p>The encryption data is written into {@link DecoderInputBuffer#cryptoInfo}, and {@link
     * SampleExtrasHolder#size} is adjusted to subtract the number of bytes that were read. The same
     * value is added to {@link SampleExtrasHolder#offset}.
     *
     * @param buffer The buffer into which the encryption data should be written.
     * @param extrasHolder The extras holder whose offset should be read and subsequently adjusted.
     */
    private void readEncryptionData(DecoderInputBuffer buffer, SampleExtrasHolder extrasHolder) {
        long offset = extrasHolder.offset;
        // Read the signal byte.
        scratch.reset(1);
        readData(offset, scratch.data, 1);
        offset++;
        byte signalByte = scratch.data[0];
        boolean subsampleEncryption = (signalByte & 0x80) != 0;
        int ivSize = signalByte & 0x7F;
        // Read the initialization vector.
        CryptoInfo cryptoInfo = buffer.cryptoInfo;
        if (cryptoInfo.iv == null) {
            cryptoInfo.iv = new byte[16];
        } else {
            // Zero out cryptoInfo.iv so that if ivSize < 16, the remaining bytes are correctly set to 0.
            Arrays.fill(cryptoInfo.iv, (byte) 0);
        }
        readData(offset, cryptoInfo.iv, ivSize);
        offset += ivSize;
        // Read the subsample count, if present.
        int subsampleCount;
        if (subsampleEncryption) {
            scratch.reset(2);
            readData(offset, scratch.data, 2);
            offset += 2;
            subsampleCount = scratch.readUnsignedShort();
        } else {
            subsampleCount = 1;
        }
        // Write the clear and encrypted subsample sizes.
        @Nullable
        int[] clearDataSizes = cryptoInfo.numBytesOfClearData;
        if (clearDataSizes == null || clearDataSizes.length < subsampleCount) {
            clearDataSizes = new int[subsampleCount];
        }
        @Nullable
        int[] encryptedDataSizes = cryptoInfo.numBytesOfEncryptedData;
        if (encryptedDataSizes == null || encryptedDataSizes.length < subsampleCount) {
            encryptedDataSizes = new int[subsampleCount];
        }
        if (subsampleEncryption) {
            int subsampleDataLength = 6 * subsampleCount;
            scratch.reset(subsampleDataLength);
            readData(offset, scratch.data, subsampleDataLength);
            offset += subsampleDataLength;
            scratch.setPosition(0);
            for (int i = 0; i < subsampleCount; i++) {
                clearDataSizes[i] = scratch.readUnsignedShort();
                encryptedDataSizes[i] = scratch.readUnsignedIntToInt();
            }
        } else {
            clearDataSizes[0] = 0;
            encryptedDataSizes[0] = extrasHolder.size - (int) (offset - extrasHolder.offset);
        }
        // Populate the cryptoInfo.
        CryptoData cryptoData = extrasHolder.cryptoData;
        cryptoInfo.set(subsampleCount, clearDataSizes, encryptedDataSizes, cryptoData.encryptionKey, cryptoInfo.iv, cryptoData.cryptoMode, cryptoData.encryptedBlocks, cryptoData.clearBlocks);
        // Adjust the offset and size to take into account the bytes read.
        int bytesRead = (int) (offset - extrasHolder.offset);
        extrasHolder.offset += bytesRead;
        extrasHolder.size -= bytesRead;
    }

    /**
     * Reads data from the front of the rolling buffer.
     *
     * @param absolutePosition The absolute position from which data should be read.
     * @param target The buffer into which data should be written.
     * @param length The number of bytes to read.
     */
    private void readData(long absolutePosition, ByteBuffer target, int length) {
        advanceReadTo(absolutePosition);
        int remaining = length;
        while (remaining > 0) {
            int toCopy = Math.min(remaining, (int) (readAllocationNode.endPosition - absolutePosition));
            Allocation allocation = readAllocationNode.allocation;
            target.put(allocation.data, readAllocationNode.translateOffset(absolutePosition), toCopy);
            remaining -= toCopy;
            absolutePosition += toCopy;
            if (absolutePosition == readAllocationNode.endPosition) {
                readAllocationNode = readAllocationNode.next;
            }
        }
    }

    /**
     * Reads data from the front of the rolling buffer.
     *
     * @param absolutePosition The absolute position from which data should be read.
     * @param target The array into which data should be written.
     * @param length The number of bytes to read.
     */
    private void readData(long absolutePosition, byte[] target, int length) {
        advanceReadTo(absolutePosition);
        int remaining = length;
        while (remaining > 0) {
            int toCopy = Math.min(remaining, (int) (readAllocationNode.endPosition - absolutePosition));
            Allocation allocation = readAllocationNode.allocation;
            System.arraycopy(allocation.data, readAllocationNode.translateOffset(absolutePosition), target, length - remaining, toCopy);
            remaining -= toCopy;
            absolutePosition += toCopy;
            if (absolutePosition == readAllocationNode.endPosition) {
                readAllocationNode = readAllocationNode.next;
            }
        }
    }

    /**
     * Advances the read position to the specified absolute position.
     *
     * @param absolutePosition The position to which {@link #readAllocationNode} should be advanced.
     */
    private void advanceReadTo(long absolutePosition) {
        while (absolutePosition >= readAllocationNode.endPosition) {
            readAllocationNode = readAllocationNode.next;
        }
    }

    /**
     * Clears allocation nodes starting from {@code fromNode}.
     *
     * @param fromNode The node from which to clear.
     */
    private void clearAllocationNodes(AllocationNode fromNode) {
        if (!fromNode.wasInitialized) {
            return;
        }
        // Bulk release allocations for performance (it's significantly faster when using
        // DefaultAllocator because the allocator's lock only needs to be acquired and released once)
        // [Internal: See b/29542039].
        int allocationCount = (writeAllocationNode.wasInitialized ? 1 : 0) + ((int) (writeAllocationNode.startPosition - fromNode.startPosition) / allocationLength);
        Allocation[] allocationsToRelease = new Allocation[allocationCount];
        AllocationNode currentNode = fromNode;
        for (int i = 0; i < allocationsToRelease.length; i++) {
            allocationsToRelease[i] = currentNode.allocation;
            currentNode = currentNode.clear();
        }
        allocator.release(allocationsToRelease);
    }

    /**
     * Called before writing sample data to {@link #writeAllocationNode}. May cause {@link
     * #writeAllocationNode} to be initialized.
     *
     * @param length The number of bytes that the caller wishes to write.
     * @return The number of bytes that the caller is permitted to write, which may be less than
     *     {@code length}.
     */
    private int preAppend(int length) {
        if (!writeAllocationNode.wasInitialized) {
            writeAllocationNode.initialize(allocator.allocate(), new AllocationNode(writeAllocationNode.endPosition, allocationLength));
        }
        return Math.min(length, (int) (writeAllocationNode.endPosition - totalBytesWritten));
    }

    /**
     * Called after writing sample data. May cause {@link #writeAllocationNode} to be advanced.
     *
     * @param length The number of bytes that were written.
     */
    private void postAppend(int length) {
        totalBytesWritten += length;
        if (totalBytesWritten == writeAllocationNode.endPosition) {
            writeAllocationNode = writeAllocationNode.next;
        }
    }

    /**
     * A node in a linked list of {@link Allocation}s held by the output.
     */
    private static final clreplaced AllocationNode {

        /**
         * The absolute position of the start of the data (inclusive).
         */
        public final long startPosition;

        /**
         * The absolute position of the end of the data (exclusive).
         */
        public final long endPosition;

        /**
         * Whether the node has been initialized. Remains true after {@link #clear()}.
         */
        public boolean wasInitialized;

        /**
         * The {@link Allocation}, or {@code null} if the node is not initialized.
         */
        @Nullable
        public Allocation allocation;

        /**
         * The next {@link AllocationNode} in the list, or {@code null} if the node has not been
         * initialized. Remains set after {@link #clear()}.
         */
        @Nullable
        public AllocationNode next;

        /**
         * @param startPosition See {@link #startPosition}.
         * @param allocationLength The length of the {@link Allocation} with which this node will be
         *     initialized.
         */
        public AllocationNode(long startPosition, int allocationLength) {
            this.startPosition = startPosition;
            this.endPosition = startPosition + allocationLength;
        }

        /**
         * Initializes the node.
         *
         * @param allocation The node's {@link Allocation}.
         * @param next The next {@link AllocationNode}.
         */
        public void initialize(Allocation allocation, AllocationNode next) {
            this.allocation = allocation;
            this.next = next;
            wasInitialized = true;
        }

        /**
         * Gets the offset into the {@link #allocation}'s {@link Allocation#data} that corresponds to
         * the specified absolute position.
         *
         * @param absolutePosition The absolute position.
         * @return The corresponding offset into the allocation's data.
         */
        public int translateOffset(long absolutePosition) {
            return (int) (absolutePosition - startPosition) + allocation.offset;
        }

        /**
         * Clears {@link #allocation} and {@link #next}.
         *
         * @return The cleared next {@link AllocationNode}.
         */
        public AllocationNode clear() {
            allocation = null;
            AllocationNode temp = next;
            next = null;
            return temp;
        }
    }
}

19 Source : Id3Decoder.java
with Apache License 2.0
from PaulWoitaschek

/**
 * Performs in-place removal of unsynchronization for {@code length} bytes starting from
 * {@link ParsableByteArray#getPosition()}
 *
 * @param data Contains the data to be processed.
 * @param length The length of the data to be processed.
 * @return The length of the data after processing.
 */
private static int removeUnsynchronization(ParsableByteArray data, int length) {
    byte[] bytes = data.data;
    int startPosition = data.getPosition();
    for (int i = startPosition; i + 1 < startPosition + length; i++) {
        if ((bytes[i] & 0xFF) == 0xFF && bytes[i + 1] == 0x00) {
            int relativePosition = i - startPosition;
            System.arraycopy(bytes, i + 2, bytes, i + 1, length - relativePosition - 2);
            length--;
        }
    }
    return length;
}

19 Source : VorbisUtil.java
with Apache License 2.0
from PaulWoitaschek

/**
 * Reads a Vorbis identification header from {@code headerData}.
 *
 * @see <a href="https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-630004.2.2">Vorbis
 *     spec/Identification header</a>
 * @param headerData a {@link ParsableByteArray} wrapping the header data.
 * @return a {@link VorbisUtil.VorbisIdHeader} with meta data.
 * @throws ParserException thrown if invalid capture pattern is detected.
 */
public static VorbisIdHeader readVorbisIdentificationHeader(ParsableByteArray headerData) throws ParserException {
    verifyVorbisHeaderCapturePattern(0x01, headerData, false);
    long version = headerData.readLittleEndianUnsignedInt();
    int channels = headerData.readUnsignedByte();
    long sampleRate = headerData.readLittleEndianUnsignedInt();
    int bitrateMax = headerData.readLittleEndianInt();
    int bitrateNominal = headerData.readLittleEndianInt();
    int bitrateMin = headerData.readLittleEndianInt();
    int blockSize = headerData.readUnsignedByte();
    int blockSize0 = (int) Math.pow(2, blockSize & 0x0F);
    int blockSize1 = (int) Math.pow(2, (blockSize & 0xF0) >> 4);
    boolean framingFlag = (headerData.readUnsignedByte() & 0x01) > 0;
    // raw data of Vorbis setup header has to be preplaceded to decoder as CSD buffer #1
    byte[] data = Arrays.copyOf(headerData.data, headerData.limit());
    return new VorbisIdHeader(version, channels, sampleRate, bitrateMax, bitrateNominal, bitrateMin, blockSize0, blockSize1, framingFlag, data);
}

19 Source : VorbisUtil.java
with Apache License 2.0
from PaulWoitaschek

/**
 * Reads a Vorbis comment header.
 *
 * @see <a href="https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-640004.2.3">Vorbis
 *     spec/Comment header</a>
 * @param headerData A {@link ParsableByteArray} wrapping the header data.
 * @return A {@link VorbisUtil.CommentHeader} with all the comments.
 * @throws ParserException If an error occurs parsing the comment header.
 */
public static CommentHeader readVorbisCommentHeader(ParsableByteArray headerData) throws ParserException {
    return readVorbisCommentHeader(headerData, /* hasMetadataHeader= */
    true, /* hasFramingBit= */
    true);
}

19 Source : VorbisUtil.java
with Apache License 2.0
from PaulWoitaschek

/**
 * This method reads the modes which are located at the very end of the Vorbis setup header.
 * That's why we need to partially decode or at least read the entire setup header to know where
 * to start reading the modes.
 *
 * @see <a href="https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-650004.2.4">Vorbis
 *     spec/Setup header</a>
 * @param headerData a {@link ParsableByteArray} containing setup header data.
 * @param channels the number of channels.
 * @return an array of {@link Mode}s.
 * @throws ParserException thrown if bit stream is invalid.
 */
public static Mode[] readVorbisModes(ParsableByteArray headerData, int channels) throws ParserException {
    verifyVorbisHeaderCapturePattern(0x05, headerData, false);
    int numberOfBooks = headerData.readUnsignedByte() + 1;
    VorbisBitArray bitArray = new VorbisBitArray(headerData.data);
    bitArray.skipBits(headerData.getPosition() * 8);
    for (int i = 0; i < numberOfBooks; i++) {
        readBook(bitArray);
    }
    int timeCount = bitArray.readBits(6) + 1;
    for (int i = 0; i < timeCount; i++) {
        if (bitArray.readBits(16) != 0x00) {
            throw new ParserException("placeholder of time domain transforms not zeroed out");
        }
    }
    readFloors(bitArray);
    readResidues(bitArray);
    readMappings(channels, bitArray);
    Mode[] modes = readModes(bitArray);
    if (!bitArray.readBit()) {
        throw new ParserException("framing bit after modes not set as expected");
    }
    return modes;
}

19 Source : VorbisUtil.java
with Apache License 2.0
from PaulWoitaschek

/**
 * Verifies whether the next bytes in {@code header} are a Vorbis header of the given {@code
 * headerType}.
 *
 * @param headerType the type of the header expected.
 * @param header the alleged header bytes.
 * @param quiet if {@code true} no exceptions are thrown. Instead {@code false} is returned.
 * @return the number of bytes read.
 * @throws ParserException thrown if header type or capture pattern is not as expected.
 */
public static boolean verifyVorbisHeaderCapturePattern(int headerType, ParsableByteArray header, boolean quiet) throws ParserException {
    if (header.bytesLeft() < 7) {
        if (quiet) {
            return false;
        } else {
            throw new ParserException("too short header: " + header.bytesLeft());
        }
    }
    if (header.readUnsignedByte() != headerType) {
        if (quiet) {
            return false;
        } else {
            throw new ParserException("expected header type " + Integer.toHexString(headerType));
        }
    }
    if (!(header.readUnsignedByte() == 'v' && header.readUnsignedByte() == 'o' && header.readUnsignedByte() == 'r' && header.readUnsignedByte() == 'b' && header.readUnsignedByte() == 'i' && header.readUnsignedByte() == 's')) {
        if (quiet) {
            return false;
        } else {
            throw new ParserException("expected characters 'vorbis'");
        }
    }
    return true;
}

19 Source : SectionReader.java
with Apache License 2.0
from PaulWoitaschek

/**
 * Reads section data packets and feeds the whole sections to a given {@link SectionPayloadReader}.
 * Useful information on PSI sections can be found in ISO/IEC 13818-1, section 2.4.4.
 */
public final clreplaced SectionReader implements TsPayloadReader {

    private static final int SECTION_HEADER_LENGTH = 3;

    private static final int DEFAULT_SECTION_BUFFER_LENGTH = 32;

    private static final int MAX_SECTION_LENGTH = 4098;

    private final SectionPayloadReader reader;

    private final ParsableByteArray sectionData;

    private int totalSectionLength;

    private int bytesRead;

    private boolean sectionSyntaxIndicator;

    private boolean waitingForPayloadStart;

    public SectionReader(SectionPayloadReader reader) {
        this.reader = reader;
        sectionData = new ParsableByteArray(DEFAULT_SECTION_BUFFER_LENGTH);
    }

    @Override
    public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
        reader.init(timestampAdjuster, extractorOutput, idGenerator);
        waitingForPayloadStart = true;
    }

    @Override
    public void seek() {
        waitingForPayloadStart = true;
    }

    @Override
    public void consume(ParsableByteArray data, @Flags int flags) {
        boolean payloadUnitStartIndicator = (flags & FLAG_PAYLOAD_UNIT_START_INDICATOR) != 0;
        int payloadStartPosition = C.POSITION_UNSET;
        if (payloadUnitStartIndicator) {
            int payloadStartOffset = data.readUnsignedByte();
            payloadStartPosition = data.getPosition() + payloadStartOffset;
        }
        if (waitingForPayloadStart) {
            if (!payloadUnitStartIndicator) {
                return;
            }
            waitingForPayloadStart = false;
            data.setPosition(payloadStartPosition);
            bytesRead = 0;
        }
        while (data.bytesLeft() > 0) {
            if (bytesRead < SECTION_HEADER_LENGTH) {
                // Note: see ISO/IEC 13818-1, section 2.4.4.3 for detailed information on the format of
                // the header.
                if (bytesRead == 0) {
                    int tableId = data.readUnsignedByte();
                    data.setPosition(data.getPosition() - 1);
                    if (tableId == 0xFF) /* forbidden value */
                    {
                        // No more sections in this ts packet.
                        waitingForPayloadStart = true;
                        return;
                    }
                }
                int headerBytesToRead = Math.min(data.bytesLeft(), SECTION_HEADER_LENGTH - bytesRead);
                data.readBytes(sectionData.data, bytesRead, headerBytesToRead);
                bytesRead += headerBytesToRead;
                if (bytesRead == SECTION_HEADER_LENGTH) {
                    sectionData.reset(SECTION_HEADER_LENGTH);
                    // Skip table id (8).
                    sectionData.skipBytes(1);
                    int secondHeaderByte = sectionData.readUnsignedByte();
                    int thirdHeaderByte = sectionData.readUnsignedByte();
                    sectionSyntaxIndicator = (secondHeaderByte & 0x80) != 0;
                    totalSectionLength = (((secondHeaderByte & 0x0F) << 8) | thirdHeaderByte) + SECTION_HEADER_LENGTH;
                    if (sectionData.capacity() < totalSectionLength) {
                        // Ensure there is enough space to keep the whole section.
                        byte[] bytes = sectionData.data;
                        sectionData.reset(Math.min(MAX_SECTION_LENGTH, Math.max(totalSectionLength, bytes.length * 2)));
                        System.arraycopy(bytes, 0, sectionData.data, 0, SECTION_HEADER_LENGTH);
                    }
                }
            } else {
                // Reading the body.
                int bodyBytesToRead = Math.min(data.bytesLeft(), totalSectionLength - bytesRead);
                data.readBytes(sectionData.data, bytesRead, bodyBytesToRead);
                bytesRead += bodyBytesToRead;
                if (bytesRead == totalSectionLength) {
                    if (sectionSyntaxIndicator) {
                        // This section has common syntax as defined in ISO/IEC 13818-1, section 2.4.4.11.
                        if (Util.crc32(sectionData.data, 0, totalSectionLength, 0xFFFFFFFF) != 0) {
                            // The CRC is invalid so discard the section.
                            waitingForPayloadStart = true;
                            return;
                        }
                        // Exclude the CRC_32 field.
                        sectionData.reset(totalSectionLength - 4);
                    } else {
                        // This is a private section with private defined syntax.
                        sectionData.reset(totalSectionLength);
                    }
                    reader.consume(sectionData);
                    bytesRead = 0;
                }
            }
        }
    }
}

See More Examples