From 7957554442e751dd9bbe08b46f9a08860180e026 Mon Sep 17 00:00:00 2001 From: rohks Date: Tue, 3 Oct 2023 09:02:40 -0700 Subject: [PATCH] Add nullness annotations to `MediaCodecRenderer` #fixit PiperOrigin-RevId: 570403923 (cherry picked from commit 7a91474af9f84595743655b18f4164e193bf2fc1) --- .../mediacodec/MediaCodecRenderer.java | 107 +++++++++++------- 1 file changed, 66 insertions(+), 41 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java index c749b49a1f..c8e0570ee2 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java @@ -17,6 +17,7 @@ package androidx.media3.exoplayer.mediacodec; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; +import static androidx.media3.common.util.Assertions.checkStateNotNull; import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_DRM_SESSION_CHANGED; import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_OPERATING_RATE_CHANGED; import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_REUSE_NOT_IMPLEMENTED; @@ -124,7 +125,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { private static final int DECODER_QUERY_ERROR = CUSTOM_ERROR_CODE_BASE + 2; /** The MIME type for which a decoder was being initialized. */ - public final String mimeType; + @Nullable public final String mimeType; /** Whether it was required that the decoder support a secure output path. */ public final boolean secureDecoderRequired; @@ -173,9 +174,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { } private DecoderInitializationException( - String message, + @Nullable String message, @Nullable Throwable cause, - String mimeType, + @Nullable String mimeType, boolean secureDecoderRequired, @Nullable MediaCodecInfo mediaCodecInfo, @Nullable String diagnosticInfo, @@ -440,7 +441,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { targetPlaybackSpeed = 1f; renderTimeLimitMs = C.TIME_UNSET; pendingOutputStreamChanges = new ArrayDeque<>(); - setOutputStreamInfo(OutputStreamInfo.UNSET); + outputStreamInfo = OutputStreamInfo.UNSET; // MediaCodec outputs audio buffers in native endian: // https://developer.android.com/reference/android/media/MediaCodec#raw-audio-buffers // and code called from MediaCodecAudioRenderer.processOutputBuffer expects this endianness. @@ -461,6 +462,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { lastProcessedOutputBufferTimeUs = C.TIME_UNSET; codecDrainState = DRAIN_STATE_NONE; codecDrainAction = DRAIN_ACTION_NONE; + decoderCounters = new DecoderCounters(); } /** @@ -544,7 +546,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer { setCodecDrmSession(sourceDrmSession); - String mimeType = inputFormat.sampleMimeType; + String mimeType = checkNotNull(inputFormat).sampleMimeType; + @Nullable DrmSession codecDrmSession = this.codecDrmSession; if (codecDrmSession != null) { @Nullable CryptoConfig cryptoConfig = codecDrmSession.getCryptoConfig(); if (mediaCrypto == null) { @@ -568,7 +571,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { } mediaCryptoRequiresSecureDecoder = !frameworkCryptoConfig.forceAllowInsecureDecoderComponents - && mediaCrypto.requiresSecureDecoderComponent(mimeType); + && mediaCrypto.requiresSecureDecoderComponent(checkStateNotNull(mimeType)); } } if (FrameworkCryptoConfig.WORKAROUND_DEVICE_NEEDS_KEYS_TO_CONFIGURE_CODEC @@ -681,7 +684,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { outputFormatChanged = true; } if (outputFormatChanged || (codecOutputMediaFormatChanged && outputFormat != null)) { - onOutputFormatChanged(outputFormat, codecOutputMediaFormat); + onOutputFormatChanged(checkNotNull(outputFormat), codecOutputMediaFormat); codecOutputMediaFormatChanged = false; needToNotifyOutputFormatChangeAfterStreamChange = false; } @@ -800,7 +803,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { if (codec != null) { codec.release(); decoderCounters.decoderReleaseCount++; - onCodecReleased(codecInfo.name); + onCodecReleased(checkNotNull(codecInfo).name); } } finally { codec = null; @@ -941,7 +944,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { /** Flushes the codec. */ private void flushCodec() { try { - codec.flush(); + checkStateNotNull(codec).flush(); } finally { resetCodecStateForFlush(); } @@ -1035,6 +1038,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { private void maybeInitCodecWithFallback( @Nullable MediaCrypto crypto, boolean mediaCryptoRequiresSecureDecoder) throws DecoderInitializationException { + Format inputFormat = checkNotNull(this.inputFormat); if (availableCodecInfos == null) { try { List allAvailableCodecInfos = @@ -1063,9 +1067,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer { DecoderInitializationException.NO_SUITABLE_DECODER_ERROR); } + ArrayDeque availableCodecInfos = checkNotNull(this.availableCodecInfos); MediaCodecInfo preferredCodecInfo = availableCodecInfos.peekFirst(); while (codec == null) { - MediaCodecInfo codecInfo = availableCodecInfos.peekFirst(); + MediaCodecInfo codecInfo = checkNotNull(availableCodecInfos.peekFirst()); if (!shouldInitCodec(codecInfo)) { return; } @@ -1106,11 +1111,12 @@ public abstract class MediaCodecRenderer extends BaseRenderer { } } - availableCodecInfos = null; + this.availableCodecInfos = null; } private List getAvailableCodecInfos(boolean mediaCryptoRequiresSecureDecoder) throws DecoderQueryException { + Format inputFormat = checkNotNull(this.inputFormat); List codecInfos = getDecoderInfos(mediaCodecSelector, inputFormat, mediaCryptoRequiresSecureDecoder); if (codecInfos.isEmpty() && mediaCryptoRequiresSecureDecoder) { @@ -1150,6 +1156,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { } private void initCodec(MediaCodecInfo codecInfo, @Nullable MediaCrypto crypto) throws Exception { + Format inputFormat = checkNotNull(this.inputFormat); long codecInitializingTimestamp; long codecInitializedTimestamp; String codecName = codecInfo.name; @@ -1188,17 +1195,17 @@ public abstract class MediaCodecRenderer extends BaseRenderer { codecInputFormat = inputFormat; codecAdaptationWorkaroundMode = codecAdaptationWorkaroundMode(codecName); codecNeedsDiscardToSpsWorkaround = - codecNeedsDiscardToSpsWorkaround(codecName, codecInputFormat); + codecNeedsDiscardToSpsWorkaround(codecName, checkNotNull(codecInputFormat)); codecNeedsFlushWorkaround = codecNeedsFlushWorkaround(codecName); codecNeedsSosFlushWorkaround = codecNeedsSosFlushWorkaround(codecName); codecNeedsEosFlushWorkaround = codecNeedsEosFlushWorkaround(codecName); codecNeedsEosOutputExceptionWorkaround = codecNeedsEosOutputExceptionWorkaround(codecName); codecNeedsEosBufferTimestampWorkaround = codecNeedsEosBufferTimestampWorkaround(codecName); codecNeedsMonoChannelCountWorkaround = - codecNeedsMonoChannelCountWorkaround(codecName, codecInputFormat); + codecNeedsMonoChannelCountWorkaround(codecName, checkNotNull(codecInputFormat)); codecNeedsEosPropagation = codecNeedsEosPropagationWorkaround(codecInfo) || getCodecNeedsEosPropagation(); - if (codec.needsReconfiguration()) { + if (checkNotNull(codec).needsReconfiguration()) { this.codecReconfigured = true; this.codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING; this.codecNeedsAdaptationWorkaroundBuffer = @@ -1255,6 +1262,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { drainAndReinitializeCodec(); } + MediaCodecAdapter codec = checkNotNull(this.codec); if (inputIndex < 0) { inputIndex = codec.dequeueInputBufferIndex(); if (inputIndex < 0) { @@ -1280,7 +1288,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { if (codecNeedsAdaptationWorkaroundBuffer) { codecNeedsAdaptationWorkaroundBuffer = false; - buffer.data.put(ADAPTATION_WORKAROUND_BUFFER); + checkNotNull(buffer.data).put(ADAPTATION_WORKAROUND_BUFFER); codec.queueInputBuffer(inputIndex, 0, ADAPTATION_WORKAROUND_BUFFER.length, 0, 0); resetInputBuffer(); codecReceivedBuffers = true; @@ -1290,13 +1298,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer { // For adaptive reconfiguration, decoders expect all reconfiguration data to be supplied at // 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++) { + for (int i = 0; i < checkNotNull(codecInputFormat).initializationData.size(); i++) { byte[] data = codecInputFormat.initializationData.get(i); - buffer.data.put(data); + checkNotNull(buffer.data).put(data); } codecReconfigurationState = RECONFIGURATION_STATE_QUEUE_PENDING; } - int adaptiveReconfigurationBytes = buffer.data.position(); + int adaptiveReconfigurationBytes = checkNotNull(buffer.data).position(); FormatHolder formatHolder = getFormatHolder(); @@ -1386,8 +1394,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer { buffer.cryptoInfo.increaseClearDataFirstSubSampleBy(adaptiveReconfigurationBytes); } if (codecNeedsDiscardToSpsWorkaround && !bufferEncrypted) { - NalUnitUtil.discardToSps(buffer.data); - if (buffer.data.position() == 0) { + NalUnitUtil.discardToSps(checkNotNull(buffer.data)); + if (checkNotNull(buffer.data).position() == 0) { return true; } codecNeedsDiscardToSpsWorkaround = false; @@ -1397,9 +1405,12 @@ public abstract class MediaCodecRenderer extends BaseRenderer { if (waitingForFirstSampleInFormat) { if (!pendingOutputStreamChanges.isEmpty()) { - pendingOutputStreamChanges.peekLast().formatQueue.add(presentationTimeUs, inputFormat); + pendingOutputStreamChanges + .peekLast() + .formatQueue + .add(presentationTimeUs, checkNotNull(inputFormat)); } else { - outputStreamInfo.formatQueue.add(presentationTimeUs, inputFormat); + outputStreamInfo.formatQueue.add(presentationTimeUs, checkNotNull(inputFormat)); } waitingForFirstSampleInFormat = false; } @@ -1416,11 +1427,17 @@ public abstract class MediaCodecRenderer extends BaseRenderer { onQueueInputBuffer(buffer); try { if (bufferEncrypted) { - codec.queueSecureInputBuffer( - inputIndex, /* offset= */ 0, buffer.cryptoInfo, presentationTimeUs, /* flags= */ 0); + checkNotNull(codec) + .queueSecureInputBuffer( + inputIndex, /* offset= */ 0, buffer.cryptoInfo, presentationTimeUs, /* flags= */ 0); } else { - codec.queueInputBuffer( - inputIndex, /* offset= */ 0, buffer.data.limit(), presentationTimeUs, /* flags= */ 0); + checkNotNull(codec) + .queueInputBuffer( + inputIndex, + /* offset= */ 0, + checkNotNull(buffer.data).limit(), + presentationTimeUs, + /* flags= */ 0); } } catch (CryptoException e) { throw createRendererException( @@ -1535,9 +1552,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { // Copy the current codec and codecInfo to local variables so they remain accessible if the // member variables are updated during the logic below. MediaCodecAdapter codec = this.codec; - MediaCodecInfo codecInfo = this.codecInfo; + MediaCodecInfo codecInfo = checkNotNull(this.codecInfo); - Format oldFormat = codecInputFormat; + Format oldFormat = checkNotNull(codecInputFormat); if (drmNeedsCodecReinitialization(codecInfo, newFormat, codecDrmSession, sourceDrmSession)) { drainAndReinitializeCodec(); return new DecoderReuseEvaluation( @@ -1663,7 +1680,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { lastProcessedOutputBufferTimeUs = presentationTimeUs; while (!pendingOutputStreamChanges.isEmpty() && presentationTimeUs >= pendingOutputStreamChanges.peek().previousStreamLastBufferTimeUs) { - setOutputStreamInfo(pendingOutputStreamChanges.poll()); + setOutputStreamInfo(checkNotNull(pendingOutputStreamChanges.poll())); onProcessedStreamChange(); } } @@ -1767,7 +1784,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { * @throws ExoPlaybackException If an error occurs releasing or initializing a codec. * @return False if codec release and re-initialization was triggered. True in all other cases. */ - private boolean updateCodecOperatingRate(Format format) throws ExoPlaybackException { + private boolean updateCodecOperatingRate(@Nullable Format format) throws ExoPlaybackException { if (Util.SDK_INT < 23) { return true; } @@ -1780,7 +1797,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { } float newCodecOperatingRate = - getCodecOperatingRateV23(targetPlaybackSpeed, format, getStreamFormats()); + getCodecOperatingRateV23(targetPlaybackSpeed, checkNotNull(format), getStreamFormats()); if (codecOperatingRate == newCodecOperatingRate) { // No change. return true; @@ -1795,7 +1812,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { // above the assumed minimum rate. Bundle codecParameters = new Bundle(); codecParameters.putFloat(MediaFormat.KEY_OPERATING_RATE, newCodecOperatingRate); - codec.setParameters(codecParameters); + checkNotNull(codec).setParameters(codecParameters); codecOperatingRate = newCodecOperatingRate; return true; } @@ -1871,6 +1888,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { */ private boolean drainOutputBuffer(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { + MediaCodecAdapter codec = checkNotNull(this.codec); if (!hasOutputBuffer()) { int outputIndex; if (codecNeedsEosOutputExceptionWorkaround && codecReceivedEos) { @@ -1950,7 +1968,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { outputBufferInfo.presentationTimeUs, isDecodeOnlyOutputBuffer, isLastOutputBuffer, - outputFormat); + checkNotNull(outputFormat)); } catch (IllegalStateException e) { processEndOfStream(); if (outputStreamEnded) { @@ -1972,7 +1990,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { outputBufferInfo.presentationTimeUs, isDecodeOnlyOutputBuffer, isLastOutputBuffer, - outputFormat); + checkNotNull(outputFormat)); } if (processedOutputBuffer) { @@ -1991,7 +2009,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { /** Processes a change in the decoder output {@link MediaFormat}. */ private void processOutputMediaFormatChanged() { codecHasOutputMediaFormat = true; - MediaFormat mediaFormat = codec.getOutputFormat(); + MediaFormat mediaFormat = checkNotNull(codec).getOutputFormat(); if (codecAdaptationWorkaroundMode != ADAPTATION_WORKAROUND_MODE_NEVER && mediaFormat.getInteger(MediaFormat.KEY_WIDTH) == ADAPTATION_WORKAROUND_SLICE_WIDTH_HEIGHT && mediaFormat.getInteger(MediaFormat.KEY_HEIGHT) @@ -2209,7 +2227,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer { if (newFrameworkCryptoConfig.forceAllowInsecureDecoderComponents) { requiresSecureDecoder = false; } else { - requiresSecureDecoder = newSession.requiresSecureDecoder(newFormat.sampleMimeType); + requiresSecureDecoder = + newSession.requiresSecureDecoder(checkNotNull(newFormat.sampleMimeType)); } if (!codecInfo.secure && requiresSecureDecoder) { // Re-initialization is required because newSession might require switching to the secure @@ -2227,10 +2246,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer { @RequiresApi(23) private void updateDrmSessionV23() throws ExoPlaybackException { - CryptoConfig cryptoConfig = sourceDrmSession.getCryptoConfig(); + CryptoConfig cryptoConfig = checkNotNull(sourceDrmSession).getCryptoConfig(); if (cryptoConfig instanceof FrameworkCryptoConfig) { try { - mediaCrypto.setMediaDrmSession(((FrameworkCryptoConfig) cryptoConfig).sessionId); + checkNotNull(mediaCrypto) + .setMediaDrmSession(((FrameworkCryptoConfig) cryptoConfig).sessionId); } catch (MediaCryptoException e) { throw createRendererException( e, inputFormat, PlaybackException.ERROR_CODE_DRM_SYSTEM_ERROR); @@ -2271,7 +2291,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { bypassBatchBuffer.getFirstSampleTimeUs(), isDecodeOnly, bypassBatchBuffer.isEndOfStream(), - outputFormat)) { + checkNotNull(outputFormat))) { // The batch buffer has been fully processed. onProcessedOutputBuffer(bypassBatchBuffer.getLastSampleTimeUs()); bypassBatchBuffer.clear(); @@ -2347,7 +2367,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer { // TODO(b/298634018): Adjust encoderDelay value based on starting position. int numberPreSkipSamples = OpusUtil.getPreSkipSamples(outputFormat.initializationData.get(0)); - outputFormat = outputFormat.buildUpon().setEncoderDelay(numberPreSkipSamples).build(); + outputFormat = + checkNotNull(outputFormat) + .buildUpon() + .setEncoderDelay(numberPreSkipSamples) + .build(); } onOutputFormatChanged(outputFormat, /* mediaFormat= */ null); waitingForFirstSampleInFormat = false; @@ -2362,7 +2386,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer { bypassSampleBuffer.format = outputFormat; handleInputBufferSupplementalData(bypassSampleBuffer); } - oggOpusAudioPacketizer.packetize(bypassSampleBuffer, outputFormat.initializationData); + oggOpusAudioPacketizer.packetize( + bypassSampleBuffer, checkNotNull(outputFormat).initializationData); } if (!haveBypassBatchBufferAndNewSampleSameDecodeOnlyState() || !bypassBatchBuffer.append(bypassSampleBuffer)) {