diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/GlEffectsFrameProcessorPixelTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/GlEffectsFrameProcessorPixelTest.java index 910e724981..d5e4f7c96e 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/GlEffectsFrameProcessorPixelTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/GlEffectsFrameProcessorPixelTest.java @@ -366,7 +366,6 @@ public final class GlEffectsFrameProcessorPixelTest { frameProcessingEnded = true; } }, - pixelWidthHeightRatio, /* streamOffsetUs= */ 0L, effects, /* outputSurfaceProvider= */ (requestedWidth, requestedHeight) -> { @@ -381,7 +380,8 @@ public final class GlEffectsFrameProcessorPixelTest { }, Transformer.DebugViewProvider.NONE, /* enableExperimentalHdrEditing= */ false)); - glEffectsFrameProcessor.setInputFrameInfo(new FrameInfo(inputWidth, inputHeight)); + glEffectsFrameProcessor.setInputFrameInfo( + new FrameInfo(inputWidth, inputHeight, pixelWidthHeightRatio)); glEffectsFrameProcessor.registerInputFrame(); // Queue the first video frame from the extractor. diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameInfo.java b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameInfo.java index 0b0af7821b..8975f93383 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameInfo.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameInfo.java @@ -23,15 +23,17 @@ import static androidx.media3.common.util.Assertions.checkArgument; public final int width; /** The height of the frame, in pixels. */ public final int height; + /** The ratio of width over height for each pixel. */ + public final float pixelWidthHeightRatio; - // TODO(b/227625423): Add pixelWidthHeightRatio. // TODO(b/227624622): Add color space information for HDR. - public FrameInfo(int width, int height) { + public FrameInfo(int width, int height, float pixelWidthHeightRatio) { checkArgument(width > 0, "width must be positive, but is: " + width); checkArgument(height > 0, "height must be positive, but is: " + height); this.width = width; this.height = height; + this.pixelWidthHeightRatio = pixelWidthHeightRatio; } } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameProcessor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameProcessor.java index db0a3bec7b..17c60e801d 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameProcessor.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameProcessor.java @@ -46,6 +46,9 @@ import android.view.Surface; * *

