Rename GlFrameProcessor to SingleFrameGlTextureProcessor.
Also update names of implementations to match design doc. In follow-ups, SingleFrameGlTextureProcessor will become an abstract implementation of a new GlTextureProcessor interface. Texture processor makes sense as it processes OpenGL textures. The term frame processor will be used for something else in follow-ups. PiperOrigin-RevId: 451142085
This commit is contained in:
parent
8f3b4f590f
commit
0eaf3d30c8
@ -32,19 +32,20 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.util.GlProgram;
|
import androidx.media3.common.util.GlProgram;
|
||||||
import androidx.media3.common.util.GlUtil;
|
import androidx.media3.common.util.GlUtil;
|
||||||
import androidx.media3.transformer.FrameProcessingException;
|
import androidx.media3.transformer.FrameProcessingException;
|
||||||
import androidx.media3.transformer.GlFrameProcessor;
|
import androidx.media3.transformer.SingleFrameGlTextureProcessor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link GlFrameProcessor} that overlays a bitmap with a logo and timer on each frame.
|
* A {@link SingleFrameGlTextureProcessor} that overlays a bitmap with a logo and timer on each
|
||||||
|
* frame.
|
||||||
*
|
*
|
||||||
* <p>The bitmap is drawn using an Android {@link Canvas}.
|
* <p>The bitmap is drawn using an Android {@link Canvas}.
|
||||||
*/
|
*/
|
||||||
// TODO(b/227625365): Delete this class and use a frame processor from the Transformer library, once
|
// TODO(b/227625365): Delete this class and use a texture processor from the Transformer library,
|
||||||
// overlaying a bitmap and text is supported in Transformer.
|
// once overlaying a bitmap and text is supported in Transformer.
|
||||||
/* package */ final class BitmapOverlayFrameProcessor implements GlFrameProcessor {
|
/* package */ final class BitmapOverlayProcessor implements SingleFrameGlTextureProcessor {
|
||||||
static {
|
static {
|
||||||
GlUtil.glAssertionsEnabled = true;
|
GlUtil.glAssertionsEnabled = true;
|
||||||
}
|
}
|
||||||
@ -65,7 +66,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
private @MonotonicNonNull Bitmap logoBitmap;
|
private @MonotonicNonNull Bitmap logoBitmap;
|
||||||
private @MonotonicNonNull GlProgram glProgram;
|
private @MonotonicNonNull GlProgram glProgram;
|
||||||
|
|
||||||
public BitmapOverlayFrameProcessor() {
|
public BitmapOverlayProcessor() {
|
||||||
paint = new Paint();
|
paint = new Paint();
|
||||||
paint.setTextSize(64);
|
paint.setTextSize(64);
|
||||||
paint.setAntiAlias(true);
|
paint.setAntiAlias(true);
|
@ -24,15 +24,15 @@ import android.util.Size;
|
|||||||
import androidx.media3.common.util.GlProgram;
|
import androidx.media3.common.util.GlProgram;
|
||||||
import androidx.media3.common.util.GlUtil;
|
import androidx.media3.common.util.GlUtil;
|
||||||
import androidx.media3.transformer.FrameProcessingException;
|
import androidx.media3.transformer.FrameProcessingException;
|
||||||
import androidx.media3.transformer.GlFrameProcessor;
|
import androidx.media3.transformer.SingleFrameGlTextureProcessor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link GlFrameProcessor} that periodically dims the frames such that pixels are darker the
|
* A {@link SingleFrameGlTextureProcessor} that periodically dims the frames such that pixels are
|
||||||
* further they are away from the frame center.
|
* darker the further they are away from the frame center.
|
||||||
*/
|
*/
|
||||||
/* package */ final class PeriodicVignetteFrameProcessor implements GlFrameProcessor {
|
/* package */ final class PeriodicVignetteProcessor implements SingleFrameGlTextureProcessor {
|
||||||
static {
|
static {
|
||||||
GlUtil.glAssertionsEnabled = true;
|
GlUtil.glAssertionsEnabled = true;
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
* @param maxInnerRadius The upper bound of the radius that is unaffected by the effect.
|
* @param maxInnerRadius The upper bound of the radius that is unaffected by the effect.
|
||||||
* @param outerRadius The radius after which all pixels are black.
|
* @param outerRadius The radius after which all pixels are black.
|
||||||
*/
|
*/
|
||||||
public PeriodicVignetteFrameProcessor(
|
public PeriodicVignetteProcessor(
|
||||||
float centerX, float centerY, float minInnerRadius, float maxInnerRadius, float outerRadius) {
|
float centerX, float centerY, float minInnerRadius, float maxInnerRadius, float outerRadius) {
|
||||||
checkArgument(minInnerRadius <= maxInnerRadius);
|
checkArgument(minInnerRadius <= maxInnerRadius);
|
||||||
checkArgument(maxInnerRadius <= outerRadius);
|
checkArgument(maxInnerRadius <= outerRadius);
|
@ -42,8 +42,8 @@ import androidx.media3.exoplayer.util.DebugTextViewHelper;
|
|||||||
import androidx.media3.transformer.DefaultEncoderFactory;
|
import androidx.media3.transformer.DefaultEncoderFactory;
|
||||||
import androidx.media3.transformer.EncoderSelector;
|
import androidx.media3.transformer.EncoderSelector;
|
||||||
import androidx.media3.transformer.GlEffect;
|
import androidx.media3.transformer.GlEffect;
|
||||||
import androidx.media3.transformer.GlFrameProcessor;
|
|
||||||
import androidx.media3.transformer.ProgressHolder;
|
import androidx.media3.transformer.ProgressHolder;
|
||||||
|
import androidx.media3.transformer.SingleFrameGlTextureProcessor;
|
||||||
import androidx.media3.transformer.TransformationException;
|
import androidx.media3.transformer.TransformationException;
|
||||||
import androidx.media3.transformer.TransformationRequest;
|
import androidx.media3.transformer.TransformationRequest;
|
||||||
import androidx.media3.transformer.TransformationResult;
|
import androidx.media3.transformer.TransformationResult;
|
||||||
@ -253,14 +253,13 @@ public final class TransformerActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
if (selectedEffects[1]) {
|
if (selectedEffects[1]) {
|
||||||
try {
|
try {
|
||||||
Class<?> clazz =
|
Class<?> clazz = Class.forName("androidx.media3.demo.transformer.MediaPipeProcessor");
|
||||||
Class.forName("androidx.media3.demo.transformer.MediaPipeFrameProcessor");
|
|
||||||
Constructor<?> constructor =
|
Constructor<?> constructor =
|
||||||
clazz.getConstructor(String.class, String.class, String.class);
|
clazz.getConstructor(String.class, String.class, String.class);
|
||||||
effects.add(
|
effects.add(
|
||||||
() -> {
|
() -> {
|
||||||
try {
|
try {
|
||||||
return (GlFrameProcessor)
|
return (SingleFrameGlTextureProcessor)
|
||||||
constructor.newInstance(
|
constructor.newInstance(
|
||||||
/* graphName= */ "edge_detector_mediapipe_graph.binarypb",
|
/* graphName= */ "edge_detector_mediapipe_graph.binarypb",
|
||||||
/* inputStreamName= */ "input_video",
|
/* inputStreamName= */ "input_video",
|
||||||
@ -277,7 +276,7 @@ public final class TransformerActivity extends AppCompatActivity {
|
|||||||
if (selectedEffects[2]) {
|
if (selectedEffects[2]) {
|
||||||
effects.add(
|
effects.add(
|
||||||
() ->
|
() ->
|
||||||
new PeriodicVignetteFrameProcessor(
|
new PeriodicVignetteProcessor(
|
||||||
bundle.getFloat(ConfigurationActivity.PERIODIC_VIGNETTE_CENTER_X),
|
bundle.getFloat(ConfigurationActivity.PERIODIC_VIGNETTE_CENTER_X),
|
||||||
bundle.getFloat(ConfigurationActivity.PERIODIC_VIGNETTE_CENTER_Y),
|
bundle.getFloat(ConfigurationActivity.PERIODIC_VIGNETTE_CENTER_Y),
|
||||||
/* minInnerRadius= */ bundle.getFloat(
|
/* minInnerRadius= */ bundle.getFloat(
|
||||||
@ -290,7 +289,7 @@ public final class TransformerActivity extends AppCompatActivity {
|
|||||||
effects.add(MatrixTransformationFactory.createSpin3dEffect());
|
effects.add(MatrixTransformationFactory.createSpin3dEffect());
|
||||||
}
|
}
|
||||||
if (selectedEffects[4]) {
|
if (selectedEffects[4]) {
|
||||||
effects.add(BitmapOverlayFrameProcessor::new);
|
effects.add(BitmapOverlayProcessor::new);
|
||||||
}
|
}
|
||||||
if (selectedEffects[5]) {
|
if (selectedEffects[5]) {
|
||||||
effects.add(MatrixTransformationFactory.createZoomInTransition());
|
effects.add(MatrixTransformationFactory.createZoomInTransition());
|
||||||
|
@ -27,7 +27,7 @@ import androidx.media3.common.util.GlProgram;
|
|||||||
import androidx.media3.common.util.GlUtil;
|
import androidx.media3.common.util.GlUtil;
|
||||||
import androidx.media3.common.util.LibraryLoader;
|
import androidx.media3.common.util.LibraryLoader;
|
||||||
import androidx.media3.transformer.FrameProcessingException;
|
import androidx.media3.transformer.FrameProcessingException;
|
||||||
import androidx.media3.transformer.GlFrameProcessor;
|
import androidx.media3.transformer.SingleFrameGlTextureProcessor;
|
||||||
import com.google.mediapipe.components.FrameProcessor;
|
import com.google.mediapipe.components.FrameProcessor;
|
||||||
import com.google.mediapipe.framework.AndroidAssetUtil;
|
import com.google.mediapipe.framework.AndroidAssetUtil;
|
||||||
import com.google.mediapipe.framework.AppTextureFrame;
|
import com.google.mediapipe.framework.AppTextureFrame;
|
||||||
@ -40,7 +40,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
* Runs a MediaPipe graph on input frames. The implementation is currently limited to graphs that
|
* Runs a MediaPipe graph on input frames. The implementation is currently limited to graphs that
|
||||||
* can immediately produce one output frame per input frame.
|
* can immediately produce one output frame per input frame.
|
||||||
*/
|
*/
|
||||||
/* package */ final class MediaPipeFrameProcessor implements GlFrameProcessor {
|
/* package */ final class MediaPipeProcessor implements SingleFrameGlTextureProcessor {
|
||||||
|
|
||||||
private static final LibraryLoader LOADER =
|
private static final LibraryLoader LOADER =
|
||||||
new LibraryLoader("mediapipe_jni") {
|
new LibraryLoader("mediapipe_jni") {
|
||||||
@ -77,14 +77,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
private @MonotonicNonNull RuntimeException frameProcessorPendingError;
|
private @MonotonicNonNull RuntimeException frameProcessorPendingError;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new frame processor that wraps a MediaPipe graph.
|
* Creates a new texture processor that wraps a MediaPipe graph.
|
||||||
*
|
*
|
||||||
* @param graphName Name of a MediaPipe graph asset to load.
|
* @param graphName Name of a MediaPipe graph asset to load.
|
||||||
* @param inputStreamName Name of the input video stream in the graph.
|
* @param inputStreamName Name of the input video stream in the graph.
|
||||||
* @param outputStreamName Name of the input video stream in the graph.
|
* @param outputStreamName Name of the input video stream in the graph.
|
||||||
*/
|
*/
|
||||||
public MediaPipeFrameProcessor(
|
public MediaPipeProcessor(String graphName, String inputStreamName, String outputStreamName) {
|
||||||
String graphName, String inputStreamName, String outputStreamName) {
|
|
||||||
checkState(LOADER.isAvailable());
|
checkState(LOADER.isAvailable());
|
||||||
this.graphName = graphName;
|
this.graphName = graphName;
|
||||||
this.inputStreamName = inputStreamName;
|
this.inputStreamName = inputStreamName;
|
||||||
@ -141,7 +140,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
// Propagate the interrupted flag so the next blocking operation will throw.
|
// Propagate the interrupted flag so the next blocking operation will throw.
|
||||||
// TODO(b/230469581): The next processor that runs will not have valid input due to returning
|
// TODO(b/230469581): The next processor that runs will not have valid input due to returning
|
||||||
// early here. This could be fixed by checking for interruption in the outer loop that runs
|
// early here. This could be fixed by checking for interruption in the outer loop that runs
|
||||||
// through the frame processors.
|
// through the texture processors.
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
@ -40,7 +40,7 @@ import java.nio.ByteBuffer;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Utilities for instrumentation tests for the {@link FrameProcessorChain} and {@link
|
* Utilities for instrumentation tests for the {@link FrameProcessorChain} and {@link
|
||||||
* GlFrameProcessor GlFrameProcessors}.
|
* SingleFrameGlTextureProcessor SingleFrameGlTextureProcessors}.
|
||||||
*/
|
*/
|
||||||
public class BitmapTestUtil {
|
public class BitmapTestUtil {
|
||||||
|
|
||||||
@ -50,15 +50,15 @@ public class BitmapTestUtil {
|
|||||||
* Maximum allowed average pixel difference between the expected and actual edited images in pixel
|
* Maximum allowed average pixel difference between the expected and actual edited images in pixel
|
||||||
* difference-based tests. The value is chosen so that differences in decoder behavior across
|
* difference-based tests. The value is chosen so that differences in decoder behavior across
|
||||||
* emulator versions don't affect whether the test passes for most emulators, but substantial
|
* emulator versions don't affect whether the test passes for most emulators, but substantial
|
||||||
* distortions introduced by changes in the behavior of the {@link GlFrameProcessor
|
* distortions introduced by changes in the behavior of the {@link SingleFrameGlTextureProcessor
|
||||||
* GlFrameProcessors} will cause the test to fail.
|
* SingleFrameGlTextureProcessors} will cause the test to fail.
|
||||||
*
|
*
|
||||||
* <p>To run pixel difference-based tests on physical devices, please use a value of 5f, rather
|
* <p>To run pixel difference-based tests on physical devices, please use a value of 5f, rather
|
||||||
* than 0.1f. This higher value will ignore some very small errors, but will allow for some
|
* than 0.1f. This higher value will ignore some very small errors, but will allow for some
|
||||||
* differences caused by graphics implementations to be ignored. When the difference is close to
|
* differences caused by graphics implementations to be ignored. When the difference is close to
|
||||||
* the threshold, manually inspect expected/actual bitmaps to confirm failure, as it's possible
|
* the threshold, manually inspect expected/actual bitmaps to confirm failure, as it's possible
|
||||||
* this is caused by a difference in the codec or graphics implementation as opposed to a {@link
|
* this is caused by a difference in the codec or graphics implementation as opposed to a {@link
|
||||||
* GlFrameProcessor} issue.
|
* SingleFrameGlTextureProcessor} issue.
|
||||||
*/
|
*/
|
||||||
public static final float MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE = 0.1f;
|
public static final float MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE = 0.1f;
|
||||||
|
|
||||||
|
@ -41,10 +41,10 @@ public final class FrameProcessorChainTest {
|
|||||||
public void getOutputSize_noOperation_returnsInputSize() throws Exception {
|
public void getOutputSize_noOperation_returnsInputSize() throws Exception {
|
||||||
Size inputSize = new Size(200, 100);
|
Size inputSize = new Size(200, 100);
|
||||||
FrameProcessorChain frameProcessorChain =
|
FrameProcessorChain frameProcessorChain =
|
||||||
createFrameProcessorChainWithFakeFrameProcessors(
|
createFrameProcessorChainWithFakeTextureProcessors(
|
||||||
/* pixelWidthHeightRatio= */ 1f,
|
/* pixelWidthHeightRatio= */ 1f,
|
||||||
inputSize,
|
inputSize,
|
||||||
/* frameProcessorOutputSizes= */ ImmutableList.of());
|
/* textureProcessorOutputSizes= */ ImmutableList.of());
|
||||||
|
|
||||||
Size outputSize = frameProcessorChain.getOutputSize();
|
Size outputSize = frameProcessorChain.getOutputSize();
|
||||||
|
|
||||||
@ -56,10 +56,10 @@ public final class FrameProcessorChainTest {
|
|||||||
public void getOutputSize_withWidePixels_returnsWiderOutputSize() throws Exception {
|
public void getOutputSize_withWidePixels_returnsWiderOutputSize() throws Exception {
|
||||||
Size inputSize = new Size(200, 100);
|
Size inputSize = new Size(200, 100);
|
||||||
FrameProcessorChain frameProcessorChain =
|
FrameProcessorChain frameProcessorChain =
|
||||||
createFrameProcessorChainWithFakeFrameProcessors(
|
createFrameProcessorChainWithFakeTextureProcessors(
|
||||||
/* pixelWidthHeightRatio= */ 2f,
|
/* pixelWidthHeightRatio= */ 2f,
|
||||||
inputSize,
|
inputSize,
|
||||||
/* frameProcessorOutputSizes= */ ImmutableList.of());
|
/* textureProcessorOutputSizes= */ ImmutableList.of());
|
||||||
|
|
||||||
Size outputSize = frameProcessorChain.getOutputSize();
|
Size outputSize = frameProcessorChain.getOutputSize();
|
||||||
|
|
||||||
@ -71,10 +71,10 @@ public final class FrameProcessorChainTest {
|
|||||||
public void getOutputSize_withTallPixels_returnsTallerOutputSize() throws Exception {
|
public void getOutputSize_withTallPixels_returnsTallerOutputSize() throws Exception {
|
||||||
Size inputSize = new Size(200, 100);
|
Size inputSize = new Size(200, 100);
|
||||||
FrameProcessorChain frameProcessorChain =
|
FrameProcessorChain frameProcessorChain =
|
||||||
createFrameProcessorChainWithFakeFrameProcessors(
|
createFrameProcessorChainWithFakeTextureProcessors(
|
||||||
/* pixelWidthHeightRatio= */ .5f,
|
/* pixelWidthHeightRatio= */ .5f,
|
||||||
inputSize,
|
inputSize,
|
||||||
/* frameProcessorOutputSizes= */ ImmutableList.of());
|
/* textureProcessorOutputSizes= */ ImmutableList.of());
|
||||||
|
|
||||||
Size outputSize = frameProcessorChain.getOutputSize();
|
Size outputSize = frameProcessorChain.getOutputSize();
|
||||||
|
|
||||||
@ -83,32 +83,32 @@ public final class FrameProcessorChainTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getOutputSize_withOneFrameProcessor_returnsItsOutputSize() throws Exception {
|
public void getOutputSize_withOneTextureProcessor_returnsItsOutputSize() throws Exception {
|
||||||
Size inputSize = new Size(200, 100);
|
Size inputSize = new Size(200, 100);
|
||||||
Size frameProcessorOutputSize = new Size(300, 250);
|
Size textureProcessorOutputSize = new Size(300, 250);
|
||||||
FrameProcessorChain frameProcessorChain =
|
FrameProcessorChain frameProcessorChain =
|
||||||
createFrameProcessorChainWithFakeFrameProcessors(
|
createFrameProcessorChainWithFakeTextureProcessors(
|
||||||
/* pixelWidthHeightRatio= */ 1f,
|
/* pixelWidthHeightRatio= */ 1f,
|
||||||
inputSize,
|
inputSize,
|
||||||
/* frameProcessorOutputSizes= */ ImmutableList.of(frameProcessorOutputSize));
|
/* textureProcessorOutputSizes= */ ImmutableList.of(textureProcessorOutputSize));
|
||||||
|
|
||||||
Size frameProcessorChainOutputSize = frameProcessorChain.getOutputSize();
|
Size frameProcessorChainOutputSize = frameProcessorChain.getOutputSize();
|
||||||
|
|
||||||
assertThat(frameProcessorChainOutputSize).isEqualTo(frameProcessorOutputSize);
|
assertThat(frameProcessorChainOutputSize).isEqualTo(textureProcessorOutputSize);
|
||||||
assertThat(frameProcessingException.get()).isNull();
|
assertThat(frameProcessingException.get()).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getOutputSize_withThreeFrameProcessors_returnsLastOutputSize() throws Exception {
|
public void getOutputSize_withThreeTextureProcessors_returnsLastOutputSize() throws Exception {
|
||||||
Size inputSize = new Size(200, 100);
|
Size inputSize = new Size(200, 100);
|
||||||
Size outputSize1 = new Size(300, 250);
|
Size outputSize1 = new Size(300, 250);
|
||||||
Size outputSize2 = new Size(400, 244);
|
Size outputSize2 = new Size(400, 244);
|
||||||
Size outputSize3 = new Size(150, 160);
|
Size outputSize3 = new Size(150, 160);
|
||||||
FrameProcessorChain frameProcessorChain =
|
FrameProcessorChain frameProcessorChain =
|
||||||
createFrameProcessorChainWithFakeFrameProcessors(
|
createFrameProcessorChainWithFakeTextureProcessors(
|
||||||
/* pixelWidthHeightRatio= */ 1f,
|
/* pixelWidthHeightRatio= */ 1f,
|
||||||
inputSize,
|
inputSize,
|
||||||
/* frameProcessorOutputSizes= */ ImmutableList.of(
|
/* textureProcessorOutputSizes= */ ImmutableList.of(
|
||||||
outputSize1, outputSize2, outputSize3));
|
outputSize1, outputSize2, outputSize3));
|
||||||
|
|
||||||
Size frameProcessorChainOutputSize = frameProcessorChain.getOutputSize();
|
Size frameProcessorChainOutputSize = frameProcessorChain.getOutputSize();
|
||||||
@ -117,12 +117,12 @@ public final class FrameProcessorChainTest {
|
|||||||
assertThat(frameProcessingException.get()).isNull();
|
assertThat(frameProcessingException.get()).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
private FrameProcessorChain createFrameProcessorChainWithFakeFrameProcessors(
|
private FrameProcessorChain createFrameProcessorChainWithFakeTextureProcessors(
|
||||||
float pixelWidthHeightRatio, Size inputSize, List<Size> frameProcessorOutputSizes)
|
float pixelWidthHeightRatio, Size inputSize, List<Size> textureProcessorOutputSizes)
|
||||||
throws FrameProcessingException {
|
throws FrameProcessingException {
|
||||||
ImmutableList.Builder<GlEffect> effects = new ImmutableList.Builder<>();
|
ImmutableList.Builder<GlEffect> effects = new ImmutableList.Builder<>();
|
||||||
for (Size element : frameProcessorOutputSizes) {
|
for (Size element : textureProcessorOutputSizes) {
|
||||||
effects.add(() -> new FakeFrameProcessor(element));
|
effects.add(() -> new FakeTextureProcessor(element));
|
||||||
}
|
}
|
||||||
return FrameProcessorChain.create(
|
return FrameProcessorChain.create(
|
||||||
getApplicationContext(),
|
getApplicationContext(),
|
||||||
@ -134,11 +134,11 @@ public final class FrameProcessorChainTest {
|
|||||||
/* enableExperimentalHdrEditing= */ false);
|
/* enableExperimentalHdrEditing= */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class FakeFrameProcessor implements GlFrameProcessor {
|
private static class FakeTextureProcessor implements SingleFrameGlTextureProcessor {
|
||||||
|
|
||||||
private final Size outputSize;
|
private final Size outputSize;
|
||||||
|
|
||||||
private FakeFrameProcessor(Size outputSize) {
|
private FakeTextureProcessor(Size outputSize) {
|
||||||
this.outputSize = outputSize;
|
this.outputSize = outputSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pixel test for frame processing via {@link MatrixTransformationFrameProcessor}.
|
* Pixel test for texture processing via {@link MatrixTransformationProcessor}.
|
||||||
*
|
*
|
||||||
* <p>Expected images are taken from an emulator, so tests on different emulators or physical
|
* <p>Expected images are taken from an emulator, so tests on different emulators or physical
|
||||||
* devices may fail. To test on other devices, please increase the {@link
|
* devices may fail. To test on other devices, please increase the {@link
|
||||||
@ -42,7 +42,7 @@ import org.junit.runner.RunWith;
|
|||||||
* as recommended in {@link FrameProcessorChainPixelTest}.
|
* as recommended in {@link FrameProcessorChainPixelTest}.
|
||||||
*/
|
*/
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public final class MatrixTransformationFrameProcessorPixelTest {
|
public final class MatrixTransformationProcessorPixelTest {
|
||||||
public static final String ORIGINAL_PNG_ASSET_PATH =
|
public static final String ORIGINAL_PNG_ASSET_PATH =
|
||||||
"media/bitmap/sample_mp4_first_frame/original.png";
|
"media/bitmap/sample_mp4_first_frame/original.png";
|
||||||
public static final String TRANSLATE_RIGHT_PNG_ASSET_PATH =
|
public static final String TRANSLATE_RIGHT_PNG_ASSET_PATH =
|
||||||
@ -58,7 +58,7 @@ public final class MatrixTransformationFrameProcessorPixelTest {
|
|||||||
|
|
||||||
private final EGLDisplay eglDisplay = GlUtil.createEglDisplay();
|
private final EGLDisplay eglDisplay = GlUtil.createEglDisplay();
|
||||||
private final EGLContext eglContext = GlUtil.createEglContext(eglDisplay);
|
private final EGLContext eglContext = GlUtil.createEglContext(eglDisplay);
|
||||||
private @MonotonicNonNull GlFrameProcessor matrixTransformationFrameProcessor;
|
private @MonotonicNonNull SingleFrameGlTextureProcessor matrixTransformationProcessor;
|
||||||
private int inputTexId;
|
private int inputTexId;
|
||||||
private int outputTexId;
|
private int outputTexId;
|
||||||
private int width;
|
private int width;
|
||||||
@ -80,8 +80,8 @@ public final class MatrixTransformationFrameProcessorPixelTest {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
public void release() {
|
public void release() {
|
||||||
if (matrixTransformationFrameProcessor != null) {
|
if (matrixTransformationProcessor != null) {
|
||||||
matrixTransformationFrameProcessor.release();
|
matrixTransformationProcessor.release();
|
||||||
}
|
}
|
||||||
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
||||||
}
|
}
|
||||||
@ -90,13 +90,12 @@ public final class MatrixTransformationFrameProcessorPixelTest {
|
|||||||
public void drawFrame_noEdits_producesExpectedOutput() throws Exception {
|
public void drawFrame_noEdits_producesExpectedOutput() throws Exception {
|
||||||
String testId = "drawFrame_noEdits";
|
String testId = "drawFrame_noEdits";
|
||||||
Matrix identityMatrix = new Matrix();
|
Matrix identityMatrix = new Matrix();
|
||||||
matrixTransformationFrameProcessor =
|
matrixTransformationProcessor =
|
||||||
new MatrixTransformationFrameProcessor((long presentationTimeUs) -> identityMatrix);
|
new MatrixTransformationProcessor((long presentationTimeUs) -> identityMatrix);
|
||||||
matrixTransformationFrameProcessor.initialize(
|
matrixTransformationProcessor.initialize(getApplicationContext(), inputTexId, width, height);
|
||||||
getApplicationContext(), inputTexId, width, height);
|
|
||||||
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ORIGINAL_PNG_ASSET_PATH);
|
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ORIGINAL_PNG_ASSET_PATH);
|
||||||
|
|
||||||
matrixTransformationFrameProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
matrixTransformationProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
||||||
Bitmap actualBitmap =
|
Bitmap actualBitmap =
|
||||||
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(width, height);
|
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(width, height);
|
||||||
|
|
||||||
@ -114,13 +113,12 @@ public final class MatrixTransformationFrameProcessorPixelTest {
|
|||||||
String testId = "drawFrame_translateRight";
|
String testId = "drawFrame_translateRight";
|
||||||
Matrix translateRightMatrix = new Matrix();
|
Matrix translateRightMatrix = new Matrix();
|
||||||
translateRightMatrix.postTranslate(/* dx= */ 1, /* dy= */ 0);
|
translateRightMatrix.postTranslate(/* dx= */ 1, /* dy= */ 0);
|
||||||
matrixTransformationFrameProcessor =
|
matrixTransformationProcessor =
|
||||||
new MatrixTransformationFrameProcessor((long presentationTimeUs) -> translateRightMatrix);
|
new MatrixTransformationProcessor((long presentationTimeUs) -> translateRightMatrix);
|
||||||
matrixTransformationFrameProcessor.initialize(
|
matrixTransformationProcessor.initialize(getApplicationContext(), inputTexId, width, height);
|
||||||
getApplicationContext(), inputTexId, width, height);
|
|
||||||
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(TRANSLATE_RIGHT_PNG_ASSET_PATH);
|
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(TRANSLATE_RIGHT_PNG_ASSET_PATH);
|
||||||
|
|
||||||
matrixTransformationFrameProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
matrixTransformationProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
||||||
Bitmap actualBitmap =
|
Bitmap actualBitmap =
|
||||||
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(width, height);
|
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(width, height);
|
||||||
|
|
||||||
@ -138,13 +136,12 @@ public final class MatrixTransformationFrameProcessorPixelTest {
|
|||||||
String testId = "drawFrame_scaleNarrow";
|
String testId = "drawFrame_scaleNarrow";
|
||||||
Matrix scaleNarrowMatrix = new Matrix();
|
Matrix scaleNarrowMatrix = new Matrix();
|
||||||
scaleNarrowMatrix.postScale(.5f, 1.2f);
|
scaleNarrowMatrix.postScale(.5f, 1.2f);
|
||||||
matrixTransformationFrameProcessor =
|
matrixTransformationProcessor =
|
||||||
new MatrixTransformationFrameProcessor((long presentationTimeUs) -> scaleNarrowMatrix);
|
new MatrixTransformationProcessor((long presentationTimeUs) -> scaleNarrowMatrix);
|
||||||
matrixTransformationFrameProcessor.initialize(
|
matrixTransformationProcessor.initialize(getApplicationContext(), inputTexId, width, height);
|
||||||
getApplicationContext(), inputTexId, width, height);
|
|
||||||
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(SCALE_NARROW_PNG_ASSET_PATH);
|
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(SCALE_NARROW_PNG_ASSET_PATH);
|
||||||
|
|
||||||
matrixTransformationFrameProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
matrixTransformationProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
||||||
Bitmap actualBitmap =
|
Bitmap actualBitmap =
|
||||||
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(width, height);
|
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(width, height);
|
||||||
|
|
||||||
@ -162,13 +159,12 @@ public final class MatrixTransformationFrameProcessorPixelTest {
|
|||||||
String testId = "drawFrame_rotate90";
|
String testId = "drawFrame_rotate90";
|
||||||
Matrix rotate90Matrix = new Matrix();
|
Matrix rotate90Matrix = new Matrix();
|
||||||
rotate90Matrix.postRotate(/* degrees= */ 90);
|
rotate90Matrix.postRotate(/* degrees= */ 90);
|
||||||
matrixTransformationFrameProcessor =
|
matrixTransformationProcessor =
|
||||||
new MatrixTransformationFrameProcessor((long presentationTimeUs) -> rotate90Matrix);
|
new MatrixTransformationProcessor((long presentationTimeUs) -> rotate90Matrix);
|
||||||
matrixTransformationFrameProcessor.initialize(
|
matrixTransformationProcessor.initialize(getApplicationContext(), inputTexId, width, height);
|
||||||
getApplicationContext(), inputTexId, width, height);
|
|
||||||
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ROTATE_90_PNG_ASSET_PATH);
|
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ROTATE_90_PNG_ASSET_PATH);
|
||||||
|
|
||||||
matrixTransformationFrameProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
matrixTransformationProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
||||||
Bitmap actualBitmap =
|
Bitmap actualBitmap =
|
||||||
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(width, height);
|
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(width, height);
|
||||||
|
|
@ -35,7 +35,7 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pixel test for frame processing via {@link Presentation}.
|
* Pixel test for texture processing via {@link Presentation}.
|
||||||
*
|
*
|
||||||
* <p>Expected images are taken from an emulator, so tests on different emulators or physical
|
* <p>Expected images are taken from an emulator, so tests on different emulators or physical
|
||||||
* devices may fail. To test on other devices, please increase the {@link
|
* devices may fail. To test on other devices, please increase the {@link
|
||||||
@ -69,7 +69,7 @@ public final class PresentationPixelTest {
|
|||||||
|
|
||||||
private final EGLDisplay eglDisplay = GlUtil.createEglDisplay();
|
private final EGLDisplay eglDisplay = GlUtil.createEglDisplay();
|
||||||
private final EGLContext eglContext = GlUtil.createEglContext(eglDisplay);
|
private final EGLContext eglContext = GlUtil.createEglContext(eglDisplay);
|
||||||
private @MonotonicNonNull GlFrameProcessor presentationFrameProcessor;
|
private @MonotonicNonNull SingleFrameGlTextureProcessor presentationTextureProcessor;
|
||||||
private @MonotonicNonNull EGLSurface placeholderEglSurface;
|
private @MonotonicNonNull EGLSurface placeholderEglSurface;
|
||||||
private int inputTexId;
|
private int inputTexId;
|
||||||
private int outputTexId;
|
private int outputTexId;
|
||||||
@ -88,8 +88,8 @@ public final class PresentationPixelTest {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
public void release() {
|
public void release() {
|
||||||
if (presentationFrameProcessor != null) {
|
if (presentationTextureProcessor != null) {
|
||||||
presentationFrameProcessor.release();
|
presentationTextureProcessor.release();
|
||||||
}
|
}
|
||||||
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
||||||
}
|
}
|
||||||
@ -97,14 +97,14 @@ public final class PresentationPixelTest {
|
|||||||
@Test
|
@Test
|
||||||
public void drawFrame_noEdits_producesExpectedOutput() throws Exception {
|
public void drawFrame_noEdits_producesExpectedOutput() throws Exception {
|
||||||
String testId = "drawFrame_noEdits";
|
String testId = "drawFrame_noEdits";
|
||||||
presentationFrameProcessor = new Presentation.Builder().build().toGlFrameProcessor();
|
presentationTextureProcessor = new Presentation.Builder().build().toGlTextureProcessor();
|
||||||
presentationFrameProcessor.initialize(
|
presentationTextureProcessor.initialize(
|
||||||
getApplicationContext(), inputTexId, inputWidth, inputHeight);
|
getApplicationContext(), inputTexId, inputWidth, inputHeight);
|
||||||
Size outputSize = presentationFrameProcessor.getOutputSize();
|
Size outputSize = presentationTextureProcessor.getOutputSize();
|
||||||
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
||||||
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ORIGINAL_PNG_ASSET_PATH);
|
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ORIGINAL_PNG_ASSET_PATH);
|
||||||
|
|
||||||
presentationFrameProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
presentationTextureProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
||||||
Bitmap actualBitmap =
|
Bitmap actualBitmap =
|
||||||
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
|
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
|
||||||
outputSize.getWidth(), outputSize.getHeight());
|
outputSize.getWidth(), outputSize.getHeight());
|
||||||
@ -121,18 +121,18 @@ public final class PresentationPixelTest {
|
|||||||
@Test
|
@Test
|
||||||
public void drawFrame_cropSmaller_producesExpectedOutput() throws Exception {
|
public void drawFrame_cropSmaller_producesExpectedOutput() throws Exception {
|
||||||
String testId = "drawFrame_cropSmaller";
|
String testId = "drawFrame_cropSmaller";
|
||||||
GlFrameProcessor presentationFrameProcessor =
|
presentationTextureProcessor =
|
||||||
new Presentation.Builder()
|
new Presentation.Builder()
|
||||||
.setCrop(/* left= */ -.9f, /* right= */ .1f, /* bottom= */ -1f, /* top= */ .5f)
|
.setCrop(/* left= */ -.9f, /* right= */ .1f, /* bottom= */ -1f, /* top= */ .5f)
|
||||||
.build()
|
.build()
|
||||||
.toGlFrameProcessor();
|
.toGlTextureProcessor();
|
||||||
presentationFrameProcessor.initialize(
|
presentationTextureProcessor.initialize(
|
||||||
getApplicationContext(), inputTexId, inputWidth, inputHeight);
|
getApplicationContext(), inputTexId, inputWidth, inputHeight);
|
||||||
Size outputSize = presentationFrameProcessor.getOutputSize();
|
Size outputSize = presentationTextureProcessor.getOutputSize();
|
||||||
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
||||||
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(CROP_SMALLER_PNG_ASSET_PATH);
|
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(CROP_SMALLER_PNG_ASSET_PATH);
|
||||||
|
|
||||||
presentationFrameProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
presentationTextureProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
||||||
Bitmap actualBitmap =
|
Bitmap actualBitmap =
|
||||||
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
|
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
|
||||||
outputSize.getWidth(), outputSize.getHeight());
|
outputSize.getWidth(), outputSize.getHeight());
|
||||||
@ -149,18 +149,18 @@ public final class PresentationPixelTest {
|
|||||||
@Test
|
@Test
|
||||||
public void drawFrame_cropLarger_producesExpectedOutput() throws Exception {
|
public void drawFrame_cropLarger_producesExpectedOutput() throws Exception {
|
||||||
String testId = "drawFrame_cropSmaller";
|
String testId = "drawFrame_cropSmaller";
|
||||||
GlFrameProcessor presentationFrameProcessor =
|
presentationTextureProcessor =
|
||||||
new Presentation.Builder()
|
new Presentation.Builder()
|
||||||
.setCrop(/* left= */ -2f, /* right= */ 2f, /* bottom= */ -1f, /* top= */ 2f)
|
.setCrop(/* left= */ -2f, /* right= */ 2f, /* bottom= */ -1f, /* top= */ 2f)
|
||||||
.build()
|
.build()
|
||||||
.toGlFrameProcessor();
|
.toGlTextureProcessor();
|
||||||
presentationFrameProcessor.initialize(
|
presentationTextureProcessor.initialize(
|
||||||
getApplicationContext(), inputTexId, inputWidth, inputHeight);
|
getApplicationContext(), inputTexId, inputWidth, inputHeight);
|
||||||
Size outputSize = presentationFrameProcessor.getOutputSize();
|
Size outputSize = presentationTextureProcessor.getOutputSize();
|
||||||
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
||||||
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(CROP_LARGER_PNG_ASSET_PATH);
|
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(CROP_LARGER_PNG_ASSET_PATH);
|
||||||
|
|
||||||
presentationFrameProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
presentationTextureProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
||||||
Bitmap actualBitmap =
|
Bitmap actualBitmap =
|
||||||
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
|
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
|
||||||
outputSize.getWidth(), outputSize.getHeight());
|
outputSize.getWidth(), outputSize.getHeight());
|
||||||
@ -178,19 +178,19 @@ public final class PresentationPixelTest {
|
|||||||
public void drawFrame_changeAspectRatio_scaleToFit_narrow_producesExpectedOutput()
|
public void drawFrame_changeAspectRatio_scaleToFit_narrow_producesExpectedOutput()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
String testId = "drawFrame_changeAspectRatio_scaleToFit_narrow";
|
String testId = "drawFrame_changeAspectRatio_scaleToFit_narrow";
|
||||||
presentationFrameProcessor =
|
presentationTextureProcessor =
|
||||||
new Presentation.Builder()
|
new Presentation.Builder()
|
||||||
.setAspectRatio(1f, Presentation.LAYOUT_SCALE_TO_FIT)
|
.setAspectRatio(1f, Presentation.LAYOUT_SCALE_TO_FIT)
|
||||||
.build()
|
.build()
|
||||||
.toGlFrameProcessor();
|
.toGlTextureProcessor();
|
||||||
presentationFrameProcessor.initialize(
|
presentationTextureProcessor.initialize(
|
||||||
getApplicationContext(), inputTexId, inputWidth, inputHeight);
|
getApplicationContext(), inputTexId, inputWidth, inputHeight);
|
||||||
Size outputSize = presentationFrameProcessor.getOutputSize();
|
Size outputSize = presentationTextureProcessor.getOutputSize();
|
||||||
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
||||||
Bitmap expectedBitmap =
|
Bitmap expectedBitmap =
|
||||||
BitmapTestUtil.readBitmap(ASPECT_RATIO_SCALE_TO_FIT_NARROW_PNG_ASSET_PATH);
|
BitmapTestUtil.readBitmap(ASPECT_RATIO_SCALE_TO_FIT_NARROW_PNG_ASSET_PATH);
|
||||||
|
|
||||||
presentationFrameProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
presentationTextureProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
||||||
Bitmap actualBitmap =
|
Bitmap actualBitmap =
|
||||||
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
|
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
|
||||||
outputSize.getWidth(), outputSize.getHeight());
|
outputSize.getWidth(), outputSize.getHeight());
|
||||||
@ -208,19 +208,19 @@ public final class PresentationPixelTest {
|
|||||||
public void drawFrame_changeAspectRatio_scaleToFit_wide_producesExpectedOutput()
|
public void drawFrame_changeAspectRatio_scaleToFit_wide_producesExpectedOutput()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
String testId = "drawFrame_changeAspectRatio_scaleToFit_wide";
|
String testId = "drawFrame_changeAspectRatio_scaleToFit_wide";
|
||||||
presentationFrameProcessor =
|
presentationTextureProcessor =
|
||||||
new Presentation.Builder()
|
new Presentation.Builder()
|
||||||
.setAspectRatio(2f, Presentation.LAYOUT_SCALE_TO_FIT)
|
.setAspectRatio(2f, Presentation.LAYOUT_SCALE_TO_FIT)
|
||||||
.build()
|
.build()
|
||||||
.toGlFrameProcessor();
|
.toGlTextureProcessor();
|
||||||
presentationFrameProcessor.initialize(
|
presentationTextureProcessor.initialize(
|
||||||
getApplicationContext(), inputTexId, inputWidth, inputHeight);
|
getApplicationContext(), inputTexId, inputWidth, inputHeight);
|
||||||
Size outputSize = presentationFrameProcessor.getOutputSize();
|
Size outputSize = presentationTextureProcessor.getOutputSize();
|
||||||
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
||||||
Bitmap expectedBitmap =
|
Bitmap expectedBitmap =
|
||||||
BitmapTestUtil.readBitmap(ASPECT_RATIO_SCALE_TO_FIT_WIDE_PNG_ASSET_PATH);
|
BitmapTestUtil.readBitmap(ASPECT_RATIO_SCALE_TO_FIT_WIDE_PNG_ASSET_PATH);
|
||||||
|
|
||||||
presentationFrameProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
presentationTextureProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
||||||
Bitmap actualBitmap =
|
Bitmap actualBitmap =
|
||||||
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
|
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
|
||||||
outputSize.getWidth(), outputSize.getHeight());
|
outputSize.getWidth(), outputSize.getHeight());
|
||||||
@ -238,19 +238,19 @@ public final class PresentationPixelTest {
|
|||||||
public void drawFrame_changeAspectRatio_scaleToFitWithCrop_narrow_producesExpectedOutput()
|
public void drawFrame_changeAspectRatio_scaleToFitWithCrop_narrow_producesExpectedOutput()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
String testId = "drawFrame_changeAspectRatio_scaleToFitWithCrop_narrow";
|
String testId = "drawFrame_changeAspectRatio_scaleToFitWithCrop_narrow";
|
||||||
presentationFrameProcessor =
|
presentationTextureProcessor =
|
||||||
new Presentation.Builder()
|
new Presentation.Builder()
|
||||||
.setAspectRatio(1f, Presentation.LAYOUT_SCALE_TO_FIT_WITH_CROP)
|
.setAspectRatio(1f, Presentation.LAYOUT_SCALE_TO_FIT_WITH_CROP)
|
||||||
.build()
|
.build()
|
||||||
.toGlFrameProcessor();
|
.toGlTextureProcessor();
|
||||||
presentationFrameProcessor.initialize(
|
presentationTextureProcessor.initialize(
|
||||||
getApplicationContext(), inputTexId, inputWidth, inputHeight);
|
getApplicationContext(), inputTexId, inputWidth, inputHeight);
|
||||||
Size outputSize = presentationFrameProcessor.getOutputSize();
|
Size outputSize = presentationTextureProcessor.getOutputSize();
|
||||||
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
||||||
Bitmap expectedBitmap =
|
Bitmap expectedBitmap =
|
||||||
BitmapTestUtil.readBitmap(ASPECT_RATIO_SCALE_TO_FIT_WITH_CROP_NARROW_PNG_ASSET_PATH);
|
BitmapTestUtil.readBitmap(ASPECT_RATIO_SCALE_TO_FIT_WITH_CROP_NARROW_PNG_ASSET_PATH);
|
||||||
|
|
||||||
presentationFrameProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
presentationTextureProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
||||||
Bitmap actualBitmap =
|
Bitmap actualBitmap =
|
||||||
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
|
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
|
||||||
outputSize.getWidth(), outputSize.getHeight());
|
outputSize.getWidth(), outputSize.getHeight());
|
||||||
@ -268,19 +268,19 @@ public final class PresentationPixelTest {
|
|||||||
public void drawFrame_changeAspectRatio_scaleToFitWithCrop_wide_producesExpectedOutput()
|
public void drawFrame_changeAspectRatio_scaleToFitWithCrop_wide_producesExpectedOutput()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
String testId = "drawFrame_changeAspectRatio_scaleToFitWithCrop_wide";
|
String testId = "drawFrame_changeAspectRatio_scaleToFitWithCrop_wide";
|
||||||
presentationFrameProcessor =
|
presentationTextureProcessor =
|
||||||
new Presentation.Builder()
|
new Presentation.Builder()
|
||||||
.setAspectRatio(2f, Presentation.LAYOUT_SCALE_TO_FIT_WITH_CROP)
|
.setAspectRatio(2f, Presentation.LAYOUT_SCALE_TO_FIT_WITH_CROP)
|
||||||
.build()
|
.build()
|
||||||
.toGlFrameProcessor();
|
.toGlTextureProcessor();
|
||||||
presentationFrameProcessor.initialize(
|
presentationTextureProcessor.initialize(
|
||||||
getApplicationContext(), inputTexId, inputWidth, inputHeight);
|
getApplicationContext(), inputTexId, inputWidth, inputHeight);
|
||||||
Size outputSize = presentationFrameProcessor.getOutputSize();
|
Size outputSize = presentationTextureProcessor.getOutputSize();
|
||||||
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
||||||
Bitmap expectedBitmap =
|
Bitmap expectedBitmap =
|
||||||
BitmapTestUtil.readBitmap(ASPECT_RATIO_SCALE_TO_FIT_WITH_CROP_WIDE_PNG_ASSET_PATH);
|
BitmapTestUtil.readBitmap(ASPECT_RATIO_SCALE_TO_FIT_WITH_CROP_WIDE_PNG_ASSET_PATH);
|
||||||
|
|
||||||
presentationFrameProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
presentationTextureProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
||||||
Bitmap actualBitmap =
|
Bitmap actualBitmap =
|
||||||
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
|
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
|
||||||
outputSize.getWidth(), outputSize.getHeight());
|
outputSize.getWidth(), outputSize.getHeight());
|
||||||
@ -298,19 +298,19 @@ public final class PresentationPixelTest {
|
|||||||
public void drawFrame_changeAspectRatio_stretchToFit_narrow_producesExpectedOutput()
|
public void drawFrame_changeAspectRatio_stretchToFit_narrow_producesExpectedOutput()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
String testId = "drawFrame_changeAspectRatio_stretchToFit_narrow";
|
String testId = "drawFrame_changeAspectRatio_stretchToFit_narrow";
|
||||||
presentationFrameProcessor =
|
presentationTextureProcessor =
|
||||||
new Presentation.Builder()
|
new Presentation.Builder()
|
||||||
.setAspectRatio(1f, Presentation.LAYOUT_STRETCH_TO_FIT)
|
.setAspectRatio(1f, Presentation.LAYOUT_STRETCH_TO_FIT)
|
||||||
.build()
|
.build()
|
||||||
.toGlFrameProcessor();
|
.toGlTextureProcessor();
|
||||||
presentationFrameProcessor.initialize(
|
presentationTextureProcessor.initialize(
|
||||||
getApplicationContext(), inputTexId, inputWidth, inputHeight);
|
getApplicationContext(), inputTexId, inputWidth, inputHeight);
|
||||||
Size outputSize = presentationFrameProcessor.getOutputSize();
|
Size outputSize = presentationTextureProcessor.getOutputSize();
|
||||||
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
||||||
Bitmap expectedBitmap =
|
Bitmap expectedBitmap =
|
||||||
BitmapTestUtil.readBitmap(ASPECT_RATIO_STRETCH_TO_FIT_NARROW_PNG_ASSET_PATH);
|
BitmapTestUtil.readBitmap(ASPECT_RATIO_STRETCH_TO_FIT_NARROW_PNG_ASSET_PATH);
|
||||||
|
|
||||||
presentationFrameProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
presentationTextureProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
||||||
Bitmap actualBitmap =
|
Bitmap actualBitmap =
|
||||||
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
|
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
|
||||||
outputSize.getWidth(), outputSize.getHeight());
|
outputSize.getWidth(), outputSize.getHeight());
|
||||||
@ -328,19 +328,19 @@ public final class PresentationPixelTest {
|
|||||||
public void drawFrame_changeAspectRatio_stretchToFit_wide_producesExpectedOutput()
|
public void drawFrame_changeAspectRatio_stretchToFit_wide_producesExpectedOutput()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
String testId = "drawFrame_changeAspectRatio_stretchToFit_wide";
|
String testId = "drawFrame_changeAspectRatio_stretchToFit_wide";
|
||||||
presentationFrameProcessor =
|
presentationTextureProcessor =
|
||||||
new Presentation.Builder()
|
new Presentation.Builder()
|
||||||
.setAspectRatio(2f, Presentation.LAYOUT_STRETCH_TO_FIT)
|
.setAspectRatio(2f, Presentation.LAYOUT_STRETCH_TO_FIT)
|
||||||
.build()
|
.build()
|
||||||
.toGlFrameProcessor();
|
.toGlTextureProcessor();
|
||||||
presentationFrameProcessor.initialize(
|
presentationTextureProcessor.initialize(
|
||||||
getApplicationContext(), inputTexId, inputWidth, inputHeight);
|
getApplicationContext(), inputTexId, inputWidth, inputHeight);
|
||||||
Size outputSize = presentationFrameProcessor.getOutputSize();
|
Size outputSize = presentationTextureProcessor.getOutputSize();
|
||||||
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
||||||
Bitmap expectedBitmap =
|
Bitmap expectedBitmap =
|
||||||
BitmapTestUtil.readBitmap(ASPECT_RATIO_STRETCH_TO_FIT_WIDE_PNG_ASSET_PATH);
|
BitmapTestUtil.readBitmap(ASPECT_RATIO_STRETCH_TO_FIT_WIDE_PNG_ASSET_PATH);
|
||||||
|
|
||||||
presentationFrameProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
presentationTextureProcessor.drawFrame(/* presentationTimeUs= */ 0);
|
||||||
Bitmap actualBitmap =
|
Bitmap actualBitmap =
|
||||||
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
|
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
|
||||||
outputSize.getWidth(), outputSize.getHeight());
|
outputSize.getWidth(), outputSize.getHeight());
|
||||||
|
@ -19,7 +19,6 @@ import static androidx.media3.common.util.Assertions.checkArgument;
|
|||||||
import static androidx.media3.common.util.Assertions.checkState;
|
import static androidx.media3.common.util.Assertions.checkState;
|
||||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||||
|
|
||||||
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.media3.common.C;
|
||||||
@ -77,8 +76,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
*
|
*
|
||||||
* <p>Return values may be {@code 0} or {@code 90} degrees.
|
* <p>Return values may be {@code 0} or {@code 90} degrees.
|
||||||
*
|
*
|
||||||
* <p>The frame processor must be {@linkplain GlFrameProcessor#initialize(Context, int, int, int)
|
* <p>Should only be called after {@linkplain #configure(int, int) configuration}.
|
||||||
* initialized}.
|
|
||||||
*/
|
*/
|
||||||
public int getOutputRotationDegrees() {
|
public int getOutputRotationDegrees() {
|
||||||
checkState(
|
checkState(
|
||||||
|
@ -27,7 +27,7 @@ import java.io.IOException;
|
|||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
/** Copies frames from an external texture and applies color transformations for HDR if needed. */
|
/** Copies frames from an external texture and applies color transformations for HDR if needed. */
|
||||||
/* package */ class ExternalCopyFrameProcessor implements GlFrameProcessor {
|
/* package */ class ExternalTextureProcessor implements SingleFrameGlTextureProcessor {
|
||||||
|
|
||||||
static {
|
static {
|
||||||
GlUtil.glAssertionsEnabled = true;
|
GlUtil.glAssertionsEnabled = true;
|
||||||
@ -54,7 +54,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
private @MonotonicNonNull Size size;
|
private @MonotonicNonNull Size size;
|
||||||
private @MonotonicNonNull GlProgram glProgram;
|
private @MonotonicNonNull GlProgram glProgram;
|
||||||
|
|
||||||
public ExternalCopyFrameProcessor(boolean enableExperimentalHdrEditing) {
|
public ExternalTextureProcessor(boolean enableExperimentalHdrEditing) {
|
||||||
this.enableExperimentalHdrEditing = enableExperimentalHdrEditing;
|
this.enableExperimentalHdrEditing = enableExperimentalHdrEditing;
|
||||||
}
|
}
|
||||||
|
|
@ -63,6 +63,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
* fully processed yet. Output is written to its {@linkplain #setOutputSurface(Surface, int, int,
|
* fully processed yet. Output is written to its {@linkplain #setOutputSurface(Surface, int, int,
|
||||||
* SurfaceView) output surface}.
|
* SurfaceView) output surface}.
|
||||||
*/
|
*/
|
||||||
|
// TODO(b/227625423): Factor out FrameProcessor interface and rename this class to GlFrameProcessor.
|
||||||
/* package */ final class FrameProcessorChain {
|
/* package */ final class FrameProcessorChain {
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@ -131,9 +132,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the OpenGL textures and framebuffers, initializes the {@link GlFrameProcessor
|
* Creates the OpenGL textures and framebuffers, initializes the {@link
|
||||||
* GlFrameProcessors} corresponding to the {@link GlEffect GlEffects}, and returns a new {@code
|
* SingleFrameGlTextureProcessor SingleFrameGlTextureProcessors} corresponding to the {@link
|
||||||
* FrameProcessorChain}.
|
* GlEffect GlEffects}, and returns a new {@code FrameProcessorChain}.
|
||||||
*
|
*
|
||||||
* <p>This method must be executed using the {@code singleThreadExecutorService}.
|
* <p>This method must be executed using the {@code singleThreadExecutorService}.
|
||||||
*/
|
*/
|
||||||
@ -166,23 +167,23 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
GlUtil.focusPlaceholderEglSurface(eglContext, eglDisplay);
|
GlUtil.focusPlaceholderEglSurface(eglContext, eglDisplay);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExternalCopyFrameProcessor externalCopyFrameProcessor =
|
ExternalTextureProcessor externalTextureProcessor =
|
||||||
new ExternalCopyFrameProcessor(enableExperimentalHdrEditing);
|
new ExternalTextureProcessor(enableExperimentalHdrEditing);
|
||||||
ImmutableList<GlFrameProcessor> frameProcessors =
|
ImmutableList<SingleFrameGlTextureProcessor> textureProcessors =
|
||||||
getFrameProcessors(externalCopyFrameProcessor, pixelWidthHeightRatio, effects);
|
getTextureProcessors(externalTextureProcessor, pixelWidthHeightRatio, effects);
|
||||||
|
|
||||||
// Initialize frame processors.
|
// Initialize texture processors.
|
||||||
int inputExternalTexId = GlUtil.createExternalTexture();
|
int inputExternalTexId = GlUtil.createExternalTexture();
|
||||||
externalCopyFrameProcessor.initialize(context, inputExternalTexId, inputWidth, inputHeight);
|
externalTextureProcessor.initialize(context, inputExternalTexId, inputWidth, inputHeight);
|
||||||
|
|
||||||
int[] framebuffers = new int[frameProcessors.size() - 1];
|
int[] framebuffers = new int[textureProcessors.size() - 1];
|
||||||
Size inputSize = externalCopyFrameProcessor.getOutputSize();
|
Size inputSize = externalTextureProcessor.getOutputSize();
|
||||||
for (int i = 1; i < frameProcessors.size(); i++) {
|
for (int i = 1; i < textureProcessors.size(); i++) {
|
||||||
int inputTexId = GlUtil.createTexture(inputSize.getWidth(), inputSize.getHeight());
|
int inputTexId = GlUtil.createTexture(inputSize.getWidth(), inputSize.getHeight());
|
||||||
framebuffers[i - 1] = GlUtil.createFboForTexture(inputTexId);
|
framebuffers[i - 1] = GlUtil.createFboForTexture(inputTexId);
|
||||||
GlFrameProcessor frameProcessor = frameProcessors.get(i);
|
SingleFrameGlTextureProcessor textureProcessor = textureProcessors.get(i);
|
||||||
frameProcessor.initialize(context, inputTexId, inputSize.getWidth(), inputSize.getHeight());
|
textureProcessor.initialize(context, inputTexId, inputSize.getWidth(), inputSize.getHeight());
|
||||||
inputSize = frameProcessor.getOutputSize();
|
inputSize = textureProcessor.getOutputSize();
|
||||||
}
|
}
|
||||||
return new FrameProcessorChain(
|
return new FrameProcessorChain(
|
||||||
eglDisplay,
|
eglDisplay,
|
||||||
@ -190,17 +191,17 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
singleThreadExecutorService,
|
singleThreadExecutorService,
|
||||||
inputExternalTexId,
|
inputExternalTexId,
|
||||||
framebuffers,
|
framebuffers,
|
||||||
frameProcessors,
|
textureProcessors,
|
||||||
listener,
|
listener,
|
||||||
enableExperimentalHdrEditing);
|
enableExperimentalHdrEditing);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ImmutableList<GlFrameProcessor> getFrameProcessors(
|
private static ImmutableList<SingleFrameGlTextureProcessor> getTextureProcessors(
|
||||||
ExternalCopyFrameProcessor externalCopyFrameProcessor,
|
ExternalTextureProcessor externalTextureProcessor,
|
||||||
float pixelWidthHeightRatio,
|
float pixelWidthHeightRatio,
|
||||||
List<GlEffect> effects) {
|
List<GlEffect> effects) {
|
||||||
ImmutableList.Builder<GlFrameProcessor> frameProcessors =
|
ImmutableList.Builder<SingleFrameGlTextureProcessor> textureProcessors =
|
||||||
new ImmutableList.Builder<GlFrameProcessor>().add(externalCopyFrameProcessor);
|
new ImmutableList.Builder<SingleFrameGlTextureProcessor>().add(externalTextureProcessor);
|
||||||
|
|
||||||
ImmutableList.Builder<GlMatrixTransformation> matrixTransformationListBuilder =
|
ImmutableList.Builder<GlMatrixTransformation> matrixTransformationListBuilder =
|
||||||
new ImmutableList.Builder<>();
|
new ImmutableList.Builder<>();
|
||||||
@ -217,8 +218,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combine consecutive GlMatrixTransformations into a single GlFrameProcessor and convert
|
// Combine consecutive GlMatrixTransformations into a single SingleFrameGlTextureProcessor and
|
||||||
// all other GlEffects to GlFrameProcessors.
|
// convert all other GlEffects to SingleFrameGlTextureProcessors.
|
||||||
for (int i = 0; i < effects.size(); i++) {
|
for (int i = 0; i < effects.size(); i++) {
|
||||||
GlEffect effect = effects.get(i);
|
GlEffect effect = effects.get(i);
|
||||||
if (effect instanceof GlMatrixTransformation) {
|
if (effect instanceof GlMatrixTransformation) {
|
||||||
@ -228,18 +229,18 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
ImmutableList<GlMatrixTransformation> matrixTransformations =
|
ImmutableList<GlMatrixTransformation> matrixTransformations =
|
||||||
matrixTransformationListBuilder.build();
|
matrixTransformationListBuilder.build();
|
||||||
if (!matrixTransformations.isEmpty()) {
|
if (!matrixTransformations.isEmpty()) {
|
||||||
frameProcessors.add(new MatrixTransformationFrameProcessor(matrixTransformations));
|
textureProcessors.add(new MatrixTransformationProcessor(matrixTransformations));
|
||||||
matrixTransformationListBuilder = new ImmutableList.Builder<>();
|
matrixTransformationListBuilder = new ImmutableList.Builder<>();
|
||||||
}
|
}
|
||||||
frameProcessors.add(effect.toGlFrameProcessor());
|
textureProcessors.add(effect.toGlTextureProcessor());
|
||||||
}
|
}
|
||||||
ImmutableList<GlMatrixTransformation> matrixTransformations =
|
ImmutableList<GlMatrixTransformation> matrixTransformations =
|
||||||
matrixTransformationListBuilder.build();
|
matrixTransformationListBuilder.build();
|
||||||
if (!matrixTransformations.isEmpty()) {
|
if (!matrixTransformations.isEmpty()) {
|
||||||
frameProcessors.add(new MatrixTransformationFrameProcessor(matrixTransformations));
|
textureProcessors.add(new MatrixTransformationProcessor(matrixTransformations));
|
||||||
}
|
}
|
||||||
|
|
||||||
return frameProcessors.build();
|
return textureProcessors.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String TAG = "FrameProcessorChain";
|
private static final String TAG = "FrameProcessorChain";
|
||||||
@ -264,14 +265,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
private final float[] textureTransformMatrix;
|
private final float[] textureTransformMatrix;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains an {@link ExternalCopyFrameProcessor} at the 0th index and optionally other {@link
|
* Contains an {@link ExternalTextureProcessor} at the 0th index and optionally other {@link
|
||||||
* GlFrameProcessor GlFrameProcessors} at indices >= 1.
|
* SingleFrameGlTextureProcessor SingleFrameGlTextureProcessors} at indices >= 1.
|
||||||
*/
|
*/
|
||||||
private final ImmutableList<GlFrameProcessor> frameProcessors;
|
private final ImmutableList<SingleFrameGlTextureProcessor> textureProcessors;
|
||||||
/**
|
/**
|
||||||
* Identifiers of a framebuffer object associated with the intermediate textures that receive
|
* Identifiers of a framebuffer object associated with the intermediate textures that receive
|
||||||
* output from the previous {@link GlFrameProcessor}, and provide input for the following {@link
|
* output from the previous {@link SingleFrameGlTextureProcessor}, and provide input for the
|
||||||
* GlFrameProcessor}.
|
* following {@link SingleFrameGlTextureProcessor}.
|
||||||
*/
|
*/
|
||||||
private final int[] framebuffers;
|
private final int[] framebuffers;
|
||||||
|
|
||||||
@ -289,33 +290,35 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps the output {@link Surface} that is populated with the output of the final {@link
|
* Wraps the output {@link Surface} that is populated with the output of the final {@link
|
||||||
* GlFrameProcessor} for each frame.
|
* SingleFrameGlTextureProcessor} for each frame.
|
||||||
*/
|
*/
|
||||||
private @MonotonicNonNull EGLSurface outputEglSurface;
|
private @MonotonicNonNull EGLSurface outputEglSurface;
|
||||||
/**
|
/**
|
||||||
* Wraps a debug {@link SurfaceView} that is populated with the output of the final {@link
|
* Wraps a debug {@link SurfaceView} that is populated with the output of the final {@link
|
||||||
* GlFrameProcessor} for each frame.
|
* SingleFrameGlTextureProcessor} for each frame.
|
||||||
*/
|
*/
|
||||||
private @MonotonicNonNull SurfaceViewWrapper debugSurfaceViewWrapper;
|
private @MonotonicNonNull SurfaceViewWrapper debugSurfaceViewWrapper;
|
||||||
|
|
||||||
private boolean inputStreamEnded;
|
private boolean inputStreamEnded;
|
||||||
|
|
||||||
|
// TODO(b/227625423): accept GlTextureProcessors instead of SingleFrameGlTextureProcessors once
|
||||||
|
// this interface exists.
|
||||||
private FrameProcessorChain(
|
private FrameProcessorChain(
|
||||||
EGLDisplay eglDisplay,
|
EGLDisplay eglDisplay,
|
||||||
EGLContext eglContext,
|
EGLContext eglContext,
|
||||||
ExecutorService singleThreadExecutorService,
|
ExecutorService singleThreadExecutorService,
|
||||||
int inputExternalTexId,
|
int inputExternalTexId,
|
||||||
int[] framebuffers,
|
int[] framebuffers,
|
||||||
ImmutableList<GlFrameProcessor> frameProcessors,
|
ImmutableList<SingleFrameGlTextureProcessor> textureProcessors,
|
||||||
Listener listener,
|
Listener listener,
|
||||||
boolean enableExperimentalHdrEditing) {
|
boolean enableExperimentalHdrEditing) {
|
||||||
checkState(!frameProcessors.isEmpty());
|
checkState(!textureProcessors.isEmpty());
|
||||||
|
|
||||||
this.eglDisplay = eglDisplay;
|
this.eglDisplay = eglDisplay;
|
||||||
this.eglContext = eglContext;
|
this.eglContext = eglContext;
|
||||||
this.singleThreadExecutorService = singleThreadExecutorService;
|
this.singleThreadExecutorService = singleThreadExecutorService;
|
||||||
this.framebuffers = framebuffers;
|
this.framebuffers = framebuffers;
|
||||||
this.frameProcessors = frameProcessors;
|
this.textureProcessors = textureProcessors;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.stopProcessing = new AtomicBoolean();
|
this.stopProcessing = new AtomicBoolean();
|
||||||
this.enableExperimentalHdrEditing = enableExperimentalHdrEditing;
|
this.enableExperimentalHdrEditing = enableExperimentalHdrEditing;
|
||||||
@ -336,7 +339,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
* SurfaceView) output surface}.
|
* SurfaceView) output surface}.
|
||||||
*/
|
*/
|
||||||
public Size getOutputSize() {
|
public Size getOutputSize() {
|
||||||
return getLast(frameProcessors).getOutputSize();
|
return getLast(textureProcessors).getOutputSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -356,7 +359,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
int outputHeight,
|
int outputHeight,
|
||||||
@Nullable SurfaceView debugSurfaceView) {
|
@Nullable SurfaceView debugSurfaceView) {
|
||||||
// TODO(b/218488308): Don't override output size for encoder fallback. Instead allow the final
|
// TODO(b/218488308): Don't override output size for encoder fallback. Instead allow the final
|
||||||
// GlFrameProcessor to be re-configured or append another GlFrameProcessor.
|
// SingleFrameGlTextureProcessor to be re-configured or append another
|
||||||
|
// SingleFrameGlTextureProcessor.
|
||||||
this.outputSurface = outputSurface;
|
this.outputSurface = outputSurface;
|
||||||
this.outputWidth = outputWidth;
|
this.outputWidth = outputWidth;
|
||||||
this.outputHeight = outputHeight;
|
this.outputHeight = outputHeight;
|
||||||
@ -432,7 +436,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
checkNotNull(futures.poll()).cancel(/* mayInterruptIfRunning= */ false);
|
checkNotNull(futures.poll()).cancel(/* mayInterruptIfRunning= */ false);
|
||||||
}
|
}
|
||||||
futures.add(
|
futures.add(
|
||||||
singleThreadExecutorService.submit(this::releaseFrameProcessorsAndDestroyGlContext));
|
singleThreadExecutorService.submit(this::releaseTextureProcessorsAndDestroyGlContext));
|
||||||
singleThreadExecutorService.shutdown();
|
singleThreadExecutorService.shutdown();
|
||||||
try {
|
try {
|
||||||
if (!singleThreadExecutorService.awaitTermination(RELEASE_WAIT_TIME_MS, MILLISECONDS)) {
|
if (!singleThreadExecutorService.awaitTermination(RELEASE_WAIT_TIME_MS, MILLISECONDS)) {
|
||||||
@ -475,15 +479,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
long presentationTimeNs = inputSurfaceTexture.getTimestamp();
|
long presentationTimeNs = inputSurfaceTexture.getTimestamp();
|
||||||
presentationTimeUs = presentationTimeNs / 1000;
|
presentationTimeUs = presentationTimeNs / 1000;
|
||||||
inputSurfaceTexture.getTransformMatrix(textureTransformMatrix);
|
inputSurfaceTexture.getTransformMatrix(textureTransformMatrix);
|
||||||
((ExternalCopyFrameProcessor) frameProcessors.get(0))
|
((ExternalTextureProcessor) textureProcessors.get(0))
|
||||||
.setTextureTransformMatrix(textureTransformMatrix);
|
.setTextureTransformMatrix(textureTransformMatrix);
|
||||||
|
|
||||||
for (int i = 0; i < frameProcessors.size() - 1; i++) {
|
for (int i = 0; i < textureProcessors.size() - 1; i++) {
|
||||||
if (stopProcessing.get()) {
|
if (stopProcessing.get()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Size intermediateSize = frameProcessors.get(i).getOutputSize();
|
Size intermediateSize = textureProcessors.get(i).getOutputSize();
|
||||||
GlUtil.focusFramebuffer(
|
GlUtil.focusFramebuffer(
|
||||||
eglDisplay,
|
eglDisplay,
|
||||||
eglContext,
|
eglContext,
|
||||||
@ -492,11 +496,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
intermediateSize.getWidth(),
|
intermediateSize.getWidth(),
|
||||||
intermediateSize.getHeight());
|
intermediateSize.getHeight());
|
||||||
clearOutputFrame();
|
clearOutputFrame();
|
||||||
frameProcessors.get(i).drawFrame(presentationTimeUs);
|
textureProcessors.get(i).drawFrame(presentationTimeUs);
|
||||||
}
|
}
|
||||||
GlUtil.focusEglSurface(eglDisplay, eglContext, outputEglSurface, outputWidth, outputHeight);
|
GlUtil.focusEglSurface(eglDisplay, eglContext, outputEglSurface, outputWidth, outputHeight);
|
||||||
clearOutputFrame();
|
clearOutputFrame();
|
||||||
getLast(frameProcessors).drawFrame(presentationTimeUs);
|
getLast(textureProcessors).drawFrame(presentationTimeUs);
|
||||||
|
|
||||||
EGLExt.eglPresentationTimeANDROID(eglDisplay, outputEglSurface, presentationTimeNs);
|
EGLExt.eglPresentationTimeANDROID(eglDisplay, outputEglSurface, presentationTimeNs);
|
||||||
EGL14.eglSwapBuffers(eglDisplay, outputEglSurface);
|
EGL14.eglSwapBuffers(eglDisplay, outputEglSurface);
|
||||||
@ -507,7 +511,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
() -> {
|
() -> {
|
||||||
clearOutputFrame();
|
clearOutputFrame();
|
||||||
try {
|
try {
|
||||||
getLast(frameProcessors).drawFrame(framePresentationTimeUs);
|
getLast(textureProcessors).drawFrame(framePresentationTimeUs);
|
||||||
} catch (FrameProcessingException e) {
|
} catch (FrameProcessingException e) {
|
||||||
Log.d(TAG, "Error rendering to debug preview", e);
|
Log.d(TAG, "Error rendering to debug preview", e);
|
||||||
}
|
}
|
||||||
@ -532,15 +536,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Releases the {@link GlFrameProcessor GlFrameProcessors} and destroys the OpenGL context.
|
* Releases the {@link SingleFrameGlTextureProcessor SingleFrameGlTextureProcessors} and destroys
|
||||||
|
* the OpenGL context.
|
||||||
*
|
*
|
||||||
* <p>This method must be called on the {@linkplain #THREAD_NAME background thread}.
|
* <p>This method must be called on the {@linkplain #THREAD_NAME background thread}.
|
||||||
*/
|
*/
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private void releaseFrameProcessorsAndDestroyGlContext() {
|
private void releaseTextureProcessorsAndDestroyGlContext() {
|
||||||
try {
|
try {
|
||||||
for (int i = 0; i < frameProcessors.size(); i++) {
|
for (int i = 0; i < textureProcessors.size(); i++) {
|
||||||
frameProcessors.get(i).release();
|
textureProcessors.get(i).release();
|
||||||
}
|
}
|
||||||
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
|
@ -18,14 +18,16 @@ package androidx.media3.transformer;
|
|||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for a video frame effect with a {@link GlFrameProcessor} implementation.
|
* Interface for a video frame effect with a {@link SingleFrameGlTextureProcessor} implementation.
|
||||||
*
|
*
|
||||||
* <p>Implementations contain information specifying the effect and can be {@linkplain
|
* <p>Implementations contain information specifying the effect and can be {@linkplain
|
||||||
* #toGlFrameProcessor() converted} to a {@link GlFrameProcessor} which applies the effect.
|
* #toGlTextureProcessor() converted} to a {@link SingleFrameGlTextureProcessor} which applies the
|
||||||
|
* effect.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public interface GlEffect {
|
public interface GlEffect {
|
||||||
|
|
||||||
/** Returns a {@link GlFrameProcessor} that applies the the effect. */
|
/** Returns a {@link SingleFrameGlTextureProcessor} that applies the effect. */
|
||||||
GlFrameProcessor toGlFrameProcessor();
|
// TODO(b/227625423): use GlTextureProcessor here once this interface exists.
|
||||||
|
SingleFrameGlTextureProcessor toGlTextureProcessor();
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ public interface GlMatrixTransformation extends GlEffect {
|
|||||||
float[] getGlMatrixArray(long presentationTimeUs);
|
float[] getGlMatrixArray(long presentationTimeUs);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default GlFrameProcessor toGlFrameProcessor() {
|
default SingleFrameGlTextureProcessor toGlTextureProcessor() {
|
||||||
return new MatrixTransformationFrameProcessor(this);
|
return new MatrixTransformationProcessor(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@SuppressWarnings("FunctionalInterfaceClash") // b/228192298
|
@SuppressWarnings("FunctionalInterfaceClash") // b/228192298
|
||||||
/* package */ final class MatrixTransformationFrameProcessor implements GlFrameProcessor {
|
/* package */ final class MatrixTransformationProcessor implements SingleFrameGlTextureProcessor {
|
||||||
|
|
||||||
static {
|
static {
|
||||||
GlUtil.glAssertionsEnabled = true;
|
GlUtil.glAssertionsEnabled = true;
|
||||||
@ -93,7 +93,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
* @param matrixTransformation A {@link MatrixTransformation} that specifies the transformation
|
* @param matrixTransformation A {@link MatrixTransformation} that specifies the transformation
|
||||||
* matrix to use for each frame.
|
* matrix to use for each frame.
|
||||||
*/
|
*/
|
||||||
public MatrixTransformationFrameProcessor(MatrixTransformation matrixTransformation) {
|
public MatrixTransformationProcessor(MatrixTransformation matrixTransformation) {
|
||||||
this(ImmutableList.of(matrixTransformation));
|
this(ImmutableList.of(matrixTransformation));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
* @param matrixTransformation A {@link GlMatrixTransformation} that specifies the transformation
|
* @param matrixTransformation A {@link GlMatrixTransformation} that specifies the transformation
|
||||||
* matrix to use for each frame.
|
* matrix to use for each frame.
|
||||||
*/
|
*/
|
||||||
public MatrixTransformationFrameProcessor(GlMatrixTransformation matrixTransformation) {
|
public MatrixTransformationProcessor(GlMatrixTransformation matrixTransformation) {
|
||||||
this(ImmutableList.of(matrixTransformation));
|
this(ImmutableList.of(matrixTransformation));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
* @param matrixTransformations The {@link GlMatrixTransformation GlMatrixTransformations} to
|
* @param matrixTransformations The {@link GlMatrixTransformation GlMatrixTransformations} to
|
||||||
* apply to each frame in order.
|
* apply to each frame in order.
|
||||||
*/
|
*/
|
||||||
public MatrixTransformationFrameProcessor(
|
public MatrixTransformationProcessor(
|
||||||
ImmutableList<GlMatrixTransformation> matrixTransformations) {
|
ImmutableList<GlMatrixTransformation> matrixTransformations) {
|
||||||
this.matrixTransformations = matrixTransformations;
|
this.matrixTransformations = matrixTransformations;
|
||||||
|
|
@ -34,7 +34,9 @@ import java.io.IOException;
|
|||||||
* </ol>
|
* </ol>
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public interface GlFrameProcessor {
|
// TODO(b/227625423): Add GlTextureProcessor interface for async texture processors and make this an
|
||||||
|
// abstract class with a default implementation of GlTextureProcessor methods.
|
||||||
|
public interface SingleFrameGlTextureProcessor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs all initialization that requires OpenGL, such as, loading and compiling a GLSL shader
|
* Performs all initialization that requires OpenGL, such as, loading and compiling a GLSL shader
|
||||||
@ -54,7 +56,7 @@ public interface GlFrameProcessor {
|
|||||||
/**
|
/**
|
||||||
* Returns the output {@link Size} of frames processed through {@link #drawFrame(long)}.
|
* 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
|
* <p>This method may only be called after the texture processor has been {@link
|
||||||
* #initialize(Context, int, int, int) initialized}.
|
* #initialize(Context, int, int, int) initialized}.
|
||||||
*/
|
*/
|
||||||
Size getOutputSize();
|
Size getOutputSize();
|
||||||
@ -62,7 +64,7 @@ public interface GlFrameProcessor {
|
|||||||
/**
|
/**
|
||||||
* Draws one frame.
|
* Draws one frame.
|
||||||
*
|
*
|
||||||
* <p>This method may only be called after the frame processor has been {@link
|
* <p>This method may only be called after the texture processor has been {@link
|
||||||
* #initialize(Context, int, int, int) initialized}. The caller is responsible for focussing the
|
* #initialize(Context, int, int, int) initialized}. The caller is responsible for focussing the
|
||||||
* correct render target before calling this method.
|
* correct render target before calling this method.
|
||||||
*
|
*
|
@ -27,7 +27,7 @@ import org.junit.runner.RunWith;
|
|||||||
/**
|
/**
|
||||||
* Unit tests for {@link Presentation}.
|
* Unit tests for {@link Presentation}.
|
||||||
*
|
*
|
||||||
* <p>See {@code PresentationFrameProcessorPixelTest} for pixel tests testing {@link Presentation}.
|
* <p>See {@code PresentationPixelTest} for pixel tests testing {@link Presentation}.
|
||||||
*/
|
*/
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public final class PresentationTest {
|
public final class PresentationTest {
|
||||||
@ -161,27 +161,27 @@ public final class PresentationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void configure_setAspectRatioAndCrop_throwsIllegalStateException() {
|
public void configure_setAspectRatioAndCrop_throwsIllegalStateException() {
|
||||||
Presentation.Builder presentationFrameProcessor =
|
Presentation.Builder presentationBuilder =
|
||||||
new Presentation.Builder()
|
new Presentation.Builder()
|
||||||
.setAspectRatio(/* aspectRatio= */ 2f, Presentation.LAYOUT_SCALE_TO_FIT);
|
.setAspectRatio(/* aspectRatio= */ 2f, Presentation.LAYOUT_SCALE_TO_FIT);
|
||||||
|
|
||||||
assertThrows(
|
assertThrows(
|
||||||
IllegalStateException.class,
|
IllegalStateException.class,
|
||||||
() ->
|
() ->
|
||||||
presentationFrameProcessor.setCrop(
|
presentationBuilder.setCrop(
|
||||||
/* left= */ -.5f, /* right= */ .5f, /* bottom= */ .5f, /* top= */ 1f));
|
/* left= */ -.5f, /* right= */ .5f, /* bottom= */ .5f, /* top= */ 1f));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void configure_setCropAndAspectRatio_throwsIllegalStateException() {
|
public void configure_setCropAndAspectRatio_throwsIllegalStateException() {
|
||||||
Presentation.Builder presentationFrameProcessor =
|
Presentation.Builder presentationBuilder =
|
||||||
new Presentation.Builder()
|
new Presentation.Builder()
|
||||||
.setCrop(/* left= */ -.5f, /* right= */ .5f, /* bottom= */ .5f, /* top= */ 1f);
|
.setCrop(/* left= */ -.5f, /* right= */ .5f, /* bottom= */ .5f, /* top= */ 1f);
|
||||||
|
|
||||||
assertThrows(
|
assertThrows(
|
||||||
IllegalStateException.class,
|
IllegalStateException.class,
|
||||||
() ->
|
() ->
|
||||||
presentationFrameProcessor.setAspectRatio(
|
presentationBuilder.setAspectRatio(
|
||||||
/* aspectRatio= */ 2f, Presentation.LAYOUT_SCALE_TO_FIT));
|
/* aspectRatio= */ 2f, Presentation.LAYOUT_SCALE_TO_FIT));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,8 @@ import org.junit.runner.RunWith;
|
|||||||
/**
|
/**
|
||||||
* Unit tests for {@link ScaleToFitTransformation}.
|
* Unit tests for {@link ScaleToFitTransformation}.
|
||||||
*
|
*
|
||||||
* <p>See {@code MatrixTransformationFrameProcessorPixelTest} for pixel tests testing {@link
|
* <p>See {@code MatrixTransformationPixelTest} for pixel tests testing {@link
|
||||||
* MatrixTransformationFrameProcessor} given a transformation matrix.
|
* MatrixTransformationProcessor} given a transformation matrix.
|
||||||
*/
|
*/
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public final class ScaleToFitTransformationTest {
|
public final class ScaleToFitTransformationTest {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user