diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 711b3c4be0..5839a0f7e4 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -66,6 +66,9 @@ `Surface` in `configure` and calls to a new method `detachOutputSurface` to remove a previously set `Surface` if the codec supports this (`MediaCodecInfo.detachedSurfaceSupported`). + * Use `MediaCodecAdapter` supplied pixel aspect ratio values if provided + when processing `onOutputFormatChanged` + ([#1371](https://github.com/androidx/media/pull/1371)). * Text: * Add a custom `VoiceSpan` and populate it for [WebVTT voice spans](https://www.w3.org/TR/webvtt1/#webvtt-cue-voice-span) 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 1c4b727d69..a7c31e671e 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,7 +1342,17 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer ? mediaFormat.getInteger(KEY_CROP_BOTTOM) - mediaFormat.getInteger(KEY_CROP_TOP) + 1 : mediaFormat.getInteger(MediaFormat.KEY_HEIGHT); } + pixelWidthHeightRatio = format.pixelWidthHeightRatio; + if (Util.SDK_INT >= 30 + && mediaFormat != null + && mediaFormat.containsKey(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH) + && mediaFormat.containsKey(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT)) { + pixelWidthHeightRatio = + (float) mediaFormat.getInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH) + / mediaFormat.getInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT); + } + // 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. 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..1954a14697 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; @@ -114,6 +116,7 @@ public class MediaCodecVideoRendererTest { .setSampleMimeType(MimeTypes.VIDEO_H264) .setWidth(1920) .setHeight(1080) + .setPixelWidthHeightRatio(1.0f) .build(); private static final TrackGroup TRACK_GROUP_H264 = new TrackGroup(VIDEO_H264); @@ -747,6 +750,84 @@ public class MediaCodecVideoRendererTest { new VideoSize(VIDEO_H264.width, VIDEO_H264.height, VIDEO_H264.pixelWidthHeightRatio)); } + @Config(minSdk = 30) + @Test + public void + render_withMediaCodecModifyingPixelAspectRatioWidthHeight_sendsVideoSizeChangeWithMediaFormatValues() + throws Exception { + MediaCodecAdapter.Factory codecAdapterFactory = + configuration -> + new ForwardingSynchronousMediaCodecAdapter( + new SynchronousMediaCodecAdapter.Factory().createAdapter(configuration)) { + @Override + public MediaFormat getOutputFormat() { + MediaFormat mediaFormat = adapter.getOutputFormat(); + if (Util.SDK_INT >= 30) { + // Change to 9:16 Ratio + mediaFormat.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH, 9); + mediaFormat.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT, 16); + } + return mediaFormat; + } + }; + 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); + mediaCodecVideoRendererWithCustomAdapter.handleMessage( + Renderer.MSG_SET_VIDEO_OUTPUT, new Surface(new SurfaceTexture(/* texName= */ 0))); + 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, + /* pixelWidthHeightRatio= */ VIDEO_H264.pixelWidthHeightRatio * (9.0f / 16.0f))); + } + @Test public void render_withMultipleQueued_sendsVideoSizeChangedWithCorrectPixelAspectRatioWhenMultipleQueued() @@ -1903,7 +1984,7 @@ public class MediaCodecVideoRendererTest { private abstract static class ForwardingSynchronousMediaCodecAdapter implements MediaCodecAdapter { - private final MediaCodecAdapter adapter; + protected final MediaCodecAdapter adapter; ForwardingSynchronousMediaCodecAdapter(MediaCodecAdapter adapter) { this.adapter = adapter;