Introduce GlEffect interface for effect specification.

PiperOrigin-RevId: 446143537
This commit is contained in:
hschlueter 2022-05-03 10:21:10 +01:00 committed by Ian Baker
parent 90ce9a6787
commit b410a922fe
11 changed files with 159 additions and 121 deletions

View File

@ -58,7 +58,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
public static final String ENABLE_FALLBACK = "enable_fallback"; public static final String ENABLE_FALLBACK = "enable_fallback";
public static final String ENABLE_REQUEST_SDR_TONE_MAPPING = "enable_request_sdr_tone_mapping"; public static final String ENABLE_REQUEST_SDR_TONE_MAPPING = "enable_request_sdr_tone_mapping";
public static final String ENABLE_HDR_EDITING = "enable_hdr_editing"; public static final String ENABLE_HDR_EDITING = "enable_hdr_editing";
public static final String DEMO_FRAME_PROCESSORS_SELECTIONS = "demo_frame_processors_selections"; public static final String DEMO_EFFECTS_SELECTIONS = "demo_effects_selections";
public static final String PERIODIC_VIGNETTE_CENTER_X = "periodic_vignette_center_x"; public static final String PERIODIC_VIGNETTE_CENTER_X = "periodic_vignette_center_x";
public static final String PERIODIC_VIGNETTE_CENTER_Y = "periodic_vignette_center_y"; public static final String PERIODIC_VIGNETTE_CENTER_Y = "periodic_vignette_center_y";
public static final String PERIODIC_VIGNETTE_INNER_RADIUS = "periodic_vignette_inner_radius"; public static final String PERIODIC_VIGNETTE_INNER_RADIUS = "periodic_vignette_inner_radius";
@ -91,7 +91,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
"DASH stream with non-square pixels", "DASH stream with non-square pixels",
"MP4 with HDR (HDR10) H265 video (encoding may fail)", "MP4 with HDR (HDR10) H265 video (encoding may fail)",
}; };
private static final String[] DEMO_FRAME_PROCESSORS = { private static final String[] DEMO_EFFECTS = {
"Dizzy crop", "Periodic vignette", "3D spin", "Overlay logo & timer", "Zoom in start" "Dizzy crop", "Periodic vignette", "3D spin", "Overlay logo & timer", "Zoom in start"
}; };
private static final int PERIODIC_VIGNETTE_INDEX = 1; private static final int PERIODIC_VIGNETTE_INDEX = 1;
@ -111,8 +111,8 @@ public final class ConfigurationActivity extends AppCompatActivity {
private @MonotonicNonNull CheckBox enableFallbackCheckBox; private @MonotonicNonNull CheckBox enableFallbackCheckBox;
private @MonotonicNonNull CheckBox enableRequestSdrToneMappingCheckBox; private @MonotonicNonNull CheckBox enableRequestSdrToneMappingCheckBox;
private @MonotonicNonNull CheckBox enableHdrEditingCheckBox; private @MonotonicNonNull CheckBox enableHdrEditingCheckBox;
private @MonotonicNonNull Button selectDemoFrameProcessorsButton; private @MonotonicNonNull Button selectDemoEffectsButton;
private boolean @MonotonicNonNull [] demoFrameProcessorsSelections; private boolean @MonotonicNonNull [] demoEffectsSelections;
private int inputUriPosition; private int inputUriPosition;
private float periodicVignetteCenterX; private float periodicVignetteCenterX;
private float periodicVignetteCenterY; private float periodicVignetteCenterY;
@ -187,9 +187,9 @@ public final class ConfigurationActivity extends AppCompatActivity {
findViewById(R.id.request_sdr_tone_mapping).setEnabled(isRequestSdrToneMappingSupported()); findViewById(R.id.request_sdr_tone_mapping).setEnabled(isRequestSdrToneMappingSupported());
enableHdrEditingCheckBox = findViewById(R.id.hdr_editing_checkbox); enableHdrEditingCheckBox = findViewById(R.id.hdr_editing_checkbox);
demoFrameProcessorsSelections = new boolean[DEMO_FRAME_PROCESSORS.length]; demoEffectsSelections = new boolean[DEMO_EFFECTS.length];
selectDemoFrameProcessorsButton = findViewById(R.id.select_demo_frameprocessors_button); selectDemoEffectsButton = findViewById(R.id.select_demo_effects_button);
selectDemoFrameProcessorsButton.setOnClickListener(this::selectFrameProcessors); selectDemoEffectsButton.setOnClickListener(this::selectDemoEffects);
} }
@Override @Override
@ -220,7 +220,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
"enableFallbackCheckBox", "enableFallbackCheckBox",
"enableRequestSdrToneMappingCheckBox", "enableRequestSdrToneMappingCheckBox",
"enableHdrEditingCheckBox", "enableHdrEditingCheckBox",
"demoFrameProcessorsSelections" "demoEffectsSelections"
}) })
private void startTransformation(View view) { private void startTransformation(View view) {
Intent transformerIntent = new Intent(/* packageContext= */ this, TransformerActivity.class); Intent transformerIntent = new Intent(/* packageContext= */ this, TransformerActivity.class);
@ -255,7 +255,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
bundle.putBoolean( bundle.putBoolean(
ENABLE_REQUEST_SDR_TONE_MAPPING, enableRequestSdrToneMappingCheckBox.isChecked()); ENABLE_REQUEST_SDR_TONE_MAPPING, enableRequestSdrToneMappingCheckBox.isChecked());
bundle.putBoolean(ENABLE_HDR_EDITING, enableHdrEditingCheckBox.isChecked()); bundle.putBoolean(ENABLE_HDR_EDITING, enableHdrEditingCheckBox.isChecked());
bundle.putBooleanArray(DEMO_FRAME_PROCESSORS_SELECTIONS, demoFrameProcessorsSelections); bundle.putBooleanArray(DEMO_EFFECTS_SELECTIONS, demoEffectsSelections);
bundle.putFloat(PERIODIC_VIGNETTE_CENTER_X, periodicVignetteCenterX); bundle.putFloat(PERIODIC_VIGNETTE_CENTER_X, periodicVignetteCenterX);
bundle.putFloat(PERIODIC_VIGNETTE_CENTER_Y, periodicVignetteCenterY); bundle.putFloat(PERIODIC_VIGNETTE_CENTER_Y, periodicVignetteCenterY);
bundle.putFloat(PERIODIC_VIGNETTE_INNER_RADIUS, periodicVignetteInnerRadius); bundle.putFloat(PERIODIC_VIGNETTE_INNER_RADIUS, periodicVignetteInnerRadius);
@ -278,13 +278,11 @@ public final class ConfigurationActivity extends AppCompatActivity {
.show(); .show();
} }
private void selectFrameProcessors(View view) { private void selectDemoEffects(View view) {
new AlertDialog.Builder(/* context= */ this) new AlertDialog.Builder(/* context= */ this)
.setTitle(R.string.select_demo_frameprocessors) .setTitle(R.string.select_demo_effects)
.setMultiChoiceItems( .setMultiChoiceItems(
DEMO_FRAME_PROCESSORS, DEMO_EFFECTS, checkNotNull(demoEffectsSelections), this::selectDemoEffect)
checkNotNull(demoFrameProcessorsSelections),
this::selectFrameProcessor)
.setPositiveButton(android.R.string.ok, /* listener= */ null) .setPositiveButton(android.R.string.ok, /* listener= */ null)
.create() .create()
.show(); .show();
@ -296,9 +294,9 @@ public final class ConfigurationActivity extends AppCompatActivity {
selectedFileTextView.setText(URI_DESCRIPTIONS[inputUriPosition]); selectedFileTextView.setText(URI_DESCRIPTIONS[inputUriPosition]);
} }
@RequiresNonNull("demoFrameProcessorsSelections") @RequiresNonNull("demoEffectsSelections")
private void selectFrameProcessor(DialogInterface dialog, int which, boolean isChecked) { private void selectDemoEffect(DialogInterface dialog, int which, boolean isChecked) {
demoFrameProcessorsSelections[which] = isChecked; demoEffectsSelections[which] = isChecked;
if (!isChecked || which != PERIODIC_VIGNETTE_INDEX) { if (!isChecked || which != PERIODIC_VIGNETTE_INDEX) {
return; return;
} }
@ -337,7 +335,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
"rotateSpinner", "rotateSpinner",
"enableRequestSdrToneMappingCheckBox", "enableRequestSdrToneMappingCheckBox",
"enableHdrEditingCheckBox", "enableHdrEditingCheckBox",
"selectDemoFrameProcessorsButton" "selectDemoEffectsButton"
}) })
private void onRemoveAudio(View view) { private void onRemoveAudio(View view) {
if (((CheckBox) view).isChecked()) { if (((CheckBox) view).isChecked()) {
@ -357,7 +355,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
"rotateSpinner", "rotateSpinner",
"enableRequestSdrToneMappingCheckBox", "enableRequestSdrToneMappingCheckBox",
"enableHdrEditingCheckBox", "enableHdrEditingCheckBox",
"selectDemoFrameProcessorsButton" "selectDemoEffectsButton"
}) })
private void onRemoveVideo(View view) { private void onRemoveVideo(View view) {
if (((CheckBox) view).isChecked()) { if (((CheckBox) view).isChecked()) {
@ -376,7 +374,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
"rotateSpinner", "rotateSpinner",
"enableRequestSdrToneMappingCheckBox", "enableRequestSdrToneMappingCheckBox",
"enableHdrEditingCheckBox", "enableHdrEditingCheckBox",
"selectDemoFrameProcessorsButton" "selectDemoEffectsButton"
}) })
private void enableTrackSpecificOptions(boolean isAudioEnabled, boolean isVideoEnabled) { private void enableTrackSpecificOptions(boolean isAudioEnabled, boolean isVideoEnabled) {
audioMimeSpinner.setEnabled(isAudioEnabled); audioMimeSpinner.setEnabled(isAudioEnabled);
@ -387,7 +385,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
enableRequestSdrToneMappingCheckBox.setEnabled( enableRequestSdrToneMappingCheckBox.setEnabled(
isRequestSdrToneMappingSupported() && isVideoEnabled); isRequestSdrToneMappingSupported() && isVideoEnabled);
enableHdrEditingCheckBox.setEnabled(isVideoEnabled); enableHdrEditingCheckBox.setEnabled(isVideoEnabled);
selectDemoFrameProcessorsButton.setEnabled(isVideoEnabled); selectDemoEffectsButton.setEnabled(isVideoEnabled);
findViewById(R.id.audio_mime_text_view).setEnabled(isAudioEnabled); findViewById(R.id.audio_mime_text_view).setEnabled(isAudioEnabled);
findViewById(R.id.video_mime_text_view).setEnabled(isVideoEnabled); findViewById(R.id.video_mime_text_view).setEnabled(isVideoEnabled);

View File

@ -40,7 +40,7 @@ import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.util.DebugTextViewHelper; 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.GlFrameProcessor; import androidx.media3.transformer.GlEffect;
import androidx.media3.transformer.ProgressHolder; import androidx.media3.transformer.ProgressHolder;
import androidx.media3.transformer.TransformationException; import androidx.media3.transformer.TransformationException;
import androidx.media3.transformer.TransformationRequest; import androidx.media3.transformer.TransformationRequest;
@ -240,16 +240,17 @@ public final class TransformerActivity extends AppCompatActivity {
EncoderSelector.DEFAULT, EncoderSelector.DEFAULT,
/* enableFallback= */ bundle.getBoolean(ConfigurationActivity.ENABLE_FALLBACK))); /* enableFallback= */ bundle.getBoolean(ConfigurationActivity.ENABLE_FALLBACK)));
ImmutableList.Builder<GlFrameProcessor> frameProcessors = new ImmutableList.Builder<>(); ImmutableList.Builder<GlEffect> effects = new ImmutableList.Builder<>();
@Nullable @Nullable
boolean[] selectedFrameProcessors = boolean[] selectedEffects =
bundle.getBooleanArray(ConfigurationActivity.DEMO_FRAME_PROCESSORS_SELECTIONS); bundle.getBooleanArray(ConfigurationActivity.DEMO_EFFECTS_SELECTIONS);
if (selectedFrameProcessors != null) { if (selectedEffects != null) {
if (selectedFrameProcessors[0]) { if (selectedEffects[0]) {
frameProcessors.add(AdvancedFrameProcessorFactory.createDizzyCropFrameProcessor()); effects.add(AdvancedFrameProcessorFactory::createDizzyCropFrameProcessor);
} }
if (selectedFrameProcessors[1]) { if (selectedEffects[1]) {
frameProcessors.add( effects.add(
() ->
new PeriodicVignetteFrameProcessor( new PeriodicVignetteFrameProcessor(
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),
@ -259,16 +260,16 @@ public final class TransformerActivity extends AppCompatActivity {
ConfigurationActivity.PERIODIC_VIGNETTE_OUTER_RADIUS), ConfigurationActivity.PERIODIC_VIGNETTE_OUTER_RADIUS),
bundle.getFloat(ConfigurationActivity.PERIODIC_VIGNETTE_OUTER_RADIUS))); bundle.getFloat(ConfigurationActivity.PERIODIC_VIGNETTE_OUTER_RADIUS)));
} }
if (selectedFrameProcessors[2]) { if (selectedEffects[2]) {
frameProcessors.add(AdvancedFrameProcessorFactory.createSpin3dFrameProcessor()); effects.add(AdvancedFrameProcessorFactory::createSpin3dFrameProcessor);
} }
if (selectedFrameProcessors[3]) { if (selectedEffects[3]) {
frameProcessors.add(new BitmapOverlayFrameProcessor()); effects.add(BitmapOverlayFrameProcessor::new);
} }
if (selectedFrameProcessors[4]) { if (selectedEffects[4]) {
frameProcessors.add(AdvancedFrameProcessorFactory.createZoomInTransitionFrameProcessor()); effects.add(AdvancedFrameProcessorFactory::createZoomInTransitionFrameProcessor);
} }
transformerBuilder.setFrameProcessors(frameProcessors.build()); transformerBuilder.setVideoFrameEffects(effects.build());
} }
} }
return transformerBuilder return transformerBuilder

