diff --git a/libraries/effect/src/main/java/androidx/media3/effect/MultipleInputVideoGraph.java b/libraries/effect/src/main/java/androidx/media3/effect/MultipleInputVideoGraph.java index 048d5d2d51..3ca008400b 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/MultipleInputVideoGraph.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/MultipleInputVideoGraph.java @@ -17,6 +17,7 @@ package androidx.media3.effect; import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_TEXTURE_ID; +import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkStateNotNull; @@ -25,7 +26,6 @@ import static androidx.media3.common.util.Util.newSingleThreadScheduledExecutor; import static androidx.media3.effect.DebugTraceUtil.COMPONENT_COMPOSITOR; import static androidx.media3.effect.DebugTraceUtil.COMPONENT_VFP; import static androidx.media3.effect.DebugTraceUtil.EVENT_OUTPUT_TEXTURE_RENDERED; -import static androidx.media3.effect.DefaultVideoFrameProcessor.WORKING_COLOR_SPACE_LINEAR; import static java.util.concurrent.TimeUnit.MILLISECONDS; import android.content.Context; @@ -99,6 +99,7 @@ public abstract class MultipleInputVideoGraph implements VideoGraph { protected MultipleInputVideoGraph( Context context, + VideoFrameProcessor.Factory videoFrameProcessorFactory, ColorInfo outputColorInfo, DebugViewProvider debugViewProvider, Listener listener, @@ -106,6 +107,7 @@ public abstract class MultipleInputVideoGraph implements VideoGraph { VideoCompositorSettings videoCompositorSettings, List compositionEffects, long initialTimestampOffsetUs) { + checkArgument(videoFrameProcessorFactory instanceof DefaultVideoFrameProcessor.Factory); this.context = context; this.outputColorInfo = outputColorInfo; this.debugViewProvider = debugViewProvider; @@ -118,10 +120,10 @@ public abstract class MultipleInputVideoGraph implements VideoGraph { preProcessors = new SparseArray<>(); sharedExecutorService = newSingleThreadScheduledExecutor(SHARED_EXECUTOR_NAME); glObjectsProvider = new SingleContextGlObjectsProvider(); - // TODO - b/289986435: Support injecting VideoFrameProcessor.Factory. - videoFrameProcessorFactory = - new DefaultVideoFrameProcessor.Factory.Builder() - .setSdrWorkingColorSpace(WORKING_COLOR_SPACE_LINEAR) + // TODO - b/289986435: Support injecting arbitrary VideoFrameProcessor.Factory. + this.videoFrameProcessorFactory = + ((DefaultVideoFrameProcessor.Factory) videoFrameProcessorFactory) + .buildUpon() .setGlObjectsProvider(glObjectsProvider) .setExecutorService(sharedExecutorService) .build(); diff --git a/libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesEachWithOneVideoMediaItem_succeeds[false]_0.png b/libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesEachWithOneVideoMediaItem_succeeds[false]_0.png new file mode 100644 index 0000000000..557762b4b1 Binary files /dev/null and b/libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesEachWithOneVideoMediaItem_succeeds[false]_0.png differ diff --git a/libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesEachWithOneVideoMediaItem_succeeds_0.png b/libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesEachWithOneVideoMediaItem_succeeds[true]_0.png similarity index 100% rename from libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesEachWithOneVideoMediaItem_succeeds_0.png rename to libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesEachWithOneVideoMediaItem_succeeds[true]_0.png diff --git a/libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesOneWithVideoOneWithImage_succeeds[false]_0.png b/libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesOneWithVideoOneWithImage_succeeds[false]_0.png new file mode 100644 index 0000000000..b2739e1ee3 Binary files /dev/null and b/libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesOneWithVideoOneWithImage_succeeds[false]_0.png differ diff --git a/libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesOneWithVideoOneWithImage_succeeds_0.png b/libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesOneWithVideoOneWithImage_succeeds[true]_0.png similarity index 100% rename from libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesOneWithVideoOneWithImage_succeeds_0.png rename to libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesOneWithVideoOneWithImage_succeeds[true]_0.png diff --git a/libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesWithVideoCompositorSettings_succeeds[false]_0.png b/libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesWithVideoCompositorSettings_succeeds[false]_0.png new file mode 100644 index 0000000000..3e79d4c8e9 Binary files /dev/null and b/libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesWithVideoCompositorSettings_succeeds[false]_0.png differ diff --git a/libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesWithVideoCompositorSettings_succeeds_0.png b/libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesWithVideoCompositorSettings_succeeds[true]_0.png similarity index 100% rename from libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesWithVideoCompositorSettings_succeeds_0.png rename to libraries/test_data/src/test/assets/test-generated-goldens/transformer_multi_sequence_composition_test/export_withTwoSequencesWithVideoCompositorSettings_succeeds[true]_0.png diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerMultiSequenceCompositionTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerMultiSequenceCompositionTest.java index b48cb42b27..fff4805931 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerMultiSequenceCompositionTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerMultiSequenceCompositionTest.java @@ -43,7 +43,6 @@ import androidx.media3.effect.Presentation; import androidx.media3.effect.ScaleAndRotateTransformation; import androidx.media3.effect.VideoCompositorSettings; import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.common.collect.ImmutableList; import java.io.File; import java.io.IOException; @@ -53,9 +52,12 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; /** Tests for using multiple {@link EditedMediaItemSequence} in a composition. */ -@RunWith(AndroidJUnit4.class) +@RunWith(Parameterized.class) public final class TransformerMultiSequenceCompositionTest { // Bitmaps are generated on a Pixel 6 or 7 Pro instead of an emulator, due to an emulator bug. @@ -69,11 +71,18 @@ public final class TransformerMultiSequenceCompositionTest { private static final int EXPORT_WIDTH = 360; private static final int EXPORT_HEIGHT = 240; + @Parameters(name = "{0}") + public static ImmutableList workingColorSpaceLinear() { + return ImmutableList.of(false, true); + } + private final Context context = ApplicationProvider.getApplicationContext(); @Rule public final TestName testName = new TestName(); private String testId; + @Parameter public boolean workingColorSpaceLinear; + @Before public void setUpTestId() { testId = testName.getMethodName(); @@ -106,7 +115,7 @@ public final class TransformerMultiSequenceCompositionTest { VideoCompositorSettings.DEFAULT); ExportTestResult result = - new TransformerAndroidTestRunner.Builder(context, getLinearColorSpaceTransformer()) + new TransformerAndroidTestRunner.Builder(context, buildTransformer()) .build() .run(testId, composition); @@ -142,7 +151,7 @@ public final class TransformerMultiSequenceCompositionTest { VideoCompositorSettings.DEFAULT); ExportTestResult result = - new TransformerAndroidTestRunner.Builder(context, getLinearColorSpaceTransformer()) + new TransformerAndroidTestRunner.Builder(context, buildTransformer()) .build() .run(testId, composition); @@ -200,7 +209,7 @@ public final class TransformerMultiSequenceCompositionTest { pictureInPictureVideoCompositorSettings); ExportTestResult result = - new TransformerAndroidTestRunner.Builder(context, getLinearColorSpaceTransformer()) + new TransformerAndroidTestRunner.Builder(context, buildTransformer()) .build() .run(testId, composition); @@ -209,14 +218,16 @@ public final class TransformerMultiSequenceCompositionTest { extractBitmapsFromVideo(context, checkNotNull(result.filePath)), testId); } - private Transformer getLinearColorSpaceTransformer() { + private Transformer buildTransformer() { // Use linear color space for grayscale effects. - return new Transformer.Builder(context) - .setVideoFrameProcessorFactory( - new DefaultVideoFrameProcessor.Factory.Builder() - .setSdrWorkingColorSpace(DefaultVideoFrameProcessor.WORKING_COLOR_SPACE_LINEAR) - .build()) - .build(); + Transformer.Builder builder = new Transformer.Builder(context); + if (workingColorSpaceLinear) { + builder.setVideoFrameProcessorFactory( + new DefaultVideoFrameProcessor.Factory.Builder() + .setSdrWorkingColorSpace(DefaultVideoFrameProcessor.WORKING_COLOR_SPACE_LINEAR) + .build()); + } + return builder.build(); } private static EditedMediaItem editedMediaItemByClippingVideo(String uri, List effects) { diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java index 16268e27f3..778cf9dcaa 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java @@ -463,6 +463,10 @@ public final class Transformer { *

If passing in a {@link DefaultVideoFrameProcessor.Factory}, the caller must not {@link * DefaultVideoFrameProcessor.Factory.Builder#setTextureOutput set the texture output}. * + *

If exporting a {@link Composition} with multiple video {@linkplain EditedMediaItemSequence + * sequences}, the {@link VideoFrameProcessor.Factory} must be a {@link + * DefaultVideoFrameProcessor.Factory}. + * * @param videoFrameProcessorFactory A {@link VideoFrameProcessor.Factory}. * @return This builder. */ diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerMultipleInputVideoGraph.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerMultipleInputVideoGraph.java index f206525bab..087105538d 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerMultipleInputVideoGraph.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerMultipleInputVideoGraph.java @@ -21,6 +21,7 @@ import androidx.media3.common.ColorInfo; import androidx.media3.common.DebugViewProvider; import androidx.media3.common.Effect; import androidx.media3.common.VideoFrameProcessingException; +import androidx.media3.common.VideoFrameProcessor; import androidx.media3.common.VideoGraph; import androidx.media3.effect.MultipleInputVideoGraph; import androidx.media3.effect.VideoCompositorSettings; @@ -36,6 +37,13 @@ import java.util.concurrent.Executor; /** A factory for creating {@link TransformerMultipleInputVideoGraph} instances. */ public static final class Factory implements TransformerVideoGraph.Factory { + + private final VideoFrameProcessor.Factory videoFrameProcessorFactory; + + public Factory(VideoFrameProcessor.Factory videoFrameProcessorFactory) { + this.videoFrameProcessorFactory = videoFrameProcessorFactory; + } + @Override public TransformerMultipleInputVideoGraph create( Context context, @@ -48,6 +56,7 @@ import java.util.concurrent.Executor; long initialTimestampOffsetUs) { return new TransformerMultipleInputVideoGraph( context, + videoFrameProcessorFactory, outputColorInfo, debugViewProvider, listener, @@ -60,6 +69,7 @@ import java.util.concurrent.Executor; private TransformerMultipleInputVideoGraph( Context context, + VideoFrameProcessor.Factory videoFrameProcessorFactory, ColorInfo outputColorInfo, DebugViewProvider debugViewProvider, Listener listener, @@ -69,6 +79,7 @@ import java.util.concurrent.Executor; long initialTimestampOffsetUs) { super( context, + videoFrameProcessorFactory, outputColorInfo, debugViewProvider, listener, diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoSampleExporter.java b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoSampleExporter.java index 5b2fc5a65a..63ce8adb40 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoSampleExporter.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoSampleExporter.java @@ -136,7 +136,7 @@ import org.checkerframework.dataflow.qual.Pure; new VideoGraphWrapper( context, hasMultipleInputs - ? new TransformerMultipleInputVideoGraph.Factory() + ? new TransformerMultipleInputVideoGraph.Factory(videoFrameProcessorFactory) : new TransformerSingleInputVideoGraph.Factory(videoFrameProcessorFactory), videoGraphOutputColor, errorConsumer,