Apply video composition effects in preview

PiperOrigin-RevId: 678207818
This commit is contained in:
kimvde 2024-09-24 05:43:02 -07:00 committed by Copybara-Service
parent c19d910f6b
commit d8dc513431
3 changed files with 57 additions and 0 deletions

View File

@ -121,6 +121,7 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
private VideoFrameProcessor.@MonotonicNonNull Factory videoFrameProcessorFactory; private VideoFrameProcessor.@MonotonicNonNull Factory videoFrameProcessorFactory;
private PreviewingVideoGraph.@MonotonicNonNull Factory previewingVideoGraphFactory; private PreviewingVideoGraph.@MonotonicNonNull Factory previewingVideoGraphFactory;
private List<Effect> compositionEffects;
private Clock clock; private Clock clock;
private boolean built; private boolean built;
@ -128,6 +129,7 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
public Builder(Context context, VideoFrameReleaseControl videoFrameReleaseControl) { public Builder(Context context, VideoFrameReleaseControl videoFrameReleaseControl) {
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
this.videoFrameReleaseControl = videoFrameReleaseControl; this.videoFrameReleaseControl = videoFrameReleaseControl;
compositionEffects = ImmutableList.of();
clock = Clock.DEFAULT; clock = Clock.DEFAULT;
} }
@ -164,6 +166,18 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
return this; return this;
} }
/**
* Sets the {@linkplain Effect effects} to apply after compositing the sinks' data.
*
* @param compositionEffects The composition {@linkplain Effect effects}.
* @return This builder, for convenience.
*/
@CanIgnoreReturnValue
public Builder setCompositionEffects(List<Effect> compositionEffects) {
this.compositionEffects = compositionEffects;
return this;
}
/** /**
* Sets the {@link Clock} that will be used. * Sets the {@link Clock} that will be used.
* *
@ -217,6 +231,8 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
private final VideoFrameReleaseControl videoFrameReleaseControl; private final VideoFrameReleaseControl videoFrameReleaseControl;
private final VideoFrameRenderControl videoFrameRenderControl; private final VideoFrameRenderControl videoFrameRenderControl;
private final PreviewingVideoGraph.Factory previewingVideoGraphFactory; private final PreviewingVideoGraph.Factory previewingVideoGraphFactory;
private final List<Effect> compositionEffects;
private final Clock clock; private final Clock clock;
private final CopyOnWriteArraySet<PlaybackVideoGraphWrapper.Listener> listeners; private final CopyOnWriteArraySet<PlaybackVideoGraphWrapper.Listener> listeners;
@ -244,6 +260,7 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
videoFrameRenderControl = videoFrameRenderControl =
new VideoFrameRenderControl(new FrameRendererImpl(), videoFrameReleaseControl); new VideoFrameRenderControl(new FrameRendererImpl(), videoFrameReleaseControl);
previewingVideoGraphFactory = checkStateNotNull(builder.previewingVideoGraphFactory); previewingVideoGraphFactory = checkStateNotNull(builder.previewingVideoGraphFactory);
compositionEffects = builder.compositionEffects;
listeners = new CopyOnWriteArraySet<>(); listeners = new CopyOnWriteArraySet<>();
state = STATE_CREATED; state = STATE_CREATED;
addListener(videoSinkImpl); addListener(videoSinkImpl);
@ -642,6 +659,7 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
public void setPendingVideoEffects(List<Effect> videoEffects) { public void setPendingVideoEffects(List<Effect> videoEffects) {
this.videoEffects.clear(); this.videoEffects.clear();
this.videoEffects.addAll(videoEffects); this.videoEffects.addAll(videoEffects);
this.videoEffects.addAll(compositionEffects);
} }
@Override @Override

View File

@ -376,4 +376,42 @@ public class CompositionPlaybackTest {
assertThat(inputTimestampRecordingShaderProgram.getInputTimestampsUs()).isEmpty(); assertThat(inputTimestampRecordingShaderProgram.getInputTimestampsUs()).isEmpty();
} }
@Test
public void playback_withCompositionEffect_effectIsApplied() throws Exception {
EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(VIDEO_MEDIA_ITEM).setDurationUs(VIDEO_DURATION_US).build();
InputTimestampRecordingShaderProgram inputTimestampRecordingShaderProgram =
new InputTimestampRecordingShaderProgram();
Effect videoEffect = (GlEffect) (context, useHdr) -> inputTimestampRecordingShaderProgram;
Composition composition =
new Composition.Builder(
new EditedMediaItemSequence.Builder(editedMediaItem, editedMediaItem).build())
.setEffects(
new Effects(
/* audioProcessors= */ ImmutableList.of(),
/* videoEffects= */ ImmutableList.of(videoEffect)))
.build();
ImmutableList<Long> expectedTimestampsUs =
new ImmutableList.Builder<Long>()
.addAll(VIDEO_TIMESTAMPS_US)
.addAll(
Iterables.transform(
VIDEO_TIMESTAMPS_US, timestampUs -> (VIDEO_DURATION_US + timestampUs)))
.build();
getInstrumentation()
.runOnMainSync(
() -> {
player = new CompositionPlayer.Builder(context).build();
player.addListener(playerTestListener);
player.setComposition(composition);
player.prepare();
player.play();
});
playerTestListener.waitUntilPlayerEnded();
assertThat(inputTimestampRecordingShaderProgram.getInputTimestampsUs())
.isEqualTo(expectedTimestampsUs);
}
} }

View File

@ -673,6 +673,7 @@ public final class CompositionPlayer extends SimpleBasePlayer
PlaybackVideoGraphWrapper playbackVideoGraphWrapper = PlaybackVideoGraphWrapper playbackVideoGraphWrapper =
new PlaybackVideoGraphWrapper.Builder(context, videoFrameReleaseControl) new PlaybackVideoGraphWrapper.Builder(context, videoFrameReleaseControl)
.setPreviewingVideoGraphFactory(checkNotNull(previewingVideoGraphFactory)) .setPreviewingVideoGraphFactory(checkNotNull(previewingVideoGraphFactory))
.setCompositionEffects(composition.effects.videoEffects)
.setClock(clock) .setClock(clock)
.build(); .build();
playbackVideoGraphWrapper.addListener(this); playbackVideoGraphWrapper.addListener(this);