Add context param to GlFrameProcessor initialize().

PiperOrigin-RevId: 442826391
This commit is contained in:
hschlueter 2022-04-19 17:04:21 +01:00 committed by Ian Baker
parent afa551ab8f
commit 5248bbb882
18 changed files with 132 additions and 183 deletions

View File

@ -15,7 +15,6 @@
*/
package androidx.media3.demo.transformer;
import android.content.Context;
import android.graphics.Matrix;
import androidx.media3.common.C;
import androidx.media3.common.util.Util;
@ -32,27 +31,26 @@ import androidx.media3.transformer.GlFrameProcessor;
* #ZOOM_DURATION_SECONDS} seconds, such that the rectangle filled with the input frame increases
* linearly in size from a single point to filling the full output frame.
*/
public static GlFrameProcessor createZoomInTransitionFrameProcessor(Context context) {
public static GlFrameProcessor createZoomInTransitionFrameProcessor() {
return new AdvancedFrameProcessor(
context,
/* matrixProvider= */ AdvancedFrameProcessorFactory::calculateZoomInTransitionMatrix);
}
/**
* Returns a {@link GlFrameProcessor} that crops frames to a rectangle that moves on an ellipse.
*/
public static GlFrameProcessor createDizzyCropFrameProcessor(Context context) {
public static GlFrameProcessor createDizzyCropFrameProcessor() {
return new AdvancedFrameProcessor(
context, /* matrixProvider= */ AdvancedFrameProcessorFactory::calculateDizzyCropMatrix);
/* matrixProvider= */ AdvancedFrameProcessorFactory::calculateDizzyCropMatrix);
}
/**
* Returns a {@link GlFrameProcessor} that rotates a frame in 3D around the y-axis and applies
* perspective projection to 2D.
*/
public static GlFrameProcessor createSpin3dFrameProcessor(Context context) {
public static GlFrameProcessor createSpin3dFrameProcessor() {
return new AdvancedFrameProcessor(
context, /* matrixProvider= */ AdvancedFrameProcessorFactory::calculate3dSpinMatrix);
/* matrixProvider= */ AdvancedFrameProcessorFactory::calculate3dSpinMatrix);
}
private static final float ZOOM_DURATION_SECONDS = 2f;

View File

@ -53,20 +53,18 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private static final int BITMAP_WIDTH_HEIGHT = 512;
private final Context context;
private final Paint paint;
private final Bitmap overlayBitmap;
private final Bitmap logoBitmap;
private final Canvas overlayCanvas;
private float bitmapScaleX;
private float bitmapScaleY;
private int bitmapTexId;
private @MonotonicNonNull Size outputSize;
private @MonotonicNonNull Bitmap logoBitmap;
private @MonotonicNonNull GlProgram glProgram;
public BitmapOverlayFrameProcessor(Context context) {
this.context = context;
public BitmapOverlayFrameProcessor() {
paint = new Paint();
paint.setTextSize(64);
paint.setAntiAlias(true);
@ -75,18 +73,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
overlayBitmap =
Bitmap.createBitmap(BITMAP_WIDTH_HEIGHT, BITMAP_WIDTH_HEIGHT, Bitmap.Config.ARGB_8888);
overlayCanvas = new Canvas(overlayBitmap);
try {
logoBitmap =
((BitmapDrawable)
context.getPackageManager().getApplicationIcon(context.getPackageName()))
.getBitmap();
} catch (PackageManager.NameNotFoundException e) {
throw new IllegalStateException(e);
}
}
@Override
public void initialize(int inputTexId, int inputWidth, int inputHeight) throws IOException {
public void initialize(Context context, int inputTexId, int inputWidth, int inputHeight)
throws IOException {
if (inputWidth > inputHeight) {
bitmapScaleX = inputWidth / (float) inputHeight;
bitmapScaleY = 1f;
@ -96,6 +87,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
outputSize = new Size(inputWidth, inputHeight);
try {
logoBitmap =
((BitmapDrawable)
context.getPackageManager().getApplicationIcon(context.getPackageName()))
.getBitmap();
} catch (PackageManager.NameNotFoundException e) {
throw new IllegalStateException(e);
}
bitmapTexId = GlUtil.createTexture(BITMAP_WIDTH_HEIGHT, BITMAP_WIDTH_HEIGHT);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, /* level= */ 0, overlayBitmap, /* border= */ 0);
@ -125,7 +124,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
String text =
String.format(Locale.US, "%.02f", presentationTimeUs / (float) C.MICROS_PER_SECOND);
overlayBitmap.eraseColor(Color.TRANSPARENT);
overlayCanvas.drawBitmap(logoBitmap, /* left= */ 3, /* top= */ 378, paint);
overlayCanvas.drawBitmap(checkStateNotNull(logoBitmap), /* left= */ 3, /* top= */ 378, paint);
overlayCanvas.drawText(text, /* x= */ 160, /* y= */ 466, paint);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, bitmapTexId);
GLUtils.texSubImage2D(

View File

@ -40,7 +40,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private static final String FRAGMENT_SHADER_PATH = "fragment_shader_vignette_es2.glsl";
private static final float DIMMING_PERIOD_US = 5_600_000f;
private final Context context;
private float centerX;
private float centerY;
private float minInnerRadius;
@ -69,15 +68,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
* @param outerRadius The radius after which all pixels are black.
*/
public PeriodicVignetteFrameProcessor(
Context context,
float centerX,
float centerY,
float minInnerRadius,
float maxInnerRadius,
float outerRadius) {
float centerX, float centerY, float minInnerRadius, float maxInnerRadius, float outerRadius) {
checkArgument(minInnerRadius <= maxInnerRadius);
checkArgument(maxInnerRadius <= outerRadius);
this.context = context;
this.centerX = centerX;
this.centerY = centerY;
this.minInnerRadius = minInnerRadius;
@ -86,7 +79,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
@Override
public void initialize(int inputTexId, int inputWidth, int inputHeight) throws IOException {
public void initialize(Context context, int inputTexId, int inputWidth, int inputHeight)
throws IOException {
outputSize = new Size(inputWidth, inputHeight);
glProgram = new GlProgram(context, VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH);
glProgram.setSamplerTexIdUniform("uTexSampler", inputTexId, /* texUnitIndex= */ 0);

View File

@ -246,13 +246,11 @@ public final class TransformerActivity extends AppCompatActivity {
bundle.getBooleanArray(ConfigurationActivity.DEMO_FRAME_PROCESSORS_SELECTIONS);
if (selectedFrameProcessors != null) {
if (selectedFrameProcessors[0]) {
frameProcessors.add(
AdvancedFrameProcessorFactory.createDizzyCropFrameProcessor(/* context= */ this));
frameProcessors.add(AdvancedFrameProcessorFactory.createDizzyCropFrameProcessor());
}
if (selectedFrameProcessors[1]) {
frameProcessors.add(
new PeriodicVignetteFrameProcessor(
/* context= */ this,
bundle.getFloat(ConfigurationActivity.PERIODIC_VIGNETTE_CENTER_X),
bundle.getFloat(ConfigurationActivity.PERIODIC_VIGNETTE_CENTER_Y),
/* minInnerRadius= */ bundle.getFloat(
@ -262,16 +260,13 @@ public final class TransformerActivity extends AppCompatActivity {
bundle.getFloat(ConfigurationActivity.PERIODIC_VIGNETTE_OUTER_RADIUS)));
}
if (selectedFrameProcessors[2]) {
frameProcessors.add(
AdvancedFrameProcessorFactory.createSpin3dFrameProcessor(/* context= */ this));
frameProcessors.add(AdvancedFrameProcessorFactory.createSpin3dFrameProcessor());
}
if (selectedFrameProcessors[3]) {
frameProcessors.add(new BitmapOverlayFrameProcessor(/* context= */ this));
frameProcessors.add(new BitmapOverlayFrameProcessor());
}
if (selectedFrameProcessors[4]) {
frameProcessors.add(
AdvancedFrameProcessorFactory.createZoomInTransitionFrameProcessor(
/* context= */ this));
frameProcessors.add(AdvancedFrameProcessorFactory.createZoomInTransitionFrameProcessor());
}
transformerBuilder.setFrameProcessors(frameProcessors.build());
}

View File

@ -93,8 +93,8 @@ public final class AdvancedFrameProcessorPixelTest {
public void drawFrame_noEdits_producesExpectedOutput() throws Exception {
String testId = "drawFrame_noEdits";
Matrix identityMatrix = new Matrix();
advancedFrameProcessor = new AdvancedFrameProcessor(getApplicationContext(), identityMatrix);
advancedFrameProcessor.initialize(inputTexId, width, height);
advancedFrameProcessor = new AdvancedFrameProcessor(identityMatrix);
advancedFrameProcessor.initialize(getApplicationContext(), inputTexId, width, height);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ORIGINAL_PNG_ASSET_PATH);
advancedFrameProcessor.drawFrame(/* presentationTimeUs= */ 0);
@ -115,9 +115,8 @@ public final class AdvancedFrameProcessorPixelTest {
String testId = "drawFrame_translateRight";
Matrix translateRightMatrix = new Matrix();
translateRightMatrix.postTranslate(/* dx= */ 1, /* dy= */ 0);
advancedFrameProcessor =
new AdvancedFrameProcessor(getApplicationContext(), translateRightMatrix);
advancedFrameProcessor.initialize(inputTexId, width, height);
advancedFrameProcessor = new AdvancedFrameProcessor(translateRightMatrix);
advancedFrameProcessor.initialize(getApplicationContext(), inputTexId, width, height);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(TRANSLATE_RIGHT_PNG_ASSET_PATH);
advancedFrameProcessor.drawFrame(/* presentationTimeUs= */ 0);
@ -138,8 +137,8 @@ public final class AdvancedFrameProcessorPixelTest {
String testId = "drawFrame_scaleNarrow";
Matrix scaleNarrowMatrix = new Matrix();
scaleNarrowMatrix.postScale(.5f, 1.2f);
advancedFrameProcessor = new AdvancedFrameProcessor(getApplicationContext(), scaleNarrowMatrix);
advancedFrameProcessor.initialize(inputTexId, width, height);
advancedFrameProcessor = new AdvancedFrameProcessor(scaleNarrowMatrix);
advancedFrameProcessor.initialize(getApplicationContext(), inputTexId, width, height);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(SCALE_NARROW_PNG_ASSET_PATH);
advancedFrameProcessor.drawFrame(/* presentationTimeUs= */ 0);
@ -160,8 +159,8 @@ public final class AdvancedFrameProcessorPixelTest {
String testId = "drawFrame_rotate90";
Matrix rotate90Matrix = new Matrix();
rotate90Matrix.postRotate(/* degrees= */ 90);
advancedFrameProcessor = new AdvancedFrameProcessor(getApplicationContext(), rotate90Matrix);
advancedFrameProcessor.initialize(inputTexId, width, height);
advancedFrameProcessor = new AdvancedFrameProcessor(rotate90Matrix);
advancedFrameProcessor.initialize(getApplicationContext(), inputTexId, width, height);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ROTATE_90_PNG_ASSET_PATH);
advancedFrameProcessor.drawFrame(/* presentationTimeUs= */ 0);

View File

@ -110,8 +110,7 @@ public final class FrameProcessorChainPixelTest {
String testId = "processData_withAdvancedFrameProcessor_translateRight";
Matrix translateRightMatrix = new Matrix();
translateRightMatrix.postTranslate(/* dx= */ 1, /* dy= */ 0);
GlFrameProcessor glFrameProcessor =
new AdvancedFrameProcessor(getApplicationContext(), translateRightMatrix);
GlFrameProcessor glFrameProcessor = new AdvancedFrameProcessor(translateRightMatrix);
setUpAndPrepareFirstFrame(glFrameProcessor);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(TRANSLATE_RIGHT_PNG_ASSET_PATH);
@ -133,11 +132,9 @@ public final class FrameProcessorChainPixelTest {
Matrix translateRightMatrix = new Matrix();
translateRightMatrix.postTranslate(/* dx= */ 1, /* dy= */ 0);
GlFrameProcessor translateRightFrameProcessor =
new AdvancedFrameProcessor(getApplicationContext(), translateRightMatrix);
new AdvancedFrameProcessor(translateRightMatrix);
GlFrameProcessor rotate45FrameProcessor =
new ScaleToFitFrameProcessor.Builder(getApplicationContext())
.setRotationDegrees(45)
.build();
new ScaleToFitFrameProcessor.Builder().setRotationDegrees(45).build();
setUpAndPrepareFirstFrame(translateRightFrameProcessor, rotate45FrameProcessor);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(TRANSLATE_THEN_ROTATE_PNG_ASSET_PATH);
@ -157,13 +154,11 @@ public final class FrameProcessorChainPixelTest {
throws Exception {
String testId = "processData_withScaleToFitAndAdvancedFrameProcessors";
GlFrameProcessor rotate45FrameProcessor =
new ScaleToFitFrameProcessor.Builder(getApplicationContext())
.setRotationDegrees(45)
.build();
new ScaleToFitFrameProcessor.Builder().setRotationDegrees(45).build();
Matrix translateRightMatrix = new Matrix();
translateRightMatrix.postTranslate(/* dx= */ 1, /* dy= */ 0);
GlFrameProcessor translateRightFrameProcessor =
new AdvancedFrameProcessor(getApplicationContext(), translateRightMatrix);
new AdvancedFrameProcessor(translateRightMatrix);
setUpAndPrepareFirstFrame(rotate45FrameProcessor, translateRightFrameProcessor);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ROTATE_THEN_TRANSLATE_PNG_ASSET_PATH);
@ -183,7 +178,7 @@ public final class FrameProcessorChainPixelTest {
throws Exception {
String testId = "processData_withPresentationFrameProcessor_setResolution";
GlFrameProcessor glFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext()).setResolution(480).build();
new PresentationFrameProcessor.Builder().setResolution(480).build();
setUpAndPrepareFirstFrame(glFrameProcessor);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(REQUEST_OUTPUT_HEIGHT_PNG_ASSET_PATH);
@ -203,9 +198,7 @@ public final class FrameProcessorChainPixelTest {
throws Exception {
String testId = "processData_withScaleToFitFrameProcessor_rotate45";
GlFrameProcessor glFrameProcessor =
new ScaleToFitFrameProcessor.Builder(getApplicationContext())
.setRotationDegrees(45)
.build();
new ScaleToFitFrameProcessor.Builder().setRotationDegrees(45).build();
setUpAndPrepareFirstFrame(glFrameProcessor);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ROTATE45_SCALE_TO_FIT_PNG_ASSET_PATH);

View File

@ -138,7 +138,7 @@ public final class FrameProcessorChainTest {
}
@Override
public void initialize(int inputTexId, int inputWidth, int inputHeight) {}
public void initialize(Context context, int inputTexId, int inputWidth, int inputHeight) {}
@Override
public Size getOutputSize() {

View File

@ -101,9 +101,9 @@ public final class PresentationFrameProcessorPixelTest {
@Test
public void drawFrame_noEdits_producesExpectedOutput() throws Exception {
String testId = "drawFrame_noEdits";
presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext()).build();
presentationFrameProcessor.initialize(inputTexId, inputWidth, inputHeight);
presentationFrameProcessor = new PresentationFrameProcessor.Builder().build();
presentationFrameProcessor.initialize(
getApplicationContext(), inputTexId, inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ORIGINAL_PNG_ASSET_PATH);
@ -126,10 +126,11 @@ public final class PresentationFrameProcessorPixelTest {
public void drawFrame_cropSmaller_producesExpectedOutput() throws Exception {
String testId = "drawFrame_cropSmaller";
GlFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext())
new PresentationFrameProcessor.Builder()
.setCrop(/* left= */ -.9f, /* right= */ .1f, /* bottom= */ -1f, /* top= */ .5f)
.build();
presentationFrameProcessor.initialize(inputTexId, inputWidth, inputHeight);
presentationFrameProcessor.initialize(
getApplicationContext(), inputTexId, inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(CROP_SMALLER_PNG_ASSET_PATH);
@ -152,10 +153,11 @@ public final class PresentationFrameProcessorPixelTest {
public void drawFrame_cropLarger_producesExpectedOutput() throws Exception {
String testId = "drawFrame_cropSmaller";
GlFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext())
new PresentationFrameProcessor.Builder()
.setCrop(/* left= */ -2f, /* right= */ 2f, /* bottom= */ -1f, /* top= */ 2f)
.build();
presentationFrameProcessor.initialize(inputTexId, inputWidth, inputHeight);
presentationFrameProcessor.initialize(
getApplicationContext(), inputTexId, inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(CROP_LARGER_PNG_ASSET_PATH);
@ -179,10 +181,11 @@ public final class PresentationFrameProcessorPixelTest {
throws Exception {
String testId = "drawFrame_changeAspectRatio_scaleToFit_narrow";
presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext())
new PresentationFrameProcessor.Builder()
.setAspectRatio(1f, PresentationFrameProcessor.SCALE_TO_FIT)
.build();
presentationFrameProcessor.initialize(inputTexId, inputWidth, inputHeight);
presentationFrameProcessor.initialize(
getApplicationContext(), inputTexId, inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
Bitmap expectedBitmap =
@ -207,10 +210,11 @@ public final class PresentationFrameProcessorPixelTest {
throws Exception {
String testId = "drawFrame_changeAspectRatio_scaleToFit_wide";
presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext())
new PresentationFrameProcessor.Builder()
.setAspectRatio(2f, PresentationFrameProcessor.SCALE_TO_FIT)
.build();
presentationFrameProcessor.initialize(inputTexId, inputWidth, inputHeight);
presentationFrameProcessor.initialize(
getApplicationContext(), inputTexId, inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
Bitmap expectedBitmap =
@ -235,10 +239,11 @@ public final class PresentationFrameProcessorPixelTest {
throws Exception {
String testId = "drawFrame_changeAspectRatio_scaleToFitWithCrop_narrow";
presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext())
new PresentationFrameProcessor.Builder()
.setAspectRatio(1f, PresentationFrameProcessor.SCALE_TO_FIT_WITH_CROP)
.build();
presentationFrameProcessor.initialize(inputTexId, inputWidth, inputHeight);
presentationFrameProcessor.initialize(
getApplicationContext(), inputTexId, inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
Bitmap expectedBitmap =
@ -263,10 +268,11 @@ public final class PresentationFrameProcessorPixelTest {
throws Exception {
String testId = "drawFrame_changeAspectRatio_scaleToFitWithCrop_wide";
presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext())
new PresentationFrameProcessor.Builder()
.setAspectRatio(2f, PresentationFrameProcessor.SCALE_TO_FIT_WITH_CROP)
.build();
presentationFrameProcessor.initialize(inputTexId, inputWidth, inputHeight);
presentationFrameProcessor.initialize(
getApplicationContext(), inputTexId, inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
Bitmap expectedBitmap =
@ -291,10 +297,11 @@ public final class PresentationFrameProcessorPixelTest {
throws Exception {
String testId = "drawFrame_changeAspectRatio_stretchToFit_narrow";
presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext())
new PresentationFrameProcessor.Builder()
.setAspectRatio(1f, PresentationFrameProcessor.STRETCH_TO_FIT)
.build();
presentationFrameProcessor.initialize(inputTexId, inputWidth, inputHeight);
presentationFrameProcessor.initialize(
getApplicationContext(), inputTexId, inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
Bitmap expectedBitmap =
@ -319,10 +326,11 @@ public final class PresentationFrameProcessorPixelTest {
throws Exception {
String testId = "drawFrame_changeAspectRatio_stretchToFit_wide";
presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext())
new PresentationFrameProcessor.Builder()
.setAspectRatio(2f, PresentationFrameProcessor.STRETCH_TO_FIT)
.build();
presentationFrameProcessor.initialize(inputTexId, inputWidth, inputHeight);
presentationFrameProcessor.initialize(
getApplicationContext(), inputTexId, inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
Bitmap expectedBitmap =

View File

@ -111,7 +111,6 @@ public final class AdvancedFrameProcessor implements GlFrameProcessor {
return matrix4x4Array;
}
private final Context context;
private final GlMatrixProvider matrixProvider;
private @MonotonicNonNull Size size;
@ -120,37 +119,33 @@ public final class AdvancedFrameProcessor implements GlFrameProcessor {
/**
* Creates a new instance.
*
* @param context The {@link Context}.
* @param transformationMatrix The transformation {@link android.graphics.Matrix} to apply to each
* frame. Operations are done on normalized device coordinates (-1 to 1 on x and y), and no
* automatic adjustments are applied on the transformation matrix.
*/
public AdvancedFrameProcessor(Context context, android.graphics.Matrix transformationMatrix) {
this(context, getGlMatrixArray(transformationMatrix));
public AdvancedFrameProcessor(android.graphics.Matrix transformationMatrix) {
this(getGlMatrixArray(transformationMatrix));
}
/**
* Creates a new instance.
*
* @param context The {@link Context}.
* @param matrixProvider A {@link MatrixProvider} that provides the transformation matrix to apply
* to each frame.
*/
public AdvancedFrameProcessor(Context context, MatrixProvider matrixProvider) {
this.context = context;
public AdvancedFrameProcessor(MatrixProvider matrixProvider) {
this.matrixProvider = matrixProvider;
}
/**
* Creates a new instance.
*
* @param context The {@link Context}.
* @param transformationMatrix The 4x4 transformation {@link android.opengl.Matrix} to apply to
* each frame. Operations are done on normalized device coordinates (-1 to 1 on x and y), and
* no automatic adjustments are applied on the transformation matrix.
*/
public AdvancedFrameProcessor(Context context, float[] transformationMatrix) {
this(context, /* matrixProvider= */ (long presentationTimeUs) -> transformationMatrix.clone());
public AdvancedFrameProcessor(float[] transformationMatrix) {
this(/* matrixProvider= */ (long presentationTimeUs) -> transformationMatrix.clone());
checkArgument(
transformationMatrix.length == 16, "A 4x4 transformation matrix must have 16 elements.");
}
@ -158,17 +153,16 @@ public final class AdvancedFrameProcessor implements GlFrameProcessor {
/**
* Creates a new instance.
*
* @param context The {@link Context}.
* @param matrixProvider A {@link GlMatrixProvider} that updates the transformation matrix for
* each frame.
*/
public AdvancedFrameProcessor(Context context, GlMatrixProvider matrixProvider) {
this.context = context;
public AdvancedFrameProcessor(GlMatrixProvider matrixProvider) {
this.matrixProvider = matrixProvider;
}
@Override
public void initialize(int inputTexId, int inputWidth, int inputHeight) throws IOException {
public void initialize(Context context, int inputTexId, int inputWidth, int inputHeight)
throws IOException {
checkArgument(inputWidth > 0, "inputWidth must be positive");
checkArgument(inputHeight > 0, "inputHeight must be positive");

View File

@ -49,19 +49,18 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
1.683f, -0.652f, 0.0f,
};
private final Context context;
private final boolean enableExperimentalHdrEditing;
private @MonotonicNonNull Size size;
private @MonotonicNonNull GlProgram glProgram;
public ExternalCopyFrameProcessor(Context context, boolean enableExperimentalHdrEditing) {
this.context = context;
public ExternalCopyFrameProcessor(boolean enableExperimentalHdrEditing) {
this.enableExperimentalHdrEditing = enableExperimentalHdrEditing;
}
@Override
public void initialize(int inputTexId, int inputWidth, int inputHeight) throws IOException {
public void initialize(Context context, int inputTexId, int inputWidth, int inputHeight)
throws IOException {
checkArgument(inputWidth > 0, "inputWidth must be positive");
checkArgument(inputHeight > 0, "inputHeight must be positive");

View File

@ -104,13 +104,14 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
ExecutorService singleThreadExecutorService = Util.newSingleThreadExecutor(THREAD_NAME);
ExternalCopyFrameProcessor externalCopyFrameProcessor =
new ExternalCopyFrameProcessor(context, enableExperimentalHdrEditing);
new ExternalCopyFrameProcessor(enableExperimentalHdrEditing);
try {
return singleThreadExecutorService
.submit(
() ->
createOpenGlObjectsAndFrameProcessorChain(
context,
inputWidth,
inputHeight,
frameProcessors,
@ -136,6 +137,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
*/
@WorkerThread
private static FrameProcessorChain createOpenGlObjectsAndFrameProcessorChain(
Context context,
int inputWidth,
int inputHeight,
List<GlFrameProcessor> frameProcessors,
@ -162,14 +164,16 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
}
int inputExternalTexId = GlUtil.createExternalTexture();
externalCopyFrameProcessor.initialize(inputExternalTexId, inputWidth, inputHeight);
externalCopyFrameProcessor.initialize(context, inputExternalTexId, inputWidth, inputHeight);
int[] framebuffers = new int[frameProcessors.size()];
Size inputSize = externalCopyFrameProcessor.getOutputSize();
for (int i = 0; i < frameProcessors.size(); i++) {
int inputTexId = GlUtil.createTexture(inputSize.getWidth(), inputSize.getHeight());
framebuffers[i] = GlUtil.createFboForTexture(inputTexId);
frameProcessors.get(i).initialize(inputTexId, inputSize.getWidth(), inputSize.getHeight());
frameProcessors
.get(i)
.initialize(context, inputTexId, inputSize.getWidth(), inputSize.getHeight());
inputSize = frameProcessors.get(i).getOutputSize();
}
return new FrameProcessorChain(

View File

@ -15,6 +15,7 @@
*/
package androidx.media3.transformer;
import android.content.Context;
import android.util.Size;
import androidx.media3.common.util.UnstableApi;
import java.io.IOException;
@ -26,7 +27,7 @@ import java.io.IOException;
*
* <ol>
* <li>The constructor, for implementation-specific arguments.
* <li>{@link #initialize(int,int,int)}, to set up graphics initialization.
* <li>{@link #initialize(Context, int, int, int)}, to set up graphics initialization.
* <li>{@link #drawFrame(long)}, to process one frame.
* <li>{@link #release()}, upon conclusion of processing.
* </ol>
@ -40,17 +41,19 @@ public interface GlFrameProcessor {
*
* <p>This method may only be called if there is a current OpenGL context.
*
* @param context The {@link Context}.
* @param inputTexId Identifier of a 2D OpenGL texture.
* @param inputWidth The input width, in pixels.
* @param inputHeight The input height, in pixels.
*/
void initialize(int inputTexId, int inputWidth, int inputHeight) throws IOException;
void initialize(Context context, int inputTexId, int inputWidth, int inputHeight)
throws IOException;
/**
* Returns the output {@link Size} of frames processed through {@link #drawFrame(long)}.
*
* <p>This method may only be called after the frame processor has been {@link
* #initialize(int,int,int) initialized}.
* #initialize(Context, int, int, int) initialized}.
*/
Size getOutputSize();
@ -58,8 +61,8 @@ public interface GlFrameProcessor {
* Draws one frame.
*
* <p>This method may only be called after the frame processor has been {@link
* #initialize(int,int,int) initialized}. The caller is responsible for focussing the correct
* render target before calling this method.
* #initialize(Context, int, int, int) initialized}. The caller is responsible for focussing the
* correct render target before calling this method.
*
* <p>A minimal implementation should tell OpenGL to use its shader program, bind the shader
* program's vertex attributes and uniforms, and issue a drawing command.

View File

@ -71,9 +71,6 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
/** A builder for {@link PresentationFrameProcessor} instances. */
public static final class Builder {
// Mandatory field.
private final Context context;
// Optional fields.
private int heightPixels;
private float cropLeft;
@ -83,13 +80,8 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
private float aspectRatio;
private @PresentationStrategy int presentationStrategy;
/**
* Creates a builder with default values.
*
* @param context The {@link Context}.
*/
public Builder(Context context) {
this.context = context;
/** Creates a builder with default values. */
public Builder() {
heightPixels = C.LENGTH_UNSET;
cropLeft = -1f;
cropRight = 1f;
@ -182,7 +174,6 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
public PresentationFrameProcessor build() {
return new PresentationFrameProcessor(
context,
heightPixels,
cropLeft,
cropRight,
@ -197,7 +188,6 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
GlUtil.glAssertionsEnabled = true;
}
private final Context context;
private final int requestedHeightPixels;
private final float cropLeft;
private final float cropRight;
@ -214,7 +204,6 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
/** Creates a new instance. */
private PresentationFrameProcessor(
Context context,
int requestedHeightPixels,
float cropLeft,
float cropRight,
@ -222,7 +211,6 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
float cropTop,
float requestedAspectRatio,
@PresentationStrategy int presentationStrategy) {
this.context = context;
this.requestedHeightPixels = requestedHeightPixels;
this.cropLeft = cropLeft;
this.cropRight = cropRight;
@ -238,10 +226,11 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
}
@Override
public void initialize(int inputTexId, int inputWidth, int inputHeight) throws IOException {
public void initialize(Context context, int inputTexId, int inputWidth, int inputHeight)
throws IOException {
configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
advancedFrameProcessor = new AdvancedFrameProcessor(context, transformationMatrix);
advancedFrameProcessor.initialize(inputTexId, inputWidth, inputHeight);
advancedFrameProcessor = new AdvancedFrameProcessor(transformationMatrix);
advancedFrameProcessor.initialize(context, inputTexId, inputWidth, inputHeight);
}
@Override
@ -257,7 +246,8 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
*
* <p>Return values may be {@code 0} or {@code 90} degrees.
*
* <p>The frame processor must be {@linkplain #initialize(int,int,int) initialized}.
* <p>The frame processor must be {@linkplain GlFrameProcessor#initialize(Context, int, int, int)
* initialized}.
*/
public int getOutputRotationDegrees() {
checkState(

View File

@ -40,22 +40,14 @@ public final class ScaleToFitFrameProcessor implements GlFrameProcessor {
/** A builder for {@link ScaleToFitFrameProcessor} instances. */
public static final class Builder {
// Mandatory field.
private final Context context;
// Optional fields.
private float scaleX;
private float scaleY;
private float rotationDegrees;
/**
* Creates a builder with default values.
*
* @param context The {@link Context}.
*/
public Builder(Context context) {
this.context = context;
/** Creates a builder with default values. */
public Builder() {
scaleX = 1;
scaleY = 1;
rotationDegrees = 0;
@ -90,7 +82,7 @@ public final class ScaleToFitFrameProcessor implements GlFrameProcessor {
}
public ScaleToFitFrameProcessor build() {
return new ScaleToFitFrameProcessor(context, scaleX, scaleY, rotationDegrees);
return new ScaleToFitFrameProcessor(scaleX, scaleY, rotationDegrees);
}
}
@ -98,7 +90,6 @@ public final class ScaleToFitFrameProcessor implements GlFrameProcessor {
GlUtil.glAssertionsEnabled = true;
}
private final Context context;
private final Matrix transformationMatrix;
private @MonotonicNonNull AdvancedFrameProcessor advancedFrameProcessor;
@ -108,25 +99,22 @@ public final class ScaleToFitFrameProcessor implements GlFrameProcessor {
/**
* Creates a new instance.
*
* @param context The {@link Context}.
* @param scaleX The multiplier by which the frame will scale horizontally, along the x-axis.
* @param scaleY The multiplier by which the frame will scale vertically, along the y-axis.
* @param rotationDegrees How much to rotate the frame counterclockwise, in degrees.
*/
private ScaleToFitFrameProcessor(
Context context, float scaleX, float scaleY, float rotationDegrees) {
this.context = context;
private ScaleToFitFrameProcessor(float scaleX, float scaleY, float rotationDegrees) {
this.transformationMatrix = new Matrix();
this.transformationMatrix.postScale(scaleX, scaleY);
this.transformationMatrix.postRotate(rotationDegrees);
}
@Override
public void initialize(int inputTexId, int inputWidth, int inputHeight) throws IOException {
public void initialize(Context context, int inputTexId, int inputWidth, int inputHeight)
throws IOException {
configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
advancedFrameProcessor = new AdvancedFrameProcessor(context, adjustedTransformationMatrix);
advancedFrameProcessor.initialize(inputTexId, inputWidth, inputHeight);
advancedFrameProcessor = new AdvancedFrameProcessor(adjustedTransformationMatrix);
advancedFrameProcessor.initialize(context, inputTexId, inputWidth, inputHeight);
}
@Override

View File

@ -72,12 +72,12 @@ import org.checkerframework.dataflow.qual.Pure;
// TODO(b/213190310): Don't create a ScaleToFitFrameProcessor if scale and rotation are unset.
ScaleToFitFrameProcessor scaleToFitFrameProcessor =
new ScaleToFitFrameProcessor.Builder(context)
new ScaleToFitFrameProcessor.Builder()
.setScale(transformationRequest.scaleX, transformationRequest.scaleY)
.setRotationDegrees(transformationRequest.rotationDegrees)
.build();
PresentationFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder(context)
new PresentationFrameProcessor.Builder()
.setResolution(transformationRequest.outputHeight)
.build();
frameProcessorChain =

View File

@ -15,7 +15,6 @@
*/
package androidx.media3.transformer;
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static org.junit.Assert.assertThrows;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@ -35,11 +34,11 @@ public final class AdvancedFrameProcessorTest {
public void construct_withInvalidMatrixSize_throwsException() {
assertThrows(
IllegalArgumentException.class,
() -> new AdvancedFrameProcessor(getApplicationContext(), new float[4]));
() -> new AdvancedFrameProcessor(/* transformationMatrix= */ new float[4]));
}
@Test
public void construct_withValidMatrixSize_completesSuccessfully() {
new AdvancedFrameProcessor(getApplicationContext(), new float[16]);
new AdvancedFrameProcessor(/* transformationMatrix= */ new float[16]);
}
}

View File

@ -15,7 +15,6 @@
*/
package androidx.media3.transformer;
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
@ -38,7 +37,7 @@ public final class PresentationFrameProcessorTest {
int inputWidth = 200;
int inputHeight = 150;
PresentationFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext()).build();
new PresentationFrameProcessor.Builder().build();
presentationFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
@ -53,7 +52,7 @@ public final class PresentationFrameProcessorTest {
int inputWidth = 150;
int inputHeight = 150;
PresentationFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext()).build();
new PresentationFrameProcessor.Builder().build();
presentationFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
@ -68,7 +67,7 @@ public final class PresentationFrameProcessorTest {
int inputWidth = 150;
int inputHeight = 200;
PresentationFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext()).build();
new PresentationFrameProcessor.Builder().build();
presentationFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
@ -84,9 +83,7 @@ public final class PresentationFrameProcessorTest {
int inputHeight = 150;
int requestedHeight = 300;
PresentationFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext())
.setResolution(requestedHeight)
.build();
new PresentationFrameProcessor.Builder().setResolution(requestedHeight).build();
presentationFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
@ -105,9 +102,7 @@ public final class PresentationFrameProcessorTest {
float bottom = .5f;
float top = 1f;
PresentationFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext())
.setCrop(left, right, bottom, top)
.build();
new PresentationFrameProcessor.Builder().setCrop(left, right, bottom, top).build();
presentationFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = presentationFrameProcessor.getOutputSize();
@ -129,7 +124,7 @@ public final class PresentationFrameProcessorTest {
float top = 1f;
int requestedHeight = 100;
PresentationFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext())
new PresentationFrameProcessor.Builder()
.setCrop(left, right, bottom, top)
.setResolution(requestedHeight)
.build();
@ -156,7 +151,7 @@ public final class PresentationFrameProcessorTest {
float top = 1f;
int requestedHeight = 100;
PresentationFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext())
new PresentationFrameProcessor.Builder()
.setResolution(requestedHeight)
.setCrop(left, right, bottom, top)
.build();
@ -179,7 +174,7 @@ public final class PresentationFrameProcessorTest {
int inputHeight = 200;
float aspectRatio = 2f;
PresentationFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext())
new PresentationFrameProcessor.Builder()
.setAspectRatio(aspectRatio, PresentationFrameProcessor.SCALE_TO_FIT)
.build();
@ -198,7 +193,7 @@ public final class PresentationFrameProcessorTest {
float aspectRatio = 2f;
int requestedHeight = 100;
PresentationFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext())
new PresentationFrameProcessor.Builder()
.setAspectRatio(aspectRatio, PresentationFrameProcessor.SCALE_TO_FIT)
.setResolution(requestedHeight)
.build();
@ -214,7 +209,7 @@ public final class PresentationFrameProcessorTest {
@Test
public void getOutputSize_setAspectRatioAndCrop_throwsIllegalStateException() {
PresentationFrameProcessor.Builder presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext())
new PresentationFrameProcessor.Builder()
.setAspectRatio(/* aspectRatio= */ 2f, PresentationFrameProcessor.SCALE_TO_FIT);
assertThrows(
@ -227,7 +222,7 @@ public final class PresentationFrameProcessorTest {
@Test
public void getOutputSize_setCropAndAspectRatio_throwsIllegalStateException() {
PresentationFrameProcessor.Builder presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext())
new PresentationFrameProcessor.Builder()
.setCrop(/* left= */ -.5f, /* right= */ .5f, /* bottom= */ .5f, /* top= */ 1f);
assertThrows(
@ -240,7 +235,7 @@ public final class PresentationFrameProcessorTest {
@Test
public void getOutputRotationDegreesBeforeConfigure_throwsIllegalStateException() {
PresentationFrameProcessor presentationFrameProcessor =
new PresentationFrameProcessor.Builder(getApplicationContext()).build();
new PresentationFrameProcessor.Builder().build();
// configureOutputSize not called before getOutputRotationDegrees.
assertThrows(IllegalStateException.class, presentationFrameProcessor::getOutputRotationDegrees);

View File

@ -15,7 +15,6 @@
*/
package androidx.media3.transformer;
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static com.google.common.truth.Truth.assertThat;
import android.util.Size;
@ -37,7 +36,7 @@ public final class ScaleToFitFrameProcessorTest {
int inputWidth = 200;
int inputHeight = 150;
ScaleToFitFrameProcessor scaleToFitFrameProcessor =
new ScaleToFitFrameProcessor.Builder(getApplicationContext()).build();
new ScaleToFitFrameProcessor.Builder().build();
scaleToFitFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
@ -51,7 +50,7 @@ public final class ScaleToFitFrameProcessorTest {
int inputWidth = 200;
int inputHeight = 150;
ScaleToFitFrameProcessor scaleToFitFrameProcessor =
new ScaleToFitFrameProcessor.Builder(getApplicationContext())
new ScaleToFitFrameProcessor.Builder()
.setScale(/* scaleX= */ .5f, /* scaleY= */ 1f)
.build();
@ -67,9 +66,7 @@ public final class ScaleToFitFrameProcessorTest {
int inputWidth = 200;
int inputHeight = 150;
ScaleToFitFrameProcessor scaleToFitFrameProcessor =
new ScaleToFitFrameProcessor.Builder(getApplicationContext())
.setScale(/* scaleX= */ 2f, /* scaleY= */ 1f)
.build();
new ScaleToFitFrameProcessor.Builder().setScale(/* scaleX= */ 2f, /* scaleY= */ 1f).build();
scaleToFitFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
@ -83,9 +80,7 @@ public final class ScaleToFitFrameProcessorTest {
int inputWidth = 200;
int inputHeight = 150;
ScaleToFitFrameProcessor scaleToFitFrameProcessor =
new ScaleToFitFrameProcessor.Builder(getApplicationContext())
.setScale(/* scaleX= */ 1f, /* scaleY= */ 2f)
.build();
new ScaleToFitFrameProcessor.Builder().setScale(/* scaleX= */ 1f, /* scaleY= */ 2f).build();
scaleToFitFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
@ -99,9 +94,7 @@ public final class ScaleToFitFrameProcessorTest {
int inputWidth = 200;
int inputHeight = 150;
ScaleToFitFrameProcessor scaleToFitFrameProcessor =
new ScaleToFitFrameProcessor.Builder(getApplicationContext())
.setRotationDegrees(90)
.build();
new ScaleToFitFrameProcessor.Builder().setRotationDegrees(90).build();
scaleToFitFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
@ -115,9 +108,7 @@ public final class ScaleToFitFrameProcessorTest {
int inputWidth = 200;
int inputHeight = 150;
ScaleToFitFrameProcessor scaleToFitFrameProcessor =
new ScaleToFitFrameProcessor.Builder(getApplicationContext())
.setRotationDegrees(45)
.build();
new ScaleToFitFrameProcessor.Builder().setRotationDegrees(45).build();
long expectedOutputWidthHeight = 247;
scaleToFitFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);