diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/CompositeAssetLoader.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/CompositeAssetLoader.java index 3147f4eed8..7439af33eb 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/CompositeAssetLoader.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/CompositeAssetLoader.java @@ -57,12 +57,12 @@ import java.util.concurrent.atomic.AtomicLong; private volatile long currentDurationUs; public CompositeAssetLoader( - List editedMediaItems, + EditedMediaItemSequence sequence, AssetLoader.Factory assetLoaderFactory, Looper looper, Listener listener, Clock clock) { - this.editedMediaItems = editedMediaItems; + this.editedMediaItems = sequence.editedMediaItems; this.assetLoaderFactory = assetLoaderFactory; compositeAssetLoaderListener = listener; currentMediaItemIndex = new AtomicInteger(); diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Composition.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Composition.java index c822c5e8b8..7eade92d1b 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Composition.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Composition.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.transformer; +import static com.google.android.exoplayer2.util.Assertions.checkArgument; + import com.google.android.exoplayer2.MediaItem; import com.google.common.collect.ImmutableList; @@ -29,6 +31,8 @@ public final class Composition { /** * The {@link EditedMediaItemSequence} instances to compose. {@link MediaItem} instances from * different sequences that are overlapping in time will be mixed in the output. + * + *

This list must not be empty. */ public final ImmutableList sequences; /** The {@link Effects} to apply to the composition. */ @@ -41,6 +45,7 @@ public final class Composition { * @param effects The {@link #effects}. */ public Composition(ImmutableList sequences, Effects effects) { + checkArgument(!sequences.isEmpty()); this.sequences = sequences; this.effects = effects; } diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/EditedMediaItemSequence.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/EditedMediaItemSequence.java index fcccc8efb9..2d04adb148 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/EditedMediaItemSequence.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/EditedMediaItemSequence.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.transformer; +import static com.google.android.exoplayer2.util.Assertions.checkArgument; + import com.google.common.collect.ImmutableList; /** @@ -24,7 +26,11 @@ import com.google.common.collect.ImmutableList; */ public final class EditedMediaItemSequence { - /** The {@link EditedMediaItem} instances in the sequence. */ + /** + * The {@link EditedMediaItem} instances in the sequence. + * + *

This list must not be empty. + */ public final ImmutableList editedMediaItems; /** @@ -33,6 +39,7 @@ public final class EditedMediaItemSequence { * @param editedMediaItems The {@link #editedMediaItems}. */ public EditedMediaItemSequence(ImmutableList editedMediaItems) { + checkArgument(!editedMediaItems.isEmpty()); this.editedMediaItems = editedMediaItems; } } diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java index c84ce9675d..e5051ab7e4 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java @@ -761,7 +761,7 @@ public final class Transformer { transformerInternal = new TransformerInternal( context, - editedMediaItem, + composition, path, transformationRequest, generateSilentAudio, diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerInternal.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerInternal.java index effa643ea1..fd3ac0e0eb 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerInternal.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerInternal.java @@ -111,7 +111,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; public TransformerInternal( Context context, - EditedMediaItem editedMediaItem, + Composition composition, String outputPath, TransformationRequest transformationRequest, boolean generateSilentAudio, @@ -134,9 +134,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; internalHandlerThread = new HandlerThread("Transformer:Internal"); internalHandlerThread.start(); Looper internalLooper = internalHandlerThread.getLooper(); - ComponentListener componentListener = new ComponentListener(editedMediaItem, fallbackListener); + EditedMediaItemSequence sequence = composition.sequences.get(0); + ComponentListener componentListener = new ComponentListener(sequence, fallbackListener); assetLoader = - assetLoaderFactory.createAssetLoader(editedMediaItem, internalLooper, componentListener); + new CompositeAssetLoader( + sequence, assetLoaderFactory, internalLooper, componentListener, clock); samplePipelines = new ArrayList<>(); muxerWrapper = new MuxerWrapper(outputPath, muxerFactory, componentListener); transformerConditionVariable = new ConditionVariable(); @@ -313,7 +315,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private class ComponentListener implements AssetLoader.Listener, MuxerWrapper.Listener { - private final EditedMediaItem editedMediaItem; + // The first EditedMediaItem in the sequence determines which SamplePipeline to use. + private final EditedMediaItem firstEditedMediaItem; private final FallbackListener fallbackListener; private final AtomicInteger trackCount; @@ -321,8 +324,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private volatile long durationUs; - public ComponentListener(EditedMediaItem editedMediaItem, FallbackListener fallbackListener) { - this.editedMediaItem = editedMediaItem; + public ComponentListener(EditedMediaItemSequence sequence, FallbackListener fallbackListener) { + firstEditedMediaItem = sequence.editedMediaItems.get(0); this.fallbackListener = fallbackListener; trackCount = new AtomicInteger(); durationUs = C.TIME_UNSET; @@ -468,8 +471,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; streamStartPositionUs, streamOffsetUs, transformationRequest, - editedMediaItem.flattenForSlowMotion, - editedMediaItem.effects.audioProcessors, + firstEditedMediaItem.flattenForSlowMotion, + firstEditedMediaItem.effects.audioProcessors, generateSilentAudio ? durationUs : C.TIME_UNSET, encoderFactory, muxerWrapper, @@ -481,8 +484,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; streamStartPositionUs, streamOffsetUs, transformationRequest, - editedMediaItem.effects.videoEffects, - editedMediaItem.effects.frameProcessorFactory, + firstEditedMediaItem.effects.videoEffects, + firstEditedMediaItem.effects.frameProcessorFactory, encoderFactory, muxerWrapper, /* errorConsumer= */ this::onTransformationError, @@ -510,10 +513,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; && !muxerWrapper.supportsSampleMimeType(inputFormat.sampleMimeType)) { return true; } - if (editedMediaItem.flattenForSlowMotion && isSlowMotion(inputFormat)) { + if (firstEditedMediaItem.flattenForSlowMotion && isSlowMotion(inputFormat)) { return true; } - if (!editedMediaItem.effects.audioProcessors.isEmpty()) { + if (!firstEditedMediaItem.effects.audioProcessors.isEmpty()) { return true; } if (generateSilentAudio) { @@ -539,7 +542,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private boolean shouldTranscodeVideo( Format inputFormat, long streamStartPositionUs, long streamOffsetUs) { if ((streamStartPositionUs - streamOffsetUs) != 0 - && !editedMediaItem.mediaItem.clippingConfiguration.startsAtKeyFrame) { + && !firstEditedMediaItem.mediaItem.clippingConfiguration.startsAtKeyFrame) { return true; } if (encoderFactory.videoNeedsEncoding()) { @@ -561,8 +564,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } // TODO(b/265927935): consider generalizing this logic. - for (int i = 0; i < editedMediaItem.effects.videoEffects.size(); i++) { - Effect videoEffect = editedMediaItem.effects.videoEffects.get(i); + for (int i = 0; i < firstEditedMediaItem.effects.videoEffects.size(); i++) { + Effect videoEffect = firstEditedMediaItem.effects.videoEffects.get(i); if (videoEffect instanceof Presentation) { Presentation presentation = (Presentation) videoEffect; // The decoder rotates encoded frames for display by inputFormat.rotationDegrees.