Add setForceAudioTrack method on EditedMediaItemSequence

PiperOrigin-RevId: 740808813
This commit is contained in:
sheenachhabra 2025-03-26 10:02:27 -07:00 committed by Copybara-Service
parent 0d60c5bf25
commit 87e0d7b95a
6 changed files with 77 additions and 45 deletions

View File

@ -22,7 +22,6 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import androidx.annotation.IntDef;
import androidx.media3.common.MediaItem;
import androidx.media3.common.VideoCompositorSettings;
import androidx.media3.common.audio.AudioProcessor;
import androidx.media3.common.util.UnstableApi;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
@ -121,40 +120,9 @@ public final class Composition {
}
/**
* Sets whether the output file should always contain an audio track.
*
* <p>The default value is {@code false}.
*
* <ul>
* <li>If {@code false}:
* <ul>
* <li>If the {@link Composition} export doesn't produce any audio at timestamp 0, but
* produces audio later on, the export is {@linkplain
* Transformer.Listener#onError(Composition, ExportResult, ExportException)
* aborted}.
* <li>If the {@link Composition} doesn't produce any audio during the entire export,
* the output won't contain any audio.
* <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
* Transformer.Builder#setAudioMimeType(String)}. The sample rate and channel count can be set
* by passing relevant {@link AudioProcessor} instances to the {@link Composition}.
*
* <p>Forcing an audio track and {@linkplain #setTransmuxAudio(boolean) requesting audio
* transmuxing} are not allowed together because generating silence requires transcoding.
*
* <p>This method is experimental and may be removed or changed without warning.
*
* @param forceAudioTrack Whether to force an audio track in the output.
* @return This builder.
* @deprecated Use {@link EditedMediaItemSequence.Builder#setForceAudioTrack(boolean)} instead.
*/
@Deprecated
@CanIgnoreReturnValue
public Builder experimentalSetForceAudioTrack(boolean forceAudioTrack) {
this.forceAudioTrack = forceAudioTrack;
@ -260,8 +228,20 @@ public final class Composition {
/** Builds a {@link Composition} instance. */
public Composition build() {
ImmutableList<EditedMediaItemSequence> updatedSequences;
if (forceAudioTrack) {
ImmutableList.Builder<EditedMediaItemSequence> updatedSequencesBuilder =
new ImmutableList.Builder<>();
for (int i = 0; i < sequences.size(); i++) {
updatedSequencesBuilder.add(
sequences.get(i).buildUpon().setForceAudioTrack(forceAudioTrack).build());
}
updatedSequences = updatedSequencesBuilder.build();
} else {
updatedSequences = sequences;
}
return new Composition(
sequences,
updatedSequences,
videoCompositorSettings,
effects,
forceAudioTrack,
@ -379,11 +359,10 @@ public final class Composition {
public final Effects effects;
/**
* Whether the output file should always contain an audio track.
*
* <p>For more information, see {@link Builder#experimentalSetForceAudioTrack(boolean)}.
* @deprecated Use {@link EditedMediaItemSequence.Builder#setForceAudioTrack(boolean)} to set the
* flag and {@link EditedMediaItemSequence#forceAudioTrack} to read the flag.
*/
public final boolean forceAudioTrack;
@Deprecated public final boolean forceAudioTrack;
/**
* Whether to transmux the {@linkplain MediaItem media items'} audio tracks.

View File

@ -18,6 +18,7 @@ package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkArgument;
import androidx.media3.common.MediaItem;
import androidx.media3.common.audio.AudioProcessor;
import androidx.media3.common.util.UnstableApi;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
@ -35,6 +36,7 @@ public final class EditedMediaItemSequence {
public static final class Builder {
private final ImmutableList.Builder<EditedMediaItem> items;
private boolean isLooping;
private boolean forceAudioTrack;
/** Creates an instance. */
public Builder(EditedMediaItem... editedMediaItems) {
@ -46,6 +48,15 @@ public final class EditedMediaItemSequence {
items = new ImmutableList.Builder<EditedMediaItem>().addAll(editedMediaItems);
}
/** Creates a new instance to build upon the provided {@link EditedMediaItemSequence}. */
private Builder(EditedMediaItemSequence editedMediaItemSequence) {
items =
new ImmutableList.Builder<EditedMediaItem>()
.addAll(editedMediaItemSequence.editedMediaItems);
isLooping = editedMediaItemSequence.isLooping;
forceAudioTrack = editedMediaItemSequence.forceAudioTrack;
}
/**
* Adds the {@linkplain EditedMediaItem item} to the sequence.
*
@ -116,6 +127,41 @@ public final class EditedMediaItemSequence {
return this;
}
/**
* Forces silent audio in the {@linkplain EditedMediaItemSequence sequence}.
*
* <p>This flag is necessary when:
*
* <ul>
* <li>The first {@link EditedMediaItem} in the sequence does not contain audio, but
* subsequent items do.
* <li>The first item in the sequence is a {@linkplain #addGap(long) gap} and the subsequent
* {@linkplain EditedMediaItem media items} contain audio.
* </ul>
*
* <p>If the flag is not set appropriately, then the export will {@linkplain
* Transformer.Listener#onError(Composition, ExportResult, ExportException) fail}.
*
* <p>If the first {@link EditedMediaItem} already contains audio, this flag has no effect.
*
* <p>The MIME type of the output's audio track can be set using {@link
* Transformer.Builder#setAudioMimeType(String)}. The sample rate and channel count can be set
* by passing relevant {@link AudioProcessor} instances to the {@link Composition}.
*
* <p>Forcing an audio track and {@linkplain Composition.Builder#setTransmuxAudio(boolean)
* requesting audio transmuxing} are not allowed together because generating silence requires
* transcoding.
*
* <p>The default value is {@code false}.
*
* @param forceAudioTrack Whether to force audio track.
*/
@CanIgnoreReturnValue
public Builder setForceAudioTrack(boolean forceAudioTrack) {
this.forceAudioTrack = forceAudioTrack;
return this;
}
/**
* Builds the {@link EditedMediaItemSequence}.
*
@ -147,6 +193,9 @@ public final class EditedMediaItemSequence {
*/
public final boolean isLooping;
/** Forces silent audio in the {@linkplain EditedMediaItemSequence sequence}. */
public final boolean forceAudioTrack;
/**
* @deprecated Use {@link Builder}.
*/
@ -172,11 +221,17 @@ public final class EditedMediaItemSequence {
this(new Builder().addItems(editedMediaItems).setIsLooping(isLooping));
}
/** Returns a {@link Builder} initialized with the values of this instance. */
public Builder buildUpon() {
return new Builder(this);
}
private EditedMediaItemSequence(EditedMediaItemSequence.Builder builder) {
this.editedMediaItems = builder.items.build();
checkArgument(
!editedMediaItems.isEmpty(), "The sequence must contain at least one EditedMediaItem.");
this.isLooping = builder.isLooping;
this.forceAudioTrack = builder.forceAudioTrack;
}
/** Return whether any items are a {@linkplain Builder#addGap(long) gap}. */

View File

@ -68,7 +68,7 @@ public final class Effects {
* <p>The {@linkplain AudioProcessor audio processor} and {@linkplain Effect video effect} are
* interlinked to help maintain A/V sync. When using Transformer, if the input file doesn't have
* audio, or audio is being removed, you may have to {@linkplain
* Composition.Builder#experimentalSetForceAudioTrack force an audio track} for the interlinked
* EditedMediaItemSequence.Builder#setForceAudioTrack force an audio track} for the interlinked
* effects to function correctly. Alternatively, you can use {@link SpeedChangeEffect} when input
* has no audio.
*

View File

@ -130,7 +130,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
public SequenceAssetLoader(
EditedMediaItemSequence sequence,
boolean forceAudioTrack,
Factory assetLoaderFactory,
CompositionSettings compositionSettings,
Listener listener,
@ -138,7 +137,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
Looper looper) {
editedMediaItems = sequence.editedMediaItems;
isLooping = sequence.isLooping;
this.forceAudioTrack = forceAudioTrack || sequence.editedMediaItems.get(0).isGap();
this.forceAudioTrack = sequence.forceAudioTrack || sequence.editedMediaItems.get(0).isGap();
this.assetLoaderFactory = new GapInterceptingAssetLoaderFactory(assetLoaderFactory);
this.compositionSettings = compositionSettings;
sequenceAssetLoaderListener = listener;

View File

@ -937,8 +937,8 @@ public final class Transformer {
* <li>If an {@link EditedMediaItem} in a sequence contains data of a given {@linkplain
* C.TrackType track}, so must all items in that sequence.
* <ul>
* <li>For audio, this condition can be removed by setting an experimental {@link
* Composition.Builder#experimentalSetForceAudioTrack(boolean) flag}.
* <li>For audio, this condition can be removed by setting {@link
* EditedMediaItemSequence.Builder#setForceAudioTrack(boolean)} flag.
* </ul>
* <li>If a sequence starts with an HDR {@link EditedMediaItem}, all the following items in the
* sequence must be HDR.

View File

@ -248,7 +248,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
sequenceAssetLoaders.add(
new SequenceAssetLoader(
sequence,
composition.forceAudioTrack,
assetLoaderFactory,
new CompositionSettings(
transformationRequest.hdrMode, composition.retainHdrFromUltraHdrImage),