use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class FlacFrameReaderTest method checkFrameHeaderFromPeek_validData_doesNotUpdatePositions.
@Test
public void checkFrameHeaderFromPeek_validData_doesNotUpdatePositions() throws Exception {
String file = "media/flac/bear_one_metadata_block.flac";
FlacStreamMetadataHolder streamMetadataHolder = new FlacStreamMetadataHolder(/* flacStreamMetadata= */
null);
ExtractorInput input = buildExtractorInputReadingFromFirstFrame(file, streamMetadataHolder);
int frameStartMarker = FlacMetadataReader.getFrameStartMarker(input);
long peekPosition = input.getPosition();
// Set read position to 0.
input = buildExtractorInput(file);
input.advancePeekPosition((int) peekPosition);
FlacFrameReader.checkFrameHeaderFromPeek(input, streamMetadataHolder.flacStreamMetadata, frameStartMarker, new SampleNumberHolder());
assertThat(input.getPosition()).isEqualTo(0);
assertThat(input.getPeekPosition()).isEqualTo(peekPosition);
}
use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class HlsMediaChunk method peekId3PrivTimestamp.
/**
* Peek the presentation timestamp of the first sample in the chunk from an ID3 PRIV as defined in
* the HLS spec, version 20, Section 3.4. Returns {@link C#TIME_UNSET} if the frame is not found.
* This method only modifies the peek position.
*
* @param input The {@link ExtractorInput} to obtain the PRIV frame from.
* @return The parsed, adjusted timestamp in microseconds
* @throws IOException If an error occurred peeking from the input.
*/
private long peekId3PrivTimestamp(ExtractorInput input) throws IOException {
input.resetPeekPosition();
try {
scratchId3Data.reset(Id3Decoder.ID3_HEADER_LENGTH);
input.peekFully(scratchId3Data.getData(), 0, Id3Decoder.ID3_HEADER_LENGTH);
} catch (EOFException e) {
// The input isn't long enough for there to be any ID3 data.
return C.TIME_UNSET;
}
int id = scratchId3Data.readUnsignedInt24();
if (id != Id3Decoder.ID3_TAG) {
return C.TIME_UNSET;
}
// version(2), flags(1).
scratchId3Data.skipBytes(3);
int id3Size = scratchId3Data.readSynchSafeInt();
int requiredCapacity = id3Size + Id3Decoder.ID3_HEADER_LENGTH;
if (requiredCapacity > scratchId3Data.capacity()) {
byte[] data = scratchId3Data.getData();
scratchId3Data.reset(requiredCapacity);
System.arraycopy(data, 0, scratchId3Data.getData(), 0, Id3Decoder.ID3_HEADER_LENGTH);
}
input.peekFully(scratchId3Data.getData(), Id3Decoder.ID3_HEADER_LENGTH, id3Size);
Metadata metadata = id3Decoder.decode(scratchId3Data.getData(), id3Size);
if (metadata == null) {
return C.TIME_UNSET;
}
int metadataLength = metadata.length();
for (int i = 0; i < metadataLength; i++) {
Metadata.Entry frame = metadata.get(i);
if (frame instanceof PrivFrame) {
PrivFrame privFrame = (PrivFrame) frame;
if (PRIV_TIMESTAMP_FRAME_OWNER.equals(privFrame.owner)) {
System.arraycopy(privFrame.privateData, 0, scratchId3Data.getData(), 0, 8);
scratchId3Data.setPosition(0);
scratchId3Data.setLimit(8);
// streaming provider forgot. See: https://github.com/google/ExoPlayer/pull/3495.
return scratchId3Data.readLong() & 0x1FFFFFFFFL;
}
}
}
return C.TIME_UNSET;
}
use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class TestUtil method seekToTimeUs.
/**
* Seeks to the given seek time of the stream from the given input, and keeps reading from the
* input until we can extract at least one sample following the seek position, or until
* end-of-input is reached.
*
* @param extractor The {@link Extractor} to extract from input.
* @param seekMap The {@link SeekMap} of the stream from the given input.
* @param seekTimeUs The seek time, in micro-seconds.
* @param trackOutput The {@link FakeTrackOutput} to store the extracted samples.
* @param dataSource The {@link DataSource} that will be used to read from the input.
* @param uri The Uri of the input.
* @return The index of the first extracted sample written to the given {@code trackOutput} after
* the seek is completed, or {@link C#INDEX_UNSET} if the seek is completed without any
* extracted sample.
*/
public static int seekToTimeUs(Extractor extractor, SeekMap seekMap, long seekTimeUs, DataSource dataSource, FakeTrackOutput trackOutput, Uri uri) throws IOException {
int numSampleBeforeSeek = trackOutput.getSampleCount();
SeekMap.SeekPoints seekPoints = seekMap.getSeekPoints(seekTimeUs);
long initialSeekLoadPosition = seekPoints.first.position;
extractor.seek(initialSeekLoadPosition, seekTimeUs);
PositionHolder positionHolder = new PositionHolder();
positionHolder.position = C.POSITION_UNSET;
ExtractorInput extractorInput = TestUtil.getExtractorInputFromPosition(dataSource, initialSeekLoadPosition, uri);
int extractorReadResult = Extractor.RESULT_CONTINUE;
while (true) {
try {
// Keep reading until we can read at least one sample after seek
while (extractorReadResult == Extractor.RESULT_CONTINUE && trackOutput.getSampleCount() == numSampleBeforeSeek) {
extractorReadResult = extractor.read(extractorInput, positionHolder);
}
} finally {
DataSourceUtil.closeQuietly(dataSource);
}
if (extractorReadResult == Extractor.RESULT_SEEK) {
extractorInput = TestUtil.getExtractorInputFromPosition(dataSource, positionHolder.position, uri);
extractorReadResult = Extractor.RESULT_CONTINUE;
} else if (extractorReadResult == Extractor.RESULT_END_OF_INPUT && trackOutput.getSampleCount() == numSampleBeforeSeek) {
return C.INDEX_UNSET;
} else if (trackOutput.getSampleCount() > numSampleBeforeSeek) {
// First index after seek = num sample before seek.
return numSampleBeforeSeek;
}
}
}
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 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;
}
}
Aggregations