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..405ebdb301 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerUtil.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerUtil.java @@ -17,6 +17,7 @@ package androidx.media3.transformer; import static androidx.media3.transformer.Composition.HDR_MODE_KEEP_HDR; +import static java.lang.Math.round; import android.media.MediaCodec; import androidx.annotation.Nullable; @@ -178,6 +179,9 @@ import com.google.common.collect.ImmutableList; return false; } float rotationDegrees = scaleAndRotateTransformation.rotationDegrees; + if (rotationDegrees % 90f != 0) { + return false; + } totalRotationDegrees += rotationDegrees; if (totalRotationDegrees % 90 == 0 && !widthHeightFlipped) { int temp = decodedWidth; @@ -205,7 +209,7 @@ import com.google.common.collect.ImmutableList; || totalRotationDegrees == 270f) { // The MuxerWrapper rotation is clockwise while the ScaleAndRotateTransformation rotation // is counterclockwise. - muxerWrapper.setAdditionalRotationDegrees(360 - Math.round(totalRotationDegrees)); + muxerWrapper.setAdditionalRotationDegrees(360 - round(totalRotationDegrees)); 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 30a089e95e..3dda3f8681 100644 --- a/libraries/transformer/src/test/java/androidx/media3/transformer/MediaItemExportTest.java +++ b/libraries/transformer/src/test/java/androidx/media3/transformer/MediaItemExportTest.java @@ -1073,10 +1073,9 @@ public final class MediaItemExportTest { // Total rotation is 270. ImmutableList videoEffects = ImmutableList.of( - new ScaleAndRotateTransformation.Builder().setRotationDegrees(315).build(), + new ScaleAndRotateTransformation.Builder().setRotationDegrees(90).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)) diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerUtilTest.java b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerUtilTest.java index 433406b80d..0cca46e416 100644 --- a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerUtilTest.java +++ b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerUtilTest.java @@ -81,6 +81,46 @@ public class TransformerUtilTest { .isTrue(); } + @Test + public void shouldTranscodeVideo_irregularRotationAndPresentation_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(45).build(), + Presentation.createForHeight(format.height), + new ScaleAndRotateTransformation.Builder().setRotationDegrees(45).build()); + 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