Transformer: Take effects into account when signalling duration

PiperOrigin-RevId: 616224079
This commit is contained in:
tofunmi 2024-03-15 13:17:30 -07:00 committed by Copybara-Service
parent 3136deb9b3
commit d269b93755
3 changed files with 87 additions and 0 deletions

View File

@ -77,6 +77,7 @@ import androidx.media3.effect.GlEffect;
import androidx.media3.effect.Presentation;
import androidx.media3.effect.RgbFilter;
import androidx.media3.effect.ScaleAndRotateTransformation;
import androidx.media3.effect.SpeedChangeEffect;
import androidx.media3.effect.TimestampWrapper;
import androidx.media3.exoplayer.audio.TeeAudioProcessor;
import androidx.media3.extractor.mp4.Mp4Extractor;
@ -926,6 +927,67 @@ public class TransformerEndToEndTest {
assertThat(exception).hasMessageThat().contains("video");
}
@Test
public void durationAdjustedSequence_completesWithCorrectDuration() throws Exception {
Transformer transformer = new Transformer.Builder(context).build();
if (AndroidTestUtil.skipAndLogIfFormatsUnsupported(
context,
testId,
/* inputFormat= */ MP4_ASSET_WITH_INCREASING_TIMESTAMPS_320W_240H_15S_FORMAT,
/* outputFormat= */ MP4_ASSET_WITH_INCREASING_TIMESTAMPS_320W_240H_15S_FORMAT)) {
return;
}
ImmutableList<Effect> videoEffects =
ImmutableList.of(new SpeedChangeEffect(1.5f), new SpeedChangeEffect(2f));
EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(
MediaItem.fromUri(
Uri.parse(MP4_ASSET_WITH_INCREASING_TIMESTAMPS_320W_240H_15S_URI_STRING)))
.setEffects(new Effects(/* audioProcessors= */ ImmutableList.of(), videoEffects))
.setRemoveAudio(true)
.build();
Composition composition =
new Composition.Builder(new EditedMediaItemSequence(editedMediaItem, editedMediaItem))
.build();
ExportTestResult result =
new TransformerAndroidTestRunner.Builder(context, transformer)
.build()
.run(testId, composition);
assertThat(result.exportResult.durationMs).isEqualTo(10_351L);
}
@Test
public void durationAdjustedSequence_withForcedAudioTrack_completesWithCorrectDuration()
throws Exception {
Transformer transformer = new Transformer.Builder(context).build();
if (AndroidTestUtil.skipAndLogIfFormatsUnsupported(
context,
testId,
/* inputFormat= */ MP4_ASSET_WITH_INCREASING_TIMESTAMPS_320W_240H_15S_FORMAT,
/* outputFormat= */ MP4_ASSET_WITH_INCREASING_TIMESTAMPS_320W_240H_15S_FORMAT)) {
return;
}
ImmutableList<Effect> videoEffects = ImmutableList.of(new SpeedChangeEffect(1.5f));
EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(
MediaItem.fromUri(
Uri.parse(MP4_ASSET_WITH_INCREASING_TIMESTAMPS_320W_240H_15S_URI_STRING)))
.setEffects(new Effects(/* audioProcessors= */ ImmutableList.of(), videoEffects))
.setRemoveAudio(true)
.build();
Composition composition =
new Composition.Builder(new EditedMediaItemSequence(editedMediaItem, editedMediaItem))
.experimentalSetForceAudioTrack(true)
.build();
ExportTestResult result =
new TransformerAndroidTestRunner.Builder(context, transformer)
.build()
.run(testId, composition);
assertThat(result.exportResult.durationMs).isAtMost(20_720L);
}
@Test
public void audioVideoTranscodedFromDifferentSequences_producesExpectedResult() throws Exception {
Transformer transformer = new Transformer.Builder(context).build();

View File

@ -17,10 +17,13 @@ package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkState;
import static java.lang.Math.max;
import androidx.annotation.IntRange;
import androidx.media3.common.C;
import androidx.media3.common.Effect;
import androidx.media3.common.MediaItem;
import androidx.media3.common.audio.AudioProcessor;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.extractor.mp4.Mp4Extractor;
@ -298,4 +301,24 @@ public final class EditedMediaItem {
}
return presentationDurationUs;
}
/* package */ long getDurationAfterEffectsApplied(long durationUs) {
long audioDurationUs = durationUs;
long videoDurationUs = durationUs;
if (removeAudio) {
audioDurationUs = C.TIME_UNSET;
} else {
for (AudioProcessor audioProcessor : effects.audioProcessors) {
audioDurationUs = audioProcessor.getDurationAfterProcessorApplied(audioDurationUs);
}
}
if (removeVideo) {
videoDurationUs = C.TIME_UNSET;
} else {
for (Effect videoEffect : effects.videoEffects) {
videoDurationUs = videoEffect.getDurationAfterEffectApplied(videoDurationUs);
}
}
return max(audioDurationUs, videoDurationUs);
}
}

View File

@ -335,6 +335,8 @@ import java.util.concurrent.atomic.AtomicInteger;
checkArgument(
durationUs != C.TIME_UNSET || currentMediaItemIndex == editedMediaItems.size() - 1,
"Could not retrieve required duration for EditedMediaItem " + currentMediaItemIndex);
durationUs =
editedMediaItems.get(currentMediaItemIndex).getDurationAfterEffectsApplied(durationUs);
currentAssetDurationUs = durationUs;
if (editedMediaItems.size() == 1 && !isLooping) {
sequenceAssetLoaderListener.onDurationUs(durationUs);