mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Transformer: Take effects into account when signalling duration
PiperOrigin-RevId: 616224079
This commit is contained in:
parent
3136deb9b3
commit
d269b93755
@ -77,6 +77,7 @@ import androidx.media3.effect.GlEffect;
|
|||||||
import androidx.media3.effect.Presentation;
|
import androidx.media3.effect.Presentation;
|
||||||
import androidx.media3.effect.RgbFilter;
|
import androidx.media3.effect.RgbFilter;
|
||||||
import androidx.media3.effect.ScaleAndRotateTransformation;
|
import androidx.media3.effect.ScaleAndRotateTransformation;
|
||||||
|
import androidx.media3.effect.SpeedChangeEffect;
|
||||||
import androidx.media3.effect.TimestampWrapper;
|
import androidx.media3.effect.TimestampWrapper;
|
||||||
import androidx.media3.exoplayer.audio.TeeAudioProcessor;
|
import androidx.media3.exoplayer.audio.TeeAudioProcessor;
|
||||||
import androidx.media3.extractor.mp4.Mp4Extractor;
|
import androidx.media3.extractor.mp4.Mp4Extractor;
|
||||||
@ -926,6 +927,67 @@ public class TransformerEndToEndTest {
|
|||||||
assertThat(exception).hasMessageThat().contains("video");
|
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
|
@Test
|
||||||
public void audioVideoTranscodedFromDifferentSequences_producesExpectedResult() throws Exception {
|
public void audioVideoTranscodedFromDifferentSequences_producesExpectedResult() throws Exception {
|
||||||
Transformer transformer = new Transformer.Builder(context).build();
|
Transformer transformer = new Transformer.Builder(context).build();
|
||||||
|
@ -17,10 +17,13 @@ package androidx.media3.transformer;
|
|||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||||
import static androidx.media3.common.util.Assertions.checkState;
|
import static androidx.media3.common.util.Assertions.checkState;
|
||||||
|
import static java.lang.Math.max;
|
||||||
|
|
||||||
import androidx.annotation.IntRange;
|
import androidx.annotation.IntRange;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.Effect;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
|
import androidx.media3.common.audio.AudioProcessor;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.exoplayer.source.MediaSource;
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
import androidx.media3.extractor.mp4.Mp4Extractor;
|
import androidx.media3.extractor.mp4.Mp4Extractor;
|
||||||
@ -298,4 +301,24 @@ public final class EditedMediaItem {
|
|||||||
}
|
}
|
||||||
return presentationDurationUs;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -335,6 +335,8 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
checkArgument(
|
checkArgument(
|
||||||
durationUs != C.TIME_UNSET || currentMediaItemIndex == editedMediaItems.size() - 1,
|
durationUs != C.TIME_UNSET || currentMediaItemIndex == editedMediaItems.size() - 1,
|
||||||
"Could not retrieve required duration for EditedMediaItem " + currentMediaItemIndex);
|
"Could not retrieve required duration for EditedMediaItem " + currentMediaItemIndex);
|
||||||
|
durationUs =
|
||||||
|
editedMediaItems.get(currentMediaItemIndex).getDurationAfterEffectsApplied(durationUs);
|
||||||
currentAssetDurationUs = durationUs;
|
currentAssetDurationUs = durationUs;
|
||||||
if (editedMediaItems.size() == 1 && !isLooping) {
|
if (editedMediaItems.size() == 1 && !isLooping) {
|
||||||
sequenceAssetLoaderListener.onDurationUs(durationUs);
|
sequenceAssetLoaderListener.onDurationUs(durationUs);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user