The new input information is applied from the next frame {@linkplain #registerInputFrame() * registered} onwards. + * + *

Pixels are expanded using the {@link FrameInfo#pixelWidthHeightRatio} so that the output + * frames' pixels have a ratio of 1. */ void setInputFrameInfo(FrameInfo inputFrameInfo); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/GlEffectsFrameProcessor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/GlEffectsFrameProcessor.java index 75b9b80d25..9823b57153 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/GlEffectsFrameProcessor.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/GlEffectsFrameProcessor.java @@ -50,8 +50,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * * @param context A {@link Context}. * @param listener A {@link Listener}. - * @param pixelWidthHeightRatio The ratio of width over height for each pixel. Pixels are expanded - * by this ratio so that the output frame's pixels have a ratio of 1. * @param effects The {@link GlEffect GlEffects} to apply to each frame. * @param outputSurfaceProvider A {@link SurfaceInfo.Provider} managing the output {@link * Surface}. @@ -64,7 +62,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; public static GlEffectsFrameProcessor create( Context context, FrameProcessor.Listener listener, - float pixelWidthHeightRatio, long streamOffsetUs, List effects, SurfaceInfo.Provider outputSurfaceProvider, @@ -80,7 +77,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; createOpenGlObjectsAndFrameProcessor( context, listener, - pixelWidthHeightRatio, streamOffsetUs, effects, outputSurfaceProvider, @@ -110,7 +106,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private static GlEffectsFrameProcessor createOpenGlObjectsAndFrameProcessor( Context context, FrameProcessor.Listener listener, - float pixelWidthHeightRatio, long streamOffsetUs, List effects, SurfaceInfo.Provider outputSurfaceProvider, @@ -136,20 +131,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; GlUtil.focusPlaceholderEglSurface(eglContext, eglDisplay); } - ImmutableList.Builder matrixTransformationListBuilder = - new ImmutableList.Builder<>(); - if (pixelWidthHeightRatio != 1f) { - matrixTransformationListBuilder.add( - createPixelWidthHeightRatioTransformation(pixelWidthHeightRatio)); - } - ImmutableList textureProcessors = getGlTextureProcessorsForGlEffects( context, effects, eglDisplay, eglContext, - matrixTransformationListBuilder, outputSurfaceProvider, streamOffsetUs, listener, @@ -173,26 +160,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; textureProcessors); } - /** - * Returns a new {@link GlMatrixTransformation} to expand or shrink the frame based on the {@code - * pixelWidthHeightRatio}. - * - *

If {@code pixelWidthHeightRatio} is 1, this method returns an identity transformation that - * can be ignored. - */ - private static GlMatrixTransformation createPixelWidthHeightRatioTransformation( - float pixelWidthHeightRatio) { - if (pixelWidthHeightRatio > 1f) { - return new ScaleToFitTransformation.Builder() - .setScale(/* scaleX= */ pixelWidthHeightRatio, /* scaleY= */ 1f) - .build(); - } else { - return new ScaleToFitTransformation.Builder() - .setScale(/* scaleX= */ 1f, /* scaleY= */ 1f / pixelWidthHeightRatio) - .build(); - } - } - /** * Combines consecutive {@link GlMatrixTransformation} instances into a single {@link * MatrixTransformationProcessor} and converts all other {@link GlEffect} instances to separate @@ -207,7 +174,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; List effects, EGLDisplay eglDisplay, EGLContext eglContext, - ImmutableList.Builder matrixTransformationListBuilder, SurfaceInfo.Provider outputSurfaceProvider, long streamOffsetUs, FrameProcessor.Listener listener, @@ -216,6 +182,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; throws FrameProcessingException { ImmutableList.Builder textureProcessorListBuilder = new ImmutableList.Builder<>(); + ImmutableList.Builder matrixTransformationListBuilder = + new ImmutableList.Builder<>(); for (int i = 0; i < effects.size(); i++) { GlEffect effect = effects.get(i); if (effect instanceof GlMatrixTransformation) { @@ -396,7 +364,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; long presentationTimeUs = inputFrameTimeNs / 1000 - streamOffsetUs; inputSurfaceTexture.getTransformMatrix(inputSurfaceTextureTransformMatrix); inputExternalTextureProcessor.setTextureTransformMatrix(inputSurfaceTextureTransformMatrix); - FrameInfo inputFrameInfo = pendingInputFrames.remove(); + FrameInfo inputFrameInfo = adjustForPixelWidthHeightRatio(pendingInputFrames.remove()); checkState( inputExternalTextureProcessor.maybeQueueInputFrame( new TextureInfo( @@ -409,6 +377,27 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; // asynchronously by the texture processors chained after it. } + /** + * Expands or shrinks the frame based on the {@link FrameInfo#pixelWidthHeightRatio} and returns a + * new {@link FrameInfo} instance with scaled dimensions and {@link + * FrameInfo#pixelWidthHeightRatio} 1. + */ + private FrameInfo adjustForPixelWidthHeightRatio(FrameInfo frameInfo) { + if (frameInfo.pixelWidthHeightRatio > 1f) { + return new FrameInfo( + (int) (frameInfo.width * frameInfo.pixelWidthHeightRatio), + frameInfo.height, + /* pixelWidthHeightRatio= */ 1); + } else if (frameInfo.pixelWidthHeightRatio < 1f) { + return new FrameInfo( + frameInfo.width, + (int) (frameInfo.height / frameInfo.pixelWidthHeightRatio), + /* pixelWidthHeightRatio= */ 1); + } else { + return frameInfo; + } + } + /** * Propagates the end-of-stream signal through the texture processors once no more input frames * are pending. diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java index 5454e35147..4304f3483a 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java @@ -119,7 +119,6 @@ import org.checkerframework.dataflow.qual.Pure; } } }, - inputFormat.pixelWidthHeightRatio, streamOffsetUs, effectsListBuilder.build(), /* outputSurfaceProvider= */ encoderWrapper, @@ -129,7 +128,8 @@ import org.checkerframework.dataflow.qual.Pure; throw TransformationException.createForFrameProcessingException( e, TransformationException.ERROR_CODE_GL_INIT_FAILED); } - frameProcessor.setInputFrameInfo(new FrameInfo(decodedWidth, decodedHeight)); + frameProcessor.setInputFrameInfo( + new FrameInfo(decodedWidth, decodedHeight, inputFormat.pixelWidthHeightRatio)); decoder = decoderFactory.createForVideoDecoding(