From 0f5686fe07cb5e83e629f66b0121e4c64582216f 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 | 4 ++- .../transformer/FrameProcessorChain.java | 2 +- .../media3/transformer/GlFrameProcessor.java | 4 ++- .../PresentationFrameProcessor.java | 4 ++- .../transformer/ScaleToFitFrameProcessor.java | 4 ++- .../media3/transformer/Transformer.java | 35 ++++++++++++++++++- .../transformer/TransformerVideoRenderer.java | 8 +++++ .../VideoTranscodingSamplePipeline.java | 8 +++-- 8 files changed, 61 insertions(+), 8 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/AdvancedFrameProcessor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/AdvancedFrameProcessor.java index 7fe753d1a2..c450b61e44 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/AdvancedFrameProcessor.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/AdvancedFrameProcessor.java @@ -23,6 +23,7 @@ import android.opengl.GLES20; import android.util.Size; import androidx.media3.common.util.GlProgram; import androidx.media3.common.util.GlUtil; +import androidx.media3.common.util.UnstableApi; import java.io.IOException; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -32,7 +33,8 @@ 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 { +@UnstableApi +public final class AdvancedFrameProcessor implements GlFrameProcessor { static { GlUtil.glAssertionsEnabled = true; diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameProcessorChain.java b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameProcessorChain.java index b80e32a36e..fd96d9c5a6 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameProcessorChain.java +++ b/libraries/transformer/src/main/java/androidx/media3/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/libraries/transformer/src/main/java/androidx/media3/transformer/GlFrameProcessor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/GlFrameProcessor.java index 726e1b1084..63f44b2bc2 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/GlFrameProcessor.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/GlFrameProcessor.java @@ -16,6 +16,7 @@ package androidx.media3.transformer; import android.util.Size; +import androidx.media3.common.util.UnstableApi; import java.io.IOException; /** @@ -31,7 +32,8 @@ import java.io.IOException; *
  • {@link #release()}, upon conclusion of processing. * */ -/* package */ interface GlFrameProcessor { +@UnstableApi +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/libraries/transformer/src/main/java/androidx/media3/transformer/PresentationFrameProcessor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/PresentationFrameProcessor.java index 31be674a70..f6941f2e41 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/PresentationFrameProcessor.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/PresentationFrameProcessor.java @@ -24,12 +24,14 @@ import android.util.Size; import androidx.media3.common.C; import androidx.media3.common.Format; import androidx.media3.common.util.GlUtil; +import androidx.media3.common.util.UnstableApi; import java.io.IOException; 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 { +@UnstableApi +public final class PresentationFrameProcessor implements GlFrameProcessor { /** A builder for {@link PresentationFrameProcessor} instances. */ public static final class Builder { diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/ScaleToFitFrameProcessor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/ScaleToFitFrameProcessor.java index 472cf04171..0875e706c4 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/ScaleToFitFrameProcessor.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/ScaleToFitFrameProcessor.java @@ -24,6 +24,7 @@ import android.graphics.Matrix; import android.util.Size; import androidx.media3.common.C; import androidx.media3.common.util.GlUtil; +import androidx.media3.common.util.UnstableApi; import java.io.IOException; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -32,7 +33,8 @@ 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 { +@UnstableApi +public final class ScaleToFitFrameProcessor implements GlFrameProcessor { /** A builder for {@link ScaleToFitFrameProcessor} instances. */ public static final class Builder { diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java index 7f548104cd..f642399d17 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java @@ -59,11 +59,13 @@ import androidx.media3.exoplayer.trackselection.DefaultTrackSelector; import androidx.media3.exoplayer.video.VideoRendererEventListener; import androidx.media3.extractor.DefaultExtractorsFactory; import androidx.media3.extractor.mp4.Mp4Extractor; +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; /** @@ -102,6 +104,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; @@ -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();