View File

@ -64,7 +64,7 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/selected_file_text_view" app:layout_constraintTop_toBottomOf="@+id/selected_file_text_view"
app:layout_constraintBottom_toTopOf="@+id/select_demo_frameprocessors_button"> app:layout_constraintBottom_toTopOf="@+id/select_demo_effects_button">
<TableLayout <TableLayout
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -192,13 +192,13 @@
</TableLayout> </TableLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
<Button <Button
android:id="@+id/select_demo_frameprocessors_button" android:id="@+id/select_demo_effects_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="32dp" android:layout_marginTop="32dp"
android:layout_marginStart="32dp" android:layout_marginStart="32dp"
android:layout_marginEnd="32dp" android:layout_marginEnd="32dp"
android:text="@string/select_demo_frameprocessors" android:text="@string/select_demo_effects"
app:layout_constraintBottom_toTopOf="@+id/transform_button" app:layout_constraintBottom_toTopOf="@+id/transform_button"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" /> app:layout_constraintStart_toStartOf="parent" />

View File

@ -29,7 +29,7 @@
<string name="enable_fallback" translatable="false">Enable fallback</string> <string name="enable_fallback" translatable="false">Enable fallback</string>
<string name="request_sdr_tone_mapping" translatable="false">Request SDR tone-mapping (API 31+)</string> <string name="request_sdr_tone_mapping" translatable="false">Request SDR tone-mapping (API 31+)</string>
<string name="hdr_editing" translatable="false">[Experimental] HDR editing</string> <string name="hdr_editing" translatable="false">[Experimental] HDR editing</string>
<string name="select_demo_frameprocessors" translatable="false">Add demo effects</string> <string name="select_demo_effects" translatable="false">Add demo effects</string>
<string name="periodic_vignette_options" translatable="false">Periodic vignette options</string> <string name="periodic_vignette_options" translatable="false">Periodic vignette options</string>
<string name="transform" translatable="false">Transform</string> <string name="transform" translatable="false">Transform</string>
<string name="debug_preview" translatable="false">Debug preview:</string> <string name="debug_preview" translatable="false">Debug preview:</string>

