use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class HlsMediaChunk method maybeLoadInitData.
// Internal loading methods.
private void maybeLoadInitData() throws IOException, InterruptedException {
if (previousExtractor == extractor || initLoadCompleted || initDataSpec == null) {
// According to spec, for packed audio, initDataSpec is expected to be null.
return;
}
DataSpec initSegmentDataSpec = Util.getRemainderDataSpec(initDataSpec, initSegmentBytesLoaded);
try {
ExtractorInput input = new DefaultExtractorInput(initDataSource, initSegmentDataSpec.absoluteStreamPosition, initDataSource.open(initSegmentDataSpec));
try {
int result = Extractor.RESULT_CONTINUE;
while (result == Extractor.RESULT_CONTINUE && !loadCanceled) {
result = extractor.read(input, null);
}
} finally {
initSegmentBytesLoaded = (int) (input.getPosition() - initDataSpec.absoluteStreamPosition);
}
} finally {
Util.closeQuietly(dataSource);
}
initLoadCompleted = true;
}
use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class HlsMediaChunk method loadMedia.
private void loadMedia() throws IOException, InterruptedException {
// If we previously fed part of this chunk to the extractor, we need to skip it this time. For
// encrypted content we need to skip the data by reading it through the source, so as to ensure
// correct decryption of the remainder of the chunk. For clear content, we can request the
// remainder of the chunk directly.
DataSpec loadDataSpec;
boolean skipLoadedBytes;
if (isEncrypted) {
loadDataSpec = dataSpec;
skipLoadedBytes = bytesLoaded != 0;
} else {
loadDataSpec = Util.getRemainderDataSpec(dataSpec, bytesLoaded);
skipLoadedBytes = false;
}
if (!isMasterTimestampSource) {
timestampAdjuster.waitUntilInitialized();
} else if (timestampAdjuster.getFirstSampleTimestampUs() == TimestampAdjuster.DO_NOT_OFFSET) {
// We're the master and we haven't set the desired first sample timestamp yet.
timestampAdjuster.setFirstSampleTimestampUs(startTimeUs);
}
try {
ExtractorInput input = new DefaultExtractorInput(dataSource, loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec));
if (extractor == null) {
// Media segment format is packed audio.
long id3Timestamp = peekId3PrivTimestamp(input);
extractor = buildPackedAudioExtractor(id3Timestamp != C.TIME_UNSET ? timestampAdjuster.adjustTsTimestamp(id3Timestamp) : startTimeUs);
}
if (skipLoadedBytes) {
input.skipFully(bytesLoaded);
}
try {
int result = Extractor.RESULT_CONTINUE;
while (result == Extractor.RESULT_CONTINUE && !loadCanceled) {
result = extractor.read(input, null);
}
} finally {
bytesLoaded = (int) (input.getPosition() - dataSpec.absoluteStreamPosition);
}
} finally {
Util.closeQuietly(dataSource);
}
loadCompleted = true;
}
use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class SingleSampleMediaChunk method load.
@SuppressWarnings("NonAtomicVolatileUpdate")
@Override
public void load() throws IOException, InterruptedException {
DataSpec loadDataSpec = Util.getRemainderDataSpec(dataSpec, bytesLoaded);
try {
// Create and open the input.
long length = dataSource.open(loadDataSpec);
if (length != C.LENGTH_UNSET) {
length += bytesLoaded;
}
ExtractorInput extractorInput = new DefaultExtractorInput(dataSource, bytesLoaded, length);
BaseMediaChunkOutput output = getOutput();
output.setSampleOffsetUs(0);
TrackOutput trackOutput = output.track(0, trackType);
trackOutput.format(sampleFormat);
// Load the sample data.
int result = 0;
while (result != C.RESULT_END_OF_INPUT) {
bytesLoaded += result;
result = trackOutput.sampleData(extractorInput, Integer.MAX_VALUE, true);
}
int sampleSize = bytesLoaded;
trackOutput.sampleMetadata(startTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleSize, 0, null);
} finally {
Util.closeQuietly(dataSource);
}
loadCompleted = true;
}
use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class FragmentedMp4Extractor method readAtomPayload.
private void readAtomPayload(ExtractorInput input) throws IOException, InterruptedException {
int atomPayloadSize = (int) atomSize - atomHeaderBytesRead;
if (atomData != null) {
input.readFully(atomData.data, Atom.HEADER_SIZE, atomPayloadSize);
onLeafAtomRead(new LeafAtom(atomType, atomData), input.getPosition());
} else {
input.skipFully(atomPayloadSize);
}
processAtomEnded(input.getPosition());
}
use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class MatroskaExtractor method writeSampleData.
private void writeSampleData(ExtractorInput input, Track track, int size) throws IOException, InterruptedException {
if (CODEC_ID_SUBRIP.equals(track.codecId)) {
int sizeWithPrefix = SUBRIP_PREFIX.length + size;
if (subripSample.capacity() < sizeWithPrefix) {
// Initialize subripSample to contain the required prefix and have space to hold a subtitle
// twice as long as this one.
subripSample.data = Arrays.copyOf(SUBRIP_PREFIX, sizeWithPrefix + size);
}
input.readFully(subripSample.data, SUBRIP_PREFIX.length, size);
subripSample.setPosition(0);
subripSample.setLimit(sizeWithPrefix);
// the correct end timecode, which we might not have yet.
return;
}
TrackOutput output = track.output;
if (!sampleEncodingHandled) {
if (track.hasContentEncryption) {
// If the sample is encrypted, read its encryption signal byte and set the IV size.
// Clear the encrypted flag.
blockFlags &= ~C.BUFFER_FLAG_ENCRYPTED;
if (!sampleSignalByteRead) {
input.readFully(scratch.data, 0, 1);
sampleBytesRead++;
if ((scratch.data[0] & 0x80) == 0x80) {
throw new ParserException("Extension bit is set in signal byte");
}
sampleSignalByte = scratch.data[0];
sampleSignalByteRead = true;
}
boolean isEncrypted = (sampleSignalByte & 0x01) == 0x01;
if (isEncrypted) {
boolean hasSubsampleEncryption = (sampleSignalByte & 0x02) == 0x02;
blockFlags |= C.BUFFER_FLAG_ENCRYPTED;
if (!sampleInitializationVectorRead) {
input.readFully(encryptionInitializationVector.data, 0, ENCRYPTION_IV_SIZE);
sampleBytesRead += ENCRYPTION_IV_SIZE;
sampleInitializationVectorRead = true;
// Write the signal byte, containing the IV size and the subsample encryption flag.
scratch.data[0] = (byte) (ENCRYPTION_IV_SIZE | (hasSubsampleEncryption ? 0x80 : 0x00));
scratch.setPosition(0);
output.sampleData(scratch, 1);
sampleBytesWritten++;
// Write the IV.
encryptionInitializationVector.setPosition(0);
output.sampleData(encryptionInitializationVector, ENCRYPTION_IV_SIZE);
sampleBytesWritten += ENCRYPTION_IV_SIZE;
}
if (hasSubsampleEncryption) {
if (!samplePartitionCountRead) {
input.readFully(scratch.data, 0, 1);
sampleBytesRead++;
scratch.setPosition(0);
samplePartitionCount = scratch.readUnsignedByte();
samplePartitionCountRead = true;
}
int samplePartitionDataSize = samplePartitionCount * 4;
scratch.reset(samplePartitionDataSize);
input.readFully(scratch.data, 0, samplePartitionDataSize);
sampleBytesRead += samplePartitionDataSize;
short subsampleCount = (short) (1 + (samplePartitionCount / 2));
int subsampleDataSize = 2 + 6 * subsampleCount;
if (encryptionSubsampleDataBuffer == null || encryptionSubsampleDataBuffer.capacity() < subsampleDataSize) {
encryptionSubsampleDataBuffer = ByteBuffer.allocate(subsampleDataSize);
}
encryptionSubsampleDataBuffer.position(0);
encryptionSubsampleDataBuffer.putShort(subsampleCount);
// Loop through the partition offsets and write out the data in the way ExoPlayer
// wants it (ISO 23001-7 Part 7):
// 2 bytes - sub sample count.
// for each sub sample:
// 2 bytes - clear data size.
// 4 bytes - encrypted data size.
int partitionOffset = 0;
for (int i = 0; i < samplePartitionCount; i++) {
int previousPartitionOffset = partitionOffset;
partitionOffset = scratch.readUnsignedIntToInt();
if ((i % 2) == 0) {
encryptionSubsampleDataBuffer.putShort((short) (partitionOffset - previousPartitionOffset));
} else {
encryptionSubsampleDataBuffer.putInt(partitionOffset - previousPartitionOffset);
}
}
int finalPartitionSize = size - sampleBytesRead - partitionOffset;
if ((samplePartitionCount % 2) == 1) {
encryptionSubsampleDataBuffer.putInt(finalPartitionSize);
} else {
encryptionSubsampleDataBuffer.putShort((short) finalPartitionSize);
encryptionSubsampleDataBuffer.putInt(0);
}
encryptionSubsampleData.reset(encryptionSubsampleDataBuffer.array(), subsampleDataSize);
output.sampleData(encryptionSubsampleData, subsampleDataSize);
sampleBytesWritten += subsampleDataSize;
}
}
} else if (track.sampleStrippedBytes != null) {
// If the sample has header stripping, prepare to read/output the stripped bytes first.
sampleStrippedBytes.reset(track.sampleStrippedBytes, track.sampleStrippedBytes.length);
}
sampleEncodingHandled = true;
}
size += sampleStrippedBytes.limit();
if (CODEC_ID_H264.equals(track.codecId) || CODEC_ID_H265.equals(track.codecId)) {
// 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 nalUnitLengthFieldLength = track.nalUnitLengthFieldLength;
int nalUnitLengthFieldLengthDiff = 4 - track.nalUnitLengthFieldLength;
// start codes as we encounter them.
while (sampleBytesRead < size) {
if (sampleCurrentNalBytesRemaining == 0) {
// Read the NAL length so that we know where we find the next one.
readToTarget(input, nalLengthData, nalUnitLengthFieldLengthDiff, nalUnitLengthFieldLength);
nalLength.setPosition(0);
sampleCurrentNalBytesRemaining = nalLength.readUnsignedIntToInt();
// Write a start code for the current NAL unit.
nalStartCode.setPosition(0);
output.sampleData(nalStartCode, 4);
sampleBytesWritten += 4;
} else {
// Write the payload of the NAL unit.
sampleCurrentNalBytesRemaining -= readToOutput(input, output, sampleCurrentNalBytesRemaining);
}
}
} else {
while (sampleBytesRead < size) {
readToOutput(input, output, size - sampleBytesRead);
}
}
if (CODEC_ID_VORBIS.equals(track.codecId)) {
// Vorbis decoder in android MediaCodec [1] expects the last 4 bytes of the sample to be the
// number of samples in the current page. This definition holds good only for Ogg and
// irrelevant for Matroska. So we always set this to -1 (the decoder will ignore this value if
// we set it to -1). The android platform media extractor [2] does the same.
// [1] https://android.googlesource.com/platform/frameworks/av/+/lollipop-release/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp#314
// [2] https://android.googlesource.com/platform/frameworks/av/+/lollipop-release/media/libstagefright/NuMediaExtractor.cpp#474
vorbisNumPageSamples.setPosition(0);
output.sampleData(vorbisNumPageSamples, 4);
sampleBytesWritten += 4;
}
}
Aggregations