Throw when the video track disappears during export

Throw when the output has a video track but the current MediaItem in
the sequence doesn't have any video.

PiperOrigin-RevId: 512004463
This commit is contained in:
kimvde 2023-02-24 09:08:37 +00:00 committed by tonihei
parent 672d7494f2
commit c2fd7339e1
2 changed files with 35 additions and 6 deletions

View File

@ -17,6 +17,7 @@ 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.checkNotNull; 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.common.util.Assertions.checkStateNotNull;
import static androidx.media3.transformer.Transformer.PROGRESS_STATE_AVAILABLE; import static androidx.media3.transformer.Transformer.PROGRESS_STATE_AVAILABLE;
import static androidx.media3.transformer.Transformer.PROGRESS_STATE_NOT_STARTED; 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 AssetLoader.Factory assetLoaderFactory;
private final HandlerWrapper handler; private final HandlerWrapper handler;
private final Listener compositeAssetLoaderListener; private final Listener compositeAssetLoaderListener;
/**
* A mapping from track types to {@link SampleConsumer} instances.
*
* <p>This map never contains more than 2 entries, as the only track types allowed are audio and
* video.
*/
private final Map<Integer, SampleConsumer> sampleConsumersByTrackType; private final Map<Integer, SampleConsumer> sampleConsumersByTrackType;
/**
* A mapping from track types to {@link OnMediaItemChangedListener} instances.
*
* <p>This map never contains more than 2 entries, as the only track types allowed are audio and
* video.
*/
private final Map<Integer, OnMediaItemChangedListener> mediaItemChangedListenersByTrackType; private final Map<Integer, OnMediaItemChangedListener> mediaItemChangedListenersByTrackType;
private final ImmutableList.Builder<ExportResult.ProcessedInput> processedInputsBuilder; private final ImmutableList.Builder<ExportResult.ProcessedInput> processedInputsBuilder;
private final AtomicInteger nonEndedTracks; private final AtomicInteger nonEndedTracks;
@ -133,10 +147,11 @@ import java.util.concurrent.atomic.AtomicInteger;
* *
* @param onMediaItemChangedListener The {@link OnMediaItemChangedListener}. * @param onMediaItemChangedListener The {@link OnMediaItemChangedListener}.
* @param trackType The {@link C.TrackType} for which to listen to {@link MediaItem} change * @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( public void addOnMediaItemChangedListener(
OnMediaItemChangedListener onMediaItemChangedListener, @C.TrackType int trackType) { OnMediaItemChangedListener onMediaItemChangedListener, @C.TrackType int trackType) {
checkArgument(trackType == C.TRACK_TYPE_AUDIO || trackType == C.TRACK_TYPE_VIDEO);
checkArgument(mediaItemChangedListenersByTrackType.get(trackType) == null); checkArgument(mediaItemChangedListenersByTrackType.get(trackType) == null);
mediaItemChangedListenersByTrackType.put(trackType, onMediaItemChangedListener); mediaItemChangedListenersByTrackType.put(trackType, onMediaItemChangedListener);
} }
@ -172,19 +187,21 @@ import java.util.concurrent.atomic.AtomicInteger;
long streamStartPositionUs, long streamStartPositionUs,
long streamOffsetUs) long streamOffsetUs)
throws ExportException { 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; SampleConsumer sampleConsumer;
if (currentMediaItemIndex.get() == 0) { if (currentMediaItemIndex.get() == 0) {
boolean addAudioTrack = boolean addForcedAudioTrack =
forceAudioTrack && nonEndedTracks.get() == 1 && trackType == C.TRACK_TYPE_VIDEO; 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); compositeAssetLoaderListener.onTrackCount(trackCount);
sampleConsumer = sampleConsumer =
new SampleConsumerWrapper( new SampleConsumerWrapper(
compositeAssetLoaderListener.onTrackAdded( compositeAssetLoaderListener.onTrackAdded(
format, supportedOutputTypes, streamStartPositionUs, streamOffsetUs)); format, supportedOutputTypes, streamStartPositionUs, streamOffsetUs));
sampleConsumersByTrackType.put(trackType, sampleConsumer); sampleConsumersByTrackType.put(trackType, sampleConsumer);
if (addAudioTrack) { if (addForcedAudioTrack) {
Format firstAudioFormat = Format firstAudioFormat =
new Format.Builder() new Format.Builder()
.setSampleMimeType(MimeTypes.AUDIO_AAC) .setSampleMimeType(MimeTypes.AUDIO_AAC)
@ -201,6 +218,14 @@ import java.util.concurrent.atomic.AtomicInteger;
sampleConsumersByTrackType.put(C.TRACK_TYPE_AUDIO, audioSampleConsumer); sampleConsumersByTrackType.put(C.TRACK_TYPE_AUDIO, audioSampleConsumer);
} }
} else { } 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 = sampleConsumer =
checkStateNotNull( checkStateNotNull(
sampleConsumersByTrackType.get(trackType), sampleConsumersByTrackType.get(trackType),

View File

@ -367,7 +367,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
long streamStartPositionUs, long streamStartPositionUs,
long streamOffsetUs) long streamOffsetUs)
throws ExportException { throws ExportException {
int trackType = MimeTypes.getTrackType(firstInputFormat.sampleMimeType);
if (!trackAdded) { if (!trackAdded) {
// Call setTrackCount() methods here so that they are called from the same thread as the // 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. // 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), firstInputFormat, supportedOutputTypes, streamStartPositionUs, streamOffsetUs),
streamStartPositionUs, streamStartPositionUs,
streamOffsetUs); 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); compositeAssetLoader.addOnMediaItemChangedListener(samplePipeline, trackType);
internalHandler.obtainMessage(MSG_REGISTER_SAMPLE_PIPELINE, samplePipeline).sendToTarget(); internalHandler.obtainMessage(MSG_REGISTER_SAMPLE_PIPELINE, samplePipeline).sendToTarget();