Search in sources :

Example 1 with CryptoData

use of com.google.android.exoplayer2.extractor.TrackOutput.CryptoData in project ExoPlayer by google.

the class SampleQueue method commitSample.

private synchronized void commitSample(long timeUs, @C.BufferFlags int sampleFlags, long offset, int size, @Nullable CryptoData cryptoData) {
    if (length > 0) {
        // Ensure sample data doesn't overlap.
        int previousSampleRelativeIndex = getRelativeIndex(length - 1);
        checkArgument(offsets[previousSampleRelativeIndex] + sizes[previousSampleRelativeIndex] <= offset);
    }
    isLastSampleQueued = (sampleFlags & C.BUFFER_FLAG_LAST_SAMPLE) != 0;
    largestQueuedTimestampUs = max(largestQueuedTimestampUs, timeUs);
    int relativeEndIndex = getRelativeIndex(length);
    timesUs[relativeEndIndex] = timeUs;
    offsets[relativeEndIndex] = offset;
    sizes[relativeEndIndex] = size;
    flags[relativeEndIndex] = sampleFlags;
    cryptoDatas[relativeEndIndex] = cryptoData;
    sourceIds[relativeEndIndex] = upstreamSourceId;
    if (sharedSampleMetadata.isEmpty() || !sharedSampleMetadata.getEndValue().format.equals(upstreamFormat)) {
        DrmSessionReference drmSessionReference = drmSessionManager != null ? drmSessionManager.preacquireSession(drmEventDispatcher, upstreamFormat) : DrmSessionReference.EMPTY;
        sharedSampleMetadata.appendSpan(getWriteIndex(), new SharedSampleMetadata(checkNotNull(upstreamFormat), drmSessionReference));
    }
    length++;
    if (length == capacity) {
        // Increase the capacity.
        int newCapacity = capacity + SAMPLE_CAPACITY_INCREMENT;
        int[] newSourceIds = new int[newCapacity];
        long[] newOffsets = new long[newCapacity];
        long[] newTimesUs = new long[newCapacity];
        int[] newFlags = new int[newCapacity];
        int[] newSizes = new int[newCapacity];
        CryptoData[] newCryptoDatas = new CryptoData[newCapacity];
        int beforeWrap = capacity - relativeFirstIndex;
        System.arraycopy(offsets, relativeFirstIndex, newOffsets, 0, beforeWrap);
        System.arraycopy(timesUs, relativeFirstIndex, newTimesUs, 0, beforeWrap);
        System.arraycopy(flags, relativeFirstIndex, newFlags, 0, beforeWrap);
        System.arraycopy(sizes, relativeFirstIndex, newSizes, 0, beforeWrap);
        System.arraycopy(cryptoDatas, relativeFirstIndex, newCryptoDatas, 0, beforeWrap);
        System.arraycopy(sourceIds, relativeFirstIndex, newSourceIds, 0, beforeWrap);
        int afterWrap = relativeFirstIndex;
        System.arraycopy(offsets, 0, newOffsets, beforeWrap, afterWrap);
        System.arraycopy(timesUs, 0, newTimesUs, beforeWrap, afterWrap);
        System.arraycopy(flags, 0, newFlags, beforeWrap, afterWrap);
        System.arraycopy(sizes, 0, newSizes, beforeWrap, afterWrap);
        System.arraycopy(cryptoDatas, 0, newCryptoDatas, beforeWrap, afterWrap);
        System.arraycopy(sourceIds, 0, newSourceIds, beforeWrap, afterWrap);
        offsets = newOffsets;
        timesUs = newTimesUs;
        flags = newFlags;
        sizes = newSizes;
        cryptoDatas = newCryptoDatas;
        sourceIds = newSourceIds;
        relativeFirstIndex = 0;
        capacity = newCapacity;
    }
}
Also used : DrmSessionReference(com.google.android.exoplayer2.drm.DrmSessionManager.DrmSessionReference)

Example 2 with CryptoData

use of com.google.android.exoplayer2.extractor.TrackOutput.CryptoData in project ExoPlayer by google.

the class OutputConsumerAdapterV30 method toExoPlayerCryptoData.

