use of com.google.android.exoplayer2.extractor.TrackOutput.CryptoData in project ExoPlayer by google.
the class SampleQueueTest method capacityIncreases.
@Test
public void capacityIncreases() {
int numberOfSamplesToInput = 3 * SampleQueue.SAMPLE_CAPACITY_INCREMENT + 1;
sampleQueue.format(FORMAT_1);
sampleQueue.sampleData(new ParsableByteArray(numberOfSamplesToInput), /* length= */
numberOfSamplesToInput);
for (int i = 0; i < numberOfSamplesToInput; i++) {
sampleQueue.sampleMetadata(/* timeUs= */
i * 1000, /* flags= */
C.BUFFER_FLAG_KEY_FRAME, /* size= */
1, /* offset= */
numberOfSamplesToInput - i - 1, /* cryptoData= */
null);
}
assertReadFormat(/* formatRequired= */
false, FORMAT_1);
for (int i = 0; i < numberOfSamplesToInput; i++) {
assertReadSample(/* timeUs= */
i * 1000, /* isKeyFrame= */
true, /* isDecodeOnly= */
false, /* isEncrypted= */
false, /* sampleData= */
new byte[1], /* offset= */
0, /* length= */
1);
}
assertReadNothing(/* formatRequired= */
false);
}
use of com.google.android.exoplayer2.extractor.TrackOutput.CryptoData in project ExoPlayer by google.
the class SubtitleExtractor method writeToOutput.
private void writeToOutput() {
checkStateNotNull(this.trackOutput);
checkState(timestamps.size() == samples.size());
int index = seekTimeUs == C.TIME_UNSET ? 0 : Util.binarySearchFloor(timestamps, seekTimeUs, /* inclusive= */
true, /* stayInBounds= */
true);
for (int i = index; i < samples.size(); i++) {
ParsableByteArray sample = samples.get(i);
sample.setPosition(0);
int size = sample.getData().length;
trackOutput.sampleData(sample, size);
trackOutput.sampleMetadata(/* timeUs= */
timestamps.get(i), /* flags= */
C.BUFFER_FLAG_KEY_FRAME, /* size= */
size, /* offset= */
0, /* cryptoData= */
null);
}
}
use of com.google.android.exoplayer2.extractor.TrackOutput.CryptoData in project ExoPlayer by google.
the class SampleDataQueue method readEncryptionData.
/**
* Reads encryption data for the sample described by {@code extrasHolder}.
*
* <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 allocationNode The first {@link AllocationNode} containing data yet to be read.
* @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.
* @param scratch A scratch {@link ParsableByteArray}.
* @return The first {@link AllocationNode} that contains unread bytes after this method returns.
*/
private static AllocationNode readEncryptionData(AllocationNode allocationNode, DecoderInputBuffer buffer, SampleExtrasHolder extrasHolder, ParsableByteArray scratch) {
long offset = extrasHolder.offset;
// Read the signal byte.
scratch.reset(1);
allocationNode = readData(allocationNode, offset, scratch.getData(), 1);
offset++;
byte signalByte = scratch.getData()[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);
}
allocationNode = readData(allocationNode, offset, cryptoInfo.iv, ivSize);
offset += ivSize;
// Read the subsample count, if present.
int subsampleCount;
if (subsampleEncryption) {
scratch.reset(2);
allocationNode = readData(allocationNode, offset, scratch.getData(), 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);
allocationNode = readData(allocationNode, offset, scratch.getData(), 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 = Util.castNonNull(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;
return allocationNode;
}
use of com.google.android.exoplayer2.extractor.TrackOutput.CryptoData 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.
*/
private int readSample(ExtractorInput input, PositionHolder positionHolder) throws IOException {
long inputPosition = input.getPosition();
if (sampleTrackIndex == C.INDEX_UNSET) {
sampleTrackIndex = getTrackIndexOfNextReadSample(inputPosition);
if (sampleTrackIndex == C.INDEX_UNSET) {
return RESULT_END_OF_INPUT;
}
}
Mp4Track track = castNonNull(tracks)[sampleTrackIndex];
TrackOutput trackOutput = track.trackOutput;
int sampleIndex = track.sampleIndex;
long position = track.sampleTable.offsets[sampleIndex];
int sampleSize = track.sampleTable.sizes[sampleIndex];
@Nullable TrueHdSampleRechunker trueHdSampleRechunker = track.trueHdSampleRechunker;
long skipAmount = position - inputPosition + sampleBytesRead;
if (skipAmount < 0 || skipAmount >= RELOAD_MINIMUM_SEEK_DISTANCE) {
positionHolder.position = position;
return RESULT_SEEK;
}
if (track.track.sampleTransformation == Track.TRANSFORMATION_CEA608_CDAT) {
// The sample information is contained in a cdat atom. The header must be discarded for
// committing.
skipAmount += Atom.HEADER_SIZE;
sampleSize -= Atom.HEADER_SIZE;
}
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.getData();
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(nalLengthData, nalUnitLengthFieldLengthDiff, nalUnitLengthFieldLength);
sampleBytesRead += nalUnitLengthFieldLength;
nalLength.setPosition(0);
int nalLengthInt = nalLength.readInt();
if (nalLengthInt < 0) {
throw ParserException.createForMalformedContainer("Invalid NAL length", /* cause= */
null);
}
sampleCurrentNalBytesRemaining = nalLengthInt;
// 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);
sampleBytesRead += writtenBytes;
sampleBytesWritten += writtenBytes;
sampleCurrentNalBytesRemaining -= writtenBytes;
}
}
} else {
if (MimeTypes.AUDIO_AC4.equals(track.track.format.sampleMimeType)) {
if (sampleBytesWritten == 0) {
Ac4Util.getAc4SampleHeader(sampleSize, scratch);
trackOutput.sampleData(scratch, Ac4Util.SAMPLE_HEADER_SIZE);
sampleBytesWritten += Ac4Util.SAMPLE_HEADER_SIZE;
}
sampleSize += Ac4Util.SAMPLE_HEADER_SIZE;
} else if (trueHdSampleRechunker != null) {
trueHdSampleRechunker.startSample(input);
}
while (sampleBytesWritten < sampleSize) {
int writtenBytes = trackOutput.sampleData(input, sampleSize - sampleBytesWritten, false);
sampleBytesRead += writtenBytes;
sampleBytesWritten += writtenBytes;
sampleCurrentNalBytesRemaining -= writtenBytes;
}
}
long timeUs = track.sampleTable.timestampsUs[sampleIndex];
@C.BufferFlags int flags = track.sampleTable.flags[sampleIndex];
if (trueHdSampleRechunker != null) {
trueHdSampleRechunker.sampleMetadata(trackOutput, timeUs, flags, sampleSize, /* offset= */
0, /* cryptoData= */
null);
if (sampleIndex + 1 == track.sampleTable.sampleCount) {
trueHdSampleRechunker.outputPendingSampleMetadata(trackOutput, /* cryptoData= */
null);
}
} else {
trackOutput.sampleMetadata(timeUs, flags, sampleSize, /* offset= */
0, /* cryptoData= */
null);
}
track.sampleIndex++;
sampleTrackIndex = C.INDEX_UNSET;
sampleBytesRead = 0;
sampleBytesWritten = 0;
sampleCurrentNalBytesRemaining = 0;
return RESULT_CONTINUE;
}
Aggregations