Compositor: Add VideoCompositorSettings to Composition.
This allows apps using Transformer to customize how a Composition is used. PiperOrigin-RevId: 567633129
This commit is contained in:
parent
42d9879d47
commit
ba8c85a277
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
@ -32,11 +32,14 @@ import android.content.Context;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import androidx.media3.common.Effect;
|
import androidx.media3.common.Effect;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
|
import androidx.media3.common.util.Size;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.effect.AlphaScale;
|
import androidx.media3.effect.AlphaScale;
|
||||||
import androidx.media3.effect.Contrast;
|
import androidx.media3.effect.Contrast;
|
||||||
|
import androidx.media3.effect.OverlaySettings;
|
||||||
import androidx.media3.effect.Presentation;
|
import androidx.media3.effect.Presentation;
|
||||||
import androidx.media3.effect.ScaleAndRotateTransformation;
|
import androidx.media3.effect.ScaleAndRotateTransformation;
|
||||||
|
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 androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
@ -49,6 +52,9 @@ import org.junit.runner.RunWith;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.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.
|
||||||
|
// TODO: b/301242589 - Fix this test on the crow emulator, and re-generate bitmaps using the crow
|
||||||
|
// emulator, for consistency with other pixel tests.
|
||||||
private static final String PNG_ASSET_BASE_PATH =
|
private static final String PNG_ASSET_BASE_PATH =
|
||||||
"media/bitmap/transformer_multi_sequence_composition_test";
|
"media/bitmap/transformer_multi_sequence_composition_test";
|
||||||
|
|
||||||
@ -86,7 +92,8 @@ public final class TransformerMultiSequenceCompositionTest {
|
|||||||
.build()))),
|
.build()))),
|
||||||
/* secondSequenceMediaItems= */ ImmutableList.of(
|
/* secondSequenceMediaItems= */ ImmutableList.of(
|
||||||
editedMediaItemByClippingVideo(
|
editedMediaItemByClippingVideo(
|
||||||
MP4_ASSET_URI_STRING, /* effects= */ ImmutableList.of())));
|
MP4_ASSET_URI_STRING, /* effects= */ ImmutableList.of())),
|
||||||
|
VideoCompositorSettings.DEFAULT);
|
||||||
|
|
||||||
ExportTestResult result =
|
ExportTestResult result =
|
||||||
new TransformerAndroidTestRunner.Builder(context, new Transformer.Builder(context).build())
|
new TransformerAndroidTestRunner.Builder(context, new Transformer.Builder(context).build())
|
||||||
@ -125,7 +132,70 @@ public final class TransformerMultiSequenceCompositionTest {
|
|||||||
.build()))),
|
.build()))),
|
||||||
/* secondSequenceMediaItems= */ ImmutableList.of(
|
/* secondSequenceMediaItems= */ ImmutableList.of(
|
||||||
editedMediaItemOfOneFrameImage(
|
editedMediaItemOfOneFrameImage(
|
||||||
JPG_ASSET_URI_STRING, /* effects= */ ImmutableList.of())));
|
JPG_ASSET_URI_STRING, /* effects= */ ImmutableList.of())),
|
||||||
|
VideoCompositorSettings.DEFAULT);
|
||||||
|
|
||||||
|
ExportTestResult result =
|
||||||
|
new TransformerAndroidTestRunner.Builder(context, new Transformer.Builder(context).build())
|
||||||
|
.build()
|
||||||
|
.run(testId, composition);
|
||||||
|
|
||||||
|
assertThat(result.filePath).isNotNull();
|
||||||
|
assertBitmapsMatchExpected(
|
||||||
|
extractBitmapsFromVideo(context, checkNotNull(result.filePath)), testId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void export_withTwoSequencesWithVideoCompositorSettings_succeeds() throws Exception {
|
||||||
|
String testId = "export_withTwoSequencesWithVideoCompositorSettings_succeeds";
|
||||||
|
if (AndroidTestUtil.skipAndLogIfFormatsUnsupported(
|
||||||
|
context,
|
||||||
|
testId,
|
||||||
|
/* inputFormat= */ MP4_ASSET_FORMAT,
|
||||||
|
/* outputFormat= */ MP4_ASSET_FORMAT)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoCompositorSettings pictureInPictureVideoCompositorSettings =
|
||||||
|
new VideoCompositorSettings() {
|
||||||
|
@Override
|
||||||
|
public Size getOutputSize(List<Size> inputSizes) {
|
||||||
|
return inputSizes.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OverlaySettings getOverlaySettings(int inputId, long presentationTimeUs) {
|
||||||
|
if (inputId == 0) {
|
||||||
|
// This tests all OverlaySettings builder variables.
|
||||||
|
return new OverlaySettings.Builder()
|
||||||
|
.setScale(.25f, .25f)
|
||||||
|
.setOverlayFrameAnchor(1, -1)
|
||||||
|
.setBackgroundFrameAnchor(.9f, -.7f)
|
||||||
|
.build();
|
||||||
|
} else {
|
||||||
|
return new OverlaySettings.Builder().build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Composition composition =
|
||||||
|
createComposition(
|
||||||
|
/* compositionEffects= */ ImmutableList.of(
|
||||||
|
new Contrast(0.1f),
|
||||||
|
Presentation.createForWidthAndHeight(
|
||||||
|
EXPORT_WIDTH, EXPORT_HEIGHT, Presentation.LAYOUT_SCALE_TO_FIT)),
|
||||||
|
/* firstSequenceMediaItems= */ ImmutableList.of(
|
||||||
|
editedMediaItemByClippingVideo(
|
||||||
|
MP4_ASSET_URI_STRING,
|
||||||
|
/* effects= */ ImmutableList.of(
|
||||||
|
new AlphaScale(0.5f),
|
||||||
|
new ScaleAndRotateTransformation.Builder()
|
||||||
|
.setRotationDegrees(180)
|
||||||
|
.build()))),
|
||||||
|
/* secondSequenceMediaItems= */ ImmutableList.of(
|
||||||
|
editedMediaItemByClippingVideo(
|
||||||
|
MP4_ASSET_URI_STRING, /* effects= */ ImmutableList.of())),
|
||||||
|
pictureInPictureVideoCompositorSettings);
|
||||||
|
|
||||||
ExportTestResult result =
|
ExportTestResult result =
|
||||||
new TransformerAndroidTestRunner.Builder(context, new Transformer.Builder(context).build())
|
new TransformerAndroidTestRunner.Builder(context, new Transformer.Builder(context).build())
|
||||||
@ -165,7 +235,8 @@ public final class TransformerMultiSequenceCompositionTest {
|
|||||||
private static Composition createComposition(
|
private static Composition createComposition(
|
||||||
List<Effect> compositionEffects,
|
List<Effect> compositionEffects,
|
||||||
List<EditedMediaItem> firstSequenceMediaItems,
|
List<EditedMediaItem> firstSequenceMediaItems,
|
||||||
List<EditedMediaItem> secondSequenceMediaItems) {
|
List<EditedMediaItem> secondSequenceMediaItems,
|
||||||
|
VideoCompositorSettings videoCompositorSettings) {
|
||||||
|
|
||||||
return new Composition.Builder(
|
return new Composition.Builder(
|
||||||
ImmutableList.of(
|
ImmutableList.of(
|
||||||
@ -174,6 +245,7 @@ public final class TransformerMultiSequenceCompositionTest {
|
|||||||
.setEffects(
|
.setEffects(
|
||||||
new Effects(
|
new Effects(
|
||||||
/* audioProcessors= */ ImmutableList.of(), /* videoEffects= */ compositionEffects))
|
/* audioProcessors= */ ImmutableList.of(), /* videoEffects= */ compositionEffects))
|
||||||
|
.setVideoCompositorSettings(videoCompositorSettings)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,12 +253,11 @@ public final class TransformerMultiSequenceCompositionTest {
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
for (int i = 0; i < actualBitmaps.size(); i++) {
|
for (int i = 0; i < actualBitmaps.size(); i++) {
|
||||||
Bitmap actualBitmap = actualBitmaps.get(i);
|
Bitmap actualBitmap = actualBitmaps.get(i);
|
||||||
|
maybeSaveTestBitmap(
|
||||||
|
testId, /* bitmapLabel= */ String.valueOf(i), actualBitmap, /* path= */ null);
|
||||||
String subTestId = testId + "_" + i;
|
String subTestId = testId + "_" + i;
|
||||||
Bitmap expectedBitmap =
|
Bitmap expectedBitmap =
|
||||||
readBitmap(Util.formatInvariant("%s/%s.png", PNG_ASSET_BASE_PATH, subTestId));
|
readBitmap(Util.formatInvariant("%s/%s.png", PNG_ASSET_BASE_PATH, subTestId));
|
||||||
|
|
||||||
maybeSaveTestBitmap(
|
|
||||||
testId, /* bitmapLabel= */ String.valueOf(i), actualBitmap, /* path= */ null);
|
|
||||||
float averagePixelAbsoluteDifference =
|
float averagePixelAbsoluteDifference =
|
||||||
getBitmapAveragePixelAbsoluteDifferenceArgb8888(expectedBitmap, actualBitmap, subTestId);
|
getBitmapAveragePixelAbsoluteDifferenceArgb8888(expectedBitmap, actualBitmap, subTestId);
|
||||||
assertThat(averagePixelAbsoluteDifference)
|
assertThat(averagePixelAbsoluteDifference)
|
||||||
|
@ -23,6 +23,7 @@ import androidx.annotation.IntDef;
|
|||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.audio.AudioProcessor;
|
import androidx.media3.common.audio.AudioProcessor;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
import androidx.media3.effect.VideoCompositorSettings;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
@ -43,6 +44,7 @@ public final class Composition {
|
|||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
|
|
||||||
private ImmutableList<EditedMediaItemSequence> sequences;
|
private ImmutableList<EditedMediaItemSequence> sequences;
|
||||||
|
private VideoCompositorSettings videoCompositorSettings;
|
||||||
private Effects effects;
|
private Effects effects;
|
||||||
private boolean forceAudioTrack;
|
private boolean forceAudioTrack;
|
||||||
private boolean transmuxAudio;
|
private boolean transmuxAudio;
|
||||||
@ -69,12 +71,14 @@ public final class Composition {
|
|||||||
!sequences.isEmpty(),
|
!sequences.isEmpty(),
|
||||||
"The composition must contain at least one EditedMediaItemSequence.");
|
"The composition must contain at least one EditedMediaItemSequence.");
|
||||||
this.sequences = ImmutableList.copyOf(sequences);
|
this.sequences = ImmutableList.copyOf(sequences);
|
||||||
|
videoCompositorSettings = VideoCompositorSettings.DEFAULT;
|
||||||
effects = Effects.EMPTY;
|
effects = Effects.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a new instance to build upon the provided {@link Composition}. */
|
/** Creates a new instance to build upon the provided {@link Composition}. */
|
||||||
private Builder(Composition composition) {
|
private Builder(Composition composition) {
|
||||||
sequences = composition.sequences;
|
sequences = composition.sequences;
|
||||||
|
videoCompositorSettings = composition.videoCompositorSettings;
|
||||||
effects = composition.effects;
|
effects = composition.effects;
|
||||||
forceAudioTrack = composition.forceAudioTrack;
|
forceAudioTrack = composition.forceAudioTrack;
|
||||||
transmuxAudio = composition.transmuxAudio;
|
transmuxAudio = composition.transmuxAudio;
|
||||||
@ -82,6 +86,20 @@ public final class Composition {
|
|||||||
hdrMode = composition.hdrMode;
|
hdrMode = composition.hdrMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link VideoCompositorSettings} to apply to the {@link Composition}.
|
||||||
|
*
|
||||||
|
* <p>The default value is {@link VideoCompositorSettings#DEFAULT}.
|
||||||
|
*
|
||||||
|
* @param videoCompositorSettings The {@link VideoCompositorSettings}.
|
||||||
|
* @return This builder.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setVideoCompositorSettings(VideoCompositorSettings videoCompositorSettings) {
|
||||||
|
this.videoCompositorSettings = videoCompositorSettings;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@link Effects} to apply to the {@link Composition}.
|
* Sets the {@link Effects} to apply to the {@link Composition}.
|
||||||
*
|
*
|
||||||
@ -207,7 +225,13 @@ public final class Composition {
|
|||||||
/** Builds a {@link Composition} instance. */
|
/** Builds a {@link Composition} instance. */
|
||||||
public Composition build() {
|
public Composition build() {
|
||||||
return new Composition(
|
return new Composition(
|
||||||
sequences, effects, forceAudioTrack, transmuxAudio, transmuxVideo, hdrMode);
|
sequences,
|
||||||
|
videoCompositorSettings,
|
||||||
|
effects,
|
||||||
|
forceAudioTrack,
|
||||||
|
transmuxAudio,
|
||||||
|
transmuxVideo,
|
||||||
|
hdrMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -309,6 +333,9 @@ public final class Composition {
|
|||||||
*/
|
*/
|
||||||
public final ImmutableList<EditedMediaItemSequence> sequences;
|
public final ImmutableList<EditedMediaItemSequence> sequences;
|
||||||
|
|
||||||
|
/** The {@link VideoCompositorSettings} to apply to the composition. */
|
||||||
|
public final VideoCompositorSettings videoCompositorSettings;
|
||||||
|
|
||||||
/** The {@link Effects} to apply to the composition. */
|
/** The {@link Effects} to apply to the composition. */
|
||||||
public final Effects effects;
|
public final Effects effects;
|
||||||
|
|
||||||
@ -347,6 +374,7 @@ public final class Composition {
|
|||||||
|
|
||||||
private Composition(
|
private Composition(
|
||||||
List<EditedMediaItemSequence> sequences,
|
List<EditedMediaItemSequence> sequences,
|
||||||
|
VideoCompositorSettings videoCompositorSettings,
|
||||||
Effects effects,
|
Effects effects,
|
||||||
boolean forceAudioTrack,
|
boolean forceAudioTrack,
|
||||||
boolean transmuxAudio,
|
boolean transmuxAudio,
|
||||||
@ -356,6 +384,7 @@ public final class Composition {
|
|||||||
!transmuxAudio || !forceAudioTrack,
|
!transmuxAudio || !forceAudioTrack,
|
||||||
"Audio transmuxing and audio track forcing are not allowed together.");
|
"Audio transmuxing and audio track forcing are not allowed together.");
|
||||||
this.sequences = ImmutableList.copyOf(sequences);
|
this.sequences = ImmutableList.copyOf(sequences);
|
||||||
|
this.videoCompositorSettings = videoCompositorSettings;
|
||||||
this.effects = effects;
|
this.effects = effects;
|
||||||
this.transmuxAudio = transmuxAudio;
|
this.transmuxAudio = transmuxAudio;
|
||||||
this.transmuxVideo = transmuxVideo;
|
this.transmuxVideo = transmuxVideo;
|
||||||
|
@ -72,6 +72,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
DebugViewProvider debugViewProvider,
|
DebugViewProvider debugViewProvider,
|
||||||
Listener listener,
|
Listener listener,
|
||||||
Executor listenerExecutor,
|
Executor listenerExecutor,
|
||||||
|
VideoCompositorSettings videoCompositorSettings,
|
||||||
List<Effect> compositionEffects,
|
List<Effect> compositionEffects,
|
||||||
long initialTimestampOffsetUs) {
|
long initialTimestampOffsetUs) {
|
||||||
return new MultipleInputVideoGraph(
|
return new MultipleInputVideoGraph(
|
||||||
@ -81,6 +82,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
debugViewProvider,
|
debugViewProvider,
|
||||||
listener,
|
listener,
|
||||||
listenerExecutor,
|
listenerExecutor,
|
||||||
|
videoCompositorSettings,
|
||||||
compositionEffects,
|
compositionEffects,
|
||||||
initialTimestampOffsetUs);
|
initialTimestampOffsetUs);
|
||||||
}
|
}
|
||||||
@ -99,6 +101,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
private final DebugViewProvider debugViewProvider;
|
private final DebugViewProvider debugViewProvider;
|
||||||
private final Listener listener;
|
private final Listener listener;
|
||||||
private final Executor listenerExecutor;
|
private final Executor listenerExecutor;
|
||||||
|
private final VideoCompositorSettings videoCompositorSettings;
|
||||||
private final List<Effect> compositionEffects;
|
private final List<Effect> compositionEffects;
|
||||||
private final List<VideoFrameProcessingWrapper> preProcessingWrappers;
|
private final List<VideoFrameProcessingWrapper> preProcessingWrappers;
|
||||||
|
|
||||||
@ -128,6 +131,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
DebugViewProvider debugViewProvider,
|
DebugViewProvider debugViewProvider,
|
||||||
Listener listener,
|
Listener listener,
|
||||||
Executor listenerExecutor,
|
Executor listenerExecutor,
|
||||||
|
VideoCompositorSettings videoCompositorSettings,
|
||||||
List<Effect> compositionEffects,
|
List<Effect> compositionEffects,
|
||||||
long initialTimestampOffsetUs) {
|
long initialTimestampOffsetUs) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
@ -136,6 +140,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
this.debugViewProvider = debugViewProvider;
|
this.debugViewProvider = debugViewProvider;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.listenerExecutor = listenerExecutor;
|
this.listenerExecutor = listenerExecutor;
|
||||||
|
this.videoCompositorSettings = videoCompositorSettings;
|
||||||
this.compositionEffects = new ArrayList<>(compositionEffects);
|
this.compositionEffects = new ArrayList<>(compositionEffects);
|
||||||
this.initialTimestampOffsetUs = initialTimestampOffsetUs;
|
this.initialTimestampOffsetUs = initialTimestampOffsetUs;
|
||||||
lastRenderedPresentationTimeUs = C.TIME_UNSET;
|
lastRenderedPresentationTimeUs = C.TIME_UNSET;
|
||||||
@ -219,7 +224,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
new DefaultVideoCompositor(
|
new DefaultVideoCompositor(
|
||||||
context,
|
context,
|
||||||
glObjectsProvider,
|
glObjectsProvider,
|
||||||
VideoCompositorSettings.DEFAULT,
|
videoCompositorSettings,
|
||||||
sharedExecutorService,
|
sharedExecutorService,
|
||||||
new VideoCompositor.Listener() {
|
new VideoCompositor.Listener() {
|
||||||
// All of this listener's methods are called on the sharedExecutorService.
|
// All of this listener's methods are called on the sharedExecutorService.
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package androidx.media3.transformer;
|
package androidx.media3.transformer;
|
||||||
|
|
||||||
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.checkStateNotNull;
|
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -30,6 +31,7 @@ import androidx.media3.common.VideoFrameProcessingException;
|
|||||||
import androidx.media3.common.VideoFrameProcessor;
|
import androidx.media3.common.VideoFrameProcessor;
|
||||||
import androidx.media3.common.VideoGraph;
|
import androidx.media3.common.VideoGraph;
|
||||||
import androidx.media3.effect.Presentation;
|
import androidx.media3.effect.Presentation;
|
||||||
|
import androidx.media3.effect.VideoCompositorSettings;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
@ -52,6 +54,11 @@ import java.util.concurrent.Executor;
|
|||||||
private boolean released;
|
private boolean released;
|
||||||
private volatile boolean hasProducedFrameWithTimestampZero;
|
private volatile boolean hasProducedFrameWithTimestampZero;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance.
|
||||||
|
*
|
||||||
|
* <p>{@code videoCompositorSettings} must be {@link VideoCompositorSettings#DEFAULT}.
|
||||||
|
*/
|
||||||
public SingleInputVideoGraph(
|
public SingleInputVideoGraph(
|
||||||
Context context,
|
Context context,
|
||||||
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
||||||
@ -60,9 +67,14 @@ import java.util.concurrent.Executor;
|
|||||||
Listener listener,
|
Listener listener,
|
||||||
DebugViewProvider debugViewProvider,
|
DebugViewProvider debugViewProvider,
|
||||||
Executor listenerExecutor,
|
Executor listenerExecutor,
|
||||||
|
VideoCompositorSettings videoCompositorSettings,
|
||||||
boolean renderFramesAutomatically,
|
boolean renderFramesAutomatically,
|
||||||
@Nullable Presentation presentation,
|
@Nullable Presentation presentation,
|
||||||
long initialTimestampOffsetUs) {
|
long initialTimestampOffsetUs) {
|
||||||
|
checkState(
|
||||||
|
VideoCompositorSettings.DEFAULT.equals(videoCompositorSettings),
|
||||||
|
"SingleInputVideoGraph does not use VideoCompositor, and therefore cannot apply"
|
||||||
|
+ " VideoCompositorSettings");
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.videoFrameProcessorFactory = videoFrameProcessorFactory;
|
this.videoFrameProcessorFactory = videoFrameProcessorFactory;
|
||||||
this.inputColorInfo = inputColorInfo;
|
this.inputColorInfo = inputColorInfo;
|
||||||
|
@ -582,6 +582,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
context,
|
context,
|
||||||
firstAssetLoaderInputFormat,
|
firstAssetLoaderInputFormat,
|
||||||
transformationRequest,
|
transformationRequest,
|
||||||
|
composition.videoCompositorSettings,
|
||||||
composition.effects.videoEffects,
|
composition.effects.videoEffects,
|
||||||
videoFrameProcessorFactory,
|
videoFrameProcessorFactory,
|
||||||
encoderFactory,
|
encoderFactory,
|
||||||
|
@ -23,6 +23,7 @@ import androidx.media3.common.DebugViewProvider;
|
|||||||
import androidx.media3.common.Effect;
|
import androidx.media3.common.Effect;
|
||||||
import androidx.media3.common.VideoFrameProcessor;
|
import androidx.media3.common.VideoFrameProcessor;
|
||||||
import androidx.media3.effect.Presentation;
|
import androidx.media3.effect.Presentation;
|
||||||
|
import androidx.media3.effect.VideoCompositorSettings;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
@ -46,6 +47,7 @@ import java.util.concurrent.Executor;
|
|||||||
DebugViewProvider debugViewProvider,
|
DebugViewProvider debugViewProvider,
|
||||||
Listener listener,
|
Listener listener,
|
||||||
Executor listenerExecutor,
|
Executor listenerExecutor,
|
||||||
|
VideoCompositorSettings videoCompositorSettings,
|
||||||
List<Effect> compositionEffects,
|
List<Effect> compositionEffects,
|
||||||
long initialTimestampOffsetUs) {
|
long initialTimestampOffsetUs) {
|
||||||
@Nullable Presentation presentation = null;
|
@Nullable Presentation presentation = null;
|
||||||
@ -63,6 +65,7 @@ import java.util.concurrent.Executor;
|
|||||||
listener,
|
listener,
|
||||||
debugViewProvider,
|
debugViewProvider,
|
||||||
listenerExecutor,
|
listenerExecutor,
|
||||||
|
videoCompositorSettings,
|
||||||
/* renderFramesAutomatically= */ true,
|
/* renderFramesAutomatically= */ true,
|
||||||
presentation,
|
presentation,
|
||||||
initialTimestampOffsetUs);
|
initialTimestampOffsetUs);
|
||||||
@ -77,6 +80,7 @@ import java.util.concurrent.Executor;
|
|||||||
Listener listener,
|
Listener listener,
|
||||||
DebugViewProvider debugViewProvider,
|
DebugViewProvider debugViewProvider,
|
||||||
Executor listenerExecutor,
|
Executor listenerExecutor,
|
||||||
|
VideoCompositorSettings videoCompositorSettings,
|
||||||
boolean renderFramesAutomatically,
|
boolean renderFramesAutomatically,
|
||||||
@Nullable Presentation presentation,
|
@Nullable Presentation presentation,
|
||||||
long initialTimestampOffsetUs) {
|
long initialTimestampOffsetUs) {
|
||||||
@ -88,6 +92,7 @@ import java.util.concurrent.Executor;
|
|||||||
listener,
|
listener,
|
||||||
debugViewProvider,
|
debugViewProvider,
|
||||||
listenerExecutor,
|
listenerExecutor,
|
||||||
|
videoCompositorSettings,
|
||||||
renderFramesAutomatically,
|
renderFramesAutomatically,
|
||||||
presentation,
|
presentation,
|
||||||
initialTimestampOffsetUs);
|
initialTimestampOffsetUs);
|
||||||
|
@ -23,6 +23,7 @@ import androidx.media3.common.Effect;
|
|||||||
import androidx.media3.common.VideoFrameProcessingException;
|
import androidx.media3.common.VideoFrameProcessingException;
|
||||||
import androidx.media3.common.VideoFrameProcessor;
|
import androidx.media3.common.VideoFrameProcessor;
|
||||||
import androidx.media3.common.VideoGraph;
|
import androidx.media3.common.VideoGraph;
|
||||||
|
import androidx.media3.effect.VideoCompositorSettings;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
@ -40,7 +41,10 @@ import java.util.concurrent.Executor;
|
|||||||
* @param debugViewProvider A {@link DebugViewProvider}.
|
* @param debugViewProvider A {@link DebugViewProvider}.
|
||||||
* @param listener A {@link Listener}.
|
* @param listener A {@link Listener}.
|
||||||
* @param listenerExecutor The {@link Executor} on which the {@code listener} is invoked.
|
* @param listenerExecutor The {@link Executor} on which the {@code listener} is invoked.
|
||||||
|
* @param videoCompositorSettings The {@link VideoCompositorSettings} to apply to the
|
||||||
|
* composition.
|
||||||
* @param compositionEffects A list of {@linkplain Effect effects} to apply to the composition.
|
* @param compositionEffects A list of {@linkplain Effect effects} to apply to the composition.
|
||||||
|
* @param initialTimestampOffsetUs The timestamp offset for the first frame, in microseconds.
|
||||||
* @return A new instance.
|
* @return A new instance.
|
||||||
* @throws VideoFrameProcessingException If a problem occurs while creating the {@link
|
* @throws VideoFrameProcessingException If a problem occurs while creating the {@link
|
||||||
* VideoFrameProcessor}.
|
* VideoFrameProcessor}.
|
||||||
@ -52,6 +56,7 @@ import java.util.concurrent.Executor;
|
|||||||
DebugViewProvider debugViewProvider,
|
DebugViewProvider debugViewProvider,
|
||||||
Listener listener,
|
Listener listener,
|
||||||
Executor listenerExecutor,
|
Executor listenerExecutor,
|
||||||
|
VideoCompositorSettings videoCompositorSettings,
|
||||||
List<Effect> compositionEffects,
|
List<Effect> compositionEffects,
|
||||||
long initialTimestampOffsetUs)
|
long initialTimestampOffsetUs)
|
||||||
throws VideoFrameProcessingException;
|
throws VideoFrameProcessingException;
|
||||||
|
@ -48,6 +48,7 @@ import androidx.media3.common.util.Log;
|
|||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.decoder.DecoderInputBuffer;
|
import androidx.media3.decoder.DecoderInputBuffer;
|
||||||
import androidx.media3.effect.DebugTraceUtil;
|
import androidx.media3.effect.DebugTraceUtil;
|
||||||
|
import androidx.media3.effect.VideoCompositorSettings;
|
||||||
import androidx.media3.exoplayer.mediacodec.MediaCodecUtil;
|
import androidx.media3.exoplayer.mediacodec.MediaCodecUtil;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
@ -78,6 +79,7 @@ import org.checkerframework.dataflow.qual.Pure;
|
|||||||
Context context,
|
Context context,
|
||||||
Format firstInputFormat,
|
Format firstInputFormat,
|
||||||
TransformationRequest transformationRequest,
|
TransformationRequest transformationRequest,
|
||||||
|
VideoCompositorSettings videoCompositorSettings,
|
||||||
List<Effect> compositionEffects,
|
List<Effect> compositionEffects,
|
||||||
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
||||||
Codec.EncoderFactory encoderFactory,
|
Codec.EncoderFactory encoderFactory,
|
||||||
@ -151,6 +153,7 @@ import org.checkerframework.dataflow.qual.Pure;
|
|||||||
videoGraphOutputColor,
|
videoGraphOutputColor,
|
||||||
errorConsumer,
|
errorConsumer,
|
||||||
debugViewProvider,
|
debugViewProvider,
|
||||||
|
videoCompositorSettings,
|
||||||
compositionEffects);
|
compositionEffects);
|
||||||
videoGraph.initialize();
|
videoGraph.initialize();
|
||||||
} catch (VideoFrameProcessingException e) {
|
} catch (VideoFrameProcessingException e) {
|
||||||
@ -477,6 +480,7 @@ import org.checkerframework.dataflow.qual.Pure;
|
|||||||
ColorInfo videoFrameProcessorOutputColor,
|
ColorInfo videoFrameProcessorOutputColor,
|
||||||
Consumer<ExportException> errorConsumer,
|
Consumer<ExportException> errorConsumer,
|
||||||
DebugViewProvider debugViewProvider,
|
DebugViewProvider debugViewProvider,
|
||||||
|
VideoCompositorSettings videoCompositorSettings,
|
||||||
List<Effect> compositionEffects)
|
List<Effect> compositionEffects)
|
||||||
throws VideoFrameProcessingException {
|
throws VideoFrameProcessingException {
|
||||||
this.errorConsumer = errorConsumer;
|
this.errorConsumer = errorConsumer;
|
||||||
@ -493,6 +497,7 @@ import org.checkerframework.dataflow.qual.Pure;
|
|||||||
debugViewProvider,
|
debugViewProvider,
|
||||||
/* listener= */ thisRef,
|
/* listener= */ thisRef,
|
||||||
/* listenerExecutor= */ MoreExecutors.directExecutor(),
|
/* listenerExecutor= */ MoreExecutors.directExecutor(),
|
||||||
|
videoCompositorSettings,
|
||||||
compositionEffects,
|
compositionEffects,
|
||||||
initialTimestampOffsetUs);
|
initialTimestampOffsetUs);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user