sequences;
/** The {@link Effects} to apply to the composition. */
public final Effects effects;
+ /**
+ * Whether the output file should always contain an audio track.
+ *
+ *
+ * - If {@code false}:
+ *
+ * - 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.
+ *
- If the {@link Composition} export produces audio at timestamp 0, the output will
+ * contain an audio track.
+ *
+ * - If {@code true}, the output will always contain an audio track.
+ *
+ *
+ * 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.
+ *
+ * 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}.
+ *
+ *
This parameter is experimental and may be removed or changed without warning.
+ */
+ public final boolean experimentalForceAudioTrack;
+
+ /**
+ * Creates an instance.
+ *
+ *
This is equivalent to calling {@link Composition#Composition(List, Effects, boolean)} with
+ * {@link #experimentalForceAudioTrack} set to {@code false}.
+ */
+ public Composition(List sequences, Effects effects) {
+ this(sequences, effects, /* experimentalForceAudioTrack= */ false);
+ }
/**
* Creates an instance.
*
* @param sequences The {@link #sequences}.
* @param effects The {@link #effects}.
+ * @param experimentalForceAudioTrack Whether to {@linkplain #experimentalForceAudioTrack always
+ * add an audio track in the output}.
*/
- public Composition(List sequences, Effects effects) {
+ public Composition(
+ List sequences,
+ Effects effects,
+ boolean experimentalForceAudioTrack) {
checkArgument(!sequences.isEmpty());
this.sequences = ImmutableList.copyOf(sequences);
this.effects = effects;
+ this.experimentalForceAudioTrack = experimentalForceAudioTrack;
}
}
diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java
index 9a71853a25..9009c60d5d 100644
--- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java
+++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java
@@ -29,7 +29,6 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.audio.AudioProcessor;
-import com.google.android.exoplayer2.audio.SonicAudioProcessor;
import com.google.android.exoplayer2.effect.DefaultVideoFrameProcessor;
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
import com.google.android.exoplayer2.util.Clock;
@@ -84,7 +83,6 @@ public final class Transformer {
private boolean removeVideo;
private boolean flattenForSlowMotion;
private boolean transmux;
- private boolean generateSilentAudio;
private ListenerSet listeners;
private AssetLoader.@MonotonicNonNull Factory assetLoaderFactory;
private VideoFrameProcessor.Factory videoFrameProcessorFactory;
@@ -121,7 +119,6 @@ public final class Transformer {
this.videoEffects = transformer.videoEffects;
this.removeAudio = transformer.removeAudio;
this.removeVideo = transformer.removeVideo;
- this.generateSilentAudio = transformer.generateSilentAudio;
this.listeners = transformer.listeners;
this.assetLoaderFactory = transformer.assetLoaderFactory;
this.videoFrameProcessorFactory = transformer.videoFrameProcessorFactory;
@@ -393,39 +390,6 @@ public final class Transformer {
return this;
}
- /**
- * Sets whether to generate silent audio for the output file, if there is no audio available.
- *
- * This method is experimental and may be removed or changed without warning.
- *
- *
To replace existing audio with silence, {@linkplain
- * EditedMediaItem.Builder#setRemoveAudio(boolean) remove the audio} from the {@link
- * EditedMediaItem} to export.
- *
- *
Audio properties/format:
- *
- *
- * - Duration will match duration of the input media.
- *
- Sample mime type will match {@link TransformationRequest#audioMimeType}, or {@link
- * MimeTypes#AUDIO_AAC} if {@code null}.
- *
- 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.
- *
- 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.
- *
- *
- * @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.
*
@@ -459,7 +423,6 @@ public final class Transformer {
removeVideo,
flattenForSlowMotion,
transmux,
- generateSilentAudio,
listeners,
assetLoaderFactory,
videoFrameProcessorFactory,
@@ -618,7 +581,6 @@ public final class Transformer {
private final boolean removeVideo;
private final boolean flattenForSlowMotion;
private final boolean transmux;
- private final boolean generateSilentAudio;
private final ListenerSet listeners;
private final AssetLoader.Factory assetLoaderFactory;
private final VideoFrameProcessor.Factory videoFrameProcessorFactory;
@@ -639,7 +601,6 @@ public final class Transformer {
boolean removeVideo,
boolean flattenForSlowMotion,
boolean transmux,
- boolean generateSilentAudio,
ListenerSet listeners,
AssetLoader.Factory assetLoaderFactory,
VideoFrameProcessor.Factory videoFrameProcessorFactory,
@@ -657,7 +618,6 @@ public final class Transformer {
this.removeVideo = removeVideo;
this.flattenForSlowMotion = flattenForSlowMotion;
this.transmux = transmux;
- this.generateSilentAudio = generateSilentAudio;
this.listeners = listeners;
this.assetLoaderFactory = assetLoaderFactory;
this.videoFrameProcessorFactory = videoFrameProcessorFactory;
@@ -777,7 +737,6 @@ public final class Transformer {
path,
transformationRequest,
transmux,
- generateSilentAudio,
assetLoaderFactory,
encoderFactory,
muxerFactory,
diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerInternal.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerInternal.java
index c8072a7721..a7e9576df7 100644
--- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerInternal.java
+++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerInternal.java
@@ -100,7 +100,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private final ConditionVariable transformerConditionVariable;
private final ExportResult.Builder exportResultBuilder;
- private boolean generateSilentAudio;
+ private boolean forceAudioTrack;
private boolean isDrainingPipelines;
private @Transformer.ProgressState int progressState;
private @MonotonicNonNull RuntimeException cancelException;
@@ -113,7 +113,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
String outputPath,
TransformationRequest transformationRequest,
boolean transmux,
- boolean generateSilentAudio,
AssetLoader.Factory assetLoaderFactory,
Codec.EncoderFactory encoderFactory,
Muxer.Factory muxerFactory,
@@ -124,7 +123,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
Clock clock) {
this.context = context;
this.transformationRequest = transformationRequest;
- this.generateSilentAudio = generateSilentAudio;
+ this.forceAudioTrack = composition.experimentalForceAudioTrack;
this.encoderFactory = new CapturingEncoderFactory(encoderFactory);
this.listener = listener;
this.applicationHandler = applicationHandler;
@@ -366,11 +365,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
throws ExportException {
int trackType = MimeTypes.getTrackType(firstInputFormat.sampleMimeType);
if (!trackAdded) {
- if (generateSilentAudio) {
+ if (forceAudioTrack) {
if (trackCount.get() == 1 && trackType == C.TRACK_TYPE_VIDEO) {
trackCount.incrementAndGet();
} else {
- generateSilentAudio = false;
+ forceAudioTrack = false;
}
}
@@ -387,7 +386,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
compositeAssetLoader.addOnMediaItemChangedListener(samplePipeline, trackType);
internalHandler.obtainMessage(MSG_REGISTER_SAMPLE_PIPELINE, samplePipeline).sendToTarget();
- if (generateSilentAudio) {
+ if (forceAudioTrack) {
Format silentAudioFormat =
new Format.Builder()
.setSampleMimeType(MimeTypes.AUDIO_AAC)
@@ -479,7 +478,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
transformationRequest,
firstEditedMediaItem.flattenForSlowMotion,
firstEditedMediaItem.effects.audioProcessors,
- generateSilentAudio ? durationUs : C.TIME_UNSET,
+ forceAudioTrack ? durationUs : C.TIME_UNSET,
encoderFactory,
muxerWrapper,
fallbackListener);
@@ -525,7 +524,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
if (!firstEditedMediaItem.effects.audioProcessors.isEmpty()) {
return true;
}
- if (generateSilentAudio) {
+ if (forceAudioTrack) {
return true;
}
diff --git a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java
index d03e1d1f5b..91e2bbd984 100644
--- a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java
+++ b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java
@@ -279,14 +279,17 @@ public final class TransformerEndToEndTest {
}
@Test
- public void start_silentAudioOnAudioOnly_isIgnored() throws Exception {
- Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false)
- .experimentalSetGenerateSilentAudio(true)
- .build();
+ public void start_forceAudioTrackOnAudioOnly_isIgnored() throws Exception {
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
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);
DumpFileAsserts.assertOutput(
@@ -294,31 +297,36 @@ public final class TransformerEndToEndTest {
}
@Test
- public void start_silentAudioOnAudioVideo_isIgnored() throws Exception {
- Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false)
- .experimentalSetGenerateSilentAudio(true)
- .build();
+ public void start_forceAudioTrackOnAudioVideo_isIgnored() throws Exception {
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
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);
DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO));
}
@Test
- public void start_silentAudioRemoveAudio_completesSuccessfully() throws Exception {
- Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false)
- .experimentalSetGenerateSilentAudio(true)
- .build();
+ public void start_forceAudioTrackAndRemoveAudio_generatesSilentAudio() throws Exception {
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO))
.setRemoveAudio(true)
.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);
DumpFileAsserts.assertOutput(
@@ -326,31 +334,36 @@ public final class TransformerEndToEndTest {
}
@Test
- public void start_silentAudioRemoveVideo_isIgnored() throws Exception {
- Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false)
- .experimentalSetGenerateSilentAudio(true)
- .build();
+ public void start_forceAudioTrackAndRemoveVideo_isIgnored() throws Exception {
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO))
.setRemoveVideo(true)
.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);
DumpFileAsserts.assertOutput(
context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".novideo"));
}
@Test
- public void start_silentAudioOnVideoOnly_completesSuccessfully() throws Exception {
- Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false)
- .experimentalSetGenerateSilentAudio(true)
- .build();
+ public void start_forceAudioTrackOnVideoOnly_generatesSilentAudio() throws Exception {
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
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);
DumpFileAsserts.assertOutput(