Move generateSilentAudio to Composition

Also rename to forceAudioTrack

PiperOrigin-RevId: 510394620
This commit is contained in:
kimvde 2023-02-17 12:06:47 +00:00 committed by tonihei
parent 0c17605ff0
commit 5806414fba
9 changed files with 135 additions and 118 deletions

View File

@ -62,7 +62,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
public static final String SHOULD_REMOVE_AUDIO = "should_remove_audio"; public static final String SHOULD_REMOVE_AUDIO = "should_remove_audio";
public static final String SHOULD_REMOVE_VIDEO = "should_remove_video"; public static final String SHOULD_REMOVE_VIDEO = "should_remove_video";
public static final String SHOULD_FLATTEN_FOR_SLOW_MOTION = "should_flatten_for_slow_motion"; public static final String SHOULD_FLATTEN_FOR_SLOW_MOTION = "should_flatten_for_slow_motion";
public static final String GENERATE_SILENT_AUDIO = "generate_silent_audio"; public static final String FORCE_AUDIO_TRACK = "force_audio_track";
public static final String AUDIO_MIME_TYPE = "audio_mime_type"; public static final String AUDIO_MIME_TYPE = "audio_mime_type";
public static final String VIDEO_MIME_TYPE = "video_mime_type"; public static final String VIDEO_MIME_TYPE = "video_mime_type";
public static final String RESOLUTION_HEIGHT = "resolution_height"; public static final String RESOLUTION_HEIGHT = "resolution_height";
@ -213,7 +213,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
private @MonotonicNonNull CheckBox removeAudioCheckbox; private @MonotonicNonNull CheckBox removeAudioCheckbox;
private @MonotonicNonNull CheckBox removeVideoCheckbox; private @MonotonicNonNull CheckBox removeVideoCheckbox;
private @MonotonicNonNull CheckBox flattenForSlowMotionCheckbox; private @MonotonicNonNull CheckBox flattenForSlowMotionCheckbox;
private @MonotonicNonNull CheckBox generateSilentAudioCheckbox; private @MonotonicNonNull CheckBox forceAudioTrackCheckbox;
private @MonotonicNonNull Spinner audioMimeSpinner; private @MonotonicNonNull Spinner audioMimeSpinner;
private @MonotonicNonNull Spinner videoMimeSpinner; private @MonotonicNonNull Spinner videoMimeSpinner;
private @MonotonicNonNull Spinner resolutionHeightSpinner; private @MonotonicNonNull Spinner resolutionHeightSpinner;
@ -274,7 +274,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
flattenForSlowMotionCheckbox = findViewById(R.id.flatten_for_slow_motion_checkbox); flattenForSlowMotionCheckbox = findViewById(R.id.flatten_for_slow_motion_checkbox);
generateSilentAudioCheckbox = findViewById(R.id.generate_silent_audio_checkbox); forceAudioTrackCheckbox = findViewById(R.id.force_audio_track_checkbox);
ArrayAdapter<String> audioMimeAdapter = ArrayAdapter<String> audioMimeAdapter =
new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item); new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item);
@ -385,7 +385,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
"removeAudioCheckbox", "removeAudioCheckbox",
"removeVideoCheckbox", "removeVideoCheckbox",
"flattenForSlowMotionCheckbox", "flattenForSlowMotionCheckbox",
"generateSilentAudioCheckbox", "forceAudioTrackCheckbox",
"audioMimeSpinner", "audioMimeSpinner",
"videoMimeSpinner", "videoMimeSpinner",
"resolutionHeightSpinner", "resolutionHeightSpinner",
@ -405,7 +405,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
bundle.putBoolean(SHOULD_REMOVE_AUDIO, removeAudioCheckbox.isChecked()); bundle.putBoolean(SHOULD_REMOVE_AUDIO, removeAudioCheckbox.isChecked());
bundle.putBoolean(SHOULD_REMOVE_VIDEO, removeVideoCheckbox.isChecked()); bundle.putBoolean(SHOULD_REMOVE_VIDEO, removeVideoCheckbox.isChecked());
bundle.putBoolean(SHOULD_FLATTEN_FOR_SLOW_MOTION, flattenForSlowMotionCheckbox.isChecked()); bundle.putBoolean(SHOULD_FLATTEN_FOR_SLOW_MOTION, flattenForSlowMotionCheckbox.isChecked());
bundle.putBoolean(GENERATE_SILENT_AUDIO, generateSilentAudioCheckbox.isChecked()); bundle.putBoolean(FORCE_AUDIO_TRACK, forceAudioTrackCheckbox.isChecked());
String selectedAudioMimeType = String.valueOf(audioMimeSpinner.getSelectedItem()); String selectedAudioMimeType = String.valueOf(audioMimeSpinner.getSelectedItem());
if (!SAME_AS_INPUT_OPTION.equals(selectedAudioMimeType)) { if (!SAME_AS_INPUT_OPTION.equals(selectedAudioMimeType)) {
bundle.putString(AUDIO_MIME_TYPE, selectedAudioMimeType); bundle.putString(AUDIO_MIME_TYPE, selectedAudioMimeType);
@ -739,7 +739,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
@RequiresNonNull({ @RequiresNonNull({
"removeVideoCheckbox", "removeVideoCheckbox",
"generateSilentAudioCheckbox", "forceAudioTrackCheckbox",
"audioMimeSpinner", "audioMimeSpinner",
"videoMimeSpinner", "videoMimeSpinner",
"resolutionHeightSpinner", "resolutionHeightSpinner",
@ -761,7 +761,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
@RequiresNonNull({ @RequiresNonNull({
"removeAudioCheckbox", "removeAudioCheckbox",
"generateSilentAudioCheckbox", "forceAudioTrackCheckbox",
"audioMimeSpinner", "audioMimeSpinner",
"videoMimeSpinner", "videoMimeSpinner",
"resolutionHeightSpinner", "resolutionHeightSpinner",
@ -782,7 +782,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
} }
@RequiresNonNull({ @RequiresNonNull({
"generateSilentAudioCheckbox", "forceAudioTrackCheckbox",
"audioMimeSpinner", "audioMimeSpinner",
"videoMimeSpinner", "videoMimeSpinner",
"resolutionHeightSpinner", "resolutionHeightSpinner",
@ -794,7 +794,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
"selectVideoEffectsButton" "selectVideoEffectsButton"
}) })
private void enableTrackSpecificOptions(boolean isAudioEnabled, boolean isVideoEnabled) { private void enableTrackSpecificOptions(boolean isAudioEnabled, boolean isVideoEnabled) {
generateSilentAudioCheckbox.setEnabled(isVideoEnabled); forceAudioTrackCheckbox.setEnabled(isVideoEnabled);
audioMimeSpinner.setEnabled(isAudioEnabled); audioMimeSpinner.setEnabled(isAudioEnabled);
videoMimeSpinner.setEnabled(isVideoEnabled); videoMimeSpinner.setEnabled(isVideoEnabled);
resolutionHeightSpinner.setEnabled(isVideoEnabled); resolutionHeightSpinner.setEnabled(isVideoEnabled);

View File

@ -82,6 +82,7 @@ import androidx.media3.transformer.Composition;
import androidx.media3.transformer.DefaultEncoderFactory; import androidx.media3.transformer.DefaultEncoderFactory;
import androidx.media3.transformer.DefaultMuxer; import androidx.media3.transformer.DefaultMuxer;
import androidx.media3.transformer.EditedMediaItem; import androidx.media3.transformer.EditedMediaItem;
import androidx.media3.transformer.EditedMediaItemSequence;
import androidx.media3.transformer.Effects; import androidx.media3.transformer.Effects;
import androidx.media3.transformer.ExportException; import androidx.media3.transformer.ExportException;
import androidx.media3.transformer.ExportResult; import androidx.media3.transformer.ExportResult;
@ -99,6 +100,8 @@ import com.google.common.util.concurrent.ListenableFuture;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -226,9 +229,9 @@ public final class TransformerActivity extends AppCompatActivity {
MediaItem mediaItem = createMediaItem(bundle, uri); MediaItem mediaItem = createMediaItem(bundle, uri);
try { try {
Transformer transformer = createTransformer(bundle, filePath); Transformer transformer = createTransformer(bundle, filePath);
EditedMediaItem editedMediaItem = createEditedMediaItem(mediaItem, bundle); Composition composition = createComposition(mediaItem, bundle);
exportStopwatch.start(); exportStopwatch.start();
transformer.start(editedMediaItem, filePath); transformer.start(composition, filePath);
this.transformer = transformer; this.transformer = transformer;
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
throw new IllegalStateException(e); throw new IllegalStateException(e);
@ -300,13 +303,10 @@ public final class TransformerActivity extends AppCompatActivity {
requestBuilder.setHdrMode(bundle.getInt(ConfigurationActivity.HDR_MODE)); requestBuilder.setHdrMode(bundle.getInt(ConfigurationActivity.HDR_MODE));
transformerBuilder.setTransformationRequest(requestBuilder.build()); transformerBuilder.setTransformationRequest(requestBuilder.build());
transformerBuilder transformerBuilder.setEncoderFactory(
.experimentalSetGenerateSilentAudio( new DefaultEncoderFactory.Builder(this.getApplicationContext())
bundle.getBoolean(ConfigurationActivity.GENERATE_SILENT_AUDIO)) .setEnableFallback(bundle.getBoolean(ConfigurationActivity.ENABLE_FALLBACK))
.setEncoderFactory( .build());
new DefaultEncoderFactory.Builder(this.getApplicationContext())
.setEnableFallback(bundle.getBoolean(ConfigurationActivity.ENABLE_FALLBACK))
.build());
if (!bundle.getBoolean(ConfigurationActivity.ABORT_SLOW_EXPORT)) { if (!bundle.getBoolean(ConfigurationActivity.ABORT_SLOW_EXPORT)) {
transformerBuilder.setMuxerFactory( transformerBuilder.setMuxerFactory(
@ -357,23 +357,28 @@ public final class TransformerActivity extends AppCompatActivity {
"exportStopwatch", "exportStopwatch",
"progressViewGroup", "progressViewGroup",
}) })
private EditedMediaItem createEditedMediaItem(MediaItem mediaItem, @Nullable Bundle bundle) private Composition createComposition(MediaItem mediaItem, @Nullable Bundle bundle)
throws PackageManager.NameNotFoundException { throws PackageManager.NameNotFoundException {
EditedMediaItem.Builder editedMediaItemBuilder = new EditedMediaItem.Builder(mediaItem); EditedMediaItem.Builder editedMediaItemBuilder = new EditedMediaItem.Builder(mediaItem);
if (bundle == null) {
return editedMediaItemBuilder.build();
}
// For image inputs. Automatically ignored if input is audio/video. // For image inputs. Automatically ignored if input is audio/video.
editedMediaItemBuilder.setDurationUs(5_000_000).setFrameRate(30); editedMediaItemBuilder.setDurationUs(5_000_000).setFrameRate(30);
ImmutableList<AudioProcessor> audioProcessors = createAudioProcessorsFromBundle(bundle); boolean forceAudioTrack = false;
ImmutableList<Effect> videoEffects = createVideoEffectsFromBundle(bundle); if (bundle != null) {
return editedMediaItemBuilder ImmutableList<AudioProcessor> audioProcessors = createAudioProcessorsFromBundle(bundle);
.setRemoveAudio(bundle.getBoolean(ConfigurationActivity.SHOULD_REMOVE_AUDIO)) ImmutableList<Effect> videoEffects = createVideoEffectsFromBundle(bundle);
.setRemoveVideo(bundle.getBoolean(ConfigurationActivity.SHOULD_REMOVE_VIDEO)) editedMediaItemBuilder
.setFlattenForSlowMotion( .setRemoveAudio(bundle.getBoolean(ConfigurationActivity.SHOULD_REMOVE_AUDIO))
bundle.getBoolean(ConfigurationActivity.SHOULD_FLATTEN_FOR_SLOW_MOTION)) .setRemoveVideo(bundle.getBoolean(ConfigurationActivity.SHOULD_REMOVE_VIDEO))
.setEffects(new Effects(audioProcessors, videoEffects)) .setFlattenForSlowMotion(
.build(); bundle.getBoolean(ConfigurationActivity.SHOULD_FLATTEN_FOR_SLOW_MOTION))
.setEffects(new Effects(audioProcessors, videoEffects));
forceAudioTrack = bundle.getBoolean(ConfigurationActivity.FORCE_AUDIO_TRACK);
}
List<EditedMediaItem> editedMediaItems = new ArrayList<>();
editedMediaItems.add(editedMediaItemBuilder.build());
List<EditedMediaItemSequence> sequences = new ArrayList<>();
sequences.add(new EditedMediaItemSequence(editedMediaItems));
return new Composition(sequences, Effects.EMPTY, forceAudioTrack);
} }
private ImmutableList<AudioProcessor> createAudioProcessorsFromBundle(Bundle bundle) { private ImmutableList<AudioProcessor> createAudioProcessorsFromBundle(Bundle bundle) {

View File

@ -116,9 +116,9 @@
android:layout_weight="1"> android:layout_weight="1">
<TextView <TextView
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:text="@string/generate_silent_audio" /> android:text="@string/force_audio_track" />
<CheckBox <CheckBox
android:id="@+id/generate_silent_audio_checkbox" android:id="@+id/force_audio_track_checkbox"
android:layout_gravity="end" /> android:layout_gravity="end" />
</TableRow> </TableRow>
<TableRow <TableRow

View File

@ -67,7 +67,7 @@
<string name="permission_denied">Permission Denied</string> <string name="permission_denied">Permission Denied</string>
<string name="hide_input_video">Hide input video</string> <string name="hide_input_video">Hide input video</string>
<string name="show_input_video">Show input video</string> <string name="show_input_video">Show input video</string>
<string name="generate_silent_audio">Generate silent audio</string> <string name="force_audio_track">Force audio track</string>
<string name="overlay_alpha">Alpha</string> <string name="overlay_alpha">Alpha</string>
<string name="overlay_uri">Uri</string> <string name="overlay_uri">Uri</string>
<string name="bitmap_overlay_settings">Specify bitmap overlay settings</string> <string name="bitmap_overlay_settings">Specify bitmap overlay settings</string>

View File

@ -66,16 +66,15 @@ import org.checkerframework.dataflow.qual.Pure;
TransformationRequest transformationRequest, TransformationRequest transformationRequest,
boolean flattenForSlowMotion, boolean flattenForSlowMotion,
ImmutableList<AudioProcessor> audioProcessors, ImmutableList<AudioProcessor> audioProcessors,
long generateSilentAudioDurationUs, long forceAudioTrackDurationUs,
Codec.EncoderFactory encoderFactory, Codec.EncoderFactory encoderFactory,
MuxerWrapper muxerWrapper, MuxerWrapper muxerWrapper,
FallbackListener fallbackListener) FallbackListener fallbackListener)
throws ExportException { throws ExportException {
super(firstInputFormat, streamStartPositionUs, muxerWrapper); super(firstInputFormat, streamStartPositionUs, muxerWrapper);
if (generateSilentAudioDurationUs != C.TIME_UNSET) { if (forceAudioTrackDurationUs != C.TIME_UNSET) {
silentAudioGenerator = silentAudioGenerator = new SilentAudioGenerator(firstInputFormat, forceAudioTrackDurationUs);
new SilentAudioGenerator(firstInputFormat, generateSilentAudioDurationUs);
} else { } else {
silentAudioGenerator = null; silentAudioGenerator = null;
} }

View File

@ -18,6 +18,7 @@ package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkArgument;
import androidx.media3.common.MediaItem; import androidx.media3.common.MediaItem;
import androidx.media3.common.audio.AudioProcessor;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.util.List; import java.util.List;
@ -40,16 +41,57 @@ public final class Composition {
public final ImmutableList<EditedMediaItemSequence> sequences; public final ImmutableList<EditedMediaItemSequence> sequences;
/** The {@link Effects} to apply to the composition. */ /** The {@link Effects} to apply to the composition. */
public final Effects effects; public final Effects effects;
/**
* Whether the output file should always contain an audio track.
*
* <ul>
* <li>If {@code false}:
* <ul>
* <li>If the {@link Composition} export doesn't produce any audio at timestamp 0, the
* output won't contain any audio, and audio tracks from the {@link MediaItem}
* instances in the {@link Composition} will be ignored.
* <li>If the {@link Composition} export produces audio at timestamp 0, the output will
* contain an audio track.
* </ul>
* <li>If {@code true}, the output will always contain an audio track.
* </ul>
*
* If the output contains an audio track, silent audio will be generated for the segments where
* the {@link Composition} export doesn't produce any audio.
*
* <p>The MIME type of the output's audio track can be set using {@link
* TransformationRequest.Builder#setAudioMimeType(String)}. The sample rate and channel count can
* be set by passing relevant {@link AudioProcessor} instances to the {@link Composition}.
*
* <p>This parameter is experimental and may be removed or changed without warning.
*/
public final boolean experimentalForceAudioTrack;
/**
* Creates an instance.
*
* <p>This is equivalent to calling {@link Composition#Composition(List, Effects, boolean)} with
* {@link #experimentalForceAudioTrack} set to {@code false}.
*/
public Composition(List<EditedMediaItemSequence> sequences, Effects effects) {
this(sequences, effects, /* experimentalForceAudioTrack= */ false);
}
/** /**
* Creates an instance. * Creates an instance.
* *
* @param sequences The {@link #sequences}. * @param sequences The {@link #sequences}.
* @param effects The {@link #effects}. * @param effects The {@link #effects}.
* @param experimentalForceAudioTrack Whether to {@linkplain #experimentalForceAudioTrack always
* add an audio track in the output}.
*/ */
public Composition(List<EditedMediaItemSequence> sequences, Effects effects) { public Composition(
List<EditedMediaItemSequence> sequences,
Effects effects,
boolean experimentalForceAudioTrack) {
checkArgument(!sequences.isEmpty()); checkArgument(!sequences.isEmpty());
this.sequences = ImmutableList.copyOf(sequences); this.sequences = ImmutableList.copyOf(sequences);
this.effects = effects; this.effects = effects;
this.experimentalForceAudioTrack = experimentalForceAudioTrack;
} }
} }

View File

@ -33,7 +33,6 @@ import androidx.media3.common.MediaLibraryInfo;
import androidx.media3.common.MimeTypes; import androidx.media3.common.MimeTypes;
import androidx.media3.common.VideoFrameProcessor; import androidx.media3.common.VideoFrameProcessor;
import androidx.media3.common.audio.AudioProcessor; import androidx.media3.common.audio.AudioProcessor;
import androidx.media3.common.audio.SonicAudioProcessor;
import androidx.media3.common.util.Clock; import androidx.media3.common.util.Clock;
import androidx.media3.common.util.HandlerWrapper; import androidx.media3.common.util.HandlerWrapper;
import androidx.media3.common.util.ListenerSet; import androidx.media3.common.util.ListenerSet;
@ -86,7 +85,6 @@ public final class Transformer {
private boolean removeVideo; private boolean removeVideo;
private boolean flattenForSlowMotion; private boolean flattenForSlowMotion;
private boolean transmux; private boolean transmux;
private boolean generateSilentAudio;
private ListenerSet<Transformer.Listener> listeners; private ListenerSet<Transformer.Listener> listeners;
private AssetLoader.@MonotonicNonNull Factory assetLoaderFactory; private AssetLoader.@MonotonicNonNull Factory assetLoaderFactory;
private VideoFrameProcessor.Factory videoFrameProcessorFactory; private VideoFrameProcessor.Factory videoFrameProcessorFactory;
@ -123,7 +121,6 @@ public final class Transformer {
this.videoEffects = transformer.videoEffects; this.videoEffects = transformer.videoEffects;
this.removeAudio = transformer.removeAudio; this.removeAudio = transformer.removeAudio;
this.removeVideo = transformer.removeVideo; this.removeVideo = transformer.removeVideo;
this.generateSilentAudio = transformer.generateSilentAudio;
this.listeners = transformer.listeners; this.listeners = transformer.listeners;
this.assetLoaderFactory = transformer.assetLoaderFactory; this.assetLoaderFactory = transformer.assetLoaderFactory;
this.videoFrameProcessorFactory = transformer.videoFrameProcessorFactory; this.videoFrameProcessorFactory = transformer.videoFrameProcessorFactory;
@ -395,39 +392,6 @@ public final class Transformer {
return this; return this;
} }
/**
* Sets whether to generate silent audio for the output file, if there is no audio available.
*
* <p>This method is experimental and may be removed or changed without warning.
*
* <p>To replace existing audio with silence, {@linkplain
* EditedMediaItem.Builder#setRemoveAudio(boolean) remove the audio} from the {@link
* EditedMediaItem} to export.
*
* <p>Audio properties/format:
*
* <ul>
* <li>Duration will match duration of the input media.
* <li>Sample mime type will match {@link TransformationRequest#audioMimeType}, or {@link
* MimeTypes#AUDIO_AAC} if {@code null}.
* <li>Sample rate will be {@code 44100} Hz. This can be modified by creating a {@link
* SonicAudioProcessor}, setting its {@linkplain
* SonicAudioProcessor#setOutputSampleRateHz(int) sample rate}, and passing it to the
* {@link EditedMediaItem} used to start the export.
* <li>Channel count will be {@code 2}. This can be modified by implementing a custom {@link
* AudioProcessor} and passing it to the {@link EditedMediaItem} used to start the export.
* </ul>
*
* @param generateSilentAudio Whether to generate silent audio for the output file if there is
* no audio track.
* @return This builder.
*/
@CanIgnoreReturnValue
public Builder experimentalSetGenerateSilentAudio(boolean generateSilentAudio) {
this.generateSilentAudio = generateSilentAudio;
return this;
}
/** /**
* Builds a {@link Transformer} instance. * Builds a {@link Transformer} instance.
* *
@ -461,7 +425,6 @@ public final class Transformer {
removeVideo, removeVideo,
flattenForSlowMotion, flattenForSlowMotion,
transmux, transmux,
generateSilentAudio,
listeners, listeners,
assetLoaderFactory, assetLoaderFactory,
videoFrameProcessorFactory, videoFrameProcessorFactory,
@ -620,7 +583,6 @@ public final class Transformer {
private final boolean removeVideo; private final boolean removeVideo;
private final boolean flattenForSlowMotion; private final boolean flattenForSlowMotion;
private final boolean transmux; private final boolean transmux;
private final boolean generateSilentAudio;
private final ListenerSet<Transformer.Listener> listeners; private final ListenerSet<Transformer.Listener> listeners;
private final AssetLoader.Factory assetLoaderFactory; private final AssetLoader.Factory assetLoaderFactory;
private final VideoFrameProcessor.Factory videoFrameProcessorFactory; private final VideoFrameProcessor.Factory videoFrameProcessorFactory;
@ -641,7 +603,6 @@ public final class Transformer {
boolean removeVideo, boolean removeVideo,
boolean flattenForSlowMotion, boolean flattenForSlowMotion,
boolean transmux, boolean transmux,
boolean generateSilentAudio,
ListenerSet<Listener> listeners, ListenerSet<Listener> listeners,
AssetLoader.Factory assetLoaderFactory, AssetLoader.Factory assetLoaderFactory,
VideoFrameProcessor.Factory videoFrameProcessorFactory, VideoFrameProcessor.Factory videoFrameProcessorFactory,
@ -659,7 +620,6 @@ public final class Transformer {
this.removeVideo = removeVideo; this.removeVideo = removeVideo;
this.flattenForSlowMotion = flattenForSlowMotion; this.flattenForSlowMotion = flattenForSlowMotion;
this.transmux = transmux; this.transmux = transmux;
this.generateSilentAudio = generateSilentAudio;
this.listeners = listeners; this.listeners = listeners;
this.assetLoaderFactory = assetLoaderFactory; this.assetLoaderFactory = assetLoaderFactory;
this.videoFrameProcessorFactory = videoFrameProcessorFactory; this.videoFrameProcessorFactory = videoFrameProcessorFactory;
@ -779,7 +739,6 @@ public final class Transformer {
path, path,
transformationRequest, transformationRequest,
transmux, transmux,
generateSilentAudio,
assetLoaderFactory, assetLoaderFactory,
encoderFactory, encoderFactory,
muxerFactory, muxerFactory,

View File

@ -100,7 +100,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private final ConditionVariable transformerConditionVariable; private final ConditionVariable transformerConditionVariable;
private final ExportResult.Builder exportResultBuilder; private final ExportResult.Builder exportResultBuilder;
private boolean generateSilentAudio; private boolean forceAudioTrack;
private boolean isDrainingPipelines; private boolean isDrainingPipelines;
private @Transformer.ProgressState int progressState; private @Transformer.ProgressState int progressState;
private @MonotonicNonNull RuntimeException cancelException; private @MonotonicNonNull RuntimeException cancelException;
@ -113,7 +113,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
String outputPath, String outputPath,
TransformationRequest transformationRequest, TransformationRequest transformationRequest,
boolean transmux, boolean transmux,
boolean generateSilentAudio,
AssetLoader.Factory assetLoaderFactory, AssetLoader.Factory assetLoaderFactory,
Codec.EncoderFactory encoderFactory, Codec.EncoderFactory encoderFactory,
Muxer.Factory muxerFactory, Muxer.Factory muxerFactory,
@ -124,7 +123,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
Clock clock) { Clock clock) {
this.context = context; this.context = context;
this.transformationRequest = transformationRequest; this.transformationRequest = transformationRequest;
this.generateSilentAudio = generateSilentAudio; this.forceAudioTrack = composition.experimentalForceAudioTrack;
this.encoderFactory = new CapturingEncoderFactory(encoderFactory); this.encoderFactory = new CapturingEncoderFactory(encoderFactory);
this.listener = listener; this.listener = listener;
this.applicationHandler = applicationHandler; this.applicationHandler = applicationHandler;
@ -366,11 +365,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
throws ExportException { throws ExportException {
int trackType = MimeTypes.getTrackType(firstInputFormat.sampleMimeType); int trackType = MimeTypes.getTrackType(firstInputFormat.sampleMimeType);
if (!trackAdded) { if (!trackAdded) {
if (generateSilentAudio) { if (forceAudioTrack) {
if (trackCount.get() == 1 && trackType == C.TRACK_TYPE_VIDEO) { if (trackCount.get() == 1 && trackType == C.TRACK_TYPE_VIDEO) {
trackCount.incrementAndGet(); trackCount.incrementAndGet();
} else { } else {
generateSilentAudio = false; forceAudioTrack = false;
} }
} }
@ -387,7 +386,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
compositeAssetLoader.addOnMediaItemChangedListener(samplePipeline, trackType); compositeAssetLoader.addOnMediaItemChangedListener(samplePipeline, trackType);
internalHandler.obtainMessage(MSG_REGISTER_SAMPLE_PIPELINE, samplePipeline).sendToTarget(); internalHandler.obtainMessage(MSG_REGISTER_SAMPLE_PIPELINE, samplePipeline).sendToTarget();
if (generateSilentAudio) { if (forceAudioTrack) {
Format silentAudioFormat = Format silentAudioFormat =
new Format.Builder() new Format.Builder()
.setSampleMimeType(MimeTypes.AUDIO_AAC) .setSampleMimeType(MimeTypes.AUDIO_AAC)
@ -479,7 +478,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
transformationRequest, transformationRequest,
firstEditedMediaItem.flattenForSlowMotion, firstEditedMediaItem.flattenForSlowMotion,
firstEditedMediaItem.effects.audioProcessors, firstEditedMediaItem.effects.audioProcessors,
generateSilentAudio ? durationUs : C.TIME_UNSET, forceAudioTrack ? durationUs : C.TIME_UNSET,
encoderFactory, encoderFactory,
muxerWrapper, muxerWrapper,
fallbackListener); fallbackListener);
@ -525,7 +524,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
if (!firstEditedMediaItem.effects.audioProcessors.isEmpty()) { if (!firstEditedMediaItem.effects.audioProcessors.isEmpty()) {
return true; return true;
} }
if (generateSilentAudio) { if (forceAudioTrack) {
return true; return true;
} }

View File

@ -279,14 +279,17 @@ public final class TransformerEndToEndTest {
} }
@Test @Test
public void start_silentAudioOnAudioOnly_isIgnored() throws Exception { public void start_forceAudioTrackOnAudioOnly_isIgnored() throws Exception {
Transformer transformer = Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
createTransformerBuilder(/* enableFallback= */ false)
.experimentalSetGenerateSilentAudio(true)
.build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_ENCODER); MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_ENCODER);
EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(mediaItem).build();
EditedMediaItemSequence sequence =
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
Composition composition =
new Composition(
ImmutableList.of(sequence), Effects.EMPTY, /* experimentalForceAudioTrack= */ true);
transformer.start(mediaItem, outputPath); transformer.start(composition, outputPath);
TransformerTestRunner.runLooper(transformer); TransformerTestRunner.runLooper(transformer);
DumpFileAsserts.assertOutput( DumpFileAsserts.assertOutput(
@ -294,31 +297,36 @@ public final class TransformerEndToEndTest {
} }
@Test @Test
public void start_silentAudioOnAudioVideo_isIgnored() throws Exception { public void start_forceAudioTrackOnAudioVideo_isIgnored() throws Exception {
Transformer transformer = Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
createTransformerBuilder(/* enableFallback= */ false)
.experimentalSetGenerateSilentAudio(true)
.build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO); MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(mediaItem).build();
EditedMediaItemSequence sequence =
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
Composition composition =
new Composition(
ImmutableList.of(sequence), Effects.EMPTY, /* experimentalForceAudioTrack= */ true);
transformer.start(mediaItem, outputPath); transformer.start(composition, outputPath);
TransformerTestRunner.runLooper(transformer); TransformerTestRunner.runLooper(transformer);
DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO)); DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO));
} }
@Test @Test
public void start_silentAudioRemoveAudio_completesSuccessfully() throws Exception { public void start_forceAudioTrackAndRemoveAudio_generatesSilentAudio() throws Exception {
Transformer transformer = Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
createTransformerBuilder(/* enableFallback= */ false)
.experimentalSetGenerateSilentAudio(true)
.build();
EditedMediaItem editedMediaItem = EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO)) new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO))
.setRemoveAudio(true) .setRemoveAudio(true)
.build(); .build();
EditedMediaItemSequence sequence =
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
Composition composition =
new Composition(
ImmutableList.of(sequence), Effects.EMPTY, /* experimentalForceAudioTrack= */ true);
transformer.start(editedMediaItem, outputPath); transformer.start(composition, outputPath);
TransformerTestRunner.runLooper(transformer); TransformerTestRunner.runLooper(transformer);
DumpFileAsserts.assertOutput( DumpFileAsserts.assertOutput(
@ -326,31 +334,36 @@ public final class TransformerEndToEndTest {
} }
@Test @Test
public void start_silentAudioRemoveVideo_isIgnored() throws Exception { public void start_forceAudioTrackAndRemoveVideo_isIgnored() throws Exception {
Transformer transformer = Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
createTransformerBuilder(/* enableFallback= */ false)
.experimentalSetGenerateSilentAudio(true)
.build();
EditedMediaItem editedMediaItem = EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO)) new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO))
.setRemoveVideo(true) .setRemoveVideo(true)
.build(); .build();
EditedMediaItemSequence sequence =
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
Composition composition =
new Composition(
ImmutableList.of(sequence), Effects.EMPTY, /* experimentalForceAudioTrack= */ true);
transformer.start(editedMediaItem, outputPath); transformer.start(composition, outputPath);
TransformerTestRunner.runLooper(transformer); TransformerTestRunner.runLooper(transformer);
DumpFileAsserts.assertOutput( DumpFileAsserts.assertOutput(
context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".novideo")); context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".novideo"));
} }
@Test @Test
public void start_silentAudioOnVideoOnly_completesSuccessfully() throws Exception { public void start_forceAudioTrackOnVideoOnly_generatesSilentAudio() throws Exception {
Transformer transformer = Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
createTransformerBuilder(/* enableFallback= */ false)
.experimentalSetGenerateSilentAudio(true)
.build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_VIDEO_ONLY); MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_VIDEO_ONLY);
EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(mediaItem).build();
EditedMediaItemSequence sequence =
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
Composition composition =
new Composition(
ImmutableList.of(sequence), Effects.EMPTY, /* experimentalForceAudioTrack= */ true);
transformer.start(mediaItem, outputPath); transformer.start(composition, outputPath);
TransformerTestRunner.runLooper(transformer); TransformerTestRunner.runLooper(transformer);
DumpFileAsserts.assertOutput( DumpFileAsserts.assertOutput(