@Nullable
private CryptoData toExoPlayerCryptoData(int trackIndex, @Nullable CryptoInfo cryptoInfo) {
    if (cryptoInfo == null) {
        return null;
    }
    @Nullable CryptoInfo lastReceivedCryptoInfo = lastReceivedCryptoInfos.get(trackIndex);
    CryptoData cryptoDataToOutput;
    // MediaParser keeps identity and value equality aligned for efficient comparison.
    if (lastReceivedCryptoInfo == cryptoInfo) {
        // They match, we can reuse the last one we created.
        cryptoDataToOutput = Assertions.checkNotNull(lastOutputCryptoDatas.get(trackIndex));
    } else {
        // They don't match, we create a new CryptoData.
        // TODO: Access pattern encryption info directly once the Android SDK makes it visible.
        // See [Internal ref: b/154248283].
        int encryptedBlocks;
        int clearBlocks;
        try {
            Matcher matcher = REGEX_CRYPTO_INFO_PATTERN.matcher(cryptoInfo.toString());
            matcher.find();
            encryptedBlocks = Integer.parseInt(Util.castNonNull(matcher.group(1)));
            clearBlocks = Integer.parseInt(Util.castNonNull(matcher.group(2)));
        } catch (RuntimeException e) {
            // Should never happen.
            Log.e(TAG, "Unexpected error while parsing CryptoInfo: " + cryptoInfo, e);
            // Assume no-pattern encryption.
            encryptedBlocks = 0;
            clearBlocks = 0;
        }
        cryptoDataToOutput = new CryptoData(cryptoInfo.mode, cryptoInfo.key, encryptedBlocks, clearBlocks);
        lastReceivedCryptoInfos.set(trackIndex, cryptoInfo);
        lastOutputCryptoDatas.set(trackIndex, cryptoDataToOutput);
    }
    return cryptoDataToOutput;
}
Also used : CryptoData(com.google.android.exoplayer2.extractor.TrackOutput.CryptoData) CryptoInfo(android.media.MediaCodec.CryptoInfo) Matcher(java.util.regex.Matcher) Nullable(androidx.annotation.Nullable) SeekPoint(com.google.android.exoplayer2.extractor.SeekPoint) SuppressLint(android.annotation.SuppressLint) Nullable(androidx.annotation.Nullable)

Example 3 with CryptoData

use of com.google.android.exoplayer2.extractor.TrackOutput.CryptoData in project ExoPlayer by google.

the class AdtsReaderTest method skipToNextSampleResetsState.

@Test
public void skipToNextSampleResetsState() throws Exception {
    data = new ParsableByteArray(Bytes.concat(ADTS_HEADER, ADTS_CONTENT, ADTS_HEADER, ADTS_CONTENT, // The Reader should be able to read the next sample.
    Arrays.copyOfRange(ADTS_HEADER, 1, ADTS_HEADER.length), ADTS_CONTENT, ADTS_HEADER, ADTS_CONTENT));
    feed();
    assertSampleCounts(0, 3);
    for (int i = 0; i < 3; i++) {
        adtsOutput.assertSample(/* index= */
        i, /* data= */
        ADTS_CONTENT, /* timeUs= */
        ADTS_SAMPLE_DURATION * i, /* flags= */
        C.BUFFER_FLAG_KEY_FRAME, /* cryptoData= */
        null);
    }
}
Also used : ParsableByteArray(com.google.android.exoplayer2.util.ParsableByteArray) Test(org.junit.Test)

Example 4 with CryptoData

use of com.google.android.exoplayer2.extractor.TrackOutput.CryptoData in project ExoPlayer by google.

the class FragmentedMp4Extractor method readSample.

/**
 * Attempts to read the next sample in the current mdat atom. The read sample may be output or
 * skipped.
 *
 * <p>If there are no more samples in the current mdat atom then the parser state is transitioned
 * to {@link #STATE_READING_ATOM_HEADER} and {@code false} is returned.
 *
 * <p>It is possible for a sample to be partially read in the case that an exception is thrown. In
 * this case the method can be called again to read the remainder of the sample.
 *
 * @param input The {@link ExtractorInput} from which to read data.
 * @return Whether a sample was read. The read sample may have been output or skipped. False
 *     indicates that there are no samples left to read in the current mdat.
 * @throws IOException If an error occurs reading from the input.
 */
