From f42d09cf158278ff8aeba8dabf67202197c24bb1 Mon Sep 17 00:00:00 2001 From: hschlueter Date: Fri, 25 Mar 2022 12:58:43 +0000 Subject: [PATCH] Allow frame processors to be set on Transformer.Builder. Also make GlFrameProcessor, ScaleToFitFrameProcessor, and AdvancedFrameProcessor public. PiperOrigin-RevId: 437227388 --- .../transformer/AdvancedFrameProcessor.java | 2 +- .../transformer/FrameProcessorChain.java | 2 +- .../transformer/GlFrameProcessor.java | 2 +- .../PresentationFrameProcessor.java | 2 +- .../transformer/ScaleToFitFrameProcessor.java | 2 +- .../exoplayer2/transformer/Transformer.java | 35 ++++++++++++++++++- .../transformer/TransformerVideoRenderer.java | 8 +++++ .../VideoTranscodingSamplePipeline.java | 8 +++-- 8 files changed, 53 insertions(+), 8 deletions(-) diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/AdvancedFrameProcessor.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/AdvancedFrameProcessor.java index e7114e84fc..80f7a08ab6 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/AdvancedFrameProcessor.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/AdvancedFrameProcessor.java @@ -32,7 +32,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * ScaleToFitFrameProcessor}) are applied on the transformation. Width and height are not modified. * The background color will default to black. */ -/* package */ final class AdvancedFrameProcessor implements GlFrameProcessor { +public final class AdvancedFrameProcessor implements GlFrameProcessor { static { GlUtil.glAssertionsEnabled = true; diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/FrameProcessorChain.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/FrameProcessorChain.java index cbddb3a3e2..bdad1b33b6 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/FrameProcessorChain.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/FrameProcessorChain.java @@ -152,7 +152,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; } this.enableExperimentalHdrEditing = enableExperimentalHdrEditing; - this.frameProcessors = frameProcessors; + this.frameProcessors = ImmutableList.copyOf(frameProcessors); try { eglDisplay = GlUtil.createEglDisplay(); diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/GlFrameProcessor.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/GlFrameProcessor.java index 499bdbb2a8..433a96c1d7 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/GlFrameProcessor.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/GlFrameProcessor.java @@ -31,7 +31,7 @@ import java.io.IOException; *
  • {@link #release()}, upon conclusion of processing. * */ -/* package */ interface GlFrameProcessor { +public interface GlFrameProcessor { // TODO(b/214975934): Investigate whether all configuration can be moved to initialize by // using a placeholder surface until the encoder surface is known. If so, convert // configureOutputSize to a simple getter. diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/PresentationFrameProcessor.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/PresentationFrameProcessor.java index b4e9935b7a..d79af5d914 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/PresentationFrameProcessor.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/PresentationFrameProcessor.java @@ -29,7 +29,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** Controls how a frame is viewed, by changing resolution. */ // TODO(b/213190310): Implement crop, aspect ratio changes, etc. -/* package */ final class PresentationFrameProcessor implements GlFrameProcessor { +public final class PresentationFrameProcessor implements GlFrameProcessor { /** A builder for {@link PresentationFrameProcessor} instances. */ public static final class Builder { diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/ScaleToFitFrameProcessor.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/ScaleToFitFrameProcessor.java index aa79f41b5a..9ff0720176 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/ScaleToFitFrameProcessor.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/ScaleToFitFrameProcessor.java @@ -32,7 +32,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * preserved, potentially changing the width and height of the frame by scaling dimensions to fit. * The background color will default to black. */ -/* package */ final class ScaleToFitFrameProcessor implements GlFrameProcessor { +public final class ScaleToFitFrameProcessor implements GlFrameProcessor { /** A builder for {@link ScaleToFitFrameProcessor} instances. */ public static final class Builder { diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java index 28b325def9..532b7dcc76 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java @@ -58,11 +58,13 @@ import com.google.android.exoplayer2.util.ListenerSet; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.video.VideoRendererEventListener; +import com.google.common.collect.ImmutableList; import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.List; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** @@ -100,6 +102,7 @@ public final class Transformer { private boolean removeVideo; private String containerMimeType; private TransformationRequest transformationRequest; + private ImmutableList frameProcessors; private ListenerSet listeners; private DebugViewProvider debugViewProvider; private Looper looper; @@ -119,6 +122,7 @@ public final class Transformer { debugViewProvider = DebugViewProvider.NONE; containerMimeType = MimeTypes.VIDEO_MP4; transformationRequest = new TransformationRequest.Builder().build(); + frameProcessors = ImmutableList.of(); } /** @@ -135,7 +139,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}. */ @@ -147,6 +152,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; @@ -178,6 +184,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. * @@ -406,6 +430,7 @@ public final class Transformer { removeVideo, containerMimeType, transformationRequest, + frameProcessors, listeners, looper, clock, @@ -527,6 +552,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; @@ -547,6 +573,7 @@ public final class Transformer { boolean removeVideo, String containerMimeType, TransformationRequest transformationRequest, + ImmutableList frameProcessors, ListenerSet listeners, Looper looper, Clock clock, @@ -561,6 +588,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; @@ -701,6 +729,7 @@ public final class Transformer { removeAudio, removeVideo, transformationRequest, + frameProcessors, encoderFactory, decoderFactory, new FallbackListener(mediaItem, listeners, transformationRequest), @@ -812,6 +841,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; @@ -823,6 +853,7 @@ public final class Transformer { boolean removeAudio, boolean removeVideo, TransformationRequest transformationRequest, + ImmutableList frameProcessors, Codec.EncoderFactory encoderFactory, Codec.DecoderFactory decoderFactory, FallbackListener fallbackListener, @@ -832,6 +863,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; @@ -867,6 +899,7 @@ public final class Transformer { muxerWrapper, mediaClock, transformationRequest, + frameProcessors, encoderFactory, decoderFactory, fallbackListener, diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerVideoRenderer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerVideoRenderer.java index 4e79f35148..c0c8adcbfa 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerVideoRenderer.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerVideoRenderer.java @@ -25,6 +25,7 @@ import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.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/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/VideoTranscodingSamplePipeline.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/VideoTranscodingSamplePipeline.java index e314a0a1c1..0d61832395 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/VideoTranscodingSamplePipeline.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/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();