Commit 45c42ed3 by kimvde Committed by microkatz

Add an API entry point to pass a Composition

PiperOrigin-RevId: 508031337
parent 79b688ef
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.transformer; package com.google.android.exoplayer2.transformer;
import static com.google.android.exoplayer2.util.Assertions.checkArgument;
import static com.google.android.exoplayer2.util.Assertions.checkState; import static com.google.android.exoplayer2.util.Assertions.checkState;
import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.ElementType.TYPE_USE;
...@@ -674,38 +675,55 @@ public final class Transformer { ...@@ -674,38 +675,55 @@ public final class Transformer {
} }
/** /**
* Starts an asynchronous operation to export the given {@link EditedMediaItem}. * Starts an asynchronous operation to export the given {@link Composition}.
*
* <p>This method is under implementation. Concatenating audio/video inputs with the same format
* and the same {@link Effects} applied is the only supported use case. More precisely:
*
* <ul>
* <li>The {@link Composition} must contain exactly one {@link Composition#sequences
* EditedMediaItemSequence} and its {@link Composition#effects Effects} must be {@linkplain
* Effects#EMPTY empty}.
* <li>The {@link EditedMediaItem} instances in the {@link EditedMediaItemSequence} must:
* <ul>
* <li>have identical tracks of the same format (after {@linkplain
* EditedMediaItem#removeAudio audio} or {@linkplain EditedMediaItem#removeVideo
* video} removal and {@linkplain EditedMediaItem#flattenForSlowMotion slow motion
* flattening}).
* <li>have identical {@link EditedMediaItem#effects Effects} applied.
* <li>not represent an image.
* <li>be such that either all or none requires transcoding.
* </ul>
* </ul>
* *
* <p>The export state is notified through the {@linkplain Builder#addListener(Listener) * <p>The export state is notified through the {@linkplain Builder#addListener(Listener)
* listener}. * listener}.
* *
* <p>Concurrent exports on the same Transformer object are not allowed. * <p>Concurrent exports on the same Transformer object are not allowed.
* *
* <p>If no custom {@link Muxer.Factory} is specified, the output is an MP4 file. * <p>If no custom {@link Transformer.Builder#setMuxerFactory(Muxer.Factory) Muxer.Factory} is
* specified, the output is an MP4 file.
* *
* <p>The output can contain at most one video track and one audio track. Other track types are * <p>The output can contain at most one video track and one audio track. Other track types are
* ignored. For adaptive bitrate, if no custom {@link AssetLoader.Factory} is specified, the * ignored. For adaptive bitrate inputs, if no custom {@link
* highest bitrate video and audio streams are selected. * Transformer.Builder#setAssetLoaderFactory(AssetLoader.Factory) AssetLoader.Factory} is
* specified, the highest bitrate video and audio streams are selected.
* *
* <p>If encoding the output's video track is needed, the output frames' dimensions will be * <p>If exporting the video track entails transcoding, the output frames' dimensions will be
* swapped if the height is larger than the width. This is to improve compatibility among * swapped if the output video's height is larger than the width. This is to improve compatibility
* different device encoders. * among different device encoders.
* *
* @param editedMediaItem The {@link MediaItem} to export, with the transformations to apply to * @param composition The {@link Composition} to export.
* it.
* @param path The path to the output file. * @param path The path to the output file.
* @throws IllegalArgumentException If the path is invalid.
* @throws IllegalStateException If this method is called from the wrong thread. * @throws IllegalStateException If this method is called from the wrong thread.
* @throws IllegalStateException If an export is already in progress. * @throws IllegalStateException If an export is already in progress.
*/ */
public void start(EditedMediaItem editedMediaItem, String path) { public void start(Composition composition, String path) {
checkArgument(composition.sequences.size() == 1);
checkArgument(composition.effects == Effects.EMPTY);
verifyApplicationThread(); verifyApplicationThread();
if (transformerInternal != null) { checkState(transformerInternal == null, "There is already a export in progress.");
throw new IllegalStateException("There is already a export in progress.");
}
EditedMediaItemSequence sequence =
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
Composition composition = new Composition(ImmutableList.of(sequence), Effects.EMPTY);
TransformerInternalListener transformerInternalListener = TransformerInternalListener transformerInternalListener =
new TransformerInternalListener(composition); new TransformerInternalListener(composition);
HandlerWrapper applicationHandler = clock.createHandler(looper, /* callback= */ null); HandlerWrapper applicationHandler = clock.createHandler(looper, /* callback= */ null);
...@@ -730,6 +748,37 @@ public final class Transformer { ...@@ -730,6 +748,37 @@ public final class Transformer {
} }
/** /**
* Starts an asynchronous operation to export the given {@link EditedMediaItem}.
*
* <p>The export state is notified through the {@linkplain Builder#addListener(Listener)
* listener}.
*
* <p>Concurrent exports on the same Transformer object are not allowed.
*
* <p>If no custom {@link Transformer.Builder#setMuxerFactory(Muxer.Factory) Muxer.Factory} is
* specified, the output is an MP4 file.
*
* <p>The output can contain at most one video track and one audio track. Other track types are
* ignored. For adaptive bitrate inputs, if no custom {@link
* Transformer.Builder#setAssetLoaderFactory(AssetLoader.Factory) AssetLoader.Factory} is
* specified, the highest bitrate video and audio streams are selected.
*
* <p>If exporting the video track entails transcoding, the output frames' dimensions will be
* swapped if the output video's height is larger than the width. This is to improve compatibility
* among different device encoders.
*
* @param editedMediaItem The {@link EditedMediaItem} to export.
* @param path The path to the output file.
* @throws IllegalStateException If this method is called from the wrong thread.
* @throws IllegalStateException If an export is already in progress.
*/
public void start(EditedMediaItem editedMediaItem, String path) {
EditedMediaItemSequence sequence =
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
start(new Composition(ImmutableList.of(sequence), Effects.EMPTY), path);
}
/**
* Starts an asynchronous operation to export the given {@link MediaItem}. * Starts an asynchronous operation to export the given {@link MediaItem}.
* *
* <p>The export state is notified through the {@linkplain Builder#addListener(Listener) * <p>The export state is notified through the {@linkplain Builder#addListener(Listener)
...@@ -737,19 +786,20 @@ public final class Transformer { ...@@ -737,19 +786,20 @@ public final class Transformer {
* *
* <p>Concurrent exports on the same Transformer object are not allowed. * <p>Concurrent exports on the same Transformer object are not allowed.
* *
* <p>If no custom {@link Muxer.Factory} is specified, the output is an MP4 file. * <p>If no custom {@link Transformer.Builder#setMuxerFactory(Muxer.Factory) Muxer.Factory} is
* specified, the output is an MP4 file.
* *
* <p>The output can contain at most one video track and one audio track. Other track types are * <p>The output can contain at most one video track and one audio track. Other track types are
* ignored. For adaptive bitrate, if no custom {@link AssetLoader.Factory} is specified, the * ignored. For adaptive bitrate inputs, if no custom {@link
* highest bitrate video and audio streams are selected. * Transformer.Builder#setAssetLoaderFactory(AssetLoader.Factory) AssetLoader.Factory} is
* specified, the highest bitrate video and audio streams are selected.
* *
* <p>If encoding the output's video track is needed, the output frames' dimensions will be * <p>If exporting the video track entails transcoding, the output frames' dimensions will be
* swapped if the height is larger than the width. This is to improve compatibility among * swapped if the output video's height is larger than the width. This is to improve compatibility
* different device encoders. * among different device encoders.
* *
* @param mediaItem The {@link MediaItem} to export. * @param mediaItem The {@link MediaItem} to export.
* @param path The path to the output file. * @param path The path to the output file.
* @throws IllegalArgumentException If the path is invalid.
* @throws IllegalArgumentException If the {@link MediaItem} is not supported. * @throws IllegalArgumentException If the {@link MediaItem} is not supported.
* @throws IllegalStateException If this method is called from the wrong thread. * @throws IllegalStateException If this method is called from the wrong thread.
* @throws IllegalStateException If an export is already in progress. * @throws IllegalStateException If an export is already in progress.
......
...@@ -47,6 +47,7 @@ import com.google.android.exoplayer2.C; ...@@ -47,6 +47,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.audio.AudioProcessor; import com.google.android.exoplayer2.audio.AudioProcessor;
import com.google.android.exoplayer2.audio.SilenceSkippingAudioProcessor;
import com.google.android.exoplayer2.audio.SonicAudioProcessor; import com.google.android.exoplayer2.audio.SonicAudioProcessor;
import com.google.android.exoplayer2.effect.Presentation; import com.google.android.exoplayer2.effect.Presentation;
import com.google.android.exoplayer2.effect.ScaleToFitTransformation; import com.google.android.exoplayer2.effect.ScaleToFitTransformation;
...@@ -376,6 +377,53 @@ public final class TransformerEndToEndTest { ...@@ -376,6 +377,53 @@ public final class TransformerEndToEndTest {
} }
@Test @Test
public void startTransformation_concatenateMediaItemsWithSameFormat_completesSuccessfully()
throws Exception {
Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(mediaItem).setEffects(Effects.EMPTY).build();
EditedMediaItemSequence editedMediaItemSequence =
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem, editedMediaItem));
Composition composition =
new Composition(ImmutableList.of(editedMediaItemSequence), Effects.EMPTY);
transformer.start(composition, outputPath);
TransformerTestRunner.runLooper(transformer);
DumpFileAsserts.assertOutput(
context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".concatenated"));
}
@Test
public void
startTransformation_concatenateMediaItemsWithSameFormatAndEffects_completesSuccessfully()
throws Exception {
Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
AudioProcessor audioProcessor = new SilenceSkippingAudioProcessor();
Effects effects =
new Effects(ImmutableList.of(audioProcessor), /* videoEffects= */ ImmutableList.of());
// The video track must be removed in order for the export to end. Indeed, the
// Robolectric decoder just copies the input buffers to the output and the audio timestamps are
// therefore computed based on the encoded samples. As a result, the audio timestamps are much
// smaller than they should be and the muxer waits for more audio samples before writing video
// samples.
EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(mediaItem).setEffects(effects).setRemoveVideo(true).build();
EditedMediaItemSequence editedMediaItemSequence =
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem, editedMediaItem));
Composition composition =
new Composition(ImmutableList.of(editedMediaItemSequence), Effects.EMPTY);
transformer.start(composition, outputPath);
TransformerTestRunner.runLooper(transformer);
DumpFileAsserts.assertOutput(
context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".silence_skipped_concatenated"));
}
@Test
public void startTransformation_withMultipleListeners_callsEachOnCompletion() throws Exception { public void startTransformation_withMultipleListeners_callsEachOnCompletion() throws Exception {
Transformer.Listener mockListener1 = mock(Transformer.Listener.class); Transformer.Listener mockListener1 = mock(Transformer.Listener.class);
Transformer.Listener mockListener2 = mock(Transformer.Listener.class); Transformer.Listener mockListener2 = mock(Transformer.Listener.class);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment