listeners;
private DebugViewProvider debugViewProvider;
private Looper looper;
@@ -121,6 +124,7 @@ public final class Transformer {
debugViewProvider = DebugViewProvider.NONE;
containerMimeType = MimeTypes.VIDEO_MP4;
transformationRequest = new TransformationRequest.Builder().build();
+ frameProcessors = ImmutableList.of();
}
/**
@@ -137,7 +141,8 @@ public final class Transformer {
encoderFactory = Codec.EncoderFactory.DEFAULT;
debugViewProvider = DebugViewProvider.NONE;
containerMimeType = MimeTypes.VIDEO_MP4;
- this.transformationRequest = new TransformationRequest.Builder().build();
+ transformationRequest = new TransformationRequest.Builder().build();
+ frameProcessors = ImmutableList.of();
}
/** Creates a builder with the values of the provided {@link Transformer}. */
@@ -149,6 +154,7 @@ public final class Transformer {
this.removeVideo = transformer.removeVideo;
this.containerMimeType = transformer.containerMimeType;
this.transformationRequest = transformer.transformationRequest;
+ this.frameProcessors = transformer.frameProcessors;
this.listeners = transformer.listeners;
this.looper = transformer.looper;
this.encoderFactory = transformer.encoderFactory;
@@ -180,6 +186,24 @@ public final class Transformer {
return this;
}
+ /**
+ * Sets the {@linkplain GlFrameProcessor frame processors} to apply to each frame.
+ *
+ * The {@linkplain GlFrameProcessor frame processors} are applied before any {@linkplain
+ * TransformationRequest.Builder#setScale(float, float) scale}, {@linkplain
+ * TransformationRequest.Builder#setRotationDegrees(float) rotation}, or {@linkplain
+ * TransformationRequest.Builder#setResolution(int) resolution} changes specified in the {@link
+ * #setTransformationRequest(TransformationRequest) TransformationRequest} but after {@linkplain
+ * TransformationRequest.Builder#setFlattenForSlowMotion(boolean) slow-motion flattening}.
+ *
+ * @param frameProcessors The {@linkplain GlFrameProcessor frame processors}.
+ * @return This builder.
+ */
+ public Builder setFrameProcessors(List frameProcessors) {
+ this.frameProcessors = ImmutableList.copyOf(frameProcessors);
+ return this;
+ }
+
/**
* Sets the {@link MediaSource.Factory} to be used to retrieve the inputs to transform.
*
@@ -408,6 +432,7 @@ public final class Transformer {
removeVideo,
containerMimeType,
transformationRequest,
+ frameProcessors,
listeners,
looper,
clock,
@@ -529,6 +554,7 @@ public final class Transformer {
private final boolean removeVideo;
private final String containerMimeType;
private final TransformationRequest transformationRequest;
+ private final ImmutableList frameProcessors;
private final Looper looper;
private final Clock clock;
private final Codec.EncoderFactory encoderFactory;
@@ -549,6 +575,7 @@ public final class Transformer {
boolean removeVideo,
String containerMimeType,
TransformationRequest transformationRequest,
+ ImmutableList frameProcessors,
ListenerSet listeners,
Looper looper,
Clock clock,
@@ -563,6 +590,7 @@ public final class Transformer {
this.removeVideo = removeVideo;
this.containerMimeType = containerMimeType;
this.transformationRequest = transformationRequest;
+ this.frameProcessors = frameProcessors;
this.listeners = listeners;
this.looper = looper;
this.clock = clock;
@@ -703,6 +731,7 @@ public final class Transformer {
removeAudio,
removeVideo,
transformationRequest,
+ frameProcessors,
encoderFactory,
decoderFactory,
new FallbackListener(mediaItem, listeners, transformationRequest),
@@ -814,6 +843,7 @@ public final class Transformer {
private final boolean removeAudio;
private final boolean removeVideo;
private final TransformationRequest transformationRequest;
+ private final ImmutableList frameProcessors;
private final Codec.EncoderFactory encoderFactory;
private final Codec.DecoderFactory decoderFactory;
private final FallbackListener fallbackListener;
@@ -825,6 +855,7 @@ public final class Transformer {
boolean removeAudio,
boolean removeVideo,
TransformationRequest transformationRequest,
+ ImmutableList frameProcessors,
Codec.EncoderFactory encoderFactory,
Codec.DecoderFactory decoderFactory,
FallbackListener fallbackListener,
@@ -834,6 +865,7 @@ public final class Transformer {
this.removeAudio = removeAudio;
this.removeVideo = removeVideo;
this.transformationRequest = transformationRequest;
+ this.frameProcessors = frameProcessors;
this.encoderFactory = encoderFactory;
this.decoderFactory = decoderFactory;
this.fallbackListener = fallbackListener;
@@ -869,6 +901,7 @@ public final class Transformer {
muxerWrapper,
mediaClock,
transformationRequest,
+ frameProcessors,
encoderFactory,
decoderFactory,
fallbackListener,
diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerVideoRenderer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerVideoRenderer.java
index 32b251d000..b3042a922b 100644
--- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerVideoRenderer.java
+++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerVideoRenderer.java
@@ -25,6 +25,7 @@ import androidx.media3.common.Format;
import androidx.media3.decoder.DecoderInputBuffer;
import androidx.media3.exoplayer.FormatHolder;
import androidx.media3.exoplayer.source.SampleStream.ReadDataResult;
+import com.google.common.collect.ImmutableList;
import java.nio.ByteBuffer;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
@@ -34,6 +35,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private static final String TAG = "TVideoRenderer";
private final Context context;
+ private final ImmutableList frameProcessors;
private final Codec.EncoderFactory encoderFactory;
private final Codec.DecoderFactory decoderFactory;
private final Transformer.DebugViewProvider debugViewProvider;
@@ -46,12 +48,14 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
MuxerWrapper muxerWrapper,
TransformerMediaClock mediaClock,
TransformationRequest transformationRequest,
+ ImmutableList frameProcessors,
Codec.EncoderFactory encoderFactory,
Codec.DecoderFactory decoderFactory,
FallbackListener fallbackListener,
Transformer.DebugViewProvider debugViewProvider) {
super(C.TRACK_TYPE_VIDEO, muxerWrapper, mediaClock, transformationRequest, fallbackListener);
this.context = context;
+ this.frameProcessors = frameProcessors;
this.encoderFactory = encoderFactory;
this.decoderFactory = decoderFactory;
this.debugViewProvider = debugViewProvider;
@@ -86,6 +90,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
context,
inputFormat,
transformationRequest,
+ frameProcessors,
decoderFactory,
encoderFactory,
muxerWrapper.getSupportedSampleMimeTypes(getTrackType()),
@@ -126,6 +131,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
&& transformationRequest.outputHeight != inputFormat.height) {
return false;
}
+ if (!frameProcessors.isEmpty()) {
+ return false;
+ }
return true;
}
diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java
index 4fa5ae8520..5b4da873c7 100644
--- a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java
+++ b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java
@@ -52,6 +52,7 @@ import org.checkerframework.dataflow.qual.Pure;
Context context,
Format inputFormat,
TransformationRequest transformationRequest,
+ ImmutableList frameProcessors,
Codec.DecoderFactory decoderFactory,
Codec.EncoderFactory encoderFactory,
List allowedOutputMimeTypes,
@@ -69,7 +70,6 @@ import org.checkerframework.dataflow.qual.Pure;
int decodedHeight =
(inputFormat.rotationDegrees % 180 == 0) ? inputFormat.height : inputFormat.width;
- // TODO(b/214975934): Allow a list of frame processors to be passed into the sample pipeline.
// TODO(b/213190310): Don't create a ScaleToFitFrameProcessor if scale and rotation are unset.
ScaleToFitFrameProcessor scaleToFitFrameProcessor =
new ScaleToFitFrameProcessor.Builder(context)
@@ -86,7 +86,11 @@ import org.checkerframework.dataflow.qual.Pure;
inputFormat.pixelWidthHeightRatio,
/* inputWidth= */ decodedWidth,
/* inputHeight= */ decodedHeight,
- ImmutableList.of(scaleToFitFrameProcessor, presentationFrameProcessor),
+ new ImmutableList.Builder()
+ .addAll(frameProcessors)
+ .add(scaleToFitFrameProcessor)
+ .add(presentationFrameProcessor)
+ .build(),
transformationRequest.enableHdrEditing);
Size requestedEncoderSize = frameProcessorChain.getOutputSize();
outputRotationDegrees = presentationFrameProcessor.getOutputRotationDegrees();