Add APIs for redrawing effects.

PiperOrigin-RevId: 731255074
This commit is contained in:
claincly 2025-02-26 04:06:36 -08:00 committed by Copybara-Service
parent a6debf4904
commit 5d9c2e309e
9 changed files with 51 additions and 0 deletions

View File

@ -26,6 +26,7 @@ import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.media3.common.util.TimestampIterator; import androidx.media3.common.util.TimestampIterator;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import com.google.common.collect.ImmutableList;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@ -201,6 +202,9 @@ public interface VideoFrameProcessor {
@SuppressWarnings("GoodTime-ApiWithNumericTimeUnit") // This is a named constant, not a time unit. @SuppressWarnings("GoodTime-ApiWithNumericTimeUnit") // This is a named constant, not a time unit.
long RENDER_OUTPUT_FRAME_WITH_PRESENTATION_TIME = -3; long RENDER_OUTPUT_FRAME_WITH_PRESENTATION_TIME = -3;
/** A maker for passing to {@link #registerInputStream} to signal a redraw. */
ImmutableList<Effect> REDRAW = ImmutableList.of(new Effect() {});
/** /**
* Provides an input {@link Bitmap} to the {@link VideoFrameProcessor}. * Provides an input {@link Bitmap} to the {@link VideoFrameProcessor}.
* *
@ -272,6 +276,12 @@ public interface VideoFrameProcessor {
*/ */
Surface getInputSurface(); Surface getInputSurface();
/**
* Updates an {@linkplain Listener#onOutputFrameAvailableForRendering available frame} with the
* modified effects.
*/
void redraw();
/** /**
* Informs the {@code VideoFrameProcessor} that a new input stream will be queued with the list of * Informs the {@code VideoFrameProcessor} that a new input stream will be queued with the list of
* {@link Effect Effects} to apply to the new input stream. * {@link Effect Effects} to apply to the new input stream.

View File

@ -624,6 +624,11 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
return inputSwitcher.getInputSurface(); return inputSwitcher.getInputSurface();
} }
@Override
public void redraw() {
throw new UnsupportedOperationException();
}
/** /**
* {@inheritDoc} * {@inheritDoc}
* *

View File

@ -1459,6 +1459,10 @@ public interface ExoPlayer extends Player {
* Renderer#MSG_SET_VIDEO_OUTPUT_RESOLUTION} after calling this method. For {@link SurfaceView}, * Renderer#MSG_SET_VIDEO_OUTPUT_RESOLUTION} after calling this method. For {@link SurfaceView},
* {@link TextureView} and {@link SurfaceHolder} output this happens automatically. * {@link TextureView} and {@link SurfaceHolder} output this happens automatically.
* *
* <p>Pass {@link androidx.media3.common.VideoFrameProcessor#REDRAW} to force the effect pipeline
* to redraw the effects immediately. To accurately interleave redraws, listen to {@link
* VideoFrameMetadataListener#onVideoFrameAboutToBeRendered} events.
*
* <p>The following limitations exist for using {@linkplain Effect video effects}: * <p>The following limitations exist for using {@linkplain Effect video effects}:
* *
* <ul> * <ul>

View File

@ -118,6 +118,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
return true; return true;
} }
@Override
public void redraw() {
throw new UnsupportedOperationException();
}
@Override @Override
public void flush(boolean resetPosition) { public void flush(boolean resetPosition) {
if (resetPosition) { if (resetPosition) {

View File

@ -53,6 +53,7 @@ import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes; import androidx.media3.common.MimeTypes;
import androidx.media3.common.PlaybackException; import androidx.media3.common.PlaybackException;
import androidx.media3.common.Timeline; import androidx.media3.common.Timeline;
import androidx.media3.common.VideoFrameProcessor;
import androidx.media3.common.VideoSize; import androidx.media3.common.VideoSize;
import androidx.media3.common.util.Log; import androidx.media3.common.util.Log;
import androidx.media3.common.util.MediaFormatUtil; import androidx.media3.common.util.MediaFormatUtil;
@ -1389,6 +1390,14 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
/** Sets the {@linkplain Effect video effects} to apply. */ /** Sets the {@linkplain Effect video effects} to apply. */
public void setVideoEffects(List<Effect> effects) { public void setVideoEffects(List<Effect> effects) {
if (effects.equals(VideoFrameProcessor.REDRAW)) {
if (videoSink == null || !videoSink.isInitialized()) {
return;
}
videoSink.redraw();
return;
}
videoEffects = effects; videoEffects = effects;
if (videoSink != null) { if (videoSink != null) {
videoSink.setVideoEffects(effects); videoSink.setVideoEffects(effects);

View File

@ -19,6 +19,7 @@ import static androidx.media3.common.VideoFrameProcessor.DROP_OUTPUT_FRAME;
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;
import static androidx.media3.common.util.Util.castNonNull;
import static androidx.media3.common.util.Util.contains; import static androidx.media3.common.util.Util.contains;
import static androidx.media3.common.util.Util.getMaxPendingFramesCountForMediaCodecDecoders; import static androidx.media3.common.util.Util.getMaxPendingFramesCountForMediaCodecDecoders;
import static androidx.media3.exoplayer.video.VideoSink.INPUT_TYPE_SURFACE; import static androidx.media3.exoplayer.video.VideoSink.INPUT_TYPE_SURFACE;
@ -700,6 +701,12 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
return videoFrameProcessor != null; return videoFrameProcessor != null;
} }
@Override
public void redraw() {
checkState(isInitialized());
castNonNull(videoFrameProcessor).redraw();
}
@Override @Override
public void flush(boolean resetPosition) { public void flush(boolean resetPosition) {
if (isInitialized()) { if (isInitialized()) {

View File

@ -157,6 +157,9 @@ public interface VideoSink {
/** Returns whether the video sink is {@linkplain #initialize(Format) initialized}. */ /** Returns whether the video sink is {@linkplain #initialize(Format) initialized}. */
boolean isInitialized(); boolean isInitialized();
/** Redraws the {@linkplain #setVideoEffects video effects} immediately. */
void redraw();
/** /**
* Flushes the video sink. * Flushes the video sink.
* *

View File

@ -151,6 +151,9 @@ public final class PlaybackVideoGraphWrapperTest {
return null; return null;
} }
@Override
public void redraw() {}
@Override @Override
public void registerInputStream( public void registerInputStream(
@InputType int inputType, Format format, List<Effect> effects, long offsetToAddUs) { @InputType int inputType, Format format, List<Effect> effects, long offsetToAddUs) {

View File

@ -126,6 +126,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
return isInitialized; return isInitialized;
} }
@Override
public void redraw() {
throw new UnsupportedOperationException();
}
@Override @Override
public void flush(boolean resetPosition) { public void flush(boolean resetPosition) {
executeOrDelay(videoSink -> videoSink.flush(resetPosition)); executeOrDelay(videoSink -> videoSink.flush(resetPosition));