From 104cfc322c095b40f88e705eb4a6c2f029bacdd6 Mon Sep 17 00:00:00 2001 From: kimvde Date: Thu, 30 Mar 2023 16:38:46 +0000 Subject: [PATCH] Clarify and fix SequenceAssetLoader threading PiperOrigin-RevId: 520663415 --- .../transformer/SequenceAssetLoader.java | 117 +++++++++--------- 1 file changed, 61 insertions(+), 56 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/SequenceAssetLoader.java b/libraries/transformer/src/main/java/androidx/media3/transformer/SequenceAssetLoader.java index 638ba4baf7..5c3e6cbf76 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/SequenceAssetLoader.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/SequenceAssetLoader.java @@ -40,7 +40,6 @@ import com.google.common.collect.ImmutableMap; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; /** @@ -88,8 +87,6 @@ import java.util.concurrent.atomic.AtomicInteger; private boolean decodeAudio; private boolean decodeVideo; private long totalDurationUs; - private long maxSequenceDurationUs; - private boolean isMaxSequenceDurationUsFinal; private int sequenceLoopCount; private boolean audioLoopingEnded; private boolean videoLoopingEnded; @@ -97,6 +94,8 @@ import java.util.concurrent.atomic.AtomicInteger; private boolean released; private volatile long currentAssetDurationUs; + private volatile long maxSequenceDurationUs; + private volatile boolean isMaxSequenceDurationUsFinal; public SequenceAssetLoader( EditedMediaItemSequence sequence, @@ -112,7 +111,7 @@ import java.util.concurrent.atomic.AtomicInteger; sequenceAssetLoaderListener = listener; handler = clock.createHandler(looper, /* callback= */ null); sampleConsumersByTrackType = new HashMap<>(); - mediaItemChangedListenersByTrackType = new ConcurrentHashMap<>(); + mediaItemChangedListenersByTrackType = new HashMap<>(); processedInputsBuilder = new ImmutableList.Builder<>(); nonEndedTracks = new AtomicInteger(); isCurrentAssetFirstAsset = true; @@ -124,6 +123,8 @@ import java.util.concurrent.atomic.AtomicInteger; this.currentAssetLoader = currentAssetLoader; } + // Methods called from TransformerInternal thread. + @Override public void start() { currentAssetLoader.start(); @@ -172,12 +173,27 @@ import java.util.concurrent.atomic.AtomicInteger; } } + private void addCurrentProcessedInput() { + if ((sequenceLoopCount * editedMediaItems.size() + currentMediaItemIndex) + >= processedInputsSize) { + MediaItem mediaItem = editedMediaItems.get(currentMediaItemIndex).mediaItem; + ImmutableMap decoders = currentAssetLoader.getDecoderNames(); + processedInputsBuilder.add( + new ExportResult.ProcessedInput( + mediaItem, decoders.get(C.TRACK_TYPE_AUDIO), decoders.get(C.TRACK_TYPE_VIDEO))); + processedInputsSize++; + } + } + + // Methods called from AssetLoader threads. + /** * Adds an {@link OnMediaItemChangedListener} for the given track type. * *

There can't be more than one {@link OnMediaItemChangedListener} for the same track type. * - *

Must always be called from the same thread. This thread can be any thread. + *