private boolean readSample(ExtractorInput input) throws IOException {
    @Nullable TrackBundle trackBundle = currentTrackBundle;
    if (trackBundle == null) {
        trackBundle = getNextTrackBundle(trackBundles);
        if (trackBundle == null) {
            // We've run out of samples in the current mdat. Discard any trailing data and prepare to
            // read the header of the next atom.
            int bytesToSkip = (int) (endOfMdatPosition - input.getPosition());
            if (bytesToSkip < 0) {
                throw ParserException.createForMalformedContainer("Offset to end of mdat was negative.", /* cause= */
                null);
            }
            input.skipFully(bytesToSkip);
            enterReadingAtomHeaderState();
            return false;
        }
        long nextDataPosition = trackBundle.getCurrentSampleOffset();
        // We skip bytes preceding the next sample to read.
        int bytesToSkip = (int) (nextDataPosition - input.getPosition());
        if (bytesToSkip < 0) {
            // Assume the sample data must be contiguous in the mdat with no preceding data.
            Log.w(TAG, "Ignoring negative offset to sample data.");
            bytesToSkip = 0;
        }
        input.skipFully(bytesToSkip);
        currentTrackBundle = trackBundle;
    }
    if (parserState == STATE_READING_SAMPLE_START) {
        sampleSize = trackBundle.getCurrentSampleSize();
        if (trackBundle.currentSampleIndex < trackBundle.firstSampleToOutputIndex) {
            input.skipFully(sampleSize);
            trackBundle.skipSampleEncryptionData();
            if (!trackBundle.next()) {
                currentTrackBundle = null;
            }
            parserState = STATE_READING_SAMPLE_START;
            return true;
        }
        if (trackBundle.moovSampleTable.track.sampleTransformation == Track.TRANSFORMATION_CEA608_CDAT) {
            sampleSize -= Atom.HEADER_SIZE;
            input.skipFully(Atom.HEADER_SIZE);
        }
        if (MimeTypes.AUDIO_AC4.equals(trackBundle.moovSampleTable.track.format.sampleMimeType)) {
            // AC4 samples need to be prefixed with a clear sample header.
            sampleBytesWritten = trackBundle.outputSampleEncryptionData(sampleSize, Ac4Util.SAMPLE_HEADER_SIZE);
            Ac4Util.getAc4SampleHeader(sampleSize, scratch);
            trackBundle.output.sampleData(scratch, Ac4Util.SAMPLE_HEADER_SIZE);
            sampleBytesWritten += Ac4Util.SAMPLE_HEADER_SIZE;
        } else {
            sampleBytesWritten = trackBundle.outputSampleEncryptionData(sampleSize, /* clearHeaderSize= */
            0);
        }
        sampleSize += sampleBytesWritten;
        parserState = STATE_READING_SAMPLE_CONTINUE;
        sampleCurrentNalBytesRemaining = 0;
    }
    Track track = trackBundle.moovSampleTable.track;
    TrackOutput output = trackBundle.output;
    long sampleTimeUs = trackBundle.getCurrentSamplePresentationTimeUs();
    if (timestampAdjuster != null) {
        sampleTimeUs = timestampAdjuster.adjustSampleTimestamp(sampleTimeUs);
    }
    if (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[] nalPrefixData = nalPrefix.getData();
        nalPrefixData[0] = 0;
        nalPrefixData[1] = 0;
        nalPrefixData[2] = 0;
        int nalUnitPrefixLength = track.nalUnitLengthFieldLength + 1;
        int nalUnitLengthFieldLengthDiff = 4 - 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, and its type.
                input.readFully(nalPrefixData, nalUnitLengthFieldLengthDiff, nalUnitPrefixLength);
                nalPrefix.setPosition(0);
                int nalLengthInt = nalPrefix.readInt();
                if (nalLengthInt < 1) {
                    throw ParserException.createForMalformedContainer("Invalid NAL length", /* cause= */
                    null);
                }
                sampleCurrentNalBytesRemaining = nalLengthInt - 1;
                // Write a start code for the current NAL unit.
                nalStartCode.setPosition(0);
                output.sampleData(nalStartCode, 4);
                // Write the NAL unit type byte.
                output.sampleData(nalPrefix, 1);
                processSeiNalUnitPayload = ceaTrackOutputs.length > 0 && NalUnitUtil.isNalUnitSei(track.format.sampleMimeType, nalPrefixData[4]);
                sampleBytesWritten += 5;
                sampleSize += nalUnitLengthFieldLengthDiff;
            } else {
                int writtenBytes;
                if (processSeiNalUnitPayload) {
                    // Read and write the payload of the SEI NAL unit.
                    nalBuffer.reset(sampleCurrentNalBytesRemaining);
                    input.readFully(nalBuffer.getData(), 0, sampleCurrentNalBytesRemaining);
                    output.sampleData(nalBuffer, sampleCurrentNalBytesRemaining);
                    writtenBytes = sampleCurrentNalBytesRemaining;
                    // Unescape and process the SEI NAL unit.
                    int unescapedLength = NalUnitUtil.unescapeStream(nalBuffer.getData(), nalBuffer.limit());
                    // If the format is H.265/HEVC the NAL unit header has two bytes so skip one more byte.
                    nalBuffer.setPosition(MimeTypes.VIDEO_H265.equals(track.format.sampleMimeType) ? 1 : 0);
                    nalBuffer.setLimit(unescapedLength);
                    CeaUtil.consume(sampleTimeUs, nalBuffer, ceaTrackOutputs);
                } else {
                    // Write the payload of the NAL unit.
                    writtenBytes = output.sampleData(input, sampleCurrentNalBytesRemaining, false);
                }
                sampleBytesWritten += writtenBytes;
                sampleCurrentNalBytesRemaining -= writtenBytes;
            }
        }
    } else {
        while (sampleBytesWritten < sampleSize) {
            int writtenBytes = output.sampleData(input, sampleSize - sampleBytesWritten, false);
            sampleBytesWritten += writtenBytes;
        }
    }
    @C.BufferFlags int sampleFlags = trackBundle.getCurrentSampleFlags();
    // Encryption data.
    @Nullable TrackOutput.CryptoData cryptoData = null;
    @Nullable TrackEncryptionBox encryptionBox = trackBundle.getEncryptionBoxIfEncrypted();
    if (encryptionBox != null) {
        cryptoData = encryptionBox.cryptoData;
    }
    output.sampleMetadata(sampleTimeUs, sampleFlags, sampleSize, 0, cryptoData);
    // After we have the sampleTimeUs, we can commit all the pending metadata samples
    outputPendingMetadataSamples(sampleTimeUs);
    if (!trackBundle.next()) {
        currentTrackBundle = null;
    }
    parserState = STATE_READING_SAMPLE_START;
    return true;
}
Also used : Nullable(androidx.annotation.Nullable) TrackOutput(com.google.android.exoplayer2.extractor.TrackOutput)