View File

@ -130,7 +130,7 @@ public final class FrameProcessorChainPixelTest {
Matrix translateRightMatrix = new Matrix(); Matrix translateRightMatrix = new Matrix();
translateRightMatrix.postTranslate(/* dx= */ 1, /* dy= */ 0); translateRightMatrix.postTranslate(/* dx= */ 1, /* dy= */ 0);
GlFrameProcessor glFrameProcessor = new AdvancedFrameProcessor(translateRightMatrix); GlFrameProcessor glFrameProcessor = new AdvancedFrameProcessor(translateRightMatrix);
setUpAndPrepareFirstFrame(DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO, glFrameProcessor); setUpAndPrepareFirstFrame(DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO, () -> glFrameProcessor);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(TRANSLATE_RIGHT_PNG_ASSET_PATH); Bitmap expectedBitmap = BitmapTestUtil.readBitmap(TRANSLATE_RIGHT_PNG_ASSET_PATH);
Bitmap actualBitmap = processFirstFrameAndEnd(); Bitmap actualBitmap = processFirstFrameAndEnd();
@ -155,7 +155,9 @@ public final class FrameProcessorChainPixelTest {
GlFrameProcessor rotate45FrameProcessor = GlFrameProcessor rotate45FrameProcessor =
new ScaleToFitFrameProcessor.Builder().setRotationDegrees(45).build(); new ScaleToFitFrameProcessor.Builder().setRotationDegrees(45).build();
setUpAndPrepareFirstFrame( setUpAndPrepareFirstFrame(
DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO, translateRightFrameProcessor, rotate45FrameProcessor); DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO,
() -> translateRightFrameProcessor,
() -> rotate45FrameProcessor);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(TRANSLATE_THEN_ROTATE_PNG_ASSET_PATH); Bitmap expectedBitmap = BitmapTestUtil.readBitmap(TRANSLATE_THEN_ROTATE_PNG_ASSET_PATH);
Bitmap actualBitmap = processFirstFrameAndEnd(); Bitmap actualBitmap = processFirstFrameAndEnd();
@ -180,7 +182,9 @@ public final class FrameProcessorChainPixelTest {
GlFrameProcessor translateRightFrameProcessor = GlFrameProcessor translateRightFrameProcessor =
new AdvancedFrameProcessor(translateRightMatrix); new AdvancedFrameProcessor(translateRightMatrix);
setUpAndPrepareFirstFrame( setUpAndPrepareFirstFrame(
DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO, rotate45FrameProcessor, translateRightFrameProcessor); DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO,
() -> rotate45FrameProcessor,
() -> translateRightFrameProcessor);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ROTATE_THEN_TRANSLATE_PNG_ASSET_PATH); Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ROTATE_THEN_TRANSLATE_PNG_ASSET_PATH);
Bitmap actualBitmap = processFirstFrameAndEnd(); Bitmap actualBitmap = processFirstFrameAndEnd();
@ -200,7 +204,7 @@ public final class FrameProcessorChainPixelTest {
String testId = "processData_withPresentationFrameProcessor_setResolution"; String testId = "processData_withPresentationFrameProcessor_setResolution";
GlFrameProcessor glFrameProcessor = GlFrameProcessor glFrameProcessor =
new PresentationFrameProcessor.Builder().setResolution(480).build(); new PresentationFrameProcessor.Builder().setResolution(480).build();
setUpAndPrepareFirstFrame(DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO, glFrameProcessor); setUpAndPrepareFirstFrame(DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO, () -> glFrameProcessor);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(REQUEST_OUTPUT_HEIGHT_PNG_ASSET_PATH); Bitmap expectedBitmap = BitmapTestUtil.readBitmap(REQUEST_OUTPUT_HEIGHT_PNG_ASSET_PATH);
Bitmap actualBitmap = processFirstFrameAndEnd(); Bitmap actualBitmap = processFirstFrameAndEnd();
@ -220,7 +224,7 @@ public final class FrameProcessorChainPixelTest {
String testId = "processData_withScaleToFitFrameProcessor_rotate45"; String testId = "processData_withScaleToFitFrameProcessor_rotate45";
GlFrameProcessor glFrameProcessor = GlFrameProcessor glFrameProcessor =
new ScaleToFitFrameProcessor.Builder().setRotationDegrees(45).build(); new ScaleToFitFrameProcessor.Builder().setRotationDegrees(45).build();
setUpAndPrepareFirstFrame(DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO, glFrameProcessor); setUpAndPrepareFirstFrame(DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO, () -> glFrameProcessor);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ROTATE45_SCALE_TO_FIT_PNG_ASSET_PATH); Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ROTATE45_SCALE_TO_FIT_PNG_ASSET_PATH);
Bitmap actualBitmap = processFirstFrameAndEnd(); Bitmap actualBitmap = processFirstFrameAndEnd();
@ -240,11 +244,10 @@ public final class FrameProcessorChainPixelTest {
* accessed on the {@link FrameProcessorChain}'s output {@code outputImageReader}. * accessed on the {@link FrameProcessorChain}'s output {@code outputImageReader}.
* *
* @param pixelWidthHeightRatio The ratio of width over height for each pixel. * @param pixelWidthHeightRatio The ratio of width over height for each pixel.
* @param frameProcessors The {@link GlFrameProcessor GlFrameProcessors} that will apply changes * @param effects The {@link GlEffect GlEffects} to apply to the input frame.
* to the input frame.
*/ */
private void setUpAndPrepareFirstFrame( private void setUpAndPrepareFirstFrame(float pixelWidthHeightRatio, GlEffect... effects)
float pixelWidthHeightRatio, GlFrameProcessor... frameProcessors) throws Exception { throws Exception {
// Set up the extractor to read the first video frame and get its format. // Set up the extractor to read the first video frame and get its format.
MediaExtractor mediaExtractor = new MediaExtractor(); MediaExtractor mediaExtractor = new MediaExtractor();
@Nullable MediaCodec mediaCodec = null; @Nullable MediaCodec mediaCodec = null;
@ -267,7 +270,7 @@ public final class FrameProcessorChainPixelTest {
pixelWidthHeightRatio, pixelWidthHeightRatio,
inputWidth, inputWidth,
inputHeight, inputHeight,
asList(frameProcessors), asList(effects),
/* enableExperimentalHdrEditing= */ false); /* enableExperimentalHdrEditing= */ false);
Size outputSize = frameProcessorChain.getOutputSize(); Size outputSize = frameProcessorChain.getOutputSize();
outputImageReader = outputImageReader =

View File

@ -112,16 +112,16 @@ public final class FrameProcessorChainTest {
private static FrameProcessorChain createFrameProcessorChainWithFakeFrameProcessors( private static FrameProcessorChain createFrameProcessorChainWithFakeFrameProcessors(
float pixelWidthHeightRatio, Size inputSize, List<Size> frameProcessorOutputSizes) float pixelWidthHeightRatio, Size inputSize, List<Size> frameProcessorOutputSizes)
throws TransformationException { throws TransformationException {
ImmutableList.Builder<GlFrameProcessor> frameProcessors = new ImmutableList.Builder<>(); ImmutableList.Builder<GlEffect> effects = new ImmutableList.Builder<>();
for (Size element : frameProcessorOutputSizes) { for (Size element : frameProcessorOutputSizes) {
frameProcessors.add(new FakeFrameProcessor(element)); effects.add(() -> new FakeFrameProcessor(element));
} }
return FrameProcessorChain.create( return FrameProcessorChain.create(
getApplicationContext(), getApplicationContext(),
pixelWidthHeightRatio, pixelWidthHeightRatio,
inputSize.getWidth(), inputSize.getWidth(),
inputSize.getHeight(), inputSize.getHeight(),
frameProcessors.build(), effects.build(),
/* enableExperimentalHdrEditing= */ false); /* enableExperimentalHdrEditing= */ false);
} }

View File

@ -75,7 +75,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
* by this ratio so that the output frame's pixels have a ratio of 1. * by this ratio so that the output frame's pixels have a ratio of 1.
* @param inputWidth The input frame width, in pixels. * @param inputWidth The input frame width, in pixels.
* @param inputHeight The input frame height, in pixels. * @param inputHeight The input frame height, in pixels.
* @param frameProcessors The {@link GlFrameProcessor GlFrameProcessors} to apply to each frame. * @param effects The {@link GlEffect GlEffects} to apply to each frame.
* @param enableExperimentalHdrEditing Whether to attempt to process the input as an HDR signal. * @param enableExperimentalHdrEditing Whether to attempt to process the input as an HDR signal.
* @return A new instance. * @return A new instance.
* @throws TransformationException If reading shader files fails, or an OpenGL error occurs while * @throws TransformationException If reading shader files fails, or an OpenGL error occurs while
@ -86,7 +86,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
float pixelWidthHeightRatio, float pixelWidthHeightRatio,
int inputWidth, int inputWidth,
int inputHeight, int inputHeight,
List<GlFrameProcessor> frameProcessors, List<GlEffect> effects,
boolean enableExperimentalHdrEditing) boolean enableExperimentalHdrEditing)
throws TransformationException { throws TransformationException {
checkArgument(inputWidth > 0, "inputWidth must be positive"); checkArgument(inputWidth > 0, "inputWidth must be positive");
@ -103,7 +103,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
pixelWidthHeightRatio, pixelWidthHeightRatio,
inputWidth, inputWidth,
inputHeight, inputHeight,
frameProcessors, effects,
enableExperimentalHdrEditing, enableExperimentalHdrEditing,
singleThreadExecutorService)) singleThreadExecutorService))
.get(); .get();
@ -118,8 +118,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
} }
/** /**
* Creates the OpenGL textures, framebuffers, initializes the {@link GlFrameProcessor * Creates the OpenGL textures and framebuffers, initializes the {@link GlFrameProcessor
* GlFrameProcessors} and returns a new {@code FrameProcessorChain}. * GlFrameProcessors} corresponding to the {@link 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}.
*/ */
@ -129,15 +130,13 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
float pixelWidthHeightRatio, float pixelWidthHeightRatio,
int inputWidth, int inputWidth,
int inputHeight, int inputHeight,
List<GlFrameProcessor> frameProcessors, List<GlEffect> effects,
boolean enableExperimentalHdrEditing, boolean enableExperimentalHdrEditing,
ExecutorService singleThreadExecutorService) ExecutorService singleThreadExecutorService)
throws IOException { throws IOException {
checkState(Thread.currentThread().getName().equals(THREAD_NAME)); checkState(Thread.currentThread().getName().equals(THREAD_NAME));
EGLDisplay eglDisplay = GlUtil.createEglDisplay(); EGLDisplay eglDisplay = GlUtil.createEglDisplay();
ExternalCopyFrameProcessor externalCopyFrameProcessor =
new ExternalCopyFrameProcessor(enableExperimentalHdrEditing);
EGLContext eglContext = EGLContext eglContext =
enableExperimentalHdrEditing enableExperimentalHdrEditing
? GlUtil.createEglContextEs3Rgba1010102(eglDisplay) ? GlUtil.createEglContextEs3Rgba1010102(eglDisplay)
@ -153,20 +152,21 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
GlUtil.focusPlaceholderEglSurface(eglContext, eglDisplay); GlUtil.focusPlaceholderEglSurface(eglContext, eglDisplay);
} }
ImmutableList<GlFrameProcessor> expandedFrameProcessors = ExternalCopyFrameProcessor externalCopyFrameProcessor =
getExpandedFrameProcessors( new ExternalCopyFrameProcessor(enableExperimentalHdrEditing);
externalCopyFrameProcessor, pixelWidthHeightRatio, frameProcessors); ImmutableList<GlFrameProcessor> frameProcessors =
getFrameProcessors(externalCopyFrameProcessor, pixelWidthHeightRatio, effects);
// Initialize frame processors. // Initialize frame processors.
int inputExternalTexId = GlUtil.createExternalTexture(); int inputExternalTexId = GlUtil.createExternalTexture();
externalCopyFrameProcessor.initialize(context, inputExternalTexId, inputWidth, inputHeight); externalCopyFrameProcessor.initialize(context, inputExternalTexId, inputWidth, inputHeight);
int[] framebuffers = new int[expandedFrameProcessors.size() - 1]; int[] framebuffers = new int[frameProcessors.size() - 1];
Size inputSize = externalCopyFrameProcessor.getOutputSize(); Size inputSize = externalCopyFrameProcessor.getOutputSize();
for (int i = 1; i < expandedFrameProcessors.size(); i++) { for (int i = 1; i < frameProcessors.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 = expandedFrameProcessors.get(i); GlFrameProcessor frameProcessor = frameProcessors.get(i);
frameProcessor.initialize(context, inputTexId, inputSize.getWidth(), inputSize.getHeight()); frameProcessor.initialize(context, inputTexId, inputSize.getWidth(), inputSize.getHeight());
inputSize = frameProcessor.getOutputSize(); inputSize = frameProcessor.getOutputSize();
} }
@ -176,31 +176,34 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
singleThreadExecutorService, singleThreadExecutorService,
inputExternalTexId, inputExternalTexId,
framebuffers, framebuffers,
expandedFrameProcessors, frameProcessors,
enableExperimentalHdrEditing); enableExperimentalHdrEditing);
} }
private static ImmutableList<GlFrameProcessor> getExpandedFrameProcessors( private static ImmutableList<GlFrameProcessor> getFrameProcessors(
ExternalCopyFrameProcessor externalCopyFrameProcessor, ExternalCopyFrameProcessor externalCopyFrameProcessor,
float pixelWidthHeightRatio, float pixelWidthHeightRatio,
List<GlFrameProcessor> frameProcessors) { List<GlEffect> effects) {
ImmutableList.Builder<GlFrameProcessor> frameProcessorListBuilder = ImmutableList.Builder<GlFrameProcessor> frameProcessors =
new ImmutableList.Builder<GlFrameProcessor>().add(externalCopyFrameProcessor); new ImmutableList.Builder<GlFrameProcessor>().add(externalCopyFrameProcessor);
// Scale to expand the frame to apply the pixelWidthHeightRatio. // Scale to expand the frame to apply the pixelWidthHeightRatio.
if (pixelWidthHeightRatio > 1f) { if (pixelWidthHeightRatio > 1f) {
frameProcessorListBuilder.add( frameProcessors.add(
new ScaleToFitFrameProcessor.Builder() new ScaleToFitFrameProcessor.Builder()
.setScale(/* scaleX= */ pixelWidthHeightRatio, /* scaleY= */ 1f) .setScale(/* scaleX= */ pixelWidthHeightRatio, /* scaleY= */ 1f)
.build()); .build());
} else if (pixelWidthHeightRatio < 1f) { } else if (pixelWidthHeightRatio < 1f) {
frameProcessorListBuilder.add( frameProcessors.add(
new ScaleToFitFrameProcessor.Builder() new ScaleToFitFrameProcessor.Builder()
.setScale(/* scaleX= */ 1f, /* scaleY= */ 1f / pixelWidthHeightRatio) .setScale(/* scaleX= */ 1f, /* scaleY= */ 1f / pixelWidthHeightRatio)
.build()); .build());
} }
frameProcessorListBuilder.addAll(frameProcessors);
return frameProcessorListBuilder.build(); for (int i = 0; i < effects.size(); i++) {
frameProcessors.add(effects.get(i).toGlFrameProcessor());
}
return frameProcessors.build();
} }
private static final String TAG = "FrameProcessorChain"; private static final String TAG = "FrameProcessorChain";

