Search in sources :

Example 1 with CryptoException

use of com.google.android.exoplayer2.decoder.CryptoException in project ExoPlayer by google.

the class VpxDecoder method decode.

@Override
@Nullable
protected VpxDecoderException decode(DecoderInputBuffer inputBuffer, VideoDecoderOutputBuffer outputBuffer, boolean reset) {
    if (reset && lastSupplementalData != null) {
        // Don't propagate supplemental data across calls to flush the decoder.
        lastSupplementalData.clear();
    }
    ByteBuffer inputData = Util.castNonNull(inputBuffer.data);
    int inputSize = inputData.limit();
    CryptoInfo cryptoInfo = inputBuffer.cryptoInfo;
    final long result = inputBuffer.isEncrypted() ? vpxSecureDecode(vpxDecContext, inputData, inputSize, cryptoConfig, cryptoInfo.mode, Assertions.checkNotNull(cryptoInfo.key), Assertions.checkNotNull(cryptoInfo.iv), cryptoInfo.numSubSamples, cryptoInfo.numBytesOfClearData, cryptoInfo.numBytesOfEncryptedData) : vpxDecode(vpxDecContext, inputData, inputSize);
    if (result != NO_ERROR) {
        if (result == DRM_ERROR) {
            String message = "Drm error: " + vpxGetErrorMessage(vpxDecContext);
            CryptoException cause = new CryptoException(vpxGetErrorCode(vpxDecContext), message);
            return new VpxDecoderException(message, cause);
        } else {
            return new VpxDecoderException("Decode error: " + vpxGetErrorMessage(vpxDecContext));
        }
    }
    if (inputBuffer.hasSupplementalData()) {
        ByteBuffer supplementalData = Assertions.checkNotNull(inputBuffer.supplementalData);
        int size = supplementalData.remaining();
        if (size > 0) {
            if (lastSupplementalData == null || lastSupplementalData.capacity() < size) {
                lastSupplementalData = ByteBuffer.allocate(size);
            } else {
                lastSupplementalData.clear();
            }
            lastSupplementalData.put(supplementalData);
            lastSupplementalData.flip();
        }
    }
    if (!inputBuffer.isDecodeOnly()) {
        outputBuffer.init(inputBuffer.timeUs, outputMode, lastSupplementalData);
        int getFrameResult = vpxGetFrame(vpxDecContext, outputBuffer);
        if (getFrameResult == 1) {
            outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
        } else if (getFrameResult == -1) {
            return new VpxDecoderException("Buffer initialization failed.");
        }
        outputBuffer.format = inputBuffer.format;
    }
    return null;
}
Also used : CryptoInfo(com.google.android.exoplayer2.decoder.CryptoInfo) CryptoException(com.google.android.exoplayer2.decoder.CryptoException) ByteBuffer(java.nio.ByteBuffer) Nullable(androidx.annotation.Nullable)

Example 2 with CryptoException

use of com.google.android.exoplayer2.decoder.CryptoException in project ExoPlayer by google.

the class OpusDecoder method decode.

@Override
@Nullable
protected OpusDecoderException decode(DecoderInputBuffer inputBuffer, SimpleDecoderOutputBuffer outputBuffer, boolean reset) {
    if (reset) {
        opusReset(nativeDecoderContext);
        // When seeking to 0, skip number of samples as specified in opus header. When seeking to
        // any other time, skip number of samples as specified by seek preroll.
        skipSamples = (inputBuffer.timeUs == 0) ? preSkipSamples : seekPreRollSamples;
    }
    ByteBuffer inputData = Util.castNonNull(inputBuffer.data);
    CryptoInfo cryptoInfo = inputBuffer.cryptoInfo;
    int result = inputBuffer.isEncrypted() ? opusSecureDecode(nativeDecoderContext, inputBuffer.timeUs, inputData, inputData.limit(), outputBuffer, SAMPLE_RATE, cryptoConfig, cryptoInfo.mode, Assertions.checkNotNull(cryptoInfo.key), Assertions.checkNotNull(cryptoInfo.iv), cryptoInfo.numSubSamples, cryptoInfo.numBytesOfClearData, cryptoInfo.numBytesOfEncryptedData) : opusDecode(nativeDecoderContext, inputBuffer.timeUs, inputData, inputData.limit(), outputBuffer);
    if (result < 0) {
        if (result == DRM_ERROR) {
            String message = "Drm error: " + opusGetErrorMessage(nativeDecoderContext);
            CryptoException cause = new CryptoException(opusGetErrorCode(nativeDecoderContext), message);
            return new OpusDecoderException(message, cause);
        } else {
            return new OpusDecoderException("Decode error: " + opusGetErrorMessage(result));
        }
    }
    ByteBuffer outputData = Util.castNonNull(outputBuffer.data);
    outputData.position(0);
    outputData.limit(result);
    if (skipSamples > 0) {
        int bytesPerSample = samplesToBytes(1, channelCount, outputFloat);
        int skipBytes = skipSamples * bytesPerSample;
        if (result <= skipBytes) {
            skipSamples -= result / bytesPerSample;
            outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
            outputData.position(result);
        } else {
            skipSamples = 0;
            outputData.position(skipBytes);
        }
    }
    return null;
}
Also used : CryptoInfo(com.google.android.exoplayer2.decoder.CryptoInfo) CryptoException(com.google.android.exoplayer2.decoder.CryptoException) ByteBuffer(java.nio.ByteBuffer) Nullable(androidx.annotation.Nullable)

