diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/CompositionPlayer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/CompositionPlayer.java index 79735067e5..d610c97b55 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/CompositionPlayer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/CompositionPlayer.java @@ -1097,8 +1097,11 @@ public final class CompositionPlayer extends SimpleBasePlayer checkState(!composition.sequences.isEmpty()); long longestSequenceDurationUs = Integer.MIN_VALUE; for (int i = 0; i < composition.sequences.size(); i++) { - longestSequenceDurationUs = - max(longestSequenceDurationUs, getSequenceDurationUs(composition.sequences.get(i))); + EditedMediaItemSequence sequence = composition.sequences.get(i); + if (sequence.isLooping) { + continue; + } + longestSequenceDurationUs = max(longestSequenceDurationUs, getSequenceDurationUs(sequence)); } return longestSequenceDurationUs; } diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/CompositionExportTest.java b/libraries/transformer/src/test/java/androidx/media3/transformer/CompositionExportTest.java index c0b4ae5fc2..55d5a015b5 100644 --- a/libraries/transformer/src/test/java/androidx/media3/transformer/CompositionExportTest.java +++ b/libraries/transformer/src/test/java/androidx/media3/transformer/CompositionExportTest.java @@ -195,6 +195,32 @@ public class CompositionExportTest { assertThat(exportResult.fileSizeBytes).isEqualTo(5292662); } + @Test + public void start_longerLoopingSequence_hasNonLoopingSequenceDuration() throws Exception { + Transformer transformer = new TestTransformerBuilder(context).build(); + EditedMediaItem audioEditedMediaItem = + new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_ONLY)).build(); + EditedMediaItemSequence loopingAudioSequence = + new EditedMediaItemSequence.Builder(audioEditedMediaItem, audioEditedMediaItem) + .setIsLooping(true) + .build(); + EditedMediaItem videoEditedMediaItem = + new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_VIDEO_ONLY)).build(); + EditedMediaItemSequence videoSequence = + new EditedMediaItemSequence.Builder(videoEditedMediaItem).build(); + Composition composition = + new Composition.Builder(loopingAudioSequence, videoSequence) + .setTransmuxAudio(true) + .setTransmuxVideo(true) + .build(); + + transformer.start(composition, outputDir.newFile().getPath()); + ExportResult exportResult = TransformerTestRunner.runLooper(transformer); + + // Video file duration is 1001 ms and audio file duration is 1044 ms. + assertThat(exportResult.durationMs).isLessThan(1_001); + } + @Test public void start_compositionOfConcurrentAudio_isCorrect() throws Exception { CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true); diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/CompositionPlayerAudioPlaybackTest.java b/libraries/transformer/src/test/java/androidx/media3/transformer/CompositionPlayerAudioPlaybackTest.java index fbe39a6215..7aea09de4e 100644 --- a/libraries/transformer/src/test/java/androidx/media3/transformer/CompositionPlayerAudioPlaybackTest.java +++ b/libraries/transformer/src/test/java/androidx/media3/transformer/CompositionPlayerAudioPlaybackTest.java @@ -22,6 +22,7 @@ import static androidx.media3.transformer.TestUtil.createAudioEffects; import static androidx.media3.transformer.TestUtil.createChannelCountChangingAudioProcessor; import static androidx.media3.transformer.TestUtil.createSampleRateChangingAudioProcessor; import static androidx.media3.transformer.TestUtil.createVolumeScalingAudioProcessor; +import static com.google.common.truth.Truth.assertThat; import android.content.Context; import androidx.media3.common.MediaItem; @@ -342,6 +343,30 @@ public final class CompositionPlayerAudioPlaybackTest { + "wav/compositionPlayback_withLongLoopingSequence_outputsCorrectSamples.dump"); } + @Test + public void playTwoSequences_withLongLoopingSequence_hasNonLoopingSequenceDuration() { + CompositionPlayer player = createCompositionPlayer(context, capturingAudioSink); + EditedMediaItemSequence primarySequence = + new EditedMediaItemSequence.Builder( + new EditedMediaItem.Builder( + MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_RAW_STEREO_48000KHZ)) + .setDurationUs(348_000L) + .build()) + .build(); + EditedMediaItemSequence loopingSequence = + new EditedMediaItemSequence.Builder( + new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_RAW)) + .setDurationUs(1_000_000L) + .build()) + .setIsLooping(true) + .build(); + Composition composition = new Composition.Builder(primarySequence, loopingSequence).build(); + player.setComposition(composition); + player.prepare(); + + assertThat(player.getDuration()).isEqualTo(348); + } + @Test public void playSingleSequence_withRepeatModeEnabled_outputsCorrectSamples() throws Exception { CompositionPlayer player = createCompositionPlayer(context, capturingAudioSink);