Use WORKING_COLOR_SPACE_DEFAULT in multi-sequence compositions
Build upon Transformer.videoFrameProcessorFactory in MultipleInputVideoGraph Check that Transformer.videoFrameProcessorFactory is DefaultVideoFrameProcessor for multi-input video Fixes https://github.com/androidx/media/issues/1509 PiperOrigin-RevId: 655232381
@ -17,6 +17,7 @@
|
|||||||
package androidx.media3.effect;
|
package androidx.media3.effect;
|
||||||
|
|
||||||
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_TEXTURE_ID;
|
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.checkNotNull;
|
||||||
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;
|
||||||
@ -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_COMPOSITOR;
|
||||||
import static androidx.media3.effect.DebugTraceUtil.COMPONENT_VFP;
|
import static androidx.media3.effect.DebugTraceUtil.COMPONENT_VFP;
|
||||||
import static androidx.media3.effect.DebugTraceUtil.EVENT_OUTPUT_TEXTURE_RENDERED;
|
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 static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -99,6 +99,7 @@ public abstract class MultipleInputVideoGraph implements VideoGraph {
|
|||||||
|
|
||||||
protected MultipleInputVideoGraph(
|
protected MultipleInputVideoGraph(
|
||||||
Context context,
|
Context context,
|
||||||
|
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
||||||
ColorInfo outputColorInfo,
|
ColorInfo outputColorInfo,
|
||||||
DebugViewProvider debugViewProvider,
|
DebugViewProvider debugViewProvider,
|
||||||
Listener listener,
|
Listener listener,
|
||||||
@ -106,6 +107,7 @@ public abstract class MultipleInputVideoGraph implements VideoGraph {
|
|||||||
VideoCompositorSettings videoCompositorSettings,
|
VideoCompositorSettings videoCompositorSettings,
|
||||||
List<Effect> compositionEffects,
|
List<Effect> compositionEffects,
|
||||||
long initialTimestampOffsetUs) {
|
long initialTimestampOffsetUs) {
|
||||||
|
checkArgument(videoFrameProcessorFactory instanceof DefaultVideoFrameProcessor.Factory);
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.outputColorInfo = outputColorInfo;
|
this.outputColorInfo = outputColorInfo;
|
||||||
this.debugViewProvider = debugViewProvider;
|
this.debugViewProvider = debugViewProvider;
|
||||||
@ -118,10 +120,10 @@ public abstract class MultipleInputVideoGraph implements VideoGraph {
|
|||||||
preProcessors = new SparseArray<>();
|
preProcessors = new SparseArray<>();
|
||||||
sharedExecutorService = newSingleThreadScheduledExecutor(SHARED_EXECUTOR_NAME);
|
sharedExecutorService = newSingleThreadScheduledExecutor(SHARED_EXECUTOR_NAME);
|
||||||
glObjectsProvider = new SingleContextGlObjectsProvider();
|
glObjectsProvider = new SingleContextGlObjectsProvider();
|
||||||
// TODO - b/289986435: Support injecting VideoFrameProcessor.Factory.
|
// TODO - b/289986435: Support injecting arbitrary VideoFrameProcessor.Factory.
|
||||||
videoFrameProcessorFactory =
|
this.videoFrameProcessorFactory =
|
||||||
new DefaultVideoFrameProcessor.Factory.Builder()
|
((DefaultVideoFrameProcessor.Factory) videoFrameProcessorFactory)
|
||||||
.setSdrWorkingColorSpace(WORKING_COLOR_SPACE_LINEAR)
|
.buildUpon()
|
||||||
.setGlObjectsProvider(glObjectsProvider)
|
.setGlObjectsProvider(glObjectsProvider)
|
||||||
.setExecutorService(sharedExecutorService)
|
.setExecutorService(sharedExecutorService)
|
||||||
.build();
|
.build();
|
||||||
|
After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
After Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
@ -43,7 +43,6 @@ import androidx.media3.effect.Presentation;
|
|||||||
import androidx.media3.effect.ScaleAndRotateTransformation;
|
import androidx.media3.effect.ScaleAndRotateTransformation;
|
||||||
import androidx.media3.effect.VideoCompositorSettings;
|
import androidx.media3.effect.VideoCompositorSettings;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -53,9 +52,12 @@ import org.junit.Rule;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.TestName;
|
import org.junit.rules.TestName;
|
||||||
import org.junit.runner.RunWith;
|
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. */
|
/** Tests for using multiple {@link EditedMediaItemSequence} in a composition. */
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(Parameterized.class)
|
||||||
public final class TransformerMultiSequenceCompositionTest {
|
public final class TransformerMultiSequenceCompositionTest {
|
||||||
|
|
||||||
// Bitmaps are generated on a Pixel 6 or 7 Pro instead of an emulator, due to an emulator bug.
|
// 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_WIDTH = 360;
|
||||||
private static final int EXPORT_HEIGHT = 240;
|
private static final int EXPORT_HEIGHT = 240;
|
||||||
|
|
||||||
|
@Parameters(name = "{0}")
|
||||||
|
public static ImmutableList<Boolean> workingColorSpaceLinear() {
|
||||||
|
return ImmutableList.of(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
@Rule public final TestName testName = new TestName();
|
@Rule public final TestName testName = new TestName();
|
||||||
|
|
||||||
private String testId;
|
private String testId;
|
||||||
|
|
||||||
|
@Parameter public boolean workingColorSpaceLinear;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUpTestId() {
|
public void setUpTestId() {
|
||||||
testId = testName.getMethodName();
|
testId = testName.getMethodName();
|
||||||
@ -106,7 +115,7 @@ public final class TransformerMultiSequenceCompositionTest {
|
|||||||
VideoCompositorSettings.DEFAULT);
|
VideoCompositorSettings.DEFAULT);
|
||||||
|
|
||||||
ExportTestResult result =
|
ExportTestResult result =
|
||||||
new TransformerAndroidTestRunner.Builder(context, getLinearColorSpaceTransformer())
|
new TransformerAndroidTestRunner.Builder(context, buildTransformer())
|
||||||
.build()
|
.build()
|
||||||
.run(testId, composition);
|
.run(testId, composition);
|
||||||
|
|
||||||
@ -142,7 +151,7 @@ public final class TransformerMultiSequenceCompositionTest {
|
|||||||
VideoCompositorSettings.DEFAULT);
|
VideoCompositorSettings.DEFAULT);
|
||||||
|
|
||||||
ExportTestResult result =
|
ExportTestResult result =
|
||||||
new TransformerAndroidTestRunner.Builder(context, getLinearColorSpaceTransformer())
|
new TransformerAndroidTestRunner.Builder(context, buildTransformer())
|
||||||
.build()
|
.build()
|
||||||
.run(testId, composition);
|
.run(testId, composition);
|
||||||
|
|
||||||
@ -200,7 +209,7 @@ public final class TransformerMultiSequenceCompositionTest {
|
|||||||
pictureInPictureVideoCompositorSettings);
|
pictureInPictureVideoCompositorSettings);
|
||||||
|
|
||||||
ExportTestResult result =
|
ExportTestResult result =
|
||||||
new TransformerAndroidTestRunner.Builder(context, getLinearColorSpaceTransformer())
|
new TransformerAndroidTestRunner.Builder(context, buildTransformer())
|
||||||
.build()
|
.build()
|
||||||
.run(testId, composition);
|
.run(testId, composition);
|
||||||
|
|
||||||
@ -209,14 +218,16 @@ public final class TransformerMultiSequenceCompositionTest {
|
|||||||
extractBitmapsFromVideo(context, checkNotNull(result.filePath)), testId);
|
extractBitmapsFromVideo(context, checkNotNull(result.filePath)), testId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Transformer getLinearColorSpaceTransformer() {
|
private Transformer buildTransformer() {
|
||||||
// Use linear color space for grayscale effects.
|
// Use linear color space for grayscale effects.
|
||||||
return new Transformer.Builder(context)
|
Transformer.Builder builder = new Transformer.Builder(context);
|
||||||
.setVideoFrameProcessorFactory(
|
if (workingColorSpaceLinear) {
|
||||||
|
builder.setVideoFrameProcessorFactory(
|
||||||
new DefaultVideoFrameProcessor.Factory.Builder()
|
new DefaultVideoFrameProcessor.Factory.Builder()
|
||||||
.setSdrWorkingColorSpace(DefaultVideoFrameProcessor.WORKING_COLOR_SPACE_LINEAR)
|
.setSdrWorkingColorSpace(DefaultVideoFrameProcessor.WORKING_COLOR_SPACE_LINEAR)
|
||||||
.build())
|
.build());
|
||||||
.build();
|
}
|
||||||
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EditedMediaItem editedMediaItemByClippingVideo(String uri, List<Effect> effects) {
|
private static EditedMediaItem editedMediaItemByClippingVideo(String uri, List<Effect> effects) {
|
||||||
|
@ -463,6 +463,10 @@ public final class Transformer {
|
|||||||
* <p>If passing in a {@link DefaultVideoFrameProcessor.Factory}, the caller must not {@link
|
* <p>If passing in a {@link DefaultVideoFrameProcessor.Factory}, the caller must not {@link
|
||||||
* DefaultVideoFrameProcessor.Factory.Builder#setTextureOutput set the texture output}.
|
* DefaultVideoFrameProcessor.Factory.Builder#setTextureOutput set the texture output}.
|
||||||
*
|
*
|
||||||
|
* <p>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}.
|
* @param videoFrameProcessorFactory A {@link VideoFrameProcessor.Factory}.
|
||||||
* @return This builder.
|
* @return This builder.
|
||||||
*/
|
*/
|
||||||
|
@ -21,6 +21,7 @@ import androidx.media3.common.ColorInfo;
|
|||||||
import androidx.media3.common.DebugViewProvider;
|
import androidx.media3.common.DebugViewProvider;
|
||||||
import androidx.media3.common.Effect;
|
import androidx.media3.common.Effect;
|
||||||
import androidx.media3.common.VideoFrameProcessingException;
|
import androidx.media3.common.VideoFrameProcessingException;
|
||||||
|
import androidx.media3.common.VideoFrameProcessor;
|
||||||
import androidx.media3.common.VideoGraph;
|
import androidx.media3.common.VideoGraph;
|
||||||
import androidx.media3.effect.MultipleInputVideoGraph;
|
import androidx.media3.effect.MultipleInputVideoGraph;
|
||||||
import androidx.media3.effect.VideoCompositorSettings;
|
import androidx.media3.effect.VideoCompositorSettings;
|
||||||
@ -36,6 +37,13 @@ import java.util.concurrent.Executor;
|
|||||||
|
|
||||||
/** A factory for creating {@link TransformerMultipleInputVideoGraph} instances. */
|
/** A factory for creating {@link TransformerMultipleInputVideoGraph} instances. */
|
||||||
public static final class Factory implements TransformerVideoGraph.Factory {
|
public static final class Factory implements TransformerVideoGraph.Factory {
|
||||||
|
|
||||||
|
private final VideoFrameProcessor.Factory videoFrameProcessorFactory;
|
||||||
|
|
||||||
|
public Factory(VideoFrameProcessor.Factory videoFrameProcessorFactory) {
|
||||||
|
this.videoFrameProcessorFactory = videoFrameProcessorFactory;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransformerMultipleInputVideoGraph create(
|
public TransformerMultipleInputVideoGraph create(
|
||||||
Context context,
|
Context context,
|
||||||
@ -48,6 +56,7 @@ import java.util.concurrent.Executor;
|
|||||||
long initialTimestampOffsetUs) {
|
long initialTimestampOffsetUs) {
|
||||||
return new TransformerMultipleInputVideoGraph(
|
return new TransformerMultipleInputVideoGraph(
|
||||||
context,
|
context,
|
||||||
|
videoFrameProcessorFactory,
|
||||||
outputColorInfo,
|
outputColorInfo,
|
||||||
debugViewProvider,
|
debugViewProvider,
|
||||||
listener,
|
listener,
|
||||||
@ -60,6 +69,7 @@ import java.util.concurrent.Executor;
|
|||||||
|
|
||||||
private TransformerMultipleInputVideoGraph(
|
private TransformerMultipleInputVideoGraph(
|
||||||
Context context,
|
Context context,
|
||||||
|
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
||||||
ColorInfo outputColorInfo,
|
ColorInfo outputColorInfo,
|
||||||
DebugViewProvider debugViewProvider,
|
DebugViewProvider debugViewProvider,
|
||||||
Listener listener,
|
Listener listener,
|
||||||
@ -69,6 +79,7 @@ import java.util.concurrent.Executor;
|
|||||||
long initialTimestampOffsetUs) {
|
long initialTimestampOffsetUs) {
|
||||||
super(
|
super(
|
||||||
context,
|
context,
|
||||||
|
videoFrameProcessorFactory,
|
||||||
outputColorInfo,
|
outputColorInfo,
|
||||||
debugViewProvider,
|
debugViewProvider,
|
||||||
listener,
|
listener,
|
||||||
|
@ -136,7 +136,7 @@ import org.checkerframework.dataflow.qual.Pure;
|
|||||||
new VideoGraphWrapper(
|
new VideoGraphWrapper(
|
||||||
context,
|
context,
|
||||||
hasMultipleInputs
|
hasMultipleInputs
|
||||||
? new TransformerMultipleInputVideoGraph.Factory()
|
? new TransformerMultipleInputVideoGraph.Factory(videoFrameProcessorFactory)
|
||||||
: new TransformerSingleInputVideoGraph.Factory(videoFrameProcessorFactory),
|
: new TransformerSingleInputVideoGraph.Factory(videoFrameProcessorFactory),
|
||||||
videoGraphOutputColor,
|
videoGraphOutputColor,
|
||||||
errorConsumer,
|
errorConsumer,
|
||||||
|