diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TranscodingTransformer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TranscodingTransformer.java index 643705c00d..ea59207926 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TranscodingTransformer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TranscodingTransformer.java @@ -736,8 +736,7 @@ public final class TranscodingTransformer { } if (!transformation.removeVideo) { renderers[index] = - new TransformerTranscodingVideoRenderer( - context, muxerWrapper, mediaClock, transformation); + new TransformerVideoRenderer(context, muxerWrapper, mediaClock, transformation); index++; } return renderers; 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 74c9a0e0c0..6455ae389f 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java @@ -509,7 +509,7 @@ public final class Transformer { .build(); player = new ExoPlayer.Builder( - context, new TransformerRenderersFactory(muxerWrapper, transformation)) + context, new TransformerRenderersFactory(context, muxerWrapper, transformation)) .setMediaSourceFactory(mediaSourceFactory) .setTrackSelector(trackSelector) .setLoadControl(loadControl) @@ -594,11 +594,14 @@ public final class Transformer { private static final class TransformerRenderersFactory implements RenderersFactory { + private final Context context; private final MuxerWrapper muxerWrapper; private final TransformerMediaClock mediaClock; private final Transformation transformation; - public TransformerRenderersFactory(MuxerWrapper muxerWrapper, Transformation transformation) { + public TransformerRenderersFactory( + Context context, MuxerWrapper muxerWrapper, Transformation transformation) { + this.context = context; this.muxerWrapper = muxerWrapper; this.transformation = transformation; mediaClock = new TransformerMediaClock(); @@ -620,7 +623,7 @@ public final class Transformer { } if (!transformation.removeVideo) { renderers[index] = - new TransformerMuxingVideoRenderer(muxerWrapper, mediaClock, transformation); + new TransformerVideoRenderer(context, muxerWrapper, mediaClock, transformation); index++; } return renderers; diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerAudioRenderer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerAudioRenderer.java index 6dc59a4a77..6820049533 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerAudioRenderer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerAudioRenderer.java @@ -101,8 +101,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; } /** - * Attempts to write sample pipeline output data to the muxer, and returns whether it may be - * possible to write more data immediately by calling this method again. + * Attempts to write sample pipeline output data to the muxer. + * + * @return Whether it may be possible to write more data immediately by calling this method again. */ @RequiresNonNull("samplePipeline") private boolean feedMuxerFromPipeline() { @@ -136,8 +137,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; } /** - * Attempts to pass input data to the sample pipeline, and returns whether it may be possible to - * pass more data immediately by calling this method again. + * Attempts to pass input data to the sample pipeline. + * + * @return Whether it may be possible to pass more data immediately by calling this method again. */ @RequiresNonNull("samplePipeline") private boolean feedPipelineFromInput() { diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerMuxingVideoRenderer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerMuxingVideoRenderer.java deleted file mode 100644 index 2704c0d42f..0000000000 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerMuxingVideoRenderer.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package androidx.media3.transformer; - -import static androidx.media3.common.util.Assertions.checkNotNull; -import static androidx.media3.exoplayer.source.SampleStream.FLAG_REQUIRE_FORMAT; - -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.media3.common.C; -import androidx.media3.common.Format; -import androidx.media3.decoder.DecoderInputBuffer; -import androidx.media3.exoplayer.FormatHolder; -import androidx.media3.exoplayer.source.SampleStream.ReadDataResult; -import java.nio.ByteBuffer; - -/** A video renderer that doesn't re-encoder the samples. */ -@RequiresApi(18) -/* package */ final class TransformerMuxingVideoRenderer extends TransformerBaseRenderer { - - private static final String TAG = "TransformerVideoRenderer"; - - private final DecoderInputBuffer buffer; - - @Nullable private SampleTransformer sampleTransformer; - - private boolean formatRead; - private boolean isBufferPending; - private boolean isInputStreamEnded; - - public TransformerMuxingVideoRenderer( - MuxerWrapper muxerWrapper, TransformerMediaClock mediaClock, Transformation transformation) { - super(C.TRACK_TYPE_VIDEO, muxerWrapper, mediaClock, transformation); - buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DIRECT); - } - - @Override - public String getName() { - return TAG; - } - - @Override - public void render(long positionUs, long elapsedRealtimeUs) { - if (!isRendererStarted || isEnded()) { - return; - } - - if (!formatRead) { - FormatHolder formatHolder = getFormatHolder(); - @ReadDataResult int result = readSource(formatHolder, buffer, FLAG_REQUIRE_FORMAT); - if (result != C.RESULT_FORMAT_READ) { - return; - } - Format format = checkNotNull(formatHolder.format); - formatRead = true; - if (transformation.flattenForSlowMotion) { - sampleTransformer = new SefSlowMotionVideoSampleTransformer(format); - } - muxerWrapper.addTrackFormat(format); - } - - while (true) { - // Read sample. - if (!isBufferPending && !readAndTransformBuffer()) { - return; - } - // Write sample. - isBufferPending = - !muxerWrapper.writeSample( - getTrackType(), buffer.data, buffer.isKeyFrame(), buffer.timeUs); - if (isBufferPending) { - return; - } - } - } - - @Override - public boolean isEnded() { - return isInputStreamEnded; - } - - /** - * Checks whether a sample can be read and, if so, reads it, transforms it and writes the - * resulting sample to the {@link #buffer}. - * - *
The buffer data can be set to null if the transformation applied discards the sample. - * - * @return Whether a sample has been read and transformed. - */ - private boolean readAndTransformBuffer() { - buffer.clear(); - @ReadDataResult int result = readSource(getFormatHolder(), buffer, /* readFlags= */ 0); - if (result == C.RESULT_FORMAT_READ) { - throw new IllegalStateException("Format changes are not supported."); - } else if (result == C.RESULT_NOTHING_READ) { - return false; - } - - // Buffer read. - - if (buffer.isEndOfStream()) { - isInputStreamEnded = true; - muxerWrapper.endTrack(getTrackType()); - return false; - } - mediaClock.updateTimeForTrackType(getTrackType(), buffer.timeUs); - buffer.timeUs -= streamOffsetUs; - ByteBuffer data = checkNotNull(buffer.data); - data.flip(); - if (sampleTransformer != null) { - sampleTransformer.transformSample(buffer); - } - return true; - } -} diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerTranscodingVideoRenderer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerVideoRenderer.java similarity index 81% rename from libraries/transformer/src/main/java/androidx/media3/transformer/TransformerTranscodingVideoRenderer.java rename to libraries/transformer/src/main/java/androidx/media3/transformer/TransformerVideoRenderer.java index 8c498887a4..be56180889 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerTranscodingVideoRenderer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerVideoRenderer.java @@ -33,18 +33,19 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.RequiresNonNull; @RequiresApi(18) -/* package */ final class TransformerTranscodingVideoRenderer extends TransformerBaseRenderer { +/* package */ final class TransformerVideoRenderer extends TransformerBaseRenderer { private static final String TAG = "TransformerTranscodingVideoRenderer"; private final Context context; private final DecoderInputBuffer decoderInputBuffer; + private @MonotonicNonNull SampleTransformer slowMotionSampleTransformer; private @MonotonicNonNull SamplePipeline samplePipeline; private boolean muxerWrapperTrackAdded; private boolean muxerWrapperTrackEnded; - public TransformerTranscodingVideoRenderer( + public TransformerVideoRenderer( Context context, MuxerWrapper muxerWrapper, TransformerMediaClock mediaClock, @@ -105,12 +106,16 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; } else { samplePipeline = new PassthroughSamplePipeline(decoderInputFormat); } + if (transformation.flattenForSlowMotion) { + slowMotionSampleTransformer = new SefSlowMotionVideoSampleTransformer(decoderInputFormat); + } return true; } /** - * Attempts to write sample pipeline output data to the muxer, and returns whether it may be - * possible to write more data immediately by calling this method again. + * Attempts to write sample pipeline output data to the muxer. + * + * @return Whether it may be possible to write more data immediately by calling this method again. */ @RequiresNonNull("samplePipeline") private boolean feedMuxerFromPipeline() { @@ -146,8 +151,15 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; } /** - * Attempts to pass input data to the sample pipeline, and returns whether it may be possible to - * pass more data immediately by calling this method again. + * Attempts to: + * + *