Must be called from the thread used by the current {@link AssetLoader} to pass data to the + * {@link SampleConsumer}. * * @param onMediaItemChangedListener The {@link OnMediaItemChangedListener}. * @param trackType The {@link C.TrackType} for which to listen to {@link MediaItem} change @@ -190,43 +206,6 @@ import java.util.concurrent.atomic.AtomicInteger; mediaItemChangedListenersByTrackType.put(trackType, onMediaItemChangedListener); } - /** - * Sets the maximum {@link EditedMediaItemSequence} duration in the {@link Composition}. - * - *

The duration passed is the current maximum duration. This method can be called multiple - * times as this duration increases. Indeed, a sequence duration will increase during an export - * when a new {@link MediaItem} is loaded, which can increase the maximum sequence duration. - * - *

Must be called from the thread used by the current {@link AssetLoader} to pass data to the - * {@link SampleConsumer}. - * - * @param maxSequenceDurationUs The current maximum sequence duration, in microseconds. - * @param isFinal Whether the duration passed is final. Setting this value to {@code true} means - * that the duration passed will not change anymore during the entire export. - */ - public void setMaxSequenceDurationUs(long maxSequenceDurationUs, boolean isFinal) { - this.maxSequenceDurationUs = maxSequenceDurationUs; - isMaxSequenceDurationUsFinal = isFinal; - } - - // AssetLoader.Listener implementation. - - @Override - public void onDurationUs(long durationUs) { - checkArgument( - durationUs != C.TIME_UNSET || currentMediaItemIndex == editedMediaItems.size() - 1, - "Could not retrieve required duration for EditedMediaItem " + currentMediaItemIndex); - currentAssetDurationUs = durationUs; - if (editedMediaItems.size() == 1 && !isLooping) { - sequenceAssetLoaderListener.onDurationUs(durationUs); - } - } - - @Override - public void onTrackCount(int trackCount) { - nonEndedTracks.set(trackCount); - } - @Override public boolean onTrackAdded( Format inputFormat, @@ -321,11 +300,6 @@ import java.util.concurrent.atomic.AtomicInteger; return sampleConsumer; } - @Override - public void onError(ExportException exportException) { - sequenceAssetLoaderListener.onError(exportException); - } - private void onMediaItemChanged(int trackType, @Nullable Format format) { @Nullable OnMediaItemChangedListener onMediaItemChangedListener = @@ -340,18 +314,49 @@ import java.util.concurrent.atomic.AtomicInteger; /* isLast= */ currentMediaItemIndex == editedMediaItems.size() - 1); } - private void addCurrentProcessedInput() { - if ((sequenceLoopCount * editedMediaItems.size() + currentMediaItemIndex) - >= processedInputsSize) { - MediaItem mediaItem = editedMediaItems.get(currentMediaItemIndex).mediaItem; - ImmutableMap decoders = currentAssetLoader.getDecoderNames(); - processedInputsBuilder.add( - new ExportResult.ProcessedInput( - mediaItem, decoders.get(C.TRACK_TYPE_AUDIO), decoders.get(C.TRACK_TYPE_VIDEO))); - processedInputsSize++; + // Methods called from any thread. + + /** + * Sets the maximum {@link EditedMediaItemSequence} duration in the {@link Composition}. + * + *

The duration passed is the current maximum duration. This method can be called multiple + * times as this duration increases. Indeed, a sequence duration will increase during an export + * when a new {@link MediaItem} is loaded, which can increase the maximum sequence duration. + * + *

Can be called from any thread. + * + * @param maxSequenceDurationUs The current maximum sequence duration, in microseconds. + * @param isFinal Whether the duration passed is final. Setting this value to {@code true} means + * that the duration passed will not change anymore during the entire export. + */ + public void setMaxSequenceDurationUs(long maxSequenceDurationUs, boolean isFinal) { + this.maxSequenceDurationUs = maxSequenceDurationUs; + isMaxSequenceDurationUsFinal = isFinal; + } + + @Override + public void onDurationUs(long durationUs) { + checkArgument( + durationUs != C.TIME_UNSET || currentMediaItemIndex == editedMediaItems.size() - 1, + "Could not retrieve required duration for EditedMediaItem " + currentMediaItemIndex); + currentAssetDurationUs = durationUs; + if (editedMediaItems.size() == 1 && !isLooping) { + sequenceAssetLoaderListener.onDurationUs(durationUs); } } + @Override + public void onTrackCount(int trackCount) { + nonEndedTracks.set(trackCount); + } + + @Override + public void onError(ExportException exportException) { + sequenceAssetLoaderListener.onError(exportException); + } + + // Classes accessed from AssetLoader threads. + private final class SampleConsumerWrapper implements SampleConsumer { private final SampleConsumer sampleConsumer;