diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerEndToEndTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerEndToEndTest.java index a257d2ddc3..971efef128 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerEndToEndTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerEndToEndTest.java @@ -327,10 +327,7 @@ public class TransformerEndToEndTest { new DefaultEncoderFactory.Builder(context).setEnableFallback(false).build()) .build(); MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_URI_STRING)); - ImmutableList videoEffects = - ImmutableList.of( - new ScaleAndRotateTransformation.Builder().setRotationDegrees(90).build(), - Presentation.createForHeight(MP4_ASSET_FORMAT.height)); + ImmutableList videoEffects = ImmutableList.of(Presentation.createForHeight(480)); Effects effects = new Effects(/* audioProcessors= */ ImmutableList.of(), videoEffects); EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(mediaItem).setEffects(effects).build(); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerUtil.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerUtil.java index 73a7c06cbb..1c7fb37d3e 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerUtil.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerUtil.java @@ -144,24 +144,20 @@ import com.google.common.collect.ImmutableList; } ImmutableList videoEffects = firstEditedMediaItem.effects.videoEffects; return !videoEffects.isEmpty() - && !areVideoEffectsAllRegularRotationsOrNoOp(videoEffects, inputFormat, muxerWrapper); + && !areVideoEffectsAllNoOp(videoEffects, inputFormat) + && !hasOnlyRegularRotationEffect(videoEffects, muxerWrapper); } /** - * Returns whether the effects, applied in the list ordering, would result in a noOp or regular - * rotation. - * - *

If {@code true}, sets the regular rotation on the {@linkplain - * MuxerWrapper#setAdditionalRotationDegrees}. + * Returns whether the collection of {@code videoEffects} would be a {@linkplain + * GlEffect#isNoOp(int, int) no-op}, if queued samples of this {@link Format}. */ - private static boolean areVideoEffectsAllRegularRotationsOrNoOp( - ImmutableList videoEffects, Format inputFormat, MuxerWrapper muxerWrapper) { + public static boolean areVideoEffectsAllNoOp( + ImmutableList videoEffects, Format inputFormat) { int decodedWidth = (inputFormat.rotationDegrees % 180 == 0) ? inputFormat.width : inputFormat.height; int decodedHeight = (inputFormat.rotationDegrees % 180 == 0) ? inputFormat.height : inputFormat.width; - boolean widthHeightFlipped = false; - float totalRotationDegrees = 0; for (int i = 0; i < videoEffects.size(); i++) { Effect videoEffect = videoEffects.get(i); if (!(videoEffect instanceof GlEffect)) { @@ -170,42 +166,32 @@ import com.google.common.collect.ImmutableList; return false; } GlEffect glEffect = (GlEffect) videoEffect; - if (videoEffect instanceof ScaleAndRotateTransformation) { - ScaleAndRotateTransformation scaleAndRotateTransformation = - (ScaleAndRotateTransformation) videoEffect; - if (scaleAndRotateTransformation.scaleX != 1f - || scaleAndRotateTransformation.scaleY != 1f) { - return false; - } - float rotationDegrees = scaleAndRotateTransformation.rotationDegrees; - totalRotationDegrees += rotationDegrees; - if (totalRotationDegrees % 90 == 0 && !widthHeightFlipped) { - int temp = decodedWidth; - decodedWidth = decodedHeight; - decodedHeight = temp; - widthHeightFlipped = true; - } else if (totalRotationDegrees % 180 == 0 && widthHeightFlipped) { - int temp = decodedWidth; - decodedWidth = decodedHeight; - decodedHeight = temp; - widthHeightFlipped = false; - } - continue; - } if (!glEffect.isNoOp(decodedWidth, decodedHeight)) { return false; } } - totalRotationDegrees %= 360; - if (totalRotationDegrees == 0) { - return true; + return true; + } + + private static boolean hasOnlyRegularRotationEffect( + ImmutableList videoEffects, MuxerWrapper muxerWrapper) { + if (videoEffects.size() != 1) { + return false; } - if (totalRotationDegrees == 90f - || totalRotationDegrees == 180f - || totalRotationDegrees == 270f) { + Effect videoEffect = videoEffects.get(0); + if (!(videoEffect instanceof ScaleAndRotateTransformation)) { + return false; + } + ScaleAndRotateTransformation scaleAndRotateTransformation = + (ScaleAndRotateTransformation) videoEffect; + if (scaleAndRotateTransformation.scaleX != 1f || scaleAndRotateTransformation.scaleY != 1f) { + return false; + } + float rotationDegrees = scaleAndRotateTransformation.rotationDegrees; + if (rotationDegrees == 90f || rotationDegrees == 180f || rotationDegrees == 270f) { // The MuxerWrapper rotation is clockwise while the ScaleAndRotateTransformation rotation // is counterclockwise. - muxerWrapper.setAdditionalRotationDegrees(360 - Math.round(totalRotationDegrees)); + muxerWrapper.setAdditionalRotationDegrees(360 - Math.round(rotationDegrees)); return true; } return false; diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/MediaItemExportTest.java b/libraries/transformer/src/test/java/androidx/media3/transformer/MediaItemExportTest.java index ae451b459c..a62af66e62 100644 --- a/libraries/transformer/src/test/java/androidx/media3/transformer/MediaItemExportTest.java +++ b/libraries/transformer/src/test/java/androidx/media3/transformer/MediaItemExportTest.java @@ -66,7 +66,6 @@ import androidx.media3.common.Format; import androidx.media3.common.MediaItem; import androidx.media3.common.MimeTypes; import androidx.media3.common.audio.SonicAudioProcessor; -import androidx.media3.effect.Contrast; import androidx.media3.effect.Presentation; import androidx.media3.effect.ScaleAndRotateTransformation; import androidx.media3.exoplayer.source.DefaultMediaSourceFactory; @@ -1066,34 +1065,6 @@ public final class MediaItemExportTest { /* originalFileName= */ FILE_AUDIO_VIDEO, /* modifications...= */ "rotated")); } - @Test - public void start_totalRotationRegularAndNoOps_transmuxes() throws Exception { - Transformer transformer = - createTransformerBuilder(muxerFactory, /* enableFallback= */ false).build(); - // Total rotation is 270. - ImmutableList videoEffects = - ImmutableList.of( - new ScaleAndRotateTransformation.Builder().setRotationDegrees(315).build(), - new Contrast(0f), - new ScaleAndRotateTransformation.Builder().setRotationDegrees(180).build(), - new ScaleAndRotateTransformation.Builder().setRotationDegrees(135).build(), - Presentation.createForHeight(1080)); - EditedMediaItem editedMediaItem = - new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO)) - .setEffects(new Effects(ImmutableList.of(), videoEffects)) - .build(); - - transformer.start(editedMediaItem, outputDir.newFile().getPath()); - TransformerTestRunner.runLooper(transformer); - - // Video transcoding in unit tests is not supported. - DumpFileAsserts.assertOutput( - context, - muxerFactory.getCreatedMuxer(), - getDumpFileName( - /* originalFileName= */ FILE_AUDIO_VIDEO, /* modifications...= */ "rotated")); - } - @Test public void getProgress_unknownDuration_returnsConsistentStates() throws Exception { Transformer transformer = diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerUtilTest.java b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerUtilTest.java deleted file mode 100644 index 433406b80d..0000000000 --- a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerUtilTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2023 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.MimeTypes.VIDEO_H264; -import static androidx.media3.transformer.MuxerWrapper.MUXER_MODE_DEFAULT; -import static androidx.media3.transformer.TestUtil.ASSET_URI_PREFIX; -import static androidx.media3.transformer.TestUtil.FILE_AUDIO_VIDEO; -import static androidx.media3.transformer.TransformerUtil.shouldTranscodeVideo; -import static androidx.test.core.app.ApplicationProvider.getApplicationContext; -import static com.google.common.truth.Truth.assertThat; - -import androidx.media3.common.C; -import androidx.media3.common.Effect; -import androidx.media3.common.Format; -import androidx.media3.common.MediaItem; -import androidx.media3.effect.Presentation; -import androidx.media3.effect.ScaleAndRotateTransformation; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.google.common.collect.ImmutableList; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; - -/** Unit tests for {@link TransformerUtil}. */ -@RunWith(AndroidJUnit4.class) -public class TransformerUtilTest { - @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); - - @Test - public void shouldTranscodeVideo_regularRotationAndTranscodingPresentation_returnsTrue() - throws Exception { - MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO); - Format format = - new Format.Builder() - .setSampleMimeType(VIDEO_H264) - .setWidth(1080) - .setHeight(720) - .setFrameRate(29.97f) - .setCodecs("avc1.64001F") - .build(); - ImmutableList videoEffects = - ImmutableList.of( - new ScaleAndRotateTransformation.Builder().setRotationDegrees(90).build(), - Presentation.createForHeight(format.height)); - Effects effects = new Effects(/* audioProcessors= */ ImmutableList.of(), videoEffects); - EditedMediaItem editedMediaItem = - new EditedMediaItem.Builder(mediaItem).setEffects(effects).build(); - Composition composition = - new Composition.Builder(new EditedMediaItemSequence(editedMediaItem)).build(); - MuxerWrapper muxerWrapper = - new MuxerWrapper( - temporaryFolder.newFile().getPath(), - new DefaultMuxer.Factory(), - new NoOpMuxerListenerImpl(), - MUXER_MODE_DEFAULT, - /* dropSamplesBeforeFirstVideoSample= */ false); - - assertThat( - shouldTranscodeVideo( - format, - composition, - /* sequenceIndex= */ 0, - new TransformationRequest.Builder().build(), - new DefaultEncoderFactory.Builder(getApplicationContext()).build(), - muxerWrapper)) - .isTrue(); - } - - private static final class NoOpMuxerListenerImpl implements MuxerWrapper.Listener { - - @Override - public void onTrackEnded( - @C.TrackType int trackType, Format format, int averageBitrate, int sampleCount) {} - - @Override - public void onEnded(long durationMs, long fileSizeBytes) {} - - @Override - public void onError(ExportException exportException) {} - } -}