use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class PsExtractor method read.
@Override
public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException {
// First peek and check what type of start code is next.
if (!input.peekFully(psPacketBuffer.data, 0, 4, true)) {
return RESULT_END_OF_INPUT;
}
psPacketBuffer.setPosition(0);
int nextStartCode = psPacketBuffer.readInt();
if (nextStartCode == MPEG_PROGRAM_END_CODE) {
return RESULT_END_OF_INPUT;
} else if (nextStartCode == PACK_START_CODE) {
// Now peek the rest of the pack_header.
input.peekFully(psPacketBuffer.data, 0, 10);
// We only care about the pack_stuffing_length in here, skip the first 77 bits.
psPacketBuffer.setPosition(9);
// Last 3 bits is the length.
int packStuffingLength = psPacketBuffer.readUnsignedByte() & 0x07;
// Now skip the stuffing and the pack header.
input.skipFully(packStuffingLength + 14);
return RESULT_CONTINUE;
} else if (nextStartCode == SYSTEM_HEADER_START_CODE) {
// We just skip all this, but we need to get the length first.
input.peekFully(psPacketBuffer.data, 0, 2);
// Length is the next 2 bytes.
psPacketBuffer.setPosition(0);
int systemHeaderLength = psPacketBuffer.readUnsignedShort();
input.skipFully(systemHeaderLength + 6);
return RESULT_CONTINUE;
} else if (((nextStartCode & 0xFFFFFF00) >> 8) != PACKET_START_CODE_PREFIX) {
// Skip bytes until we see a valid start code again.
input.skipFully(1);
return RESULT_CONTINUE;
}
// We're at the start of a regular PES packet now.
// Get the stream ID off the last byte of the start code.
int streamId = nextStartCode & 0xFF;
// Check to see if we have this one in our map yet, and if not, then add it.
PesReader payloadReader = psPayloadReaders.get(streamId);
if (!foundAllTracks) {
if (payloadReader == null) {
ElementaryStreamReader elementaryStreamReader = null;
if (!foundAudioTrack && streamId == PRIVATE_STREAM_1) {
// Private stream, used for AC3 audio.
// NOTE: This may need further parsing to determine if its DTS, but that's likely only
// valid for DVDs.
elementaryStreamReader = new Ac3Reader();
foundAudioTrack = true;
} else if (!foundAudioTrack && (streamId & AUDIO_STREAM_MASK) == AUDIO_STREAM) {
elementaryStreamReader = new MpegAudioReader();
foundAudioTrack = true;
} else if (!foundVideoTrack && (streamId & VIDEO_STREAM_MASK) == VIDEO_STREAM) {
elementaryStreamReader = new H262Reader();
foundVideoTrack = true;
}
if (elementaryStreamReader != null) {
TrackIdGenerator idGenerator = new TrackIdGenerator(streamId, MAX_STREAM_ID_PLUS_ONE);
elementaryStreamReader.createTracks(output, idGenerator);
payloadReader = new PesReader(elementaryStreamReader, timestampAdjuster);
psPayloadReaders.put(streamId, payloadReader);
}
}
if ((foundAudioTrack && foundVideoTrack) || input.getPosition() > MAX_SEARCH_LENGTH) {
foundAllTracks = true;
output.endTracks();
}
}
// The next 2 bytes are the length. Once we have that we can consume the complete packet.
input.peekFully(psPacketBuffer.data, 0, 2);
psPacketBuffer.setPosition(0);
int payloadLength = psPacketBuffer.readUnsignedShort();
int pesLength = payloadLength + 6;
if (payloadReader == null) {
// Just skip this data.
input.skipFully(pesLength);
} else {
psPacketBuffer.reset(pesLength);
// Read the whole packet and the header for consumption.
input.readFully(psPacketBuffer.data, 0, pesLength);
psPacketBuffer.setPosition(6);
payloadReader.consume(psPacketBuffer);
psPacketBuffer.setLimit(psPacketBuffer.capacity());
}
return RESULT_CONTINUE;
}
use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class WavHeaderReader method peek.
/**
* Peeks and returns a {@code WavHeader}.
*
* @param input Input stream to peek the WAV header from.
* @throws ParserException If the input file is an incorrect RIFF WAV.
* @throws IOException If peeking from the input fails.
* @throws InterruptedException If interrupted while peeking from input.
* @return A new {@code WavHeader} peeked from {@code input}, or null if the input is not a
* supported WAV format.
*/
public static WavHeader peek(ExtractorInput input) throws IOException, InterruptedException {
Assertions.checkNotNull(input);
// Allocate a scratch buffer large enough to store the format chunk.
ParsableByteArray scratch = new ParsableByteArray(16);
// Attempt to read the RIFF chunk.
ChunkHeader chunkHeader = ChunkHeader.peek(input, scratch);
if (chunkHeader.id != Util.getIntegerCodeForString("RIFF")) {
return null;
}
input.peekFully(scratch.data, 0, 4);
scratch.setPosition(0);
int riffFormat = scratch.readInt();
if (riffFormat != Util.getIntegerCodeForString("WAVE")) {
Log.e(TAG, "Unsupported RIFF format: " + riffFormat);
return null;
}
// Skip chunks until we find the format chunk.
chunkHeader = ChunkHeader.peek(input, scratch);
while (chunkHeader.id != Util.getIntegerCodeForString("fmt ")) {
input.advancePeekPosition((int) chunkHeader.size);
chunkHeader = ChunkHeader.peek(input, scratch);
}
Assertions.checkState(chunkHeader.size >= 16);
input.peekFully(scratch.data, 0, 16);
scratch.setPosition(0);
int type = scratch.readLittleEndianUnsignedShort();
int numChannels = scratch.readLittleEndianUnsignedShort();
int sampleRateHz = scratch.readLittleEndianUnsignedIntToInt();
int averageBytesPerSecond = scratch.readLittleEndianUnsignedIntToInt();
int blockAlignment = scratch.readLittleEndianUnsignedShort();
int bitsPerSample = scratch.readLittleEndianUnsignedShort();
int expectedBlockAlignment = numChannels * bitsPerSample / 8;
if (blockAlignment != expectedBlockAlignment) {
throw new ParserException("Expected block alignment: " + expectedBlockAlignment + "; got: " + blockAlignment);
}
@C.PcmEncoding int encoding = Util.getPcmEncoding(bitsPerSample);
if (encoding == C.ENCODING_INVALID) {
Log.e(TAG, "Unsupported WAV bit depth: " + bitsPerSample);
return null;
}
if (type != TYPE_PCM && type != TYPE_WAVE_FORMAT_EXTENSIBLE) {
Log.e(TAG, "Unsupported WAV format type: " + type);
return null;
}
// If present, skip extensionSize, validBitsPerSample, channelMask, subFormatGuid, ...
input.advancePeekPosition((int) chunkHeader.size - 16);
return new WavHeader(numChannels, sampleRateHz, averageBytesPerSecond, blockAlignment, bitsPerSample, encoding);
}
use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class Mp4Extractor method readAtomHeader.
private boolean readAtomHeader(ExtractorInput input) throws IOException, InterruptedException {
if (atomHeaderBytesRead == 0) {
// Read the standard length atom header.
if (!input.readFully(atomHeader.data, 0, Atom.HEADER_SIZE, true)) {
return false;
}
atomHeaderBytesRead = Atom.HEADER_SIZE;
atomHeader.setPosition(0);
atomSize = atomHeader.readUnsignedInt();
atomType = atomHeader.readInt();
}
if (atomSize == Atom.LONG_SIZE_PREFIX) {
// Read the extended atom size.
int headerBytesRemaining = Atom.LONG_HEADER_SIZE - Atom.HEADER_SIZE;
input.readFully(atomHeader.data, Atom.HEADER_SIZE, headerBytesRemaining);
atomHeaderBytesRead += headerBytesRemaining;
atomSize = atomHeader.readUnsignedLongToLong();
}
if (shouldParseContainerAtom(atomType)) {
long endPosition = input.getPosition() + atomSize - atomHeaderBytesRead;
containerAtoms.add(new ContainerAtom(atomType, endPosition));
if (atomSize == atomHeaderBytesRead) {
processAtomEnded(endPosition);
} else {
// Start reading the first child atom.
enterReadingAtomHeaderState();
}
} else if (shouldParseLeafAtom(atomType)) {
// We don't support parsing of leaf atoms that define extended atom sizes, or that have
// lengths greater than Integer.MAX_VALUE.
Assertions.checkState(atomHeaderBytesRead == Atom.HEADER_SIZE);
Assertions.checkState(atomSize <= Integer.MAX_VALUE);
atomData = new ParsableByteArray((int) atomSize);
System.arraycopy(atomHeader.data, 0, atomData.data, 0, Atom.HEADER_SIZE);
parserState = STATE_READING_ATOM_PAYLOAD;
} else {
atomData = null;
parserState = STATE_READING_ATOM_PAYLOAD;
}
return true;
}
use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class Sniffer method sniffInternal.
private static boolean sniffInternal(ExtractorInput input, boolean fragmented) throws IOException, InterruptedException {
long inputLength = input.getLength();
int bytesToSearch = (int) (inputLength == C.LENGTH_UNSET || inputLength > SEARCH_LENGTH ? SEARCH_LENGTH : inputLength);
ParsableByteArray buffer = new ParsableByteArray(64);
int bytesSearched = 0;
boolean foundGoodFileType = false;
boolean isFragmented = false;
while (bytesSearched < bytesToSearch) {
// Read an atom header.
int headerSize = Atom.HEADER_SIZE;
buffer.reset(headerSize);
input.peekFully(buffer.data, 0, headerSize);
long atomSize = buffer.readUnsignedInt();
int atomType = buffer.readInt();
if (atomSize == Atom.LONG_SIZE_PREFIX) {
headerSize = Atom.LONG_HEADER_SIZE;
input.peekFully(buffer.data, Atom.HEADER_SIZE, Atom.LONG_HEADER_SIZE - Atom.HEADER_SIZE);
buffer.setLimit(Atom.LONG_HEADER_SIZE);
atomSize = buffer.readUnsignedLongToLong();
}
if (atomSize < headerSize) {
// The file is invalid because the atom size is too small for its header.
return false;
}
bytesSearched += headerSize;
if (atomType == Atom.TYPE_moov) {
// Check for an mvex atom inside the moov atom to identify whether the file is fragmented.
continue;
}
if (atomType == Atom.TYPE_moof || atomType == Atom.TYPE_mvex) {
// The movie is fragmented. Stop searching as we must have read any ftyp atom already.
isFragmented = true;
break;
}
if (bytesSearched + atomSize - headerSize >= bytesToSearch) {
// Stop searching as peeking this atom would exceed the search limit.
break;
}
int atomDataSize = (int) (atomSize - headerSize);
bytesSearched += atomDataSize;
if (atomType == Atom.TYPE_ftyp) {
// Parse the atom and check the file type/brand is compatible with the extractors.
if (atomDataSize < 8) {
return false;
}
buffer.reset(atomDataSize);
input.peekFully(buffer.data, 0, atomDataSize);
int brandsCount = atomDataSize / 4;
for (int i = 0; i < brandsCount; i++) {
if (i == 1) {
// This index refers to the minorVersion, not a brand, so skip it.
buffer.skipBytes(4);
} else if (isCompatibleBrand(buffer.readInt())) {
foundGoodFileType = true;
break;
}
}
if (!foundGoodFileType) {
// The types were not compatible and there is only one ftyp atom, so reject the file.
return false;
}
} else if (atomDataSize != 0) {
// Skip the atom.
input.advancePeekPosition(atomDataSize);
}
}
return foundGoodFileType && fragmented == isFragmented;
}
use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class StreamReader method readPayload.
private int readPayload(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException {
long position = oggSeeker.read(input);
if (position >= 0) {
seekPosition.position = position;
return Extractor.RESULT_SEEK;
} else if (position < -1) {
onSeekEnd(-(position + 2));
}
if (!seekMapSet) {
SeekMap seekMap = oggSeeker.createSeekMap();
extractorOutput.seekMap(seekMap);
seekMapSet = true;
}
if (lengthOfReadPacket > 0 || oggPacket.populate(input)) {
lengthOfReadPacket = 0;
ParsableByteArray payload = oggPacket.getPayload();
long granulesInPacket = preparePayload(payload);
if (granulesInPacket >= 0 && currentGranule + granulesInPacket >= targetGranule) {
// calculate time and send payload data to codec
long timeUs = convertGranuleToTime(currentGranule);
trackOutput.sampleData(payload, payload.limit());
trackOutput.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, payload.limit(), 0, null);
targetGranule = -1;
}
currentGranule += granulesInPacket;
} else {
state = STATE_END_OF_INPUT;
return Extractor.RESULT_END_OF_INPUT;
}
return Extractor.RESULT_CONTINUE;
}
Aggregations