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();