mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Merge GlFrameProcessor#setInputSize() and initialize().
PiperOrigin-RevId: 439266087
This commit is contained in:
parent
ea61511a24
commit
de00030f98
@ -91,7 +91,7 @@ public final class AdvancedFrameProcessorPixelTest {
|
|||||||
String testId = "updateProgramAndDraw_noEdits";
|
String testId = "updateProgramAndDraw_noEdits";
|
||||||
Matrix identityMatrix = new Matrix();
|
Matrix identityMatrix = new Matrix();
|
||||||
advancedFrameProcessor = new AdvancedFrameProcessor(getApplicationContext(), identityMatrix);
|
advancedFrameProcessor = new AdvancedFrameProcessor(getApplicationContext(), identityMatrix);
|
||||||
advancedFrameProcessor.initialize(inputTexId);
|
advancedFrameProcessor.initialize(inputTexId, width, height);
|
||||||
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(FIRST_FRAME_PNG_ASSET_STRING);
|
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(FIRST_FRAME_PNG_ASSET_STRING);
|
||||||
|
|
||||||
advancedFrameProcessor.updateProgramAndDraw(/* presentationTimeUs= */ 0);
|
advancedFrameProcessor.updateProgramAndDraw(/* presentationTimeUs= */ 0);
|
||||||
@ -114,7 +114,7 @@ public final class AdvancedFrameProcessorPixelTest {
|
|||||||
translateRightMatrix.postTranslate(/* dx= */ 1, /* dy= */ 0);
|
translateRightMatrix.postTranslate(/* dx= */ 1, /* dy= */ 0);
|
||||||
advancedFrameProcessor =
|
advancedFrameProcessor =
|
||||||
new AdvancedFrameProcessor(getApplicationContext(), translateRightMatrix);
|
new AdvancedFrameProcessor(getApplicationContext(), translateRightMatrix);
|
||||||
advancedFrameProcessor.initialize(inputTexId);
|
advancedFrameProcessor.initialize(inputTexId, width, height);
|
||||||
Bitmap expectedBitmap =
|
Bitmap expectedBitmap =
|
||||||
BitmapTestUtil.readBitmap(TRANSLATE_RIGHT_EXPECTED_OUTPUT_PNG_ASSET_STRING);
|
BitmapTestUtil.readBitmap(TRANSLATE_RIGHT_EXPECTED_OUTPUT_PNG_ASSET_STRING);
|
||||||
|
|
||||||
@ -137,7 +137,7 @@ public final class AdvancedFrameProcessorPixelTest {
|
|||||||
Matrix scaleNarrowMatrix = new Matrix();
|
Matrix scaleNarrowMatrix = new Matrix();
|
||||||
scaleNarrowMatrix.postScale(.5f, 1.2f);
|
scaleNarrowMatrix.postScale(.5f, 1.2f);
|
||||||
advancedFrameProcessor = new AdvancedFrameProcessor(getApplicationContext(), scaleNarrowMatrix);
|
advancedFrameProcessor = new AdvancedFrameProcessor(getApplicationContext(), scaleNarrowMatrix);
|
||||||
advancedFrameProcessor.initialize(inputTexId);
|
advancedFrameProcessor.initialize(inputTexId, width, height);
|
||||||
Bitmap expectedBitmap =
|
Bitmap expectedBitmap =
|
||||||
BitmapTestUtil.readBitmap(SCALE_NARROW_EXPECTED_OUTPUT_PNG_ASSET_STRING);
|
BitmapTestUtil.readBitmap(SCALE_NARROW_EXPECTED_OUTPUT_PNG_ASSET_STRING);
|
||||||
|
|
||||||
@ -160,7 +160,7 @@ public final class AdvancedFrameProcessorPixelTest {
|
|||||||
Matrix rotate90Matrix = new Matrix();
|
Matrix rotate90Matrix = new Matrix();
|
||||||
rotate90Matrix.postRotate(/* degrees= */ 90);
|
rotate90Matrix.postRotate(/* degrees= */ 90);
|
||||||
advancedFrameProcessor = new AdvancedFrameProcessor(getApplicationContext(), rotate90Matrix);
|
advancedFrameProcessor = new AdvancedFrameProcessor(getApplicationContext(), rotate90Matrix);
|
||||||
advancedFrameProcessor.initialize(inputTexId);
|
advancedFrameProcessor.initialize(inputTexId, width, height);
|
||||||
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ROTATE_90_EXPECTED_OUTPUT_PNG_ASSET_STRING);
|
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ROTATE_90_EXPECTED_OUTPUT_PNG_ASSET_STRING);
|
||||||
|
|
||||||
advancedFrameProcessor.updateProgramAndDraw(/* presentationTimeUs= */ 0);
|
advancedFrameProcessor.updateProgramAndDraw(/* presentationTimeUs= */ 0);
|
||||||
|
@ -138,16 +138,13 @@ public final class FrameProcessorChainTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setInputSize(int inputWidth, int inputHeight) {}
|
public void initialize(int inputTexId, int inputWidth, int inputHeight) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Size getOutputSize() {
|
public Size getOutputSize() {
|
||||||
return outputSize;
|
return outputSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(int inputTexId) {}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateProgramAndDraw(long presentationTimeNs) {}
|
public void updateProgramAndDraw(long presentationTimeNs) {}
|
||||||
|
|
||||||
|
@ -106,17 +106,8 @@ public final class AdvancedFrameProcessor implements GlFrameProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setInputSize(int inputWidth, int inputHeight) {
|
public void initialize(int inputTexId, int inputWidth, int inputHeight) throws IOException {
|
||||||
size = new Size(inputWidth, inputHeight);
|
size = new Size(inputWidth, inputHeight);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Size getOutputSize() {
|
|
||||||
return checkStateNotNull(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(int inputTexId) throws IOException {
|
|
||||||
// TODO(b/205002913): check the loaded program is consistent with the attributes and uniforms
|
// TODO(b/205002913): check the loaded program is consistent with the attributes and uniforms
|
||||||
// expected in the code.
|
// expected in the code.
|
||||||
glProgram = new GlProgram(context, VERTEX_SHADER_TRANSFORMATION_PATH, FRAGMENT_SHADER_PATH);
|
glProgram = new GlProgram(context, VERTEX_SHADER_TRANSFORMATION_PATH, FRAGMENT_SHADER_PATH);
|
||||||
@ -129,6 +120,11 @@ public final class AdvancedFrameProcessor implements GlFrameProcessor {
|
|||||||
glProgram.setFloatsUniform("uTransformationMatrix", getGlMatrixArray(transformationMatrix));
|
glProgram.setFloatsUniform("uTransformationMatrix", getGlMatrixArray(transformationMatrix));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Size getOutputSize() {
|
||||||
|
return checkStateNotNull(size);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateProgramAndDraw(long presentationTimeUs) {
|
public void updateProgramAndDraw(long presentationTimeUs) {
|
||||||
checkStateNotNull(glProgram);
|
checkStateNotNull(glProgram);
|
||||||
|
@ -60,17 +60,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setInputSize(int inputWidth, int inputHeight) {
|
public void initialize(int inputTexId, int inputWidth, int inputHeight) throws IOException {
|
||||||
size = new Size(inputWidth, inputHeight);
|
size = new Size(inputWidth, inputHeight);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Size getOutputSize() {
|
|
||||||
return checkStateNotNull(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(int inputTexId) throws IOException {
|
|
||||||
// TODO(b/205002913): check the loaded program is consistent with the attributes and uniforms
|
// TODO(b/205002913): check the loaded program is consistent with the attributes and uniforms
|
||||||
// expected in the code.
|
// expected in the code.
|
||||||
String vertexShaderFilePath =
|
String vertexShaderFilePath =
|
||||||
@ -94,6 +85,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Size getOutputSize() {
|
||||||
|
return checkStateNotNull(size);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the texture transform matrix for converting an external surface texture's coordinates to
|
* Sets the texture transform matrix for converting an external surface texture's coordinates to
|
||||||
* sampling locations.
|
* sampling locations.
|
||||||
|
@ -154,16 +154,14 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
int inputExternalTexId = GlUtil.createExternalTexture();
|
int inputExternalTexId = GlUtil.createExternalTexture();
|
||||||
externalCopyFrameProcessor.setInputSize(inputWidth, inputHeight);
|
externalCopyFrameProcessor.initialize(inputExternalTexId, inputWidth, inputHeight);
|
||||||
externalCopyFrameProcessor.initialize(inputExternalTexId);
|
|
||||||
|
|
||||||
int[] framebuffers = new int[frameProcessors.size()];
|
int[] framebuffers = new int[frameProcessors.size()];
|
||||||
Size inputSize = externalCopyFrameProcessor.getOutputSize();
|
Size inputSize = externalCopyFrameProcessor.getOutputSize();
|
||||||
for (int i = 0; i < frameProcessors.size(); i++) {
|
for (int i = 0; i < frameProcessors.size(); i++) {
|
||||||
int inputTexId = GlUtil.createTexture(inputSize.getWidth(), inputSize.getHeight());
|
int inputTexId = GlUtil.createTexture(inputSize.getWidth(), inputSize.getHeight());
|
||||||
framebuffers[i] = GlUtil.createFboForTexture(inputTexId);
|
framebuffers[i] = GlUtil.createFboForTexture(inputTexId);
|
||||||
frameProcessors.get(i).setInputSize(inputSize.getWidth(), inputSize.getHeight());
|
frameProcessors.get(i).initialize(inputTexId, inputSize.getWidth(), inputSize.getHeight());
|
||||||
frameProcessors.get(i).initialize(inputTexId);
|
|
||||||
inputSize = frameProcessors.get(i).getOutputSize();
|
inputSize = frameProcessors.get(i).getOutputSize();
|
||||||
}
|
}
|
||||||
return new FrameProcessorChain(
|
return new FrameProcessorChain(
|
||||||
|
@ -26,50 +26,41 @@ import java.io.IOException;
|
|||||||
*
|
*
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>The constructor, for implementation-specific arguments.
|
* <li>The constructor, for implementation-specific arguments.
|
||||||
* <li>{@link #setInputSize(int, int)}, to configure based on input dimensions.
|
* <li>{@link #initialize(int,int,int)}, to set up graphics initialization.
|
||||||
* <li>{@link #initialize(int)}, to set up graphics initialization.
|
|
||||||
* <li>{@link #updateProgramAndDraw(long)}, to process one frame.
|
* <li>{@link #updateProgramAndDraw(long)}, to process one frame.
|
||||||
* <li>{@link #release()}, upon conclusion of processing.
|
* <li>{@link #release()}, upon conclusion of processing.
|
||||||
* </ol>
|
* </ol>
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public interface GlFrameProcessor {
|
public interface GlFrameProcessor {
|
||||||
// TODO(b/213313666): Investigate whether all configuration can be moved to initialize by
|
|
||||||
// using a placeholder surface until the encoder surface is known. If so, convert
|
|
||||||
// configureOutputSize to a simple getter.
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the input size of frames processed through {@link #updateProgramAndDraw(long)}.
|
* Performs all initialization that requires OpenGL, such as, loading and compiling a GLSL shader
|
||||||
|
* program.
|
||||||
*
|
*
|
||||||
* <p>This method must be called before {@link #initialize(int)} and does not use OpenGL, as
|
* <p>This method may only be called if there is a current OpenGL context.
|
||||||
* calling this method without a current OpenGL context is allowed.
|
|
||||||
*
|
*
|
||||||
* <p>After setting the input size, the output size can be obtained using {@link
|
* @param inputTexId Identifier of a 2D OpenGL texture.
|
||||||
* #getOutputSize()}.
|
* @param inputWidth The input width, in pixels.
|
||||||
|
* @param inputHeight The input height, in pixels.
|
||||||
*/
|
*/
|
||||||
void setInputSize(int inputWidth, int inputHeight);
|
void initialize(int inputTexId, int inputWidth, int inputHeight) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the output {@link Size} of frames processed through {@link
|
* Returns the output {@link Size} of frames processed through {@link
|
||||||
* #updateProgramAndDraw(long)}.
|
* #updateProgramAndDraw(long)}.
|
||||||
*
|
*
|
||||||
* <p>Must call {@link #setInputSize(int, int)} before calling this method.
|
* <p>This method may only be called after the frame processor has been {@link
|
||||||
|
* #initialize(int,int,int) initialized}.
|
||||||
*/
|
*/
|
||||||
Size getOutputSize();
|
Size getOutputSize();
|
||||||
|
|
||||||
/**
|
|
||||||
* Does any initialization necessary such as loading and compiling a GLSL shader programs.
|
|
||||||
*
|
|
||||||
* <p>This method may only be called after creating the OpenGL context and focusing a render
|
|
||||||
* target.
|
|
||||||
*/
|
|
||||||
void initialize(int inputTexId) throws IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the shader program's vertex attributes and uniforms, binds them, and draws.
|
* Updates the shader program's vertex attributes and uniforms, binds them, and draws.
|
||||||
*
|
*
|
||||||
* <p>The frame processor must be {@linkplain #initialize(int) initialized}. The caller is
|
* <p>This method may only be called after the frame processor has been {@link
|
||||||
* responsible for focussing the correct render target before calling this method.
|
* #initialize(int,int,int) initialized}. The caller is responsible for focussing the correct
|
||||||
|
* render target before calling this method.
|
||||||
*
|
*
|
||||||
* @param presentationTimeUs The presentation timestamp of the current frame, in microseconds.
|
* @param presentationTimeUs The presentation timestamp of the current frame, in microseconds.
|
||||||
*/
|
*/
|
||||||
|
@ -21,11 +21,13 @@ import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.util.Size;
|
import android.util.Size;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.util.GlUtil;
|
import androidx.media3.common.util.GlUtil;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
/** Controls how a frame is viewed, by changing resolution. */
|
/** Controls how a frame is viewed, by changing resolution. */
|
||||||
@ -35,6 +37,7 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
|
|||||||
|
|
||||||
/** A builder for {@link PresentationFrameProcessor} instances. */
|
/** A builder for {@link PresentationFrameProcessor} instances. */
|
||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
|
|
||||||
// Mandatory field.
|
// Mandatory field.
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
@ -80,12 +83,10 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
|
|||||||
private final Context context;
|
private final Context context;
|
||||||
private final int requestedHeight;
|
private final int requestedHeight;
|
||||||
|
|
||||||
private @MonotonicNonNull AdvancedFrameProcessor advancedFrameProcessor;
|
|
||||||
private int inputWidth;
|
|
||||||
private int inputHeight;
|
|
||||||
private int outputRotationDegrees;
|
|
||||||
private @MonotonicNonNull Size outputSize;
|
private @MonotonicNonNull Size outputSize;
|
||||||
|
private int outputRotationDegrees;
|
||||||
private @MonotonicNonNull Matrix transformationMatrix;
|
private @MonotonicNonNull Matrix transformationMatrix;
|
||||||
|
private @MonotonicNonNull AdvancedFrameProcessor advancedFrameProcessor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
@ -97,17 +98,27 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
|
|||||||
this.context = context;
|
this.context = context;
|
||||||
this.requestedHeight = requestedHeight;
|
this.requestedHeight = requestedHeight;
|
||||||
|
|
||||||
inputWidth = C.LENGTH_UNSET;
|
|
||||||
inputHeight = C.LENGTH_UNSET;
|
|
||||||
outputRotationDegrees = C.LENGTH_UNSET;
|
outputRotationDegrees = C.LENGTH_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(int inputTexId, int inputWidth, int inputHeight) throws IOException {
|
||||||
|
configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
|
||||||
|
advancedFrameProcessor = new AdvancedFrameProcessor(context, transformationMatrix);
|
||||||
|
advancedFrameProcessor.initialize(inputTexId, inputWidth, inputHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Size getOutputSize() {
|
||||||
|
return checkStateNotNull(outputSize);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@link Format#rotationDegrees} for the output frame.
|
* Returns {@link Format#rotationDegrees} for the output frame.
|
||||||
*
|
*
|
||||||
* <p>Return values may be {@code 0} or {@code 90} degrees.
|
* <p>Return values may be {@code 0} or {@code 90} degrees.
|
||||||
*
|
*
|
||||||
* <p>This method can only be called after {@link #setInputSize(int, int)}.
|
* <p>The frame processor must be {@linkplain #initialize(int,int,int) initialized}.
|
||||||
*/
|
*/
|
||||||
public int getOutputRotationDegrees() {
|
public int getOutputRotationDegrees() {
|
||||||
checkState(outputRotationDegrees != C.LENGTH_UNSET);
|
checkState(outputRotationDegrees != C.LENGTH_UNSET);
|
||||||
@ -115,20 +126,28 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setInputSize(int inputWidth, int inputHeight) {
|
public void updateProgramAndDraw(long presentationTimeUs) {
|
||||||
this.inputWidth = inputWidth;
|
checkStateNotNull(advancedFrameProcessor).updateProgramAndDraw(presentationTimeUs);
|
||||||
this.inputHeight = inputHeight;
|
}
|
||||||
transformationMatrix = new Matrix();
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void release() {
|
||||||
|
if (advancedFrameProcessor != null) {
|
||||||
|
advancedFrameProcessor.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnsuresNonNull("transformationMatrix")
|
||||||
|
@VisibleForTesting // Allows roboletric testing of output size calculation without OpenGL.
|
||||||
|
/* package */ void configureOutputSizeAndTransformationMatrix(int inputWidth, int inputHeight) {
|
||||||
|
transformationMatrix = new Matrix();
|
||||||
int displayWidth = inputWidth;
|
int displayWidth = inputWidth;
|
||||||
int displayHeight = inputHeight;
|
int displayHeight = inputHeight;
|
||||||
|
|
||||||
// Scale width and height to desired requestedHeight, preserving aspect ratio.
|
// Scale width and height to desired requestedHeight, preserving aspect ratio.
|
||||||
if (requestedHeight != C.LENGTH_UNSET && requestedHeight != displayHeight) {
|
if (requestedHeight != C.LENGTH_UNSET && requestedHeight != displayHeight) {
|
||||||
displayWidth = Math.round((float) requestedHeight * displayWidth / displayHeight);
|
displayWidth = Math.round((float) requestedHeight * displayWidth / displayHeight);
|
||||||
displayHeight = requestedHeight;
|
displayHeight = requestedHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encoders commonly support higher maximum widths than maximum heights. Rotate the decoded
|
// Encoders commonly support higher maximum widths than maximum heights. Rotate the decoded
|
||||||
// frame before encoding, so the encoded frame's width >= height, and set
|
// frame before encoding, so the encoded frame's width >= height, and set
|
||||||
// outputRotationDegrees to ensure the frame is displayed in the correct orientation.
|
// outputRotationDegrees to ensure the frame is displayed in the correct orientation.
|
||||||
@ -143,29 +162,4 @@ public final class PresentationFrameProcessor implements GlFrameProcessor {
|
|||||||
outputSize = new Size(displayWidth, displayHeight);
|
outputSize = new Size(displayWidth, displayHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Size getOutputSize() {
|
|
||||||
return checkStateNotNull(outputSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(int inputTexId) throws IOException {
|
|
||||||
checkStateNotNull(transformationMatrix);
|
|
||||||
advancedFrameProcessor = new AdvancedFrameProcessor(context, transformationMatrix);
|
|
||||||
advancedFrameProcessor.setInputSize(inputWidth, inputHeight);
|
|
||||||
advancedFrameProcessor.initialize(inputTexId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateProgramAndDraw(long presentationTimeUs) {
|
|
||||||
checkStateNotNull(advancedFrameProcessor).updateProgramAndDraw(presentationTimeUs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void release() {
|
|
||||||
if (advancedFrameProcessor != null) {
|
|
||||||
advancedFrameProcessor.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -22,10 +22,11 @@ import static java.lang.Math.min;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.util.Size;
|
import android.util.Size;
|
||||||
import androidx.media3.common.C;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.media3.common.util.GlUtil;
|
import androidx.media3.common.util.GlUtil;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,8 +101,6 @@ public final class ScaleToFitFrameProcessor implements GlFrameProcessor {
|
|||||||
private final Matrix transformationMatrix;
|
private final Matrix transformationMatrix;
|
||||||
|
|
||||||
private @MonotonicNonNull AdvancedFrameProcessor advancedFrameProcessor;
|
private @MonotonicNonNull AdvancedFrameProcessor advancedFrameProcessor;
|
||||||
private int inputWidth;
|
|
||||||
private int inputHeight;
|
|
||||||
private @MonotonicNonNull Size outputSize;
|
private @MonotonicNonNull Size outputSize;
|
||||||
private @MonotonicNonNull Matrix adjustedTransformationMatrix;
|
private @MonotonicNonNull Matrix adjustedTransformationMatrix;
|
||||||
|
|
||||||
@ -120,15 +119,35 @@ public final class ScaleToFitFrameProcessor implements GlFrameProcessor {
|
|||||||
this.transformationMatrix = new Matrix();
|
this.transformationMatrix = new Matrix();
|
||||||
this.transformationMatrix.postScale(scaleX, scaleY);
|
this.transformationMatrix.postScale(scaleX, scaleY);
|
||||||
this.transformationMatrix.postRotate(rotationDegrees);
|
this.transformationMatrix.postRotate(rotationDegrees);
|
||||||
|
|
||||||
inputWidth = C.LENGTH_UNSET;
|
|
||||||
inputHeight = C.LENGTH_UNSET;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setInputSize(int inputWidth, int inputHeight) {
|
public void initialize(int inputTexId, int inputWidth, int inputHeight) throws IOException {
|
||||||
this.inputWidth = inputWidth;
|
configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
|
||||||
this.inputHeight = inputHeight;
|
advancedFrameProcessor = new AdvancedFrameProcessor(context, adjustedTransformationMatrix);
|
||||||
|
advancedFrameProcessor.initialize(inputTexId, inputWidth, inputHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Size getOutputSize() {
|
||||||
|
return checkStateNotNull(outputSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateProgramAndDraw(long presentationTimeUs) {
|
||||||
|
checkStateNotNull(advancedFrameProcessor).updateProgramAndDraw(presentationTimeUs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void release() {
|
||||||
|
if (advancedFrameProcessor != null) {
|
||||||
|
advancedFrameProcessor.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnsuresNonNull("adjustedTransformationMatrix")
|
||||||
|
@VisibleForTesting // Allows roboletric testing of output size calculation without OpenGL.
|
||||||
|
/* package */ void configureOutputSizeAndTransformationMatrix(int inputWidth, int inputHeight) {
|
||||||
adjustedTransformationMatrix = new Matrix(transformationMatrix);
|
adjustedTransformationMatrix = new Matrix(transformationMatrix);
|
||||||
|
|
||||||
if (transformationMatrix.isIdentity()) {
|
if (transformationMatrix.isIdentity()) {
|
||||||
@ -165,29 +184,4 @@ public final class ScaleToFitFrameProcessor implements GlFrameProcessor {
|
|||||||
adjustedTransformationMatrix.postScale(1f / xScale, 1f / yScale);
|
adjustedTransformationMatrix.postScale(1f / xScale, 1f / yScale);
|
||||||
outputSize = new Size(Math.round(inputWidth * xScale), Math.round(inputHeight * yScale));
|
outputSize = new Size(Math.round(inputWidth * xScale), Math.round(inputHeight * yScale));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Size getOutputSize() {
|
|
||||||
return checkStateNotNull(outputSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(int inputTexId) throws IOException {
|
|
||||||
checkStateNotNull(adjustedTransformationMatrix);
|
|
||||||
advancedFrameProcessor = new AdvancedFrameProcessor(context, adjustedTransformationMatrix);
|
|
||||||
advancedFrameProcessor.setInputSize(inputWidth, inputHeight);
|
|
||||||
advancedFrameProcessor.initialize(inputTexId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateProgramAndDraw(long presentationTimeUs) {
|
|
||||||
checkStateNotNull(advancedFrameProcessor).updateProgramAndDraw(presentationTimeUs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void release() {
|
|
||||||
if (advancedFrameProcessor != null) {
|
|
||||||
advancedFrameProcessor.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2022 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package androidx.media3.transformer;
|
|
||||||
|
|
||||||
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import android.graphics.Matrix;
|
|
||||||
import android.util.Size;
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unit tests for {@link AdvancedFrameProcessor}.
|
|
||||||
*
|
|
||||||
* <p>See {@code AdvancedFrameProcessorPixelTest} for pixel tests testing {@link
|
|
||||||
* AdvancedFrameProcessor} given a transformation matrix.
|
|
||||||
*/
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public final class AdvancedFrameProcessorTest {
|
|
||||||
@Test
|
|
||||||
public void getOutputSize_withIdentityMatrix_leavesSizeUnchanged() {
|
|
||||||
Matrix identityMatrix = new Matrix();
|
|
||||||
int inputWidth = 200;
|
|
||||||
int inputHeight = 150;
|
|
||||||
AdvancedFrameProcessor advancedFrameProcessor =
|
|
||||||
new AdvancedFrameProcessor(getApplicationContext(), identityMatrix);
|
|
||||||
|
|
||||||
advancedFrameProcessor.setInputSize(inputWidth, inputHeight);
|
|
||||||
Size outputSize = advancedFrameProcessor.getOutputSize();
|
|
||||||
|
|
||||||
assertThat(outputSize.getWidth()).isEqualTo(inputWidth);
|
|
||||||
assertThat(outputSize.getHeight()).isEqualTo(inputHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getOutputSize_withTransformationMatrix_leavesSizeUnchanged() {
|
|
||||||
Matrix transformationMatrix = new Matrix();
|
|
||||||
transformationMatrix.postRotate(/* degrees= */ 90);
|
|
||||||
transformationMatrix.postScale(/* sx= */ .5f, /* sy= */ 1.2f);
|
|
||||||
int inputWidth = 200;
|
|
||||||
int inputHeight = 150;
|
|
||||||
AdvancedFrameProcessor advancedFrameProcessor =
|
|
||||||
new AdvancedFrameProcessor(getApplicationContext(), transformationMatrix);
|
|
||||||
|
|
||||||
advancedFrameProcessor.setInputSize(inputWidth, inputHeight);
|
|
||||||
Size outputSize = advancedFrameProcessor.getOutputSize();
|
|
||||||
|
|
||||||
assertThat(outputSize.getWidth()).isEqualTo(inputWidth);
|
|
||||||
assertThat(outputSize.getHeight()).isEqualTo(inputHeight);
|
|
||||||
}
|
|
||||||
}
|
|
@ -39,7 +39,7 @@ public final class PresentationFrameProcessorTest {
|
|||||||
PresentationFrameProcessor presentationFrameProcessor =
|
PresentationFrameProcessor presentationFrameProcessor =
|
||||||
new PresentationFrameProcessor.Builder(getApplicationContext()).build();
|
new PresentationFrameProcessor.Builder(getApplicationContext()).build();
|
||||||
|
|
||||||
presentationFrameProcessor.setInputSize(inputWidth, inputHeight);
|
presentationFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
|
||||||
Size outputSize = presentationFrameProcessor.getOutputSize();
|
Size outputSize = presentationFrameProcessor.getOutputSize();
|
||||||
|
|
||||||
assertThat(presentationFrameProcessor.getOutputRotationDegrees()).isEqualTo(0);
|
assertThat(presentationFrameProcessor.getOutputRotationDegrees()).isEqualTo(0);
|
||||||
@ -54,7 +54,7 @@ public final class PresentationFrameProcessorTest {
|
|||||||
PresentationFrameProcessor presentationFrameProcessor =
|
PresentationFrameProcessor presentationFrameProcessor =
|
||||||
new PresentationFrameProcessor.Builder(getApplicationContext()).build();
|
new PresentationFrameProcessor.Builder(getApplicationContext()).build();
|
||||||
|
|
||||||
presentationFrameProcessor.setInputSize(inputWidth, inputHeight);
|
presentationFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
|
||||||
Size outputSize = presentationFrameProcessor.getOutputSize();
|
Size outputSize = presentationFrameProcessor.getOutputSize();
|
||||||
|
|
||||||
assertThat(presentationFrameProcessor.getOutputRotationDegrees()).isEqualTo(0);
|
assertThat(presentationFrameProcessor.getOutputRotationDegrees()).isEqualTo(0);
|
||||||
@ -69,7 +69,7 @@ public final class PresentationFrameProcessorTest {
|
|||||||
PresentationFrameProcessor presentationFrameProcessor =
|
PresentationFrameProcessor presentationFrameProcessor =
|
||||||
new PresentationFrameProcessor.Builder(getApplicationContext()).build();
|
new PresentationFrameProcessor.Builder(getApplicationContext()).build();
|
||||||
|
|
||||||
presentationFrameProcessor.setInputSize(inputWidth, inputHeight);
|
presentationFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
|
||||||
Size outputSize = presentationFrameProcessor.getOutputSize();
|
Size outputSize = presentationFrameProcessor.getOutputSize();
|
||||||
|
|
||||||
assertThat(presentationFrameProcessor.getOutputRotationDegrees()).isEqualTo(90);
|
assertThat(presentationFrameProcessor.getOutputRotationDegrees()).isEqualTo(90);
|
||||||
@ -87,7 +87,7 @@ public final class PresentationFrameProcessorTest {
|
|||||||
.setResolution(requestedHeight)
|
.setResolution(requestedHeight)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
presentationFrameProcessor.setInputSize(inputWidth, inputHeight);
|
presentationFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
|
||||||
Size outputSize = presentationFrameProcessor.getOutputSize();
|
Size outputSize = presentationFrameProcessor.getOutputSize();
|
||||||
|
|
||||||
assertThat(presentationFrameProcessor.getOutputRotationDegrees()).isEqualTo(0);
|
assertThat(presentationFrameProcessor.getOutputRotationDegrees()).isEqualTo(0);
|
||||||
|
@ -17,7 +17,6 @@ package androidx.media3.transformer;
|
|||||||
|
|
||||||
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
|
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.junit.Assert.assertThrows;
|
|
||||||
|
|
||||||
import android.util.Size;
|
import android.util.Size;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
@ -40,24 +39,13 @@ public final class ScaleToFitFrameProcessorTest {
|
|||||||
ScaleToFitFrameProcessor scaleToFitFrameProcessor =
|
ScaleToFitFrameProcessor scaleToFitFrameProcessor =
|
||||||
new ScaleToFitFrameProcessor.Builder(getApplicationContext()).build();
|
new ScaleToFitFrameProcessor.Builder(getApplicationContext()).build();
|
||||||
|
|
||||||
scaleToFitFrameProcessor.setInputSize(inputWidth, inputHeight);
|
scaleToFitFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
|
||||||
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
|
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
|
||||||
|
|
||||||
assertThat(outputSize.getWidth()).isEqualTo(inputWidth);
|
assertThat(outputSize.getWidth()).isEqualTo(inputWidth);
|
||||||
assertThat(outputSize.getHeight()).isEqualTo(inputHeight);
|
assertThat(outputSize.getHeight()).isEqualTo(inputHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void initializeBeforeConfigure_throwsIllegalStateException() {
|
|
||||||
ScaleToFitFrameProcessor scaleToFitFrameProcessor =
|
|
||||||
new ScaleToFitFrameProcessor.Builder(getApplicationContext()).build();
|
|
||||||
|
|
||||||
// configureOutputSize not called before initialize.
|
|
||||||
assertThrows(
|
|
||||||
IllegalStateException.class,
|
|
||||||
() -> scaleToFitFrameProcessor.initialize(/* inputTexId= */ 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getOutputSize_scaleNarrow_decreasesWidth() {
|
public void getOutputSize_scaleNarrow_decreasesWidth() {
|
||||||
int inputWidth = 200;
|
int inputWidth = 200;
|
||||||
@ -67,7 +55,7 @@ public final class ScaleToFitFrameProcessorTest {
|
|||||||
.setScale(/* scaleX= */ .5f, /* scaleY= */ 1f)
|
.setScale(/* scaleX= */ .5f, /* scaleY= */ 1f)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
scaleToFitFrameProcessor.setInputSize(inputWidth, inputHeight);
|
scaleToFitFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
|
||||||
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
|
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
|
||||||
|
|
||||||
assertThat(outputSize.getWidth()).isEqualTo(Math.round(inputWidth * .5f));
|
assertThat(outputSize.getWidth()).isEqualTo(Math.round(inputWidth * .5f));
|
||||||
@ -83,7 +71,7 @@ public final class ScaleToFitFrameProcessorTest {
|
|||||||
.setScale(/* scaleX= */ 2f, /* scaleY= */ 1f)
|
.setScale(/* scaleX= */ 2f, /* scaleY= */ 1f)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
scaleToFitFrameProcessor.setInputSize(inputWidth, inputHeight);
|
scaleToFitFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
|
||||||
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
|
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
|
||||||
|
|
||||||
assertThat(outputSize.getWidth()).isEqualTo(inputWidth * 2);
|
assertThat(outputSize.getWidth()).isEqualTo(inputWidth * 2);
|
||||||
@ -99,7 +87,7 @@ public final class ScaleToFitFrameProcessorTest {
|
|||||||
.setScale(/* scaleX= */ 1f, /* scaleY= */ 2f)
|
.setScale(/* scaleX= */ 1f, /* scaleY= */ 2f)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
scaleToFitFrameProcessor.setInputSize(inputWidth, inputHeight);
|
scaleToFitFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
|
||||||
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
|
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
|
||||||
|
|
||||||
assertThat(outputSize.getWidth()).isEqualTo(inputWidth);
|
assertThat(outputSize.getWidth()).isEqualTo(inputWidth);
|
||||||
@ -115,7 +103,7 @@ public final class ScaleToFitFrameProcessorTest {
|
|||||||
.setRotationDegrees(90)
|
.setRotationDegrees(90)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
scaleToFitFrameProcessor.setInputSize(inputWidth, inputHeight);
|
scaleToFitFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
|
||||||
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
|
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
|
||||||
|
|
||||||
assertThat(outputSize.getWidth()).isEqualTo(inputHeight);
|
assertThat(outputSize.getWidth()).isEqualTo(inputHeight);
|
||||||
@ -132,7 +120,7 @@ public final class ScaleToFitFrameProcessorTest {
|
|||||||
.build();
|
.build();
|
||||||
long expectedOutputWidthHeight = 247;
|
long expectedOutputWidthHeight = 247;
|
||||||
|
|
||||||
scaleToFitFrameProcessor.setInputSize(inputWidth, inputHeight);
|
scaleToFitFrameProcessor.configureOutputSizeAndTransformationMatrix(inputWidth, inputHeight);
|
||||||
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
|
Size outputSize = scaleToFitFrameProcessor.getOutputSize();
|
||||||
|
|
||||||
assertThat(outputSize.getWidth()).isEqualTo(expectedOutputWidthHeight);
|
assertThat(outputSize.getWidth()).isEqualTo(expectedOutputWidthHeight);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user