use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class Mp3Extractor method setupSeeker.
/**
* Returns a {@link Seeker} to seek using metadata read from {@code input}, which should provide
* data from the start of the first frame in the stream. On returning, the input's position will
* be set to the start of the first frame of audio.
*
* @param input The {@link ExtractorInput} from which to read.
* @throws IOException Thrown if there was an error reading from the stream. Not expected if the
* next two frames were already peeked during synchronization.
* @throws InterruptedException Thrown if reading from the stream was interrupted. Not expected if
* the next two frames were already peeked during synchronization.
* @return a {@link Seeker}.
*/
private Seeker setupSeeker(ExtractorInput input) throws IOException, InterruptedException {
// Read the first frame which may contain a Xing or VBRI header with seeking metadata.
ParsableByteArray frame = new ParsableByteArray(synchronizedHeader.frameSize);
input.peekFully(frame.data, 0, synchronizedHeader.frameSize);
long position = input.getPosition();
long length = input.getLength();
int headerData = 0;
Seeker seeker = null;
// Check if there is a Xing header.
int xingBase = (synchronizedHeader.version & 1) != 0 ? // MPEG 1
(synchronizedHeader.channels != 1 ? 36 : 21) : // MPEG 2 or 2.5
(synchronizedHeader.channels != 1 ? 21 : 13);
if (frame.limit() >= xingBase + 4) {
frame.setPosition(xingBase);
headerData = frame.readInt();
}
if (headerData == XING_HEADER || headerData == INFO_HEADER) {
seeker = XingSeeker.create(synchronizedHeader, frame, position, length);
if (seeker != null && !gaplessInfoHolder.hasGaplessInfo()) {
// If there is a Xing header, read gapless playback metadata at a fixed offset.
input.resetPeekPosition();
input.advancePeekPosition(xingBase + 141);
input.peekFully(scratch.data, 0, 3);
scratch.setPosition(0);
gaplessInfoHolder.setFromXingHeaderValue(scratch.readUnsignedInt24());
}
input.skipFully(synchronizedHeader.frameSize);
} else if (frame.limit() >= 40) {
// Check if there is a VBRI header.
// MPEG audio header (4 bytes) + 32 bytes.
frame.setPosition(36);
headerData = frame.readInt();
if (headerData == VBRI_HEADER) {
seeker = VbriSeeker.create(synchronizedHeader, frame, position, length);
input.skipFully(synchronizedHeader.frameSize);
}
}
if (seeker == null || (!seeker.isSeekable() && (flags & FLAG_ENABLE_CONSTANT_BITRATE_SEEKING) != 0)) {
// Repopulate the synchronized header in case we had to skip an invalid seeking header, which
// would give an invalid CBR bitrate.
input.resetPeekPosition();
input.peekFully(scratch.data, 0, 4);
scratch.setPosition(0);
MpegAudioHeader.populateHeader(scratch.readInt(), synchronizedHeader);
seeker = new ConstantBitrateSeeker(input.getPosition(), synchronizedHeader.bitrate, length);
}
return seeker;
}
use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class Mp4Extractor method readSample.
/**
* Attempts to extract the next sample in the current mdat atom for the specified track.
* <p>
* Returns {@link #RESULT_SEEK} if the source should be reloaded from the position in
* {@code positionHolder}.
* <p>
* Returns {@link #RESULT_END_OF_INPUT} if no samples are left. Otherwise, returns
* {@link #RESULT_CONTINUE}.
*
* @param input The {@link ExtractorInput} from which to read data.
* @param positionHolder If {@link #RESULT_SEEK} is returned, this holder is updated to hold the
* position of the required data.
* @return One of the {@code RESULT_*} flags in {@link Extractor}.
* @throws IOException If an error occurs reading from the input.
* @throws InterruptedException If the thread is interrupted.
*/
private int readSample(ExtractorInput input, PositionHolder positionHolder) throws IOException, InterruptedException {
int trackIndex = getTrackIndexOfEarliestCurrentSample();
if (trackIndex == C.INDEX_UNSET) {
return RESULT_END_OF_INPUT;
}
Mp4Track track = tracks[trackIndex];
TrackOutput trackOutput = track.trackOutput;
int sampleIndex = track.sampleIndex;
long position = track.sampleTable.offsets[sampleIndex];
int sampleSize = track.sampleTable.sizes[sampleIndex];
if (track.track.sampleTransformation == Track.TRANSFORMATION_CEA608_CDAT) {
// The sample information is contained in a cdat atom. The header must be discarded for
// committing.
position += Atom.HEADER_SIZE;
sampleSize -= Atom.HEADER_SIZE;
}
long skipAmount = position - input.getPosition() + sampleBytesWritten;
if (skipAmount < 0 || skipAmount >= RELOAD_MINIMUM_SEEK_DISTANCE) {
positionHolder.position = position;
return RESULT_SEEK;
}
input.skipFully((int) skipAmount);
if (track.track.nalUnitLengthFieldLength != 0) {
// 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 nalUnitLengthFieldLength = track.track.nalUnitLengthFieldLength;
int nalUnitLengthFieldLengthDiff = 4 - track.track.nalUnitLengthFieldLength;
// start codes as we encounter them.
while (sampleBytesWritten < sampleSize) {
if (sampleCurrentNalBytesRemaining == 0) {
// Read the NAL length so that we know where we find the next one.
input.readFully(nalLength.data, nalUnitLengthFieldLengthDiff, nalUnitLengthFieldLength);
nalLength.setPosition(0);
sampleCurrentNalBytesRemaining = nalLength.readUnsignedIntToInt();
// Write a start code for the current NAL unit.
nalStartCode.setPosition(0);
trackOutput.sampleData(nalStartCode, 4);
sampleBytesWritten += 4;
sampleSize += nalUnitLengthFieldLengthDiff;
} else {
// Write the payload of the NAL unit.
int writtenBytes = trackOutput.sampleData(input, sampleCurrentNalBytesRemaining, false);
sampleBytesWritten += writtenBytes;
sampleCurrentNalBytesRemaining -= writtenBytes;
}
}
} else {
while (sampleBytesWritten < sampleSize) {
int writtenBytes = trackOutput.sampleData(input, sampleSize - sampleBytesWritten, false);
sampleBytesWritten += writtenBytes;
sampleCurrentNalBytesRemaining -= writtenBytes;
}
}
trackOutput.sampleMetadata(track.sampleTable.timestampsUs[sampleIndex], track.sampleTable.flags[sampleIndex], sampleSize, 0, null);
track.sampleIndex++;
sampleBytesWritten = 0;
sampleCurrentNalBytesRemaining = 0;
return RESULT_CONTINUE;
}
use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class OggExtractor method sniff.
@Override
public boolean sniff(ExtractorInput input) throws IOException, InterruptedException {
try {
OggPageHeader header = new OggPageHeader();
if (!header.populate(input, true) || (header.type & 0x02) != 0x02) {
return false;
}
int length = Math.min(header.bodySize, MAX_VERIFICATION_BYTES);
ParsableByteArray scratch = new ParsableByteArray(length);
input.peekFully(scratch.data, 0, length);
if (FlacReader.verifyBitstreamType(resetPosition(scratch))) {
streamReader = new FlacReader();
} else if (VorbisReader.verifyBitstreamType(resetPosition(scratch))) {
streamReader = new VorbisReader();
} else if (OpusReader.verifyBitstreamType(resetPosition(scratch))) {
streamReader = new OpusReader();
} else {
return false;
}
return true;
} catch (ParserException e) {
return false;
}
}
use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class FlacExtractor method read.
@Override
public int read(final ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException {
decoderJni.setData(input);
if (!metadataParsed) {
final FlacStreamInfo streamInfo;
try {
streamInfo = decoderJni.decodeMetadata();
if (streamInfo == null) {
throw new IOException("Metadata decoding failed");
}
} catch (IOException e) {
decoderJni.reset(0);
input.setRetryPosition(0, e);
// never executes
throw e;
}
metadataParsed = true;
extractorOutput.seekMap(new SeekMap() {
final boolean isSeekable = decoderJni.getSeekPosition(0) != -1;
final long durationUs = streamInfo.durationUs();
@Override
public boolean isSeekable() {
return isSeekable;
}
@Override
public long getPosition(long timeUs) {
return isSeekable ? decoderJni.getSeekPosition(timeUs) : 0;
}
@Override
public long getDurationUs() {
return durationUs;
}
});
Format mediaFormat = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_RAW, null, streamInfo.bitRate(), Format.NO_VALUE, streamInfo.channels, streamInfo.sampleRate, C.ENCODING_PCM_16BIT, null, null, 0, null);
trackOutput.format(mediaFormat);
outputBuffer = new ParsableByteArray(streamInfo.maxDecodedFrameSize());
outputByteBuffer = ByteBuffer.wrap(outputBuffer.data);
}
outputBuffer.reset();
long lastDecodePosition = decoderJni.getDecodePosition();
int size;
try {
size = decoderJni.decodeSample(outputByteBuffer);
} catch (IOException e) {
if (lastDecodePosition >= 0) {
decoderJni.reset(lastDecodePosition);
input.setRetryPosition(lastDecodePosition, e);
}
throw e;
}
if (size <= 0) {
return RESULT_END_OF_INPUT;
}
trackOutput.sampleData(outputBuffer, size);
trackOutput.sampleMetadata(decoderJni.getLastSampleTimestamp(), C.BUFFER_FLAG_KEY_FRAME, size, 0, null);
return decoderJni.isEndOfData() ? RESULT_END_OF_INPUT : RESULT_CONTINUE;
}
use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class OggPacketTest method assertReadPacket.
private void assertReadPacket(FakeExtractorInput extractorInput, byte[] expected) throws IOException, InterruptedException {
assertTrue(readPacket(extractorInput));
ParsableByteArray payload = oggPacket.getPayload();
MoreAsserts.assertEquals(expected, Arrays.copyOf(payload.data, payload.limit()));
}
Aggregations