Example 3 with CryptoException

use of com.google.android.exoplayer2.decoder.CryptoException in project ExoPlayer by google.

the class MediaCodecRenderer method feedInputBuffer.

/**
 * @return Whether it may be possible to feed more input data.
 * @throws ExoPlaybackException If an error occurs feeding the input buffer.
 */
private boolean feedInputBuffer() throws ExoPlaybackException {
    if (codec == null || codecDrainState == DRAIN_STATE_WAIT_END_OF_STREAM || inputStreamEnded) {
        return false;
    }
    if (codecDrainState == DRAIN_STATE_NONE && shouldReinitCodec()) {
        drainAndReinitializeCodec();
    }
    if (inputIndex < 0) {
        inputIndex = codec.dequeueInputBufferIndex();
        if (inputIndex < 0) {
            return false;
        }
        buffer.data = codec.getInputBuffer(inputIndex);
        buffer.clear();
    }
    if (codecDrainState == DRAIN_STATE_SIGNAL_END_OF_STREAM) {
        // that it outputs any remaining buffers before we release it.
        if (codecNeedsEosPropagation) {
        // Do nothing.
        } else {
            codecReceivedEos = true;
            codec.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
            resetInputBuffer();
        }
        codecDrainState = DRAIN_STATE_WAIT_END_OF_STREAM;
        return false;
    }
    if (codecNeedsAdaptationWorkaroundBuffer) {
        codecNeedsAdaptationWorkaroundBuffer = false;
        buffer.data.put(ADAPTATION_WORKAROUND_BUFFER);
        codec.queueInputBuffer(inputIndex, 0, ADAPTATION_WORKAROUND_BUFFER.length, 0, 0);
        resetInputBuffer();
        codecReceivedBuffers = true;
        return true;
    }
    // the start of the buffer that also contains the first frame in the new format.
    if (codecReconfigurationState == RECONFIGURATION_STATE_WRITE_PENDING) {
        for (int i = 0; i < codecInputFormat.initializationData.size(); i++) {
            byte[] data = codecInputFormat.initializationData.get(i);
            buffer.data.put(data);
        }
        codecReconfigurationState = RECONFIGURATION_STATE_QUEUE_PENDING;
    }
    int adaptiveReconfigurationBytes = buffer.data.position();
    FormatHolder formatHolder = getFormatHolder();
    @SampleStream.ReadDataResult int result;
    try {
        result = readSource(formatHolder, buffer, /* readFlags= */
        0);
    } catch (InsufficientCapacityException e) {
        onCodecError(e);
        // Skip the sample that's too large by reading it without its data. Then flush the codec so
        // that rendering will resume from the next key frame.
        readSourceOmittingSampleData(/* readFlags= */
        0);
        flushCodec();
        return true;
    }
    if (hasReadStreamToEnd()) {
        // Notify output queue of the last buffer's timestamp.
        lastBufferInStreamPresentationTimeUs = largestQueuedPresentationTimeUs;
    }
    if (result == C.RESULT_NOTHING_READ) {
        return false;
    }
    if (result == C.RESULT_FORMAT_READ) {
        if (codecReconfigurationState == RECONFIGURATION_STATE_QUEUE_PENDING) {
            // We received two formats in a row. Clear the current buffer of any reconfiguration data
            // associated with the first format.
            buffer.clear();
            codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING;
        }
        onInputFormatChanged(formatHolder);
        return true;
    }
    // We've read a buffer.
    if (buffer.isEndOfStream()) {
        if (codecReconfigurationState == RECONFIGURATION_STATE_QUEUE_PENDING) {
            // We received a new format immediately before the end of the stream. We need to clear
            // the corresponding reconfiguration data from the current buffer, but re-write it into
            // a subsequent buffer if there are any (for example, if the user seeks backwards).
            buffer.clear();
            codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING;
        }
        inputStreamEnded = true;
        if (!codecReceivedBuffers) {
            processEndOfStream();
            return false;
        }
        try {
            if (codecNeedsEosPropagation) {
            // Do nothing.
            } else {
                codecReceivedEos = true;
                codec.queueInputBuffer(inputIndex, /* offset= */
                0, /* size= */
                0, /* presentationTimeUs= */
                0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                resetInputBuffer();
            }
        } catch (CryptoException e) {
            throw createRendererException(e, inputFormat, Util.getErrorCodeForMediaDrmErrorCode(e.getErrorCode()));
        }
        return false;
    }
    // sample that's too large to be held in one of the decoder's input buffers.
    if (!codecReceivedBuffers && !buffer.isKeyFrame()) {
        buffer.clear();
        if (codecReconfigurationState == RECONFIGURATION_STATE_QUEUE_PENDING) {
            // The buffer we just cleared contained reconfiguration data. We need to re-write this data
            // into a subsequent buffer (if there is one).
            codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING;
        }
        return true;
    }
    boolean bufferEncrypted = buffer.isEncrypted();
    if (bufferEncrypted) {
        buffer.cryptoInfo.increaseClearDataFirstSubSampleBy(adaptiveReconfigurationBytes);
    }
    if (codecNeedsDiscardToSpsWorkaround && !bufferEncrypted) {
        NalUnitUtil.discardToSps(buffer.data);
        if (buffer.data.position() == 0) {
            return true;
        }
        codecNeedsDiscardToSpsWorkaround = false;
    }
    long presentationTimeUs = buffer.timeUs;
    if (c2Mp3TimestampTracker != null) {
        presentationTimeUs = c2Mp3TimestampTracker.updateAndGetPresentationTimeUs(inputFormat, buffer);
        // When draining the C2 MP3 decoder it produces an extra non-empty buffer with a timestamp
        // after all queued input buffer timestamps (unlike other decoders, which generally propagate
        // the input timestamps to output buffers 1:1). To detect the end of the stream when this
        // buffer is dequeued we override the largest queued timestamp accordingly.
        largestQueuedPresentationTimeUs = max(largestQueuedPresentationTimeUs, c2Mp3TimestampTracker.getLastOutputBufferPresentationTimeUs(inputFormat));
    }
    if (buffer.isDecodeOnly()) {
        decodeOnlyPresentationTimestamps.add(presentationTimeUs);
    }
    if (waitingForFirstSampleInFormat) {
        formatQueue.add(presentationTimeUs, inputFormat);
        waitingForFirstSampleInFormat = false;
    }
    largestQueuedPresentationTimeUs = max(largestQueuedPresentationTimeUs, presentationTimeUs);
    buffer.flip();
    if (buffer.hasSupplementalData()) {
        handleInputBufferSupplementalData(buffer);
    }
    onQueueInputBuffer(buffer);
    try {
        if (bufferEncrypted) {
            codec.queueSecureInputBuffer(inputIndex, /* offset= */
            0, buffer.cryptoInfo, presentationTimeUs, /* flags= */
            0);
        } else {
            codec.queueInputBuffer(inputIndex, /* offset= */
            0, buffer.data.limit(), presentationTimeUs, /* flags= */
            0);
        }
    } catch (CryptoException e) {
        throw createRendererException(e, inputFormat, Util.getErrorCodeForMediaDrmErrorCode(e.getErrorCode()));
    }
    resetInputBuffer();
    codecReceivedBuffers = true;
    codecReconfigurationState = RECONFIGURATION_STATE_NONE;
    decoderCounters.queuedInputBufferCount++;
    return true;
}
Also used : FormatHolder(com.google.android.exoplayer2.FormatHolder) ReadDataResult(com.google.android.exoplayer2.source.SampleStream.ReadDataResult) InsufficientCapacityException(com.google.android.exoplayer2.decoder.DecoderInputBuffer.InsufficientCapacityException) MediaCryptoException(android.media.MediaCryptoException) CryptoException(android.media.MediaCodec.CryptoException)

Aggregations

Nullable (androidx.annotation.Nullable)2 CryptoException (com.google.android.exoplayer2.decoder.CryptoException)2 CryptoInfo (com.google.android.exoplayer2.decoder.CryptoInfo)2 ByteBuffer (java.nio.ByteBuffer)2 CryptoException (android.media.MediaCodec.CryptoException)1 MediaCryptoException (android.media.MediaCryptoException)1 FormatHolder (com.google.android.exoplayer2.FormatHolder)1 InsufficientCapacityException (com.google.android.exoplayer2.decoder.DecoderInputBuffer.InsufficientCapacityException)1 ReadDataResult (com.google.android.exoplayer2.source.SampleStream.ReadDataResult)1