From bb9ff30c3af02f40c8249358bc3c6896c2d87863 Mon Sep 17 00:00:00 2001 From: ibaker Date: Mon, 15 Jul 2024 08:48:51 -0700 Subject: [PATCH] Remove dead code related to `MediaCodec` now minSdk is 21 This removes several workarounds that are no longer needed, including `codecNeedsMonoChannelCountWorkaround` which has been permanently disabled since the (incomplete) minSdk 19 clean-up in https://github.com/androidx/media/commit/fb7438378d8fd057d57263692cd111c70cfdb1cc. PiperOrigin-RevId: 652495578 --- api.txt | 2 +- .../androidx/media3/common/VideoSize.java | 37 +++-- .../androidx/media3/common/VideoSizeTest.java | 1 + .../mediacodec/MediaCodecRenderer.java | 137 +----------------- .../video/MediaCodecVideoRenderer.java | 121 ++++------------ .../video/MediaCodecVideoRendererTest.java | 1 + .../session/MediaControllerListenerTest.java | 2 +- .../media3/session/MediaControllerTest.java | 1 + .../session/MediaSessionProviderService.java | 2 +- .../media3/session/MediaTestUtils.java | 10 +- .../media3/test/utils/FakeVideoRenderer.java | 6 +- 11 files changed, 68 insertions(+), 252 deletions(-) diff --git a/api.txt b/api.txt index 911f5b4e5b..946ed8a3d1 100644 --- a/api.txt +++ b/api.txt @@ -1192,7 +1192,7 @@ package androidx.media3.common { field public static final androidx.media3.common.VideoSize UNKNOWN; field @IntRange(from=0) public final int height; field @FloatRange(from=0, fromInclusive=false) public final float pixelWidthHeightRatio; - field @IntRange(from=0, to=359) public final int unappliedRotationDegrees; + field @Deprecated @IntRange(from=0, to=359) public final int unappliedRotationDegrees; field @IntRange(from=0) public final int width; } diff --git a/libraries/common/src/main/java/androidx/media3/common/VideoSize.java b/libraries/common/src/main/java/androidx/media3/common/VideoSize.java index 91a25d0a73..502e77e231 100644 --- a/libraries/common/src/main/java/androidx/media3/common/VideoSize.java +++ b/libraries/common/src/main/java/androidx/media3/common/VideoSize.java @@ -41,19 +41,10 @@ public final class VideoSize { public final int height; /** - * Clockwise rotation in degrees that the application should apply for the video for it to be - * rendered in the correct orientation. - * - *

Is 0 if unknown or if no rotation is needed. - * - *

Player should apply video rotation internally, in which case unappliedRotationDegrees is 0. - * But when a player can't apply the rotation, for example before API level 21, the unapplied - * rotation is reported by this field for application to handle. - * - *

