From 7473156853fdc76dfc530a889d160486f5f3b781 Mon Sep 17 00:00:00 2001 From: Daniele Sparano Date: Wed, 28 Aug 2024 12:37:11 +0100 Subject: [PATCH] Fix output pixel aspect ratio and add test for it --- .../video/MediaCodecVideoRenderer.java | 4 +- .../video/MediaCodecVideoRendererTest.java | 91 ++++++++++++++++++- 2 files changed, 92 insertions(+), 3 deletions(-) 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 867915ca03..247e54fdf8 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 @@ -1342,10 +1342,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer ? mediaFormat.getInteger(KEY_CROP_BOTTOM) - mediaFormat.getInteger(KEY_CROP_TOP) + 1 : mediaFormat.getInteger(MediaFormat.KEY_HEIGHT); } - boolean hasPixelAspectRatio = mediaFormat.containsKey(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH) + boolean hasPixelAspectRatio = (Util.SDK_INT >= 30) && mediaFormat != null && mediaFormat.containsKey(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH) && mediaFormat.containsKey(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT); pixelWidthHeightRatio = hasPixelAspectRatio ? - mediaFormat.getInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH) / + (float)mediaFormat.getInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH) / mediaFormat.getInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT) : format.pixelWidthHeightRatio; // The decoder applies the rotation when rendering to the surface. For 90 and 270 degree 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 7f92d7efad..203dd3fcc3 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 @@ -50,6 +50,7 @@ import androidx.media3.common.MimeTypes; import androidx.media3.common.TrackGroup; import androidx.media3.common.VideoSize; import androidx.media3.common.util.Clock; +import androidx.media3.common.util.Util; import androidx.media3.decoder.CryptoInfo; import androidx.media3.exoplayer.DecoderCounters; import androidx.media3.exoplayer.ExoPlaybackException; @@ -100,6 +101,7 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.Shadows; +import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowDisplay; import org.robolectric.shadows.ShadowLooper; import org.robolectric.shadows.ShadowSystemClock; @@ -747,6 +749,71 @@ public class MediaCodecVideoRendererTest { new VideoSize(VIDEO_H264.width, VIDEO_H264.height, VIDEO_H264.pixelWidthHeightRatio)); } + @Config(minSdk = 30) + @Test + public void render_withMediaCodecAlteringPixelAspectRatioWidthHeight_sendsVideoSizeChangeWithMediaFormatValues() throws Exception { + MediaCodecAdapter.Factory codecAdapterFactory = + configuration -> + new ForwardingSynchronousMediaCodecAdapterAlteringPixelAspectRatio( + new SynchronousMediaCodecAdapter.Factory().createAdapter(configuration)); + MediaCodecVideoRenderer mediaCodecVideoRendererWithCustomAdapter = + new MediaCodecVideoRenderer( + ApplicationProvider.getApplicationContext(), + codecAdapterFactory, + mediaCodecSelector, + /* allowedJoiningTimeMs= */ 0, + /* enableDecoderFallback= */ false, + /* eventHandler= */ new Handler(testMainLooper), + /* eventListener= */ eventListener, + /* maxDroppedFramesToNotify= */ 1) { + @Override + protected @Capabilities int supportsFormat( + MediaCodecSelector mediaCodecSelector, Format format) { + return RendererCapabilities.create(C.FORMAT_HANDLED); + } + }; + mediaCodecVideoRendererWithCustomAdapter.init(/* index= */ 0, PlayerId.UNSET, Clock.DEFAULT); + surface = new Surface(new SurfaceTexture(/* texName= */ 0)); + mediaCodecVideoRendererWithCustomAdapter.handleMessage(Renderer.MSG_SET_VIDEO_OUTPUT, surface); + + FakeSampleStream fakeSampleStream = + new FakeSampleStream( + new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024), + /* mediaSourceEventDispatcher= */ null, + DrmSessionManager.DRM_UNSUPPORTED, + new DrmSessionEventListener.EventDispatcher(), + /* initialFormat= */ VIDEO_H264, + ImmutableList.of( + oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM)); + fakeSampleStream.writeData(/* startPositionUs= */ 0); + mediaCodecVideoRendererWithCustomAdapter.enable( + RendererConfiguration.DEFAULT, + new Format[] {VIDEO_H264}, + fakeSampleStream, + /* positionUs= */ 0, + /* joining= */ false, + /* mayRenderStartOfStream= */ true, + /* startPositionUs= */ 0, + /* offsetUs= */ 0, + new MediaSource.MediaPeriodId(new Object())); + mediaCodecVideoRendererWithCustomAdapter.setCurrentStreamFinal(); + mediaCodecVideoRendererWithCustomAdapter.start(); + + int positionUs = 0; + do { + mediaCodecVideoRendererWithCustomAdapter.render(positionUs, SystemClock.elapsedRealtime() * 1000); + positionUs += 10; + } while (!mediaCodecVideoRendererWithCustomAdapter.isEnded()); + shadowOf(testMainLooper).idle(); + + verify(eventListener) + .onVideoSizeChanged( + new VideoSize( + VIDEO_H264.width, + VIDEO_H264.height, + VIDEO_H264.pixelWidthHeightRatio / 2)); + } + @Test public void render_withMultipleQueued_sendsVideoSizeChangedWithCorrectPixelAspectRatioWhenMultipleQueued() @@ -1901,9 +1968,31 @@ public class MediaCodecVideoRendererTest { } } + private static final class ForwardingSynchronousMediaCodecAdapterAlteringPixelAspectRatio + extends ForwardingSynchronousMediaCodecAdapter { + + ForwardingSynchronousMediaCodecAdapterAlteringPixelAspectRatio(MediaCodecAdapter adapter) { + super(adapter); + } + + @Override + public MediaFormat getOutputFormat() { + MediaFormat mediaFormat = adapter.getOutputFormat(); + if (Util.SDK_INT >= 30) { + int pixelAspectRatioHeight = 1 << 30; + int pixelAspectRatioWidth = (int) (0.5f * pixelAspectRatioHeight); + mediaFormat.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH, + pixelAspectRatioWidth); + mediaFormat.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT, + pixelAspectRatioHeight); + } + return mediaFormat; + } + } + private abstract static class ForwardingSynchronousMediaCodecAdapter implements MediaCodecAdapter { - private final MediaCodecAdapter adapter; + protected final MediaCodecAdapter adapter; ForwardingSynchronousMediaCodecAdapter(MediaCodecAdapter adapter) { this.adapter = adapter;