Example 5 with CryptoData

use of com.google.android.exoplayer2.extractor.TrackOutput.CryptoData in project ExoPlayer by google.

the class FakeSampleStream method writeData.

/**
 * Writes all not yet written {@link FakeSampleStreamItem sample stream items} to the sample queue
 * starting at the given position.
 *
 * @param startPositionUs The start position, in microseconds.
 */
public void writeData(long startPositionUs) {
    if (sampleStreamItemsWritePosition == 0) {
        sampleQueue.setStartTimeUs(startPositionUs);
    }
    boolean writtenFirstFormat = false;
    @Nullable Format pendingFirstFormat = null;
    for (int i = 0; i < sampleStreamItems.size(); i++) {
        FakeSampleStreamItem fakeSampleStreamItem = sampleStreamItems.get(i);
        @Nullable FakeSampleStream.SampleInfo sampleInfo = fakeSampleStreamItem.sampleInfo;
        if (sampleInfo == null) {
            if (writtenFirstFormat) {
                sampleQueue.format(checkNotNull(fakeSampleStreamItem.format));
            } else {
                pendingFirstFormat = checkNotNull(fakeSampleStreamItem.format);
            }
            continue;
        }
        if ((sampleInfo.flags & C.BUFFER_FLAG_END_OF_STREAM) != 0) {
            loadingFinished = true;
            break;
        }
        if (sampleInfo.timeUs >= startPositionUs && i >= sampleStreamItemsWritePosition) {
            if (!writtenFirstFormat) {
                sampleQueue.format(checkNotNull(pendingFirstFormat));
                writtenFirstFormat = true;
            }
            sampleQueue.sampleData(new ParsableByteArray(sampleInfo.data), sampleInfo.data.length);
            sampleQueue.sampleMetadata(sampleInfo.timeUs, sampleInfo.flags, sampleInfo.data.length, /* offset= */
            0, /* cryptoData= */
            null);
        }
    }
    sampleStreamItemsWritePosition = sampleStreamItems.size();
}
Also used : ParsableByteArray(com.google.android.exoplayer2.util.ParsableByteArray) Format(com.google.android.exoplayer2.Format) Nullable(androidx.annotation.Nullable)

Aggregations

Nullable (androidx.annotation.Nullable)5 ParsableByteArray (com.google.android.exoplayer2.util.ParsableByteArray)4 SeekPoint (com.google.android.exoplayer2.extractor.SeekPoint)2 TrackOutput (com.google.android.exoplayer2.extractor.TrackOutput)2 CryptoData (com.google.android.exoplayer2.extractor.TrackOutput.CryptoData)2 Test (org.junit.Test)2 SuppressLint (android.annotation.SuppressLint)1 CryptoInfo (android.media.MediaCodec.CryptoInfo)1 Format (com.google.android.exoplayer2.Format)1 CryptoInfo (com.google.android.exoplayer2.decoder.CryptoInfo)1 DrmSessionReference (com.google.android.exoplayer2.drm.DrmSessionManager.DrmSessionReference)1 TrueHdSampleRechunker (com.google.android.exoplayer2.extractor.TrueHdSampleRechunker)1 Matcher (java.util.regex.Matcher)1