Skip to content

Commit 4b8ff72

Browse files
hmschicbaker
authored andcommitted
Use MatrixTransformation instead of wrapping its GlFrameProcssor.
ScaleToFitFrameProcessor, PresentationFrameProcessor, and EncoderCompatibilityFrameProcessor now each implement MatrixTransformation instead of wrapping MatrixTransformationFrameProcessor. PiperOrigin-RevId: 446480286
1 parent 337b7f7 commit 4b8ff72

12 files changed

+256
-346
lines changed

Diff for: libraries/transformer/src/androidTest/java/androidx/media3/transformer/FrameProcessorChainPixelTest.java

+4-5
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ public void processData_withMatrixAndScaleToFitTransformation_producesExpectedOu
154154
setUpAndPrepareFirstFrame(
155155
DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO,
156156
(MatrixTransformation) (long presentationTimeUs) -> translateRightMatrix,
157-
() -> new ScaleToFitFrameProcessor.Builder().setRotationDegrees(45).build());
157+
new ScaleToFitTransformation.Builder().setRotationDegrees(45).build());
158158
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(TRANSLATE_THEN_ROTATE_PNG_ASSET_PATH);
159159

160160
Bitmap actualBitmap = processFirstFrameAndEnd();
@@ -176,7 +176,7 @@ public void processData_withScaleToFitAndMatrixTransformation_producesExpectedOu
176176
translateRightMatrix.postTranslate(/* dx= */ 1, /* dy= */ 0);
177177
setUpAndPrepareFirstFrame(
178178
DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO,
179-
() -> new ScaleToFitFrameProcessor.Builder().setRotationDegrees(45).build(),
179+
new ScaleToFitTransformation.Builder().setRotationDegrees(45).build(),
180180
(MatrixTransformation) (long presentationTimeUs) -> translateRightMatrix);
181181
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ROTATE_THEN_TRANSLATE_PNG_ASSET_PATH);
182182

@@ -195,8 +195,7 @@ public void processData_withScaleToFitAndMatrixTransformation_producesExpectedOu
195195
public void processData_withPresentation_setResolution_producesExpectedOutput() throws Exception {
196196
String testId = "processData_withPresentation_setResolution";
197197
setUpAndPrepareFirstFrame(
198-
DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO,
199-
() -> new PresentationFrameProcessor.Builder().setResolution(480).build());
198+
DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO, new Presentation.Builder().setResolution(480).build());
200199
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(REQUEST_OUTPUT_HEIGHT_PNG_ASSET_PATH);
201200

202201
Bitmap actualBitmap = processFirstFrameAndEnd();
@@ -216,7 +215,7 @@ public void processData_withScaleToFitTransformation_rotate45_producesExpectedOu
216215
String testId = "processData_withScaleToFitTransformation_rotate45";
217216
setUpAndPrepareFirstFrame(
218217
DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO,
219-
() -> new ScaleToFitFrameProcessor.Builder().setRotationDegrees(45).build());
218+
new ScaleToFitTransformation.Builder().setRotationDegrees(45).build());
220219
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ROTATE45_SCALE_TO_FIT_PNG_ASSET_PATH);
221220

222221
Bitmap actualBitmap = processFirstFrameAndEnd();

Diff for: libraries/transformer/src/androidTest/java/androidx/media3/transformer/PresentationFrameProcessorPixelTest.java renamed to libraries/transformer/src/androidTest/java/androidx/media3/transformer/PresentationPixelTest.java

+34-26
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@
3535
import org.junit.runner.RunWith;
3636