Applications that use {@link android.view.TextureView} can apply the rotation by calling - * {@link android.view.TextureView#setTransform}. + * @deprecated Rotation is handled internally by the player, so this is always zero. */ @IntRange(from = 0, to = 359) + @Deprecated public final int unappliedRotationDegrees; /** @@ -73,7 +64,7 @@ public final class VideoSize { */ @UnstableApi public VideoSize(@IntRange(from = 0) int width, @IntRange(from = 0) int height) { - this(width, height, DEFAULT_UNAPPLIED_ROTATION_DEGREES, DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO); + this(width, height, DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO); } /** @@ -81,13 +72,25 @@ public final class VideoSize { * * @param width The video width in pixels. * @param height The video height in pixels. - * @param unappliedRotationDegrees Clockwise rotation in degrees that the application should apply - * for the video for it to be rendered in the correct orientation. See {@link - * #unappliedRotationDegrees}. * @param pixelWidthHeightRatio The width to height ratio of each pixel. For the normal case of * square pixels this will be equal to 1.0. Different values are indicative of anamorphic * content. */ + @SuppressWarnings("deprecation") // Calling through to deprecated constructor + @UnstableApi + public VideoSize( + @IntRange(from = 0) int width, + @IntRange(from = 0) int height, + @FloatRange(from = 0, fromInclusive = false) float pixelWidthHeightRatio) { + this(width, height, DEFAULT_UNAPPLIED_ROTATION_DEGREES, pixelWidthHeightRatio); + } + + /** + * @deprecated Use {@link VideoSize#VideoSize(int, int, float)} instead. {@code + * unappliedRotationDegrees} is not needed on API 21+. + */ + @SuppressWarnings("deprecation") // Setting deprecate field + @Deprecated @UnstableApi public VideoSize( @IntRange(from = 0) int width, @@ -100,6 +103,7 @@ public final class VideoSize { this.pixelWidthHeightRatio = pixelWidthHeightRatio; } + @SuppressWarnings("deprecation") // Including deprecated field in equality @Override public boolean equals(@Nullable Object obj) { if (this == obj) { @@ -115,6 +119,7 @@ public final class VideoSize { return false; } + @SuppressWarnings("deprecation") // Including deprecated field in hashCode @Override public int hashCode() { int result = 7; @@ -130,6 +135,7 @@ public final class VideoSize { private static final String FIELD_UNAPPLIED_ROTATION_DEGREES = Util.intToStringMaxRadix(2); private static final String FIELD_PIXEL_WIDTH_HEIGHT_RATIO = Util.intToStringMaxRadix(3); + @SuppressWarnings("deprecation") // Including deprecated field in bundle @UnstableApi public Bundle toBundle() { Bundle bundle = new Bundle(); @@ -141,6 +147,7 @@ public final class VideoSize { } /** Restores a {@code VideoSize} from a {@link Bundle}. */ + @SuppressWarnings("deprecation") // Parsing deprecated field from bundle @UnstableApi public static VideoSize fromBundle(Bundle bundle) { int width = bundle.getInt(FIELD_WIDTH, DEFAULT_WIDTH); diff --git a/libraries/common/src/test/java/androidx/media3/common/VideoSizeTest.java b/libraries/common/src/test/java/androidx/media3/common/VideoSizeTest.java index ad0e31bc27..213ad58e20 100644 --- a/libraries/common/src/test/java/androidx/media3/common/VideoSizeTest.java +++ b/libraries/common/src/test/java/androidx/media3/common/VideoSizeTest.java @@ -32,6 +32,7 @@ public final class VideoSizeTest { } @Test + @SuppressWarnings("deprecation") // Testing bundling of deprecated field. public void roundTripViaBundle_ofArbitraryVideoSize_yieldsEqualInstance() { VideoSize videoSize = new VideoSize( 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 c53a02f0c3..f7d8558d8b 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 @@ -58,7 +58,6 @@ import androidx.media3.common.util.TimedValueQueue; import androidx.media3.common.util.TraceUtil; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; -import androidx.media3.container.NalUnitUtil; import androidx.media3.decoder.CryptoConfig; import androidx.media3.decoder.DecoderInputBuffer; import androidx.media3.decoder.DecoderInputBuffer.InsufficientCapacityException; @@ -170,7 +169,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { format.sampleMimeType, secureDecoderRequired, mediaCodecInfo, - Util.SDK_INT >= 21 ? getDiagnosticInfoV21(cause) : null, + (cause instanceof CodecException) ? ((CodecException) cause).getDiagnosticInfo() : null, /* fallbackDecoderInitializationException= */ null); } @@ -203,15 +202,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer { fallbackException); } - @RequiresApi(21) - @Nullable - private static String getDiagnosticInfoV21(@Nullable Throwable cause) { - if (cause instanceof CodecException) { - return ((CodecException) cause).getDiagnosticInfo(); - } - return null; - } - private static String buildCustomDiagnosticInfo(int errorCode) { String sign = errorCode < 0 ? "neg_" : ""; String packageName = "androidx.media3.exoplayer.mediacodec"; @@ -373,13 +363,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { @Nullable private DecoderInitializationException preferredDecoderInitializationException; @Nullable private MediaCodecInfo codecInfo; private @AdaptationWorkaroundMode int codecAdaptationWorkaroundMode; - private boolean codecNeedsDiscardToSpsWorkaround; - private boolean codecNeedsFlushWorkaround; private boolean codecNeedsSosFlushWorkaround; private boolean codecNeedsEosFlushWorkaround; private boolean codecNeedsEosOutputExceptionWorkaround; - private boolean codecNeedsEosBufferTimestampWorkaround; - private boolean codecNeedsMonoChannelCountWorkaround; private boolean codecNeedsAdaptationWorkaroundBuffer; private boolean shouldSkipAdaptationWorkaroundOutputBuffer; private boolean codecNeedsEosPropagation; @@ -898,9 +884,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer { } decoderCounters.ensureUpdated(); } catch (IllegalStateException e) { - if (isMediaCodecException(e)) { + if (e instanceof CodecException) { onCodecError(e); - boolean isRecoverable = Util.SDK_INT >= 21 && isRecoverableMediaCodecExceptionV21(e); + boolean isRecoverable = + (e instanceof CodecException) && ((CodecException) e).isRecoverable(); if (isRecoverable) { releaseCodec(); } @@ -945,7 +932,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer { return false; } if (codecDrainAction == DRAIN_ACTION_REINITIALIZE - || codecNeedsFlushWorkaround || (codecNeedsSosFlushWorkaround && !codecHasOutputMediaFormat) || (codecNeedsEosFlushWorkaround && codecReceivedEos)) { releaseCodec(); @@ -1020,13 +1006,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { codecHasOutputMediaFormat = false; codecOperatingRate = CODEC_OPERATING_RATE_UNSET; codecAdaptationWorkaroundMode = ADAPTATION_WORKAROUND_MODE_NEVER; - codecNeedsDiscardToSpsWorkaround = false; - codecNeedsFlushWorkaround = false; codecNeedsSosFlushWorkaround = false; codecNeedsEosFlushWorkaround = false; codecNeedsEosOutputExceptionWorkaround = false; - codecNeedsEosBufferTimestampWorkaround = false; - codecNeedsMonoChannelCountWorkaround = false; codecNeedsEosPropagation = false; codecRegisteredOnBufferAvailableListener = false; codecReconfigured = false; @@ -1238,9 +1220,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { TraceUtil.beginSection("createCodec:" + codecName); codec = codecAdapterFactory.createAdapter(configuration); codecRegisteredOnBufferAvailableListener = - Util.SDK_INT >= 21 - && Api21.registerOnBufferAvailableListener( - codec, new MediaCodecRendererCodecAdapterListener()); + codec.registerOnBufferAvailableListener(new MediaCodecRendererCodecAdapterListener()); } finally { TraceUtil.endSection(); } @@ -1258,14 +1238,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { this.codecOperatingRate = codecOperatingRate; codecInputFormat = inputFormat; codecAdaptationWorkaroundMode = codecAdaptationWorkaroundMode(codecName); - codecNeedsDiscardToSpsWorkaround = - codecNeedsDiscardToSpsWorkaround(codecName, checkNotNull(codecInputFormat)); - codecNeedsFlushWorkaround = codecNeedsFlushWorkaround(codecName); codecNeedsSosFlushWorkaround = codecNeedsSosFlushWorkaround(codecName); codecNeedsEosFlushWorkaround = codecNeedsEosFlushWorkaround(codecName); codecNeedsEosOutputExceptionWorkaround = codecNeedsEosOutputExceptionWorkaround(codecName); - codecNeedsEosBufferTimestampWorkaround = codecNeedsEosBufferTimestampWorkaround(codecName); - codecNeedsMonoChannelCountWorkaround = false; codecNeedsEosPropagation = codecNeedsEosPropagationWorkaround(codecInfo) || getCodecNeedsEosPropagation(); if (checkNotNull(codec).needsReconfiguration()) { @@ -1462,13 +1437,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer { if (bufferEncrypted) { buffer.cryptoInfo.increaseClearDataFirstSubSampleBy(adaptiveReconfigurationBytes); } - if (codecNeedsDiscardToSpsWorkaround && !bufferEncrypted) { - NalUnitUtil.discardToSps(checkNotNull(buffer.data)); - if (checkNotNull(buffer.data).position() == 0) { - return true; - } - codecNeedsDiscardToSpsWorkaround = false; - } long presentationTimeUs = buffer.timeUs; @@ -1950,7 +1918,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { private boolean drainAndFlushCodec() { if (codecReceivedBuffers) { codecDrainState = DRAIN_STATE_SIGNAL_END_OF_STREAM; - if (codecNeedsFlushWorkaround || codecNeedsEosFlushWorkaround) { + if (codecNeedsEosFlushWorkaround) { codecDrainAction = DRAIN_ACTION_REINITIALIZE; return false; } else { @@ -1973,7 +1941,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { private boolean drainAndUpdateCodecDrmSessionV23() throws ExoPlaybackException { if (codecReceivedBuffers) { codecDrainState = DRAIN_STATE_SIGNAL_END_OF_STREAM; - if (codecNeedsFlushWorkaround || codecNeedsEosFlushWorkaround) { + if (codecNeedsEosFlushWorkaround) { codecDrainAction = DRAIN_ACTION_REINITIALIZE; return false; } else { @@ -2060,12 +2028,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer { outputBuffer.position(outputBufferInfo.offset); outputBuffer.limit(outputBufferInfo.offset + outputBufferInfo.size); } - if (codecNeedsEosBufferTimestampWorkaround - && outputBufferInfo.presentationTimeUs == 0 - && (outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0 - && largestQueuedPresentationTimeUs != C.TIME_UNSET) { - outputBufferInfo.presentationTimeUs = lastBufferInStreamPresentationTimeUs; - } isDecodeOnlyOutputBuffer = outputBufferInfo.presentationTimeUs < getLastResetPositionUs(); isLastOutputBuffer = lastBufferInStreamPresentationTimeUs != C.TIME_UNSET @@ -2138,9 +2100,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer { shouldSkipAdaptationWorkaroundOutputBuffer = true; return; } - if (codecNeedsMonoChannelCountWorkaround) { - mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1); - } codecOutputMediaFormat = mediaFormat; codecOutputMediaFormatChanged = true; } @@ -2557,44 +2516,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer { /* startTimeUs= */ startTimeUs, /* frameTimeUs= */ frameTimeUs)); } - private static boolean isMediaCodecException(IllegalStateException error) { - if (Util.SDK_INT >= 21 && isMediaCodecExceptionV21(error)) { - return true; - } - StackTraceElement[] stackTrace = error.getStackTrace(); - return stackTrace.length > 0 && stackTrace[0].getClassName().equals("android.media.MediaCodec"); - } - - @RequiresApi(21) - private static boolean isMediaCodecExceptionV21(IllegalStateException error) { - return error instanceof MediaCodec.CodecException; - } - - @RequiresApi(21) - private static boolean isRecoverableMediaCodecExceptionV21(IllegalStateException error) { - if (error instanceof MediaCodec.CodecException) { - return ((MediaCodec.CodecException) error).isRecoverable(); - } - return false; - } - - /** - * Returns whether the decoder is known to fail when flushed. - * - *

If true is returned, the renderer will work around the issue by releasing the decoder and - * instantiating a new one rather than flushing the current instance. - * - *

See [Internal: b/8347958, b/8543366]. - * - * @param name The name of the decoder. - * @return True if the decoder is known to fail when flushed. - */ - private static boolean codecNeedsFlushWorkaround(String name) { - return Util.SDK_INT == 19 - && Util.MODEL.startsWith("SM-G800") - && ("OMX.Exynos.avc.dec".equals(name) || "OMX.Exynos.avc.dec.secure".equals(name)); - } - /** * Returns a mode that specifies when the adaptation workaround should be enabled. * @@ -2628,23 +2549,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer { } } - /** - * Returns whether the decoder is an H.264/AVC decoder known to fail if NAL units are queued - * before the codec specific data. - * - *

If true is returned, the renderer will work around the issue by discarding data up to the - * SPS. - * - * @param name The name of the decoder. - * @param format The {@link Format} used to configure the decoder. - * @return True if the decoder is known to fail if NAL units are queued before CSD. - */ - private static boolean codecNeedsDiscardToSpsWorkaround(String name, Format format) { - return Util.SDK_INT < 21 - && format.initializationData.isEmpty() - && "OMX.MTK.VIDEO.DECODER.AVC".equals(name); - } - /** * Returns whether the decoder is known to behave incorrectly if flushed prior to having output a * {@link MediaFormat}. @@ -2708,24 +2612,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer { || "OMX.amlogic.avc.decoder.awesome.secure".equals(name))); } - /** - * Returns whether the decoder may output a non-empty buffer with timestamp 0 as the end of stream - * buffer. - * - *

See GitHub issue #5045. - */ - private static boolean codecNeedsEosBufferTimestampWorkaround(String codecName) { - return Util.SDK_INT < 21 - && "OMX.SEC.mp3.dec".equals(codecName) - && "samsung".equals(Util.MANUFACTURER) - && (Util.DEVICE.startsWith("baffin") - || Util.DEVICE.startsWith("grand") - || Util.DEVICE.startsWith("fortuna") - || Util.DEVICE.startsWith("gprimelte") - || Util.DEVICE.startsWith("j2y18lte") - || Util.DEVICE.startsWith("ms01")); - } - /** * Returns whether the decoder may throw an {@link IllegalStateException} from {@link * MediaCodec#dequeueOutputBuffer(MediaCodec.BufferInfo, long)} or {@link @@ -2763,15 +2649,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer { } } - @RequiresApi(21) - private static final class Api21 { - @DoNotInline - public static boolean registerOnBufferAvailableListener( - MediaCodecAdapter codec, MediaCodecRendererCodecAdapterListener listener) { - return codec.registerOnBufferAvailableListener(listener); - } - } - @RequiresApi(31) private static final class Api31 { private Api31() {} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java index 0890ec10ab..fb02c89f30 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java @@ -1260,7 +1260,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer } int width; int height; - int unappliedRotationDegrees = 0; float pixelWidthHeightRatio; if (tunneling) { @@ -1283,22 +1282,16 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer : mediaFormat.getInteger(MediaFormat.KEY_HEIGHT); } pixelWidthHeightRatio = format.pixelWidthHeightRatio; - if (codecAppliesRotation()) { - // On API level 21 and above the decoder applies the rotation when rendering to the surface. - // Hence currentUnappliedRotation should always be 0. For 90 and 270 degree rotations, we need - // to flip the width, height and pixel aspect ratio to reflect the rotation that was applied. - if (format.rotationDegrees == 90 || format.rotationDegrees == 270) { - int rotatedHeight = width; - width = height; - height = rotatedHeight; - pixelWidthHeightRatio = 1 / pixelWidthHeightRatio; - } - } else if (videoSink == null) { - // Neither the codec nor the video sink applies the rotation. - unappliedRotationDegrees = format.rotationDegrees; + // The decoder applies the rotation when rendering to the surface. For 90 and 270 degree + // rotations, we need to flip the width, height and pixel aspect ratio to reflect the rotation + // that was applied. + if (format.rotationDegrees == 90 || format.rotationDegrees == 270) { + int rotatedHeight = width; + width = height; + height = rotatedHeight; + pixelWidthHeightRatio = 1 / pixelWidthHeightRatio; } - decodedVideoSize = - new VideoSize(width, height, unappliedRotationDegrees, pixelWidthHeightRatio); + decodedVideoSize = new VideoSize(width, height, pixelWidthHeightRatio); if (videoSink != null && videoSinkNeedsRegisterInputStream) { onReadyToRegisterVideoSinkInputStream(); @@ -1308,7 +1301,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer .buildUpon() .setWidth(width) .setHeight(height) - .setRotationDegrees(unappliedRotationDegrees) .setPixelWidthHeightRatio(pixelWidthHeightRatio) .build()); } else { @@ -1455,7 +1447,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer case VideoFrameReleaseControl.FRAME_RELEASE_TRY_AGAIN_LATER: return false; case VideoFrameReleaseControl.FRAME_RELEASE_SCHEDULED: - return maybeReleaseFrame(checkStateNotNull(codec), bufferIndex, presentationTimeUs, format); + releaseFrame(checkStateNotNull(codec), bufferIndex, presentationTimeUs, format); + return true; default: throw new IllegalStateException(String.valueOf(frameReleaseAction)); } @@ -1469,46 +1462,22 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer return -startPositionUs; } - private boolean maybeReleaseFrame( + private void releaseFrame( MediaCodecAdapter codec, int bufferIndex, long presentationTimeUs, Format format) { long releaseTimeNs = videoFrameReleaseInfo.getReleaseTimeNs(); long earlyUs = videoFrameReleaseInfo.getEarlyUs(); - if (Util.SDK_INT >= 21) { - // Let the underlying framework time the release. - if (shouldSkipBuffersWithIdenticalReleaseTime() && releaseTimeNs == lastFrameReleaseTimeNs) { - // This frame should be displayed on the same vsync with the previous released frame. We - // are likely rendering frames at a rate higher than the screen refresh rate. Skip - // this buffer so that it's returned to MediaCodec sooner otherwise MediaCodec may not - // be able to keep decoding with this rate [b/263454203]. - skipOutputBuffer(codec, bufferIndex, presentationTimeUs); - } else { - notifyFrameMetadataListener(presentationTimeUs, releaseTimeNs, format); - renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, releaseTimeNs); - } - updateVideoFrameProcessingOffsetCounters(earlyUs); - lastFrameReleaseTimeNs = releaseTimeNs; - return true; - } else if (earlyUs < 30000) { - // We need to time the release ourselves. - if (earlyUs > 11000) { - // We're a little too early to render the frame. Sleep until the frame can be rendered. - // Note: The 11ms threshold was chosen fairly arbitrarily. - try { - // Subtracting 10000 rather than 11000 ensures the sleep time will be at least 1ms. - Thread.sleep((earlyUs - 10000) / 1000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return false; - } - } - notifyFrameMetadataListener(presentationTimeUs, releaseTimeNs, format); - renderOutputBuffer(codec, bufferIndex, presentationTimeUs); - updateVideoFrameProcessingOffsetCounters(earlyUs); - return true; + if (shouldSkipBuffersWithIdenticalReleaseTime() && releaseTimeNs == lastFrameReleaseTimeNs) { + // This frame should be displayed on the same vsync with the previous released frame. We + // are likely rendering frames at a rate higher than the screen refresh rate. Skip + // this buffer so that it's returned to MediaCodec sooner otherwise MediaCodec may not + // be able to keep decoding with this rate [b/263454203]. + skipOutputBuffer(codec, bufferIndex, presentationTimeUs); } else { - // Too soon. - return false; + notifyFrameMetadataListener(presentationTimeUs, releaseTimeNs, format); + renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, releaseTimeNs); } + updateVideoFrameProcessingOffsetCounters(earlyUs); + lastFrameReleaseTimeNs = releaseTimeNs; } private void notifyFrameMetadataListener( @@ -1715,21 +1684,14 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer */ private void renderOutputBuffer( MediaCodecAdapter codec, int index, long presentationTimeUs, long releaseTimeNs) { - if (Util.SDK_INT >= 21) { - renderOutputBufferV21(codec, index, presentationTimeUs, releaseTimeNs); - } else { - renderOutputBuffer(codec, index, presentationTimeUs); - } + renderOutputBufferV21(codec, index, presentationTimeUs, releaseTimeNs); } /** - * Renders the output buffer with the specified index. This method is only called if the platform - * API version of the device is less than 21. - * - * @param codec The codec that owns the output buffer. - * @param index The index of the output buffer to drop. - * @param presentationTimeUs The presentation time of the output buffer, in microseconds. + * @deprecated Override {@link #renderOutputBufferV21} instead. The library has min SDK 21, so + * this method is never called. */ + @Deprecated protected void renderOutputBuffer(MediaCodecAdapter codec, int index, long presentationTimeUs) { TraceUtil.beginSection("releaseOutputBuffer"); codec.releaseOutputBuffer(index, true); @@ -1751,7 +1713,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer * @param presentationTimeUs The presentation time of the output buffer, in microseconds. * @param releaseTimeNs The wallclock time at which the frame should be displayed, in nanoseconds. */ - @RequiresApi(21) protected void renderOutputBufferV21( MediaCodecAdapter codec, int index, long presentationTimeUs, long releaseTimeNs) { TraceUtil.beginSection("releaseOutputBuffer"); @@ -1903,12 +1864,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer codec.setOutputSurface(surface); } - @RequiresApi(21) - private static void configureTunnelingV21(MediaFormat mediaFormat, int tunnelingAudioSessionId) { - mediaFormat.setFeatureEnabled(CodecCapabilities.FEATURE_TunneledPlayback, true); - mediaFormat.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, tunnelingAudioSessionId); - } - /** * Returns the framework {@link MediaFormat} that should be used to configure the decoder. * @@ -1924,7 +1879,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer * @return The framework {@link MediaFormat} that should be used to configure the decoder. */ @SuppressLint("InlinedApi") - @TargetApi(21) // tunnelingAudioSessionId is unset if Util.SDK_INT < 21 protected MediaFormat getMediaFormat( Format format, String codecMimeType, @@ -1968,7 +1922,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer mediaFormat.setInteger("auto-frc", 0); } if (tunnelingAudioSessionId != C.AUDIO_SESSION_ID_UNSET) { - configureTunnelingV21(mediaFormat, tunnelingAudioSessionId); + mediaFormat.setFeatureEnabled(CodecCapabilities.FEATURE_TunneledPlayback, true); + mediaFormat.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, tunnelingAudioSessionId); } if (Util.SDK_INT >= 35) { mediaFormat.setInteger(MediaFormat.KEY_IMPORTANCE, max(0, -rendererPriority)); @@ -2066,7 +2021,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer if (longEdgePx <= formatLongEdgePx || shortEdgePx <= formatShortEdgePx) { // Don't return a size not larger than the format for which the codec is being configured. return null; - } else if (Util.SDK_INT >= 21) { + } else { Point alignedSize = codecInfo.alignVideoSizeV21( isVerticalVideo ? shortEdgePx : longEdgePx, @@ -2076,20 +2031,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer && codecInfo.isVideoSizeAndRateSupportedV21(alignedSize.x, alignedSize.y, frameRate)) { return alignedSize; } - } else { - try { - // Conservatively assume the codec requires 16px width and height alignment. - longEdgePx = Util.ceilDivide(longEdgePx, 16) * 16; - shortEdgePx = Util.ceilDivide(shortEdgePx, 16) * 16; - if (longEdgePx * shortEdgePx <= MediaCodecUtil.maxH264DecodableFrameSize()) { - return new Point( - isVerticalVideo ? shortEdgePx : longEdgePx, - isVerticalVideo ? longEdgePx : shortEdgePx); - } - } catch (DecoderQueryException e) { - // We tried our best. Give up! - return null; - } } } return null; @@ -2118,10 +2059,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer } } - private static boolean codecAppliesRotation() { - return Util.SDK_INT >= 21; - } - /** * Returns whether the device is known to do post processing by default that isn't compatible with * ExoPlayer. diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java index 12d8fee2ab..2c330bbe6a 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java @@ -636,6 +636,7 @@ public class MediaCodecVideoRendererTest { } @Test + @SuppressWarnings("deprecation") // Testing propagation of deprecated unappliedRotationDegrees. public void render_sendsVideoSizeChangeWithCurrentFormatValues() throws Exception { FakeSampleStream fakeSampleStream = new FakeSampleStream( diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java index aa9ee66fe2..050e0dfda2 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java @@ -2649,7 +2649,7 @@ public class MediaControllerListenerTest { @Test public void onVideoSizeChanged() throws Exception { - VideoSize defaultVideoSize = MediaTestUtils.createDefaultVideoSize(); + VideoSize defaultVideoSize = MediaTestUtils.getDefaultVideoSize(); RemoteMediaSession session = createRemoteMediaSession(TEST_ON_VIDEO_SIZE_CHANGED); MediaController controller = controllerTestRule.createController(session.getToken()); List videoSizeFromGetterList = new ArrayList<>(); diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java index abfce29040..0241765904 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java @@ -874,6 +874,7 @@ public class MediaControllerTest { } @Test + @SuppressWarnings("deprecation") // Testing propagation of deprecated unappliedRotationDegrees. public void getVideoSize_returnsVideoSizeOfPlayerInSession() throws Exception { VideoSize testVideoSize = new VideoSize( diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java index 708f239587..8dcf4f6a2f 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java @@ -289,7 +289,7 @@ public class MediaSessionProviderService extends Service { case TEST_ON_TRACKS_CHANGED_VIDEO_TO_AUDIO_TRANSITION: case TEST_ON_VIDEO_SIZE_CHANGED: { - mockPlayer.videoSize = MediaTestUtils.createDefaultVideoSize(); + mockPlayer.videoSize = MediaTestUtils.getDefaultVideoSize(); mockPlayer.currentTracks = MediaTestUtils.createDefaultVideoTracks(); break; } diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaTestUtils.java b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaTestUtils.java index 943fbd0df4..975bba6e41 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaTestUtils.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaTestUtils.java @@ -94,13 +94,9 @@ public final class MediaTestUtils { /* trackSelected= */ new boolean[] {true}))); } - /** Returns a new {@link VideoSize} instance for testing purpose. */ - public static VideoSize createDefaultVideoSize() { - return new VideoSize( - DEFAULT_VIDEO_SIZE.width, - DEFAULT_VIDEO_SIZE.height, - DEFAULT_VIDEO_SIZE.unappliedRotationDegrees, - DEFAULT_VIDEO_SIZE.pixelWidthHeightRatio); + /** Returns a {@link VideoSize} instance for testing purpose. */ + public static VideoSize getDefaultVideoSize() { + return DEFAULT_VIDEO_SIZE; } /** Create a media item with the mediaId for testing purpose. */ diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeVideoRenderer.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeVideoRenderer.java index 2e1ba578fd..6df5063b58 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeVideoRenderer.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeVideoRenderer.java @@ -157,11 +157,7 @@ public class FakeVideoRenderer extends FakeRenderer { handler.post( () -> { VideoSize videoSize = - new VideoSize( - format.width, - format.height, - format.rotationDegrees, - format.pixelWidthHeightRatio); + new VideoSize(format.width, format.height, format.pixelWidthHeightRatio); if (!Objects.equals(videoSize, videoSizeRef.get())) { eventListener.onVideoSizeChanged(videoSize); videoSizeRef.set(videoSize);