diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/CompositeAssetLoader.java b/libraries/transformer/src/main/java/androidx/media3/transformer/CompositeAssetLoader.java index 0d7c542cc3..45d828de31 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/CompositeAssetLoader.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/CompositeAssetLoader.java @@ -17,6 +17,7 @@ package androidx.media3.transformer; import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkNotNull; +import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkStateNotNull; import static androidx.media3.transformer.Transformer.PROGRESS_STATE_AVAILABLE; import static androidx.media3.transformer.Transformer.PROGRESS_STATE_NOT_STARTED; @@ -52,8 +53,21 @@ import java.util.concurrent.atomic.AtomicInteger; private final AssetLoader.Factory assetLoaderFactory; private final HandlerWrapper handler; private final Listener compositeAssetLoaderListener; + /** + * A mapping from track types to {@link SampleConsumer} instances. + * + *

This map never contains more than 2 entries, as the only track types allowed are audio and + * video. + */ private final Map sampleConsumersByTrackType; + /** + * A mapping from track types to {@link OnMediaItemChangedListener} instances. + * + *

This map never contains more than 2 entries, as the only track types allowed are audio and + * video. + */ private final Map mediaItemChangedListenersByTrackType; + private final ImmutableList.Builder processedInputsBuilder; private final AtomicInteger nonEndedTracks; @@ -133,10 +147,11 @@ import java.util.concurrent.atomic.AtomicInteger; * * @param onMediaItemChangedListener The {@link OnMediaItemChangedListener}. * @param trackType The {@link C.TrackType} for which to listen to {@link MediaItem} change - * events. + * events. Must be {@link C#TRACK_TYPE_AUDIO} or {@link C#TRACK_TYPE_VIDEO}. */ public void addOnMediaItemChangedListener( OnMediaItemChangedListener onMediaItemChangedListener, @C.TrackType int trackType) { + checkArgument(trackType == C.TRACK_TYPE_AUDIO || trackType == C.TRACK_TYPE_VIDEO); checkArgument(mediaItemChangedListenersByTrackType.get(trackType) == null); mediaItemChangedListenersByTrackType.put(trackType, onMediaItemChangedListener); } @@ -172,19 +187,21 @@ import java.util.concurrent.atomic.AtomicInteger; long streamStartPositionUs, long streamOffsetUs) throws ExportException { - int trackType = MimeTypes.getTrackType(format.sampleMimeType); + // Consider image as video because image inputs are fed to the VideoSamplePipeline. + int trackType = + MimeTypes.isAudio(format.sampleMimeType) ? C.TRACK_TYPE_AUDIO : C.TRACK_TYPE_VIDEO; SampleConsumer sampleConsumer; if (currentMediaItemIndex.get() == 0) { - boolean addAudioTrack = + boolean addForcedAudioTrack = forceAudioTrack && nonEndedTracks.get() == 1 && trackType == C.TRACK_TYPE_VIDEO; - int trackCount = nonEndedTracks.get() + (addAudioTrack ? 1 : 0); + int trackCount = nonEndedTracks.get() + (addForcedAudioTrack ? 1 : 0); compositeAssetLoaderListener.onTrackCount(trackCount); sampleConsumer = new SampleConsumerWrapper( compositeAssetLoaderListener.onTrackAdded( format, supportedOutputTypes, streamStartPositionUs, streamOffsetUs)); sampleConsumersByTrackType.put(trackType, sampleConsumer); - if (addAudioTrack) { + if (addForcedAudioTrack) { Format firstAudioFormat = new Format.Builder() .setSampleMimeType(MimeTypes.AUDIO_AAC) @@ -201,6 +218,14 @@ import java.util.concurrent.atomic.AtomicInteger; sampleConsumersByTrackType.put(C.TRACK_TYPE_AUDIO, audioSampleConsumer); } } else { + // TODO(b/270533049): Remove the check below when implementing blank video frames generation. + boolean videoTrackDisappeared = + nonEndedTracks.get() == 1 + && trackType == C.TRACK_TYPE_AUDIO + && sampleConsumersByTrackType.size() == 2; + checkState( + !videoTrackDisappeared, + "Inputs with no video track are not supported when the output contains a video track"); sampleConsumer = checkStateNotNull( sampleConsumersByTrackType.get(trackType), diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerInternal.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerInternal.java index 2dcda79ca4..4ee5dd01f2 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerInternal.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerInternal.java @@ -367,7 +367,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; long streamStartPositionUs, long streamOffsetUs) throws ExportException { - int trackType = MimeTypes.getTrackType(firstInputFormat.sampleMimeType); if (!trackAdded) { // Call setTrackCount() methods here so that they are called from the same thread as the // MuxerWrapper and FallbackListener methods called when building the sample pipelines. @@ -383,6 +382,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; firstInputFormat, supportedOutputTypes, streamStartPositionUs, streamOffsetUs), streamStartPositionUs, streamOffsetUs); + // Consider image as video because image inputs are fed to the VideoSamplePipeline. + int trackType = + MimeTypes.isAudio(firstInputFormat.sampleMimeType) + ? C.TRACK_TYPE_AUDIO + : C.TRACK_TYPE_VIDEO; compositeAssetLoader.addOnMediaItemChangedListener(samplePipeline, trackType); internalHandler.obtainMessage(MSG_REGISTER_SAMPLE_PIPELINE, samplePipeline).sendToTarget();