3737
/**
38-
* Pixel test for frame processing via {@link PresentationFrameProcessor}.
38+
* Pixel test for frame processing via {@link Presentation}.
3939
*
4040
* <p>Expected images are taken from an emulator, so tests on different emulators or physical
4141
* devices may fail. To test on other devices, please increase the {@link
4242
* BitmapTestUtil#MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE} and/or inspect the saved output bitmaps
4343
* as recommended in {@link FrameProcessorChainPixelTest}.
4444
*/
4545
@RunWith(AndroidJUnit4.class)
46-
public final class PresentationFrameProcessorPixelTest {
46+
public final class PresentationPixelTest {
4747
public static final String ORIGINAL_PNG_ASSET_PATH =
4848
"media/bitmap/sample_mp4_first_frame/original.png";
4949
public static final String CROP_SMALLER_PNG_ASSET_PATH =
@@ -97,7 +97,7 @@ public void release() {
9797
@Test
9898
public void drawFrame_noEdits_producesExpectedOutput() throws Exception {
9999
String testId = "drawFrame_noEdits";
100-
presentationFrameProcessor = new PresentationFrameProcessor.Builder().build();
100+
presentationFrameProcessor = new Presentation.Builder().build().toGlFrameProcessor();
101101
presentationFrameProcessor.initialize(
102102
getApplicationContext(), inputTexId, inputWidth, inputHeight);
103103
Size outputSize = presentationFrameProcessor.getOutputSize();
@@ -122,9 +122,10 @@ public void drawFrame_noEdits_producesExpectedOutput() throws Exception {
122122
public void drawFrame_cropSmaller_producesExpectedOutput() throws Exception {
123123
String testId = "drawFrame_cropSmaller";
124124
GlFrameProcessor presentationFrameProcessor =
125-
new PresentationFrameProcessor.Builder()
125+
new Presentation.Builder()
126126
.setCrop(/* left= */ -.9f, /* right= */ .1f, /* bottom= */ -1f, /* top= */ .5f)
127-
.build();
127+
.build()
128+
.toGlFrameProcessor();
128129
presentationFrameProcessor.initialize(
129130
getApplicationContext(), inputTexId, inputWidth, inputHeight);
130131
Size outputSize = presentationFrameProcessor.getOutputSize();
@@ -149,9 +150,10 @@ public void drawFrame_cropSmaller_producesExpectedOutput() throws Exception {
149150
public void drawFrame_cropLarger_producesExpectedOutput() throws Exception {
150151
String testId = "drawFrame_cropSmaller";
151152
GlFrameProcessor presentationFrameProcessor =
152-
new PresentationFrameProcessor.Builder()
153+
new Presentation.Builder()
153154
.setCrop(/* left= */ -2f, /* right= */ 2f, /* bottom= */ -1f, /* top= */ 2f)
154-
.build();
155+
.build()
156+
.toGlFrameProcessor();
155157
presentationFrameProcessor.initialize(
156158
getApplicationContext(), inputTexId, inputWidth, inputHeight);
157159
Size outputSize = presentationFrameProcessor.getOutputSize();
@@ -177,9 +179,10 @@ public void drawFrame_changeAspectRatio_scaleToFit_narrow_producesExpectedOutput
177179
throws Exception {
178180
String testId = "drawFrame_changeAspectRatio_scaleToFit_narrow";
179181
presentationFrameProcessor =
180-
new PresentationFrameProcessor.Builder()
181-
.setAspectRatio(1f, PresentationFrameProcessor.LAYOUT_SCALE_TO_FIT)
182-
.build();
182+
new Presentation.Builder()
183+
.setAspectRatio(1f, Presentation.LAYOUT_SCALE_TO_FIT)
184+
.build()
185+
.toGlFrameProcessor();
183186
presentationFrameProcessor.initialize(
184187
getApplicationContext(), inputTexId, inputWidth, inputHeight);
185188
Size outputSize = presentationFrameProcessor.getOutputSize();
@@ -206,9 +209,10 @@ public void drawFrame_changeAspectRatio_scaleToFit_wide_producesExpectedOutput()
206209
throws Exception {
207210
String testId = "drawFrame_changeAspectRatio_scaleToFit_wide";
208211
presentationFrameProcessor =
209-
new PresentationFrameProcessor.Builder()
210-
.setAspectRatio(2f, PresentationFrameProcessor.LAYOUT_SCALE_TO_FIT)
211-
.build();
212+
new Presentation.Builder()
213+
.setAspectRatio(2f, Presentation.LAYOUT_SCALE_TO_FIT)
214+
.build()
215+
.toGlFrameProcessor();
212216
presentationFrameProcessor.initialize(
213217
getApplicationContext(), inputTexId, inputWidth, inputHeight);
214218
Size outputSize = presentationFrameProcessor.getOutputSize();
@@ -235,9 +239,10 @@ public void drawFrame_changeAspectRatio_scaleToFitWithCrop_narrow_producesExpect
235239
throws Exception {
236240
String testId = "drawFrame_changeAspectRatio_scaleToFitWithCrop_narrow";
237241
presentationFrameProcessor =
238-
new PresentationFrameProcessor.Builder()
239-
.setAspectRatio(1f, PresentationFrameProcessor.LAYOUT_SCALE_TO_FIT_WITH_CROP)
240-
.build();
242+
new Presentation.Builder()
243+
.setAspectRatio(1f, Presentation.LAYOUT_SCALE_TO_FIT_WITH_CROP)
244+
.build()
245+
.toGlFrameProcessor();
241246
presentationFrameProcessor.initialize(
242247
getApplicationContext(), inputTexId, inputWidth, inputHeight);
243248
Size outputSize = presentationFrameProcessor.getOutputSize();
@@ -264,9 +269,10 @@ public void drawFrame_changeAspectRatio_scaleToFitWithCrop_wide_producesExpected
264269
throws Exception {
265270
String testId = "drawFrame_changeAspectRatio_scaleToFitWithCrop_wide";
266271
presentationFrameProcessor =
267-
new PresentationFrameProcessor.Builder()
268-
.setAspectRatio(2f, PresentationFrameProcessor.LAYOUT_SCALE_TO_FIT_WITH_CROP)
269-
.build();
272+
new Presentation.Builder()
273+
.setAspectRatio(2f, Presentation.LAYOUT_SCALE_TO_FIT_WITH_CROP)
274+
.build()
275+
.toGlFrameProcessor();
270276
presentationFrameProcessor.initialize(
271277
getApplicationContext(), inputTexId, inputWidth, inputHeight);
272278
Size outputSize = presentationFrameProcessor.getOutputSize();
@@ -293,9 +299,10 @@ public void drawFrame_changeAspectRatio_stretchToFit_narrow_producesExpectedOutp
293299
throws Exception {
294300
String testId = "drawFrame_changeAspectRatio_stretchToFit_narrow";
295301
presentationFrameProcessor =
296-
new PresentationFrameProcessor.Builder()
297-
.setAspectRatio(1f, PresentationFrameProcessor.LAYOUT_STRETCH_TO_FIT)
298-
.build();
302+
new Presentation.Builder()
303+
.setAspectRatio(1f, Presentation.LAYOUT_STRETCH_TO_FIT)
304+
.build()
305+
.toGlFrameProcessor();
299306
presentationFrameProcessor.initialize(
300307
getApplicationContext(), inputTexId, inputWidth, inputHeight);
301308
Size outputSize = presentationFrameProcessor.getOutputSize();
@@ -322,9 +329,10 @@ public void drawFrame_changeAspectRatio_stretchToFit_wide_producesExpectedOutput
322329
throws Exception {
323330
String testId = "drawFrame_changeAspectRatio_stretchToFit_wide";
324331
presentationFrameProcessor =
325-
new PresentationFrameProcessor.Builder()
326-
.setAspectRatio(2f, PresentationFrameProcessor.LAYOUT_STRETCH_TO_FIT)
327-
.build();
332+
new Presentation.Builder()
333+
.setAspectRatio(2f, Presentation.LAYOUT_STRETCH_TO_FIT)
334+
.build()
335+
.toGlFrameProcessor();
328336
presentationFrameProcessor.initialize(
329337
getApplicationContext(), inputTexId, inputWidth, inputHeight);
330338
Size outputSize = presentationFrameProcessor.getOutputSize();
@@ -346,7 +354,7 @@ public void drawFrame_changeAspectRatio_stretchToFit_wide_producesExpectedOutput
346354
assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
347355
}
348356

349-
private void setupOutputTexture(int outputWidth, int outputHeight) throws IOException {
357+
private void setupOutputTexture(int outputWidth, int outputHeight) {
350358
outputTexId = GlUtil.createTexture(outputWidth, outputHeight);
351359
int frameBuffer = GlUtil.createFboForTexture(outputTexId);
352360
GlUtil.focusFramebuffer(

Diff for: libraries/transformer/src/main/java/androidx/media3/transformer/EncoderCompatibilityFrameProcessor.java renamed to libraries/transformer/src/main/java/androidx/media3/transformer/EncoderCompatibilityTransformation.java

+22-38
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,22 @@
2020
import static androidx.media3.common.util.Assertions.checkStateNotNull;
2121

2222
import android.content.Context;
23+
import android.graphics.Matrix;
2324
import android.util.Size;
24-
import androidx.annotation.VisibleForTesting;
2525
import androidx.media3.common.C;
2626
import androidx.media3.common.Format;
2727
import androidx.media3.common.util.GlUtil;
28-
import java.io.IOException;
2928
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
3029

3130
/**
32-
* Copies frames from a texture and applies {@link Format#rotationDegrees} for encoder
33-
* compatibility, if needed.
31+
* Specifies a {@link Format#rotationDegrees} to apply to each frame for encoder compatibility, if
32+
* needed.
3433
*
3534
* <p>Encoders commonly support higher maximum widths than maximum heights. This may rotate the
3635
* decoded frame before encoding, so the encoded frame's width >= height, and set {@link
3736
* Format#rotationDegrees} to ensure the frame is displayed in the correct orientation.
3837
*/
39-
/* package */ class EncoderCompatibilityFrameProcessor implements GlFrameProcessor {
38+
/* package */ class EncoderCompatibilityTransformation implements MatrixTransformation {
4039
// TODO(b/218488308): Allow reconfiguration of the output size, as encoders may not support the
4140
// requested output resolution.
4241

@@ -45,26 +44,32 @@
4544
}
4645

4746
private int outputRotationDegrees;
48-
private @MonotonicNonNull ScaleToFitFrameProcessor rotateFrameProcessor;
47+
private @MonotonicNonNull Matrix transformationMatrix;
4948

5049
/** Creates a new instance. */
51-
/* package */ EncoderCompatibilityFrameProcessor() {
52-
50+
public EncoderCompatibilityTransformation() {
5351
outputRotationDegrees = C.LENGTH_UNSET;
5452
}
5553

5654
@Override
57-
public void initialize(Context context, int inputTexId, int inputWidth, int inputHeight)
58-
throws IOException {
59-
configureOutputSizeAndRotation(inputWidth, inputHeight);
60-
rotateFrameProcessor =
61-
new ScaleToFitFrameProcessor.Builder().setRotationDegrees(outputRotationDegrees).build();
62-
rotateFrameProcessor.initialize(context, inputTexId, inputWidth, inputHeight);
55+
public Size configure(int inputWidth, int inputHeight) {
56+
checkArgument(inputWidth > 0, "inputWidth must be positive");
57+
checkArgument(inputHeight > 0, "inputHeight must be positive");
58+
59+
transformationMatrix = new Matrix();
60+
if (inputHeight > inputWidth) {
61+
outputRotationDegrees = 90;
62+
transformationMatrix.postRotate(outputRotationDegrees);
63+
return new Size(inputHeight, inputWidth);
64+
} else {
65+
outputRotationDegrees = 0;
66+
return new Size(inputWidth, inputHeight);
67+
}
6368
}
6469

6570
@Override
66-
public Size getOutputSize() {
67-
return checkStateNotNull(rotateFrameProcessor).getOutputSize();
71+
public Matrix getMatrix(long presentationTimeUs) {
72+
return checkStateNotNull(transformationMatrix, "configure must be called first");
6873
}
6974

7075
/**
@@ -78,28 +83,7 @@ public Size getOutputSize() {
7883
public int getOutputRotationDegrees() {
7984
checkState(
8085
outputRotationDegrees != C.LENGTH_UNSET,
81-
"configureOutputSizeAndTransformationMatrix must be called before"
82-
+ " getOutputRotationDegrees");
86+
"configure must be called before getOutputRotationDegrees");
8387
return outputRotationDegrees;
8488
}
85-
86-
@Override
87-
public void drawFrame(long presentationTimeUs) {
88-
checkStateNotNull(rotateFrameProcessor).drawFrame(presentationTimeUs);
89-
}
90-
91-
@Override
92-
public void release() {
93-
if (rotateFrameProcessor != null) {
94-
rotateFrameProcessor.release();
95-
}
96-
}
97-
98-
@VisibleForTesting // Allows robolectric testing of output size calculation without OpenGL.
99-
/* package */ void configureOutputSizeAndRotation(int inputWidth, int inputHeight) {
100-
checkArgument(inputWidth > 0, "inputWidth must be positive");
101-
checkArgument(inputHeight > 0, "inputHeight must be positive");
102-
103-
outputRotationDegrees = (inputHeight > inputWidth) ? 90 : 0;
104-
}
10589
}

Diff for: libraries/transformer/src/main/java/androidx/media3/transformer/FrameProcessorChain.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -190,14 +190,16 @@ private static ImmutableList<GlFrameProcessor> getFrameProcessors(
190190
// Scale to expand the frame to apply the pixelWidthHeightRatio.
191191
if (pixelWidthHeightRatio > 1f) {
192192
frameProcessors.add(
193-
new ScaleToFitFrameProcessor.Builder()
193+
new ScaleToFitTransformation.Builder()
194194
.setScale(/* scaleX= */ pixelWidthHeightRatio, /* scaleY= */ 1f)
195-
.build());
195+
.build()
196+
.toGlFrameProcessor());
196197
} else if (pixelWidthHeightRatio < 1f) {
197198
frameProcessors.add(
198-
new ScaleToFitFrameProcessor.Builder()
199+
new ScaleToFitTransformation.Builder()
199200
.setScale(/* scaleX= */ 1f, /* scaleY= */ 1f / pixelWidthHeightRatio)
200-
.build());
201+
.build()
202+
.toGlFrameProcessor());
201203
}
202204

203205
for (int i = 0; i < effects.size(); i++) {

Diff for: libraries/transformer/src/main/java/androidx/media3/transformer/MatrixTransformationFrameProcessor.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,15 @@
3232
* Applies a transformation matrix in the vertex shader, and copies input pixels into an output
3333
* frame based on their locations after applying this matrix.
3434
*
35-
* <p>Operations are done on normalized device coordinates (-1 to 1 on x and y axes). No automatic
36-
* adjustments (like done in {@link ScaleToFitFrameProcessor}) are applied on the transformation.
35+
* <p>Operations are done on normalized device coordinates (-1 to 1 on x and y axes).
3736
*
3837
* <p>The background color of the output frame will be black.
3938
*/
4039
// TODO(b/227625423): Compose multiple transformation matrices in a single shader with clipping
4140
// after each matrix.
4241
@UnstableApi
4342
@SuppressWarnings("FunctionalInterfaceClash") // b/228192298
44-
public final class MatrixTransformationFrameProcessor implements GlFrameProcessor {
43+
/* package */ final class MatrixTransformationFrameProcessor implements GlFrameProcessor {
4544

4645
static {
4746
GlUtil.glAssertionsEnabled = true;

0 commit comments

Comments
 (0)