View File

@ -0,0 +1,31 @@
/*
* 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 androidx.media3.common.util.UnstableApi;
/**
* Interface for a video frame effect with a {@link GlFrameProcessor} implementation.
*
* <p>Implementations contain information specifying the effect and can be {@linkplain
* #toGlFrameProcessor() converted} to a {@link GlFrameProcessor} which applies the effect.
*/
@UnstableApi
public interface GlEffect {
/** Returns a {@link GlFrameProcessor} that applies the the effect. */
GlFrameProcessor toGlFrameProcessor();
}

View File

@ -104,7 +104,7 @@ public final class Transformer {
private boolean removeVideo; private boolean removeVideo;
private String containerMimeType; private String containerMimeType;
private TransformationRequest transformationRequest; private TransformationRequest transformationRequest;
private ImmutableList<GlFrameProcessor> frameProcessors; private ImmutableList<GlEffect> videoFrameEffects;
private ListenerSet<Transformer.Listener> listeners; private ListenerSet<Transformer.Listener> listeners;
private DebugViewProvider debugViewProvider; private DebugViewProvider debugViewProvider;
private Looper looper; private Looper looper;
@ -124,7 +124,7 @@ public final class Transformer {
debugViewProvider = DebugViewProvider.NONE; debugViewProvider = DebugViewProvider.NONE;
containerMimeType = MimeTypes.VIDEO_MP4; containerMimeType = MimeTypes.VIDEO_MP4;
transformationRequest = new TransformationRequest.Builder().build(); transformationRequest = new TransformationRequest.Builder().build();
frameProcessors = ImmutableList.of(); videoFrameEffects = ImmutableList.of();
} }
/** /**
@ -142,7 +142,7 @@ public final class Transformer {
debugViewProvider = DebugViewProvider.NONE; debugViewProvider = DebugViewProvider.NONE;
containerMimeType = MimeTypes.VIDEO_MP4; containerMimeType = MimeTypes.VIDEO_MP4;
transformationRequest = new TransformationRequest.Builder().build(); transformationRequest = new TransformationRequest.Builder().build();
frameProcessors = ImmutableList.of(); videoFrameEffects = ImmutableList.of();
} }
/** Creates a builder with the values of the provided {@link Transformer}. */ /** Creates a builder with the values of the provided {@link Transformer}. */
@ -154,7 +154,7 @@ public final class Transformer {
this.removeVideo = transformer.removeVideo; this.removeVideo = transformer.removeVideo;
this.containerMimeType = transformer.containerMimeType; this.containerMimeType = transformer.containerMimeType;
this.transformationRequest = transformer.transformationRequest; this.transformationRequest = transformer.transformationRequest;
this.frameProcessors = transformer.frameProcessors; this.videoFrameEffects = transformer.videoFrameEffects;
this.listeners = transformer.listeners; this.listeners = transformer.listeners;
this.looper = transformer.looper; this.looper = transformer.looper;
this.encoderFactory = transformer.encoderFactory; this.encoderFactory = transformer.encoderFactory;
@ -187,20 +187,20 @@ public final class Transformer {
} }
/** /**
* Sets the {@linkplain GlFrameProcessor frame processors} to apply to each frame. * Sets the {@linkplain GlEffect effects} to apply to each video frame.
* *
* <p>The {@linkplain GlFrameProcessor frame processors} are applied before any {@linkplain * <p>The {@linkplain GlEffect effects} are applied before any {@linkplain
* TransformationRequest.Builder#setScale(float, float) scale}, {@linkplain * TransformationRequest.Builder#setScale(float, float) scale}, {@linkplain
* TransformationRequest.Builder#setRotationDegrees(float) rotation}, or {@linkplain * TransformationRequest.Builder#setRotationDegrees(float) rotation}, or {@linkplain
* TransformationRequest.Builder#setResolution(int) resolution} changes specified in the {@link * TransformationRequest.Builder#setResolution(int) resolution} changes specified in the {@link
* #setTransformationRequest(TransformationRequest) TransformationRequest} but after {@linkplain * #setTransformationRequest(TransformationRequest) TransformationRequest} but after {@linkplain
* TransformationRequest.Builder#setFlattenForSlowMotion(boolean) slow-motion flattening}. * TransformationRequest.Builder#setFlattenForSlowMotion(boolean) slow-motion flattening}.
* *
* @param frameProcessors The {@linkplain GlFrameProcessor frame processors}. * @param effects The {@linkplain GlEffect effects} to apply to each video frame.
* @return This builder. * @return This builder.
*/ */
public Builder setFrameProcessors(List<GlFrameProcessor> frameProcessors) { public Builder setVideoFrameEffects(List<GlEffect> effects) {
this.frameProcessors = ImmutableList.copyOf(frameProcessors); this.videoFrameEffects = ImmutableList.copyOf(effects);
return this; return this;
} }
@ -432,7 +432,7 @@ public final class Transformer {
removeVideo, removeVideo,
containerMimeType, containerMimeType,
transformationRequest, transformationRequest,
frameProcessors, videoFrameEffects,
listeners, listeners,
looper, looper,
clock, clock,
@ -554,7 +554,7 @@ public final class Transformer {
private final boolean removeVideo; private final boolean removeVideo;
private final String containerMimeType; private final String containerMimeType;
private final TransformationRequest transformationRequest; private final TransformationRequest transformationRequest;
private final ImmutableList<GlFrameProcessor> frameProcessors; private final ImmutableList<GlEffect> videoFrameEffects;
private final Looper looper; private final Looper looper;
private final Clock clock; private final Clock clock;
private final Codec.EncoderFactory encoderFactory; private final Codec.EncoderFactory encoderFactory;
@ -575,7 +575,7 @@ public final class Transformer {
boolean removeVideo, boolean removeVideo,
String containerMimeType, String containerMimeType,
TransformationRequest transformationRequest, TransformationRequest transformationRequest,
ImmutableList<GlFrameProcessor> frameProcessors, ImmutableList<GlEffect> videoFrameEffects,
ListenerSet<Transformer.Listener> listeners, ListenerSet<Transformer.Listener> listeners,
Looper looper, Looper looper,
Clock clock, Clock clock,
@ -590,7 +590,7 @@ public final class Transformer {
this.removeVideo = removeVideo; this.removeVideo = removeVideo;
this.containerMimeType = containerMimeType; this.containerMimeType = containerMimeType;
this.transformationRequest = transformationRequest; this.transformationRequest = transformationRequest;
this.frameProcessors = frameProcessors; this.videoFrameEffects = videoFrameEffects;
this.listeners = listeners; this.listeners = listeners;
this.looper = looper; this.looper = looper;
this.clock = clock; this.clock = clock;
@ -730,7 +730,7 @@ public final class Transformer {
removeAudio, removeAudio,
removeVideo, removeVideo,
transformationRequest, transformationRequest,
frameProcessors, videoFrameEffects,
encoderFactory, encoderFactory,
decoderFactory, decoderFactory,
new FallbackListener(mediaItem, listeners, transformationRequest), new FallbackListener(mediaItem, listeners, transformationRequest),
@ -842,7 +842,7 @@ public final class Transformer {
private final boolean removeAudio; private final boolean removeAudio;
private final boolean removeVideo; private final boolean removeVideo;
private final TransformationRequest transformationRequest; private final TransformationRequest transformationRequest;
private final ImmutableList<GlFrameProcessor> frameProcessors; private final ImmutableList<GlEffect> videoFrameEffects;
private final Codec.EncoderFactory encoderFactory; private final Codec.EncoderFactory encoderFactory;
private final Codec.DecoderFactory decoderFactory; private final Codec.DecoderFactory decoderFactory;
private final FallbackListener fallbackListener; private final FallbackListener fallbackListener;
@ -854,7 +854,7 @@ public final class Transformer {
boolean removeAudio, boolean removeAudio,
boolean removeVideo, boolean removeVideo,
TransformationRequest transformationRequest, TransformationRequest transformationRequest,
ImmutableList<GlFrameProcessor> frameProcessors, ImmutableList<GlEffect> videoFrameEffects,
Codec.EncoderFactory encoderFactory, Codec.EncoderFactory encoderFactory,
Codec.DecoderFactory decoderFactory, Codec.DecoderFactory decoderFactory,
FallbackListener fallbackListener, FallbackListener fallbackListener,
@ -864,7 +864,7 @@ public final class Transformer {
this.removeAudio = removeAudio; this.removeAudio = removeAudio;
this.removeVideo = removeVideo; this.removeVideo = removeVideo;
this.transformationRequest = transformationRequest; this.transformationRequest = transformationRequest;
this.frameProcessors = frameProcessors; this.videoFrameEffects = videoFrameEffects;
this.encoderFactory = encoderFactory; this.encoderFactory = encoderFactory;
this.decoderFactory = decoderFactory; this.decoderFactory = decoderFactory;
this.fallbackListener = fallbackListener; this.fallbackListener = fallbackListener;
@ -900,7 +900,7 @@ public final class Transformer {
muxerWrapper, muxerWrapper,
mediaClock, mediaClock,
transformationRequest, transformationRequest,
frameProcessors, videoFrameEffects,
encoderFactory, encoderFactory,
decoderFactory, decoderFactory,
fallbackListener, fallbackListener,

View File

@ -35,7 +35,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private static final String TAG = "TVideoRenderer"; private static final String TAG = "TVideoRenderer";
private final Context context; private final Context context;
private final ImmutableList<GlFrameProcessor> frameProcessors; private final ImmutableList<GlEffect> effects;
private final Codec.EncoderFactory encoderFactory; private final Codec.EncoderFactory encoderFactory;
private final Codec.DecoderFactory decoderFactory; private final Codec.DecoderFactory decoderFactory;
private final Transformer.DebugViewProvider debugViewProvider; private final Transformer.DebugViewProvider debugViewProvider;
@ -48,14 +48,14 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
MuxerWrapper muxerWrapper, MuxerWrapper muxerWrapper,
TransformerMediaClock mediaClock, TransformerMediaClock mediaClock,
TransformationRequest transformationRequest, TransformationRequest transformationRequest,
ImmutableList<GlFrameProcessor> frameProcessors, ImmutableList<GlEffect> effects,
Codec.EncoderFactory encoderFactory, Codec.EncoderFactory encoderFactory,
Codec.DecoderFactory decoderFactory, Codec.DecoderFactory decoderFactory,
FallbackListener fallbackListener, FallbackListener fallbackListener,
Transformer.DebugViewProvider debugViewProvider) { Transformer.DebugViewProvider debugViewProvider) {
super(C.TRACK_TYPE_VIDEO, muxerWrapper, mediaClock, transformationRequest, fallbackListener); super(C.TRACK_TYPE_VIDEO, muxerWrapper, mediaClock, transformationRequest, fallbackListener);
this.context = context; this.context = context;
this.frameProcessors = frameProcessors; this.effects = effects;
this.encoderFactory = encoderFactory; this.encoderFactory = encoderFactory;
this.decoderFactory = decoderFactory; this.decoderFactory = decoderFactory;
this.debugViewProvider = debugViewProvider; this.debugViewProvider = debugViewProvider;
@ -90,7 +90,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
context, context,
inputFormat, inputFormat,
transformationRequest, transformationRequest,
frameProcessors, effects,
decoderFactory, decoderFactory,
encoderFactory, encoderFactory,
muxerWrapper.getSupportedSampleMimeTypes(getTrackType()), muxerWrapper.getSupportedSampleMimeTypes(getTrackType()),
@ -137,7 +137,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
&& transformationRequest.outputHeight != inputFormat.height) { && transformationRequest.outputHeight != inputFormat.height) {
return false; return false;
} }
if (!frameProcessors.isEmpty()) { if (!effects.isEmpty()) {
return false; return false;
} }
return true; return true;

View File

@ -50,7 +50,7 @@ import org.checkerframework.dataflow.qual.Pure;
Context context, Context context,
Format inputFormat, Format inputFormat,
TransformationRequest transformationRequest, TransformationRequest transformationRequest,
ImmutableList<GlFrameProcessor> frameProcessors, ImmutableList<GlEffect> effects,
Codec.DecoderFactory decoderFactory, Codec.DecoderFactory decoderFactory,
Codec.EncoderFactory encoderFactory, Codec.EncoderFactory encoderFactory,
List<String> allowedOutputMimeTypes, List<String> allowedOutputMimeTypes,
@ -68,33 +68,35 @@ import org.checkerframework.dataflow.qual.Pure;
int decodedHeight = int decodedHeight =
(inputFormat.rotationDegrees % 180 == 0) ? inputFormat.height : inputFormat.width; (inputFormat.rotationDegrees % 180 == 0) ? inputFormat.height : inputFormat.width;
ImmutableList.Builder<GlFrameProcessor> frameProcessorsListBuilder = ImmutableList.Builder<GlEffect> effectsListBuilder =
new ImmutableList.Builder<GlFrameProcessor>().addAll(frameProcessors); new ImmutableList.Builder<GlEffect>().addAll(effects);
if (transformationRequest.scaleX != 1f if (transformationRequest.scaleX != 1f
|| transformationRequest.scaleY != 1f || transformationRequest.scaleY != 1f
|| transformationRequest.rotationDegrees != 0f) { || transformationRequest.rotationDegrees != 0f) {
frameProcessorsListBuilder.add( effectsListBuilder.add(
() ->
new ScaleToFitFrameProcessor.Builder() new ScaleToFitFrameProcessor.Builder()
.setScale(transformationRequest.scaleX, transformationRequest.scaleY) .setScale(transformationRequest.scaleX, transformationRequest.scaleY)
.setRotationDegrees(transformationRequest.rotationDegrees) .setRotationDegrees(transformationRequest.rotationDegrees)
.build()); .build());
} }
if (transformationRequest.outputHeight != C.LENGTH_UNSET) { if (transformationRequest.outputHeight != C.LENGTH_UNSET) {
frameProcessorsListBuilder.add( effectsListBuilder.add(
() ->
new PresentationFrameProcessor.Builder() new PresentationFrameProcessor.Builder()
.setResolution(transformationRequest.outputHeight) .setResolution(transformationRequest.outputHeight)
.build()); .build());
} }
EncoderCompatibilityFrameProcessor encoderCompatibilityFrameProcessor = EncoderCompatibilityFrameProcessor encoderCompatibilityFrameProcessor =
new EncoderCompatibilityFrameProcessor(); new EncoderCompatibilityFrameProcessor();
frameProcessorsListBuilder.add(encoderCompatibilityFrameProcessor); effectsListBuilder.add(() -> encoderCompatibilityFrameProcessor);
frameProcessorChain = frameProcessorChain =
FrameProcessorChain.create( FrameProcessorChain.create(
context, context,
inputFormat.pixelWidthHeightRatio, inputFormat.pixelWidthHeightRatio,
/* inputWidth= */ decodedWidth, /* inputWidth= */ decodedWidth,
/* inputHeight= */ decodedHeight, /* inputHeight= */ decodedHeight,
frameProcessorsListBuilder.build(), effectsListBuilder.build(),
transformationRequest.enableHdrEditing); transformationRequest.enableHdrEditing);
Size requestedEncoderSize = frameProcessorChain.getOutputSize(); Size requestedEncoderSize = frameProcessorChain.getOutputSize();
outputRotationDegrees = encoderCompatibilityFrameProcessor.getOutputRotationDegrees(); outputRotationDegrees = encoderCompatibilityFrameProcessor.getOutputRotationDegrees();