Split transmux into transmuxAudio/Video
- Split the transmux setting into transmuxAudio and transmuxVideo. This is more flexible for apps and will also be useful for unit testing (particularly as we can't test video transcoding on Robolectric at the moment). - Move these settings to Composition. It makes sense for these settings to be next to forceAudioTrack. Apps may also want to set these settings based on the current Composition's MediaItems. - Add a Composition.Builder because Composition now contains a few optional fields. PiperOrigin-RevId: 511708618
This commit is contained in:
parent
2baa70206c
commit
31c44c7061
@ -378,7 +378,9 @@ public final class TransformerActivity extends AppCompatActivity {
|
|||||||
editedMediaItems.add(editedMediaItemBuilder.build());
|
editedMediaItems.add(editedMediaItemBuilder.build());
|
||||||
List<EditedMediaItemSequence> sequences = new ArrayList<>();
|
List<EditedMediaItemSequence> sequences = new ArrayList<>();
|
||||||
sequences.add(new EditedMediaItemSequence(editedMediaItems));
|
sequences.add(new EditedMediaItemSequence(editedMediaItems));
|
||||||
return new Composition(sequences, Effects.EMPTY, forceAudioTrack);
|
return new Composition.Builder(sequences)
|
||||||
|
.experimentalSetForceAudioTrack(forceAudioTrack)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImmutableList<AudioProcessor> createAudioProcessorsFromBundle(Bundle bundle) {
|
private ImmutableList<AudioProcessor> createAudioProcessorsFromBundle(Bundle bundle) {
|
||||||
|
@ -21,22 +21,153 @@ import androidx.media3.common.MediaItem;
|
|||||||
import androidx.media3.common.audio.AudioProcessor;
|
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 com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A composition of {@link MediaItem} instances, with transformations to apply to them.
|
* A composition of {@link MediaItem} instances, with transformations to apply to them.
|
||||||
*
|
*
|
||||||
* <p>The {@linkplain MediaItem} instances can be concatenated or mixed. {@link Effects} can be
|
* <p>The {@link MediaItem} instances can be concatenated or mixed. {@link Effects} can be applied
|
||||||
* applied to individual {@linkplain MediaItem} instances, as well as to the composition.
|
* to individual {@link MediaItem} instances, as well as to the composition.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class Composition {
|
public final class Composition {
|
||||||
|
|
||||||
|
/** A builder for {@link Composition} instances. */
|
||||||
|
public static final class Builder {
|
||||||
|
|
||||||
|
private final ImmutableList<EditedMediaItemSequence> sequences;
|
||||||
|
|
||||||
|
private Effects effects;
|
||||||
|
private boolean forceAudioTrack;
|
||||||
|
private boolean transmuxAudio;
|
||||||
|
private boolean transmuxVideo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance.
|
||||||
|
*
|
||||||
|
* @param sequences The {@link EditedMediaItemSequence} instances to compose. {@link MediaItem}
|
||||||
|
* instances from different sequences that are overlapping in time will be mixed in the
|
||||||
|
* output. This list must not be empty.
|
||||||
|
*/
|
||||||
|
public Builder(List<EditedMediaItemSequence> sequences) {
|
||||||
|
checkArgument(
|
||||||
|
!sequences.isEmpty(),
|
||||||
|
"The composition must contain at least one EditedMediaItemSequence.");
|
||||||
|
this.sequences = ImmutableList.copyOf(sequences);
|
||||||
|
effects = Effects.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link Effects} to apply to the {@link Composition}.
|
||||||
|
*
|
||||||
|
* <p>The default value is {@link Effects#EMPTY}.
|
||||||
|
*
|
||||||
|
* @param effects The {@link Composition} {@link Effects}.
|
||||||
|
* @return This builder.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setEffects(Effects effects) {
|
||||||
|
this.effects = effects;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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, 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>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.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder experimentalSetForceAudioTrack(boolean forceAudioTrack) {
|
||||||
|
this.forceAudioTrack = forceAudioTrack;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether to transmux the {@linkplain MediaItem media items'} audio tracks.
|
||||||
|
*
|
||||||
|
* <p>The default value is {@code false}.
|
||||||
|
*
|
||||||
|
* <p>If the {@link Composition} contains one {@link MediaItem}, the value set is ignored. The
|
||||||
|
* audio track will only be transcoded if necessary.
|
||||||
|
*
|
||||||
|
* <p>If the input {@link Composition} contains multiple {@linkplain MediaItem media items}, all
|
||||||
|
* the audio tracks are transmuxed if {@code transmuxAudio} is {@code true} and exporting the
|
||||||
|
* first {@link MediaItem} doesn't require audio transcoding. Otherwise, they are all
|
||||||
|
* transcoded. Transmuxed tracks must be compatible and must not overlap in time.
|
||||||
|
*
|
||||||
|
* <p>Requesting audio transmuxing and {@linkplain #experimentalSetForceAudioTrack(boolean)
|
||||||
|
* forcing an audio track} are not allowed together because generating silence requires
|
||||||
|
* transcoding.
|
||||||
|
*
|
||||||
|
* @param transmuxAudio Whether to transmux the audio tracks.
|
||||||
|
* @return This builder.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setTransmuxAudio(boolean transmuxAudio) {
|
||||||
|
this.transmuxAudio = transmuxAudio;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether to transmux the {@linkplain MediaItem media items'} video tracks.
|
||||||
|
*
|
||||||
|
* <p>The default value is {@code false}.
|
||||||
|
*
|
||||||
|
* <p>If the {@link Composition} contains one {@link MediaItem}, the value set is ignored. The
|
||||||
|
* video track will only be transcoded if necessary.
|
||||||
|
*
|
||||||
|
* <p>If the input {@link Composition} contains multiple {@linkplain MediaItem media items}, all
|
||||||
|
* the video tracks are transmuxed if {@code transmuxVideo} is {@code true} and exporting the
|
||||||
|
* first {@link MediaItem} doesn't require video transcoding. Otherwise, they are all
|
||||||
|
* transcoded. Transmuxed tracks must be compatible and must not overlap in time.
|
||||||
|
*
|
||||||
|
* @param transmuxVideo Whether to transmux the video tracks.
|
||||||
|
* @return This builder.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setTransmuxVideo(boolean transmuxVideo) {
|
||||||
|
this.transmuxVideo = transmuxVideo;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Builds a {@link Composition} instance. */
|
||||||
|
public Composition build() {
|
||||||
|
return new Composition(sequences, effects, forceAudioTrack, transmuxAudio, transmuxVideo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link EditedMediaItemSequence} instances to compose. {@link MediaItem} instances from
|
* The {@link EditedMediaItemSequence} instances to compose.
|
||||||
* different sequences that are overlapping in time will be mixed in the output.
|
|
||||||
*
|
*
|
||||||
* <p>This list must not be empty.
|
* <p>For more information, see {@link Builder#Builder(List)}.
|
||||||
*/
|
*/
|
||||||
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. */
|
||||||
@ -44,54 +175,35 @@ public final class Composition {
|
|||||||
/**
|
/**
|
||||||
* Whether the output file should always contain an audio track.
|
* Whether the output file should always contain an audio track.
|
||||||
*
|
*
|
||||||
* <ul>
|
* <p>For more information, see {@link Builder#experimentalSetForceAudioTrack(boolean)}.
|
||||||
* <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;
|
public final boolean forceAudioTrack;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Whether to transmux the {@linkplain MediaItem media items'} audio tracks.
|
||||||
*
|
*
|
||||||
* <p>This is equivalent to calling {@link Composition#Composition(List, Effects, boolean)} with
|
* <p>For more information, see {@link Builder#setTransmuxAudio(boolean)}.
|
||||||
* {@link #experimentalForceAudioTrack} set to {@code false}.
|
|
||||||
*/
|
*/
|
||||||
public Composition(List<EditedMediaItemSequence> sequences, Effects effects) {
|
public final boolean transmuxAudio;
|
||||||
this(sequences, effects, /* experimentalForceAudioTrack= */ false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Whether to transmux the {@linkplain MediaItem media items'} video tracks.
|
||||||
*
|
*
|
||||||
* @param sequences The {@link #sequences}.
|
* <p>For more information, see {@link Builder#setTransmuxVideo(boolean)}.
|
||||||
* @param effects The {@link #effects}.
|
|
||||||
* @param experimentalForceAudioTrack Whether to {@linkplain #experimentalForceAudioTrack always
|
|
||||||
* add an audio track in the output}.
|
|
||||||
*/
|
*/
|
||||||
public Composition(
|
public final boolean transmuxVideo;
|
||||||
|
|
||||||
|
private Composition(
|
||||||
List<EditedMediaItemSequence> sequences,
|
List<EditedMediaItemSequence> sequences,
|
||||||
Effects effects,
|
Effects effects,
|
||||||
boolean experimentalForceAudioTrack) {
|
boolean forceAudioTrack,
|
||||||
checkArgument(!sequences.isEmpty());
|
boolean transmuxAudio,
|
||||||
|
boolean transmuxVideo) {
|
||||||
|
checkArgument(
|
||||||
|
!transmuxAudio || !forceAudioTrack,
|
||||||
|
"Audio transmuxing and audio track forcing are not allowed together.");
|
||||||
this.sequences = ImmutableList.copyOf(sequences);
|
this.sequences = ImmutableList.copyOf(sequences);
|
||||||
this.effects = effects;
|
this.effects = effects;
|
||||||
this.experimentalForceAudioTrack = experimentalForceAudioTrack;
|
this.transmuxAudio = transmuxAudio;
|
||||||
|
this.transmuxVideo = transmuxVideo;
|
||||||
|
this.forceAudioTrack = forceAudioTrack;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ import androidx.media3.effect.DefaultVideoFrameProcessor;
|
|||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/** Effects to apply to a {@link MediaItem}. */
|
/** Effects to apply to a {@link MediaItem} or to a {@link Composition}. */
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class Effects {
|
public final class Effects {
|
||||||
|
|
||||||
|
@ -84,7 +84,6 @@ public final class Transformer {
|
|||||||
private boolean removeAudio;
|
private boolean removeAudio;
|
||||||
private boolean removeVideo;
|
private boolean removeVideo;
|
||||||
private boolean flattenForSlowMotion;
|
private boolean flattenForSlowMotion;
|
||||||
private boolean transmux;
|
|
||||||
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;
|
||||||
@ -204,28 +203,6 @@ public final class Transformer {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether to transmux the {@linkplain MediaItem media items} in the input {@link
|
|
||||||
* Composition}.
|
|
||||||
*
|
|
||||||
* <p>The default value is {@code false}.
|
|
||||||
*
|
|
||||||
* <p>If the input {@link Composition} contains one {@link MediaItem}, the value set is ignored.
|
|
||||||
* The {@link MediaItem} will only be transcoded if necessary.
|
|
||||||
*
|
|
||||||
* <p>If the input {@link Composition} contains multiple {@linkplain MediaItem media items},
|
|
||||||
* they are all transmuxed if {@code transmux} is {@code true} and exporting the first {@link
|
|
||||||
* MediaItem} doesn't require transcoding. Otherwise, they are all transcoded.
|
|
||||||
*
|
|
||||||
* @param transmux Whether to transmux.
|
|
||||||
* @return This builder.
|
|
||||||
*/
|
|
||||||
@CanIgnoreReturnValue
|
|
||||||
public Builder setTransmux(boolean transmux) {
|
|
||||||
this.transmux = transmux;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Use {@link #addListener(Listener)}, {@link #removeListener(Listener)} or {@link
|
* @deprecated Use {@link #addListener(Listener)}, {@link #removeListener(Listener)} or {@link
|
||||||
* #removeAllListeners()} instead.
|
* #removeAllListeners()} instead.
|
||||||
@ -416,7 +393,6 @@ public final class Transformer {
|
|||||||
removeAudio,
|
removeAudio,
|
||||||
removeVideo,
|
removeVideo,
|
||||||
flattenForSlowMotion,
|
flattenForSlowMotion,
|
||||||
transmux,
|
|
||||||
listeners,
|
listeners,
|
||||||
assetLoaderFactory,
|
assetLoaderFactory,
|
||||||
videoFrameProcessorFactory,
|
videoFrameProcessorFactory,
|
||||||
@ -576,7 +552,6 @@ public final class Transformer {
|
|||||||
private final boolean removeAudio;
|
private final boolean removeAudio;
|
||||||
private final boolean removeVideo;
|
private final boolean removeVideo;
|
||||||
private final boolean flattenForSlowMotion;
|
private final boolean flattenForSlowMotion;
|
||||||
private final boolean transmux;
|
|
||||||
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;
|
||||||
@ -596,7 +571,6 @@ public final class Transformer {
|
|||||||
boolean removeAudio,
|
boolean removeAudio,
|
||||||
boolean removeVideo,
|
boolean removeVideo,
|
||||||
boolean flattenForSlowMotion,
|
boolean flattenForSlowMotion,
|
||||||
boolean transmux,
|
|
||||||
ListenerSet<Listener> listeners,
|
ListenerSet<Listener> listeners,
|
||||||
AssetLoader.Factory assetLoaderFactory,
|
AssetLoader.Factory assetLoaderFactory,
|
||||||
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
||||||
@ -613,7 +587,6 @@ public final class Transformer {
|
|||||||
this.removeAudio = removeAudio;
|
this.removeAudio = removeAudio;
|
||||||
this.removeVideo = removeVideo;
|
this.removeVideo = removeVideo;
|
||||||
this.flattenForSlowMotion = flattenForSlowMotion;
|
this.flattenForSlowMotion = flattenForSlowMotion;
|
||||||
this.transmux = transmux;
|
|
||||||
this.listeners = listeners;
|
this.listeners = listeners;
|
||||||
this.assetLoaderFactory = assetLoaderFactory;
|
this.assetLoaderFactory = assetLoaderFactory;
|
||||||
this.videoFrameProcessorFactory = videoFrameProcessorFactory;
|
this.videoFrameProcessorFactory = videoFrameProcessorFactory;
|
||||||
@ -732,7 +705,6 @@ public final class Transformer {
|
|||||||
composition,
|
composition,
|
||||||
path,
|
path,
|
||||||
transformationRequest,
|
transformationRequest,
|
||||||
transmux,
|
|
||||||
assetLoaderFactory,
|
assetLoaderFactory,
|
||||||
encoderFactory,
|
encoderFactory,
|
||||||
muxerFactory,
|
muxerFactory,
|
||||||
@ -772,7 +744,7 @@ public final class Transformer {
|
|||||||
public void start(EditedMediaItem editedMediaItem, String path) {
|
public void start(EditedMediaItem editedMediaItem, String path) {
|
||||||
EditedMediaItemSequence sequence =
|
EditedMediaItemSequence sequence =
|
||||||
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
|
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
|
||||||
start(new Composition(ImmutableList.of(sequence), Effects.EMPTY), path);
|
start(new Composition.Builder(ImmutableList.of(sequence)).build(), path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -112,7 +112,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
Composition composition,
|
Composition composition,
|
||||||
String outputPath,
|
String outputPath,
|
||||||
TransformationRequest transformationRequest,
|
TransformationRequest transformationRequest,
|
||||||
boolean transmux,
|
|
||||||
AssetLoader.Factory assetLoaderFactory,
|
AssetLoader.Factory assetLoaderFactory,
|
||||||
Codec.EncoderFactory encoderFactory,
|
Codec.EncoderFactory encoderFactory,
|
||||||
Muxer.Factory muxerFactory,
|
Muxer.Factory muxerFactory,
|
||||||
@ -133,11 +132,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
Looper internalLooper = internalHandlerThread.getLooper();
|
Looper internalLooper = internalHandlerThread.getLooper();
|
||||||
EditedMediaItemSequence sequence = composition.sequences.get(0);
|
EditedMediaItemSequence sequence = composition.sequences.get(0);
|
||||||
ComponentListener componentListener =
|
ComponentListener componentListener =
|
||||||
new ComponentListener(sequence, transmux, fallbackListener);
|
new ComponentListener(
|
||||||
|
sequence, composition.transmuxAudio, composition.transmuxVideo, fallbackListener);
|
||||||
compositeAssetLoader =
|
compositeAssetLoader =
|
||||||
new CompositeAssetLoader(
|
new CompositeAssetLoader(
|
||||||
sequence,
|
sequence,
|
||||||
composition.experimentalForceAudioTrack,
|
composition.forceAudioTrack,
|
||||||
assetLoaderFactory,
|
assetLoaderFactory,
|
||||||
internalLooper,
|
internalLooper,
|
||||||
componentListener,
|
componentListener,
|
||||||
@ -314,17 +314,22 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
// The first EditedMediaItem in the sequence determines which SamplePipeline to use.
|
// The first EditedMediaItem in the sequence determines which SamplePipeline to use.
|
||||||
private final EditedMediaItem firstEditedMediaItem;
|
private final EditedMediaItem firstEditedMediaItem;
|
||||||
private final int mediaItemCount;
|
private final int mediaItemCount;
|
||||||
private final boolean transmux;
|
private final boolean transmuxAudio;
|
||||||
|
private final boolean transmuxVideo;
|
||||||
private final FallbackListener fallbackListener;
|
private final FallbackListener fallbackListener;
|
||||||
private final AtomicInteger trackCount;
|
private final AtomicInteger trackCount;
|
||||||
|
|
||||||
private boolean trackAdded;
|
private boolean trackAdded;
|
||||||
|
|
||||||
public ComponentListener(
|
public ComponentListener(
|
||||||
EditedMediaItemSequence sequence, boolean transmux, FallbackListener fallbackListener) {
|
EditedMediaItemSequence sequence,
|
||||||
|
boolean transmuxAudio,
|
||||||
|
boolean transmuxVideo,
|
||||||
|
FallbackListener fallbackListener) {
|
||||||
firstEditedMediaItem = sequence.editedMediaItems.get(0);
|
firstEditedMediaItem = sequence.editedMediaItems.get(0);
|
||||||
mediaItemCount = sequence.editedMediaItems.size();
|
mediaItemCount = sequence.editedMediaItems.size();
|
||||||
this.transmux = transmux;
|
this.transmuxAudio = transmuxAudio;
|
||||||
|
this.transmuxVideo = transmuxVideo;
|
||||||
this.fallbackListener = fallbackListener;
|
this.fallbackListener = fallbackListener;
|
||||||
trackCount = new AtomicInteger();
|
trackCount = new AtomicInteger();
|
||||||
}
|
}
|
||||||
@ -479,12 +484,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
boolean shouldTranscode = false;
|
boolean shouldTranscode = false;
|
||||||
if (!assetLoaderCanOutputEncoded) {
|
if (!assetLoaderCanOutputEncoded) {
|
||||||
shouldTranscode = true;
|
shouldTranscode = true;
|
||||||
} else if (mediaItemCount > 1 && !transmux) {
|
|
||||||
shouldTranscode = true;
|
|
||||||
} else if (MimeTypes.isAudio(inputFormat.sampleMimeType)) {
|
} else if (MimeTypes.isAudio(inputFormat.sampleMimeType)) {
|
||||||
shouldTranscode = shouldTranscodeAudio(inputFormat);
|
shouldTranscode =
|
||||||
|
(mediaItemCount > 1 && !transmuxAudio) || shouldTranscodeAudio(inputFormat);
|
||||||
} else if (MimeTypes.isVideo(inputFormat.sampleMimeType)) {
|
} else if (MimeTypes.isVideo(inputFormat.sampleMimeType)) {
|
||||||
shouldTranscode = shouldTranscodeVideo(inputFormat, streamStartPositionUs, streamOffsetUs);
|
shouldTranscode =
|
||||||
|
(mediaItemCount > 1 && !transmuxVideo)
|
||||||
|
|| shouldTranscodeVideo(inputFormat, streamStartPositionUs, streamOffsetUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkState(!shouldTranscode || assetLoaderCanOutputDecoded);
|
checkState(!shouldTranscode || assetLoaderCanOutputDecoded);
|
||||||
|
@ -39,12 +39,12 @@ import org.robolectric.shadows.ShadowLooper;
|
|||||||
public class FallbackListenerTest {
|
public class FallbackListenerTest {
|
||||||
|
|
||||||
private static final Composition PLACEHOLDER_COMPOSITION =
|
private static final Composition PLACEHOLDER_COMPOSITION =
|
||||||
new Composition(
|
new Composition.Builder(
|
||||||
ImmutableList.of(
|
ImmutableList.of(
|
||||||
new EditedMediaItemSequence(
|
new EditedMediaItemSequence(
|
||||||
ImmutableList.of(
|
ImmutableList.of(
|
||||||
new EditedMediaItem.Builder(MediaItem.fromUri(Uri.EMPTY)).build()))),
|
new EditedMediaItem.Builder(MediaItem.fromUri(Uri.EMPTY)).build()))))
|
||||||
Effects.EMPTY);
|
.build();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void onTransformationRequestFinalized_withoutTrackCountSet_throwsException() {
|
public void onTransformationRequestFinalized_withoutTrackCountSet_throwsException() {
|
||||||
|
@ -286,8 +286,9 @@ public final class TransformerEndToEndTest {
|
|||||||
EditedMediaItemSequence sequence =
|
EditedMediaItemSequence sequence =
|
||||||
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
|
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
|
||||||
Composition composition =
|
Composition composition =
|
||||||
new Composition(
|
new Composition.Builder(ImmutableList.of(sequence))
|
||||||
ImmutableList.of(sequence), Effects.EMPTY, /* experimentalForceAudioTrack= */ true);
|
.experimentalSetForceAudioTrack(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
transformer.start(composition, outputPath);
|
transformer.start(composition, outputPath);
|
||||||
TransformerTestRunner.runLooper(transformer);
|
TransformerTestRunner.runLooper(transformer);
|
||||||
@ -304,8 +305,9 @@ public final class TransformerEndToEndTest {
|
|||||||
EditedMediaItemSequence sequence =
|
EditedMediaItemSequence sequence =
|
||||||
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
|
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
|
||||||
Composition composition =
|
Composition composition =
|
||||||
new Composition(
|
new Composition.Builder(ImmutableList.of(sequence))
|
||||||
ImmutableList.of(sequence), Effects.EMPTY, /* experimentalForceAudioTrack= */ true);
|
.experimentalSetForceAudioTrack(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
transformer.start(composition, outputPath);
|
transformer.start(composition, outputPath);
|
||||||
TransformerTestRunner.runLooper(transformer);
|
TransformerTestRunner.runLooper(transformer);
|
||||||
@ -323,8 +325,9 @@ public final class TransformerEndToEndTest {
|
|||||||
EditedMediaItemSequence sequence =
|
EditedMediaItemSequence sequence =
|
||||||
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
|
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
|
||||||
Composition composition =
|
Composition composition =
|
||||||
new Composition(
|
new Composition.Builder(ImmutableList.of(sequence))
|
||||||
ImmutableList.of(sequence), Effects.EMPTY, /* experimentalForceAudioTrack= */ true);
|
.experimentalSetForceAudioTrack(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
transformer.start(composition, outputPath);
|
transformer.start(composition, outputPath);
|
||||||
TransformerTestRunner.runLooper(transformer);
|
TransformerTestRunner.runLooper(transformer);
|
||||||
@ -343,8 +346,9 @@ public final class TransformerEndToEndTest {
|
|||||||
EditedMediaItemSequence sequence =
|
EditedMediaItemSequence sequence =
|
||||||
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
|
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
|
||||||
Composition composition =
|
Composition composition =
|
||||||
new Composition(
|
new Composition.Builder(ImmutableList.of(sequence))
|
||||||
ImmutableList.of(sequence), Effects.EMPTY, /* experimentalForceAudioTrack= */ true);
|
.experimentalSetForceAudioTrack(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
transformer.start(composition, outputPath);
|
transformer.start(composition, outputPath);
|
||||||
TransformerTestRunner.runLooper(transformer);
|
TransformerTestRunner.runLooper(transformer);
|
||||||
@ -360,8 +364,9 @@ public final class TransformerEndToEndTest {
|
|||||||
EditedMediaItemSequence sequence =
|
EditedMediaItemSequence sequence =
|
||||||
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
|
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
|
||||||
Composition composition =
|
Composition composition =
|
||||||
new Composition(
|
new Composition.Builder(ImmutableList.of(sequence))
|
||||||
ImmutableList.of(sequence), Effects.EMPTY, /* experimentalForceAudioTrack= */ true);
|
.experimentalSetForceAudioTrack(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
transformer.start(composition, outputPath);
|
transformer.start(composition, outputPath);
|
||||||
TransformerTestRunner.runLooper(transformer);
|
TransformerTestRunner.runLooper(transformer);
|
||||||
@ -390,15 +395,17 @@ public final class TransformerEndToEndTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void start_concatenateMediaItemsWithSameFormat_completesSuccessfully() throws Exception {
|
public void start_concatenateMediaItemsWithSameFormat_completesSuccessfully() throws Exception {
|
||||||
Transformer transformer =
|
Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
|
||||||
createTransformerBuilder(/* enableFallback= */ false).setTransmux(true).build();
|
|
||||||
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
|
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
|
||||||
EditedMediaItem editedMediaItem =
|
EditedMediaItem editedMediaItem =
|
||||||
new EditedMediaItem.Builder(mediaItem).setEffects(Effects.EMPTY).build();
|
new EditedMediaItem.Builder(mediaItem).setEffects(Effects.EMPTY).build();
|
||||||
EditedMediaItemSequence editedMediaItemSequence =
|
EditedMediaItemSequence editedMediaItemSequence =
|
||||||
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem, editedMediaItem));
|
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem, editedMediaItem));
|
||||||
Composition composition =
|
Composition composition =
|
||||||
new Composition(ImmutableList.of(editedMediaItemSequence), Effects.EMPTY);
|
new Composition.Builder(ImmutableList.of(editedMediaItemSequence))
|
||||||
|
.setTransmuxAudio(true)
|
||||||
|
.setTransmuxVideo(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
transformer.start(composition, outputPath);
|
transformer.start(composition, outputPath);
|
||||||
TransformerTestRunner.runLooper(transformer);
|
TransformerTestRunner.runLooper(transformer);
|
||||||
@ -425,7 +432,7 @@ public final class TransformerEndToEndTest {
|
|||||||
EditedMediaItemSequence editedMediaItemSequence =
|
EditedMediaItemSequence editedMediaItemSequence =
|
||||||
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem, editedMediaItem));
|
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem, editedMediaItem));
|
||||||
Composition composition =
|
Composition composition =
|
||||||
new Composition(ImmutableList.of(editedMediaItemSequence), Effects.EMPTY);
|
new Composition.Builder(ImmutableList.of(editedMediaItemSequence)).build();
|
||||||
|
|
||||||
transformer.start(composition, outputPath);
|
transformer.start(composition, outputPath);
|
||||||
TransformerTestRunner.runLooper(transformer);
|
TransformerTestRunner.runLooper(transformer);
|
||||||
@ -438,15 +445,20 @@ public final class TransformerEndToEndTest {
|
|||||||
public void start_singleMediaItemAndTransmux_ignoresTransmux() throws Exception {
|
public void start_singleMediaItemAndTransmux_ignoresTransmux() throws Exception {
|
||||||
SonicAudioProcessor sonicAudioProcessor = new SonicAudioProcessor();
|
SonicAudioProcessor sonicAudioProcessor = new SonicAudioProcessor();
|
||||||
sonicAudioProcessor.setOutputSampleRateHz(48000);
|
sonicAudioProcessor.setOutputSampleRateHz(48000);
|
||||||
Transformer transformer =
|
Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
|
||||||
createTransformerBuilder(/* enableFallback= */ false).setTransmux(true).build();
|
|
||||||
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
|
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
|
||||||
ImmutableList<AudioProcessor> audioProcessors = ImmutableList.of(sonicAudioProcessor);
|
ImmutableList<AudioProcessor> audioProcessors = ImmutableList.of(sonicAudioProcessor);
|
||||||
Effects effects = new Effects(audioProcessors, /* videoEffects= */ ImmutableList.of());
|
Effects effects = new Effects(audioProcessors, /* videoEffects= */ ImmutableList.of());
|
||||||
EditedMediaItem editedMediaItem =
|
EditedMediaItem editedMediaItem =
|
||||||
new EditedMediaItem.Builder(mediaItem).setEffects(effects).build();
|
new EditedMediaItem.Builder(mediaItem).setEffects(effects).build();
|
||||||
|
EditedMediaItemSequence editedMediaItemSequence =
|
||||||
|
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
|
||||||
|
Composition composition =
|
||||||
|
new Composition.Builder(ImmutableList.of(editedMediaItemSequence))
|
||||||
|
.setTransmuxAudio(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
transformer.start(editedMediaItem, outputPath);
|
transformer.start(composition, outputPath);
|
||||||
TransformerTestRunner.runLooper(transformer);
|
TransformerTestRunner.runLooper(transformer);
|
||||||
|
|
||||||
DumpFileAsserts.assertOutput(
|
DumpFileAsserts.assertOutput(
|
||||||
@ -455,8 +467,7 @@ public final class TransformerEndToEndTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void start_multipleMediaItemsWithEffectsAndTransmux_ignoresTransmux() throws Exception {
|
public void start_multipleMediaItemsWithEffectsAndTransmux_ignoresTransmux() throws Exception {
|
||||||
Transformer transformer =
|
Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
|
||||||
createTransformerBuilder(/* enableFallback= */ false).setTransmux(true).build();
|
|
||||||
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
|
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
|
||||||
AudioProcessor audioProcessor = new SilenceSkippingAudioProcessor();
|
AudioProcessor audioProcessor = new SilenceSkippingAudioProcessor();
|
||||||
Effects effects =
|
Effects effects =
|
||||||
@ -466,7 +477,10 @@ public final class TransformerEndToEndTest {
|
|||||||
EditedMediaItemSequence editedMediaItemSequence =
|
EditedMediaItemSequence editedMediaItemSequence =
|
||||||
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem, editedMediaItem));
|
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem, editedMediaItem));
|
||||||
Composition composition =
|
Composition composition =
|
||||||
new Composition(ImmutableList.of(editedMediaItemSequence), Effects.EMPTY);
|
new Composition.Builder(ImmutableList.of(editedMediaItemSequence))
|
||||||
|
.setTransmuxAudio(true)
|
||||||
|
.setTransmuxVideo(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
transformer.start(composition, outputPath);
|
transformer.start(composition, outputPath);
|
||||||
TransformerTestRunner.runLooper(transformer);
|
TransformerTestRunner.runLooper(transformer);
|
||||||
|
@ -44,12 +44,12 @@ import org.robolectric.shadows.ShadowMediaCodecList;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public final class VideoEncoderWrapperTest {
|
public final class VideoEncoderWrapperTest {
|
||||||
private static final Composition FAKE_COMPOSITION =
|
private static final Composition FAKE_COMPOSITION =
|
||||||
new Composition(
|
new Composition.Builder(
|
||||||
ImmutableList.of(
|
ImmutableList.of(
|
||||||
new EditedMediaItemSequence(
|
new EditedMediaItemSequence(
|
||||||
ImmutableList.of(
|
ImmutableList.of(
|
||||||
new EditedMediaItem.Builder(MediaItem.fromUri(Uri.EMPTY)).build()))),
|
new EditedMediaItem.Builder(MediaItem.fromUri(Uri.EMPTY)).build()))))
|
||||||
Effects.EMPTY);
|
.build();
|
||||||
|
|
||||||
private final TransformationRequest emptyTransformationRequest =
|
private final TransformationRequest emptyTransformationRequest =
|
||||||
new TransformationRequest.Builder().build();
|
new TransformationRequest.Builder().build();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user