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
19
Source : RtpH264PayloadReader.java
with GNU General Public License v2.0
from warren-bank
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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