Skip looping sequences when computing composition duration

This fixes the following issue: in CompositionPlayer, if the
Composition was containing a looping sequence with duration larger than
all the other sequences, the Composition duration was set to the
looping sequence duration. This is incorrect because looping sequences
should be clipped to match the other sequences.

PiperOrigin-RevId: 737927987
This commit is contained in:
kimvde 2025-03-18 02:44:53 -07:00 committed by Copybara-Service
parent 9ac58fa405
commit bdc2216492
3 changed files with 56 additions and 2 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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);