From cbed80ecf36d3ff49ec9d2806670f1fda3551aba Mon Sep 17 00:00:00 2001 From: tonihei Date: Mon, 11 Mar 2024 09:46:07 -0700 Subject: [PATCH] Always set PARAMETER_KEY_TUNNEL_PEEK when tunneling This should already be the default, but some devices seem to not adhere to this contract and assume the default is unset. Issue: androidx/media#1169 PiperOrigin-RevId: 614697283 --- RELEASENOTES.md | 3 ++ .../video/MediaCodecVideoRenderer.java | 42 +++++++++++-------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b6c5d3cb7d..6b1e503afb 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -44,6 +44,9 @@ Google TV, and Lenovo M10 FHD Plus that causes 60fps H265 streams to be marked as unsupported ([#966](https://github.com/androidx/media/issues/966)). + * Add workaround that ensures the first frame is always rendered while + tunneling even if the device does not do this automatically as required + by the API ([#1169](https://github.com/androidx/media/issues/1169)). * Text: * Metadata: * Image: 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 1e5d13909e..63e44611cc 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 @@ -662,7 +662,7 @@ protected void onPositionReset(long positionUs, boolean joining) throws ExoPlayb if (joining) { videoFrameReleaseControl.join(); } - maybeUpdateOnFrameRenderedListener(); + maybeSetupTunnelingForFirstFrame(); consecutiveDroppedFrameCount = 0; } @@ -707,7 +707,7 @@ protected void onStopped() { protected void onDisabled() { reportedVideoSize = null; videoFrameReleaseControl.onDisabled(); - maybeUpdateOnFrameRenderedListener(); + maybeSetupTunnelingForFirstFrame(); haveReportedFirstFrameRenderedForCurrentSurface = false; tunnelingOnFrameRenderedListener = null; try { @@ -848,7 +848,7 @@ private void setOutput(@Nullable Object output) throws ExoPlaybackException { videoSinkProvider.clearOutputSurfaceInfo(); } } - maybeUpdateOnFrameRenderedListener(); + maybeSetupTunnelingForFirstFrame(); } else if (displaySurface != null && displaySurface != placeholderSurface) { // The display surface is set and unchanged. If we know the video size and/or have already // rendered to the display surface, report these again immediately. @@ -1130,9 +1130,7 @@ protected void onCodecInitialized( codecNeedsSetOutputSurfaceWorkaround = codecNeedsSetOutputSurfaceWorkaround(name); codecHandlesHdr10PlusOutOfBandMetadata = checkNotNull(getCodecInfo()).isHdr10PlusOutOfBandMetadataSupported(); - if (Util.SDK_INT >= 23 && tunneling) { - tunnelingOnFrameRenderedListener = new OnFrameRenderedListenerV23(checkNotNull(getCodec())); - } + maybeSetupTunnelingForFirstFrame(); } @Override @@ -1464,7 +1462,7 @@ protected void onProcessedOutputBuffer(long presentationTimeUs) { protected void onProcessedStreamChange() { super.onProcessedStreamChange(); videoFrameReleaseControl.onProcessedStreamChange(); - maybeUpdateOnFrameRenderedListener(); + maybeSetupTunnelingForFirstFrame(); if (videoSinkProvider.isInitialized()) { videoSinkProvider.setStreamOffsetUs(getOutputStreamOffsetUs()); } @@ -1694,17 +1692,25 @@ private void releasePlaceholderSurface() { } } - private void maybeUpdateOnFrameRenderedListener() { - // The first frame notification is triggered by renderOutputBuffer or renderOutputBufferV21 for - // non-tunneled playback, onQueueInputBuffer for tunneled playback prior to API level 23, and - // OnFrameRenderedListenerV23.onFrameRenderedListener for tunneled playback on API level 23 and - // above. - if (Util.SDK_INT >= 23 && tunneling) { - @Nullable MediaCodecAdapter codec = getCodec(); - // If codec is null then the listener will be instantiated in configureCodec. - if (codec != null) { - tunnelingOnFrameRenderedListener = new OnFrameRenderedListenerV23(codec); - } + private void maybeSetupTunnelingForFirstFrame() { + if (!tunneling || Util.SDK_INT < 23) { + // The first frame notification for tunneling is triggered by onQueueInputBuffer prior to API + // level 23 and no setup is needed here. + return; + } + @Nullable MediaCodecAdapter codec = getCodec(); + if (codec == null) { + // If codec is null, then the setup will be triggered again in onCodecInitialized. + return; + } + tunnelingOnFrameRenderedListener = new OnFrameRenderedListenerV23(codec); + if (Util.SDK_INT >= 33) { + // This should be the default anyway according to the API contract, but some devices are known + // to not adhere to this contract and need to get the parameter explicitly. See + // https://github.com/androidx/media/issues/1169. + Bundle codecParameters = new Bundle(); + codecParameters.putInt(MediaCodec.PARAMETER_KEY_TUNNEL_PEEK, 1); + codec.setParameters(codecParameters); } }