Make forceAudioTrack flag mandatory when gap is at start

Earlier when gap is at the start of a sequence
it was automatically filled with silent audio.
Now setting forceAudioTrack flag is mandatory to
indicate that the gap at the start of a sequence
must be filled with silent audio.

PiperOrigin-RevId: 742625236
This commit is contained in:
sheenachhabra 2025-04-01 03:38:51 -07:00 committed by Copybara-Service
parent 3ab484d93f
commit 2141d9ef9c
7 changed files with 47 additions and 10 deletions

View File

@ -10,6 +10,10 @@
error during initialization of the next media item error during initialization of the next media item
([#2229](https://github.com/androidx/media/issues/2229)). ([#2229](https://github.com/androidx/media/issues/2229)).
* Transformer: * Transformer:
* Filling an initial gap (added via `addGap()`) with silent audio now
requires explicitly setting `setForceAudioTrack(true)` in
`EditedMediaItemSequence.Builder`. If the gap is in the middle of the
sequence, then this flag is not required.
* Track Selection: * Track Selection:
* Extractors: * Extractors:
* MP3: Use duration and data size from unseekable Xing, VBRI and similar * MP3: Use duration and data size from unseekable Xing, VBRI and similar

View File

@ -1887,6 +1887,7 @@ public class TransformerEndToEndTest {
new EditedMediaItemSequence.Builder() new EditedMediaItemSequence.Builder()
.addGap(100_000) .addGap(100_000)
.addItem(editedMediaItem) .addItem(editedMediaItem)
.setForceAudioTrack(true)
.build(), .build(),
new EditedMediaItemSequence.Builder(editedMediaItem).build()) new EditedMediaItemSequence.Builder(editedMediaItem).build())
.build(); .build();

View File

@ -149,8 +149,12 @@ public class TransformerGapsTest {
TransformerAndroidTestRunner transformerAndroidTestRunner = TransformerAndroidTestRunner transformerAndroidTestRunner =
new TransformerAndroidTestRunner.Builder(context, transformer).build(); new TransformerAndroidTestRunner.Builder(context, transformer).build();
// An IllegalStateException is thrown instead of an ExportException because the exception is
// thrown very early in the setup phase and its not caught.
// TODO: b/391111085 - Throw exception when the sequence without force audio/video flag is
// built.
assertThrows( assertThrows(
ExportException.class, () -> transformerAndroidTestRunner.run(testId, composition)); IllegalStateException.class, () -> transformerAndroidTestRunner.run(testId, composition));
} }
@Test @Test
@ -234,8 +238,12 @@ public class TransformerGapsTest {
TransformerAndroidTestRunner transformerAndroidTestRunner = TransformerAndroidTestRunner transformerAndroidTestRunner =
new TransformerAndroidTestRunner.Builder(context, transformer).build(); new TransformerAndroidTestRunner.Builder(context, transformer).build();
// An IllegalStateException is thrown instead of an ExportException because the exception is
// thrown very early in the setup phase and its not caught.
// TODO: b/391111085 - Throw exception when the sequence without force audio/video flag is
// built.
assertThrows( assertThrows(
ExportException.class, () -> transformerAndroidTestRunner.run(testId, composition)); IllegalStateException.class, () -> transformerAndroidTestRunner.run(testId, composition));
} }
@Test @Test

View File

@ -137,7 +137,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
Looper looper) { Looper looper) {
editedMediaItems = sequence.editedMediaItems; editedMediaItems = sequence.editedMediaItems;
isLooping = sequence.isLooping; isLooping = sequence.isLooping;
this.forceAudioTrack = sequence.forceAudioTrack || sequence.editedMediaItems.get(0).isGap(); this.forceAudioTrack = sequence.forceAudioTrack;
this.assetLoaderFactory = new GapInterceptingAssetLoaderFactory(assetLoaderFactory); this.assetLoaderFactory = new GapInterceptingAssetLoaderFactory(assetLoaderFactory);
this.compositionSettings = compositionSettings; this.compositionSettings = compositionSettings;
sequenceAssetLoaderListener = listener; sequenceAssetLoaderListener = listener;

View File

@ -481,7 +481,10 @@ public class CompositionExportTest {
new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_RAW)).build(); new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_RAW)).build();
Composition composition = Composition composition =
new Composition.Builder( new Composition.Builder(
new EditedMediaItemSequence.Builder().addGap(1_000_000).build(), new EditedMediaItemSequence.Builder()
.addGap(1_000_000)
.setForceAudioTrack(true)
.build(),
new EditedMediaItemSequence.Builder(audioItem1000ms).build()) new EditedMediaItemSequence.Builder(audioItem1000ms).build())
.build(); .build();
@ -512,6 +515,7 @@ public class CompositionExportTest {
new EditedMediaItemSequence.Builder() new EditedMediaItemSequence.Builder()
.addGap(100_000) .addGap(100_000)
.addItem(audioEditedMediaItem) .addItem(audioEditedMediaItem)
.setForceAudioTrack(true)
.build(), .build(),
new EditedMediaItemSequence.Builder(otherAudioEditedMediaItem).build()) new EditedMediaItemSequence.Builder(otherAudioEditedMediaItem).build())
.build(); .build();
@ -591,6 +595,7 @@ public class CompositionExportTest {
new EditedMediaItemSequence.Builder() new EditedMediaItemSequence.Builder()
.addGap(200_000) .addGap(200_000)
.addItem(audioEditedMediaItem) .addItem(audioEditedMediaItem)
.setForceAudioTrack(true)
.build()) .build())
.setTransmuxVideo(true) .setTransmuxVideo(true)
.build(); .build();
@ -716,7 +721,10 @@ public class CompositionExportTest {
Composition composition = Composition composition =
new Composition.Builder( new Composition.Builder(
new EditedMediaItemSequence.Builder(audioItem1000ms).build(), new EditedMediaItemSequence.Builder(audioItem1000ms).build(),
new EditedMediaItemSequence.Builder().addGap(1_000_000).build()) new EditedMediaItemSequence.Builder()
.addGap(1_000_000)
.setForceAudioTrack(true)
.build())
.build(); .build();
transformer.start(composition, outputDir.newFile().getPath()); transformer.start(composition, outputDir.newFile().getPath());
@ -733,8 +741,14 @@ public class CompositionExportTest {
new TestTransformerBuilder(context).setMuxerFactory(muxerFactory).build(); new TestTransformerBuilder(context).setMuxerFactory(muxerFactory).build();
Composition composition = Composition composition =
new Composition.Builder( new Composition.Builder(
new EditedMediaItemSequence.Builder().addGap(500_000).build(), new EditedMediaItemSequence.Builder()
new EditedMediaItemSequence.Builder().addGap(500_000).build()) .addGap(500_000)
.setForceAudioTrack(true)
.build(),
new EditedMediaItemSequence.Builder()
.addGap(500_000)
.setForceAudioTrack(true)
.build())
.build(); .build();
transformer.start(composition, outputDir.newFile().getPath()); transformer.start(composition, outputDir.newFile().getPath());

View File

@ -148,7 +148,7 @@ public final class MediaItemExportTest {
new TestTransformerBuilder(context).setMuxerFactory(muxerFactory).build(); new TestTransformerBuilder(context).setMuxerFactory(muxerFactory).build();
EditedMediaItemSequence gapSequence = EditedMediaItemSequence gapSequence =
new EditedMediaItemSequence.Builder().addGap(500_000).build(); new EditedMediaItemSequence.Builder().addGap(500_000).setForceAudioTrack(true).build();
transformer.start(new Composition.Builder(gapSequence).build(), outputDir.newFile().getPath()); transformer.start(new Composition.Builder(gapSequence).build(), outputDir.newFile().getPath());
ExportResult result = TransformerTestRunner.runLooper(transformer); ExportResult result = TransformerTestRunner.runLooper(transformer);

View File

@ -551,7 +551,11 @@ public final class SequenceExportTest {
new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_RAW_VIDEO)) new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_RAW_VIDEO))
.build(); .build();
EditedMediaItemSequence sequence = EditedMediaItemSequence sequence =
new EditedMediaItemSequence.Builder().addGap(500_000).addItem(audioVideoItem).build(); new EditedMediaItemSequence.Builder()
.addGap(500_000)
.addItem(audioVideoItem)
.setForceAudioTrack(true)
.build();
Composition composition = new Composition.Builder(sequence).build(); Composition composition = new Composition.Builder(sequence).build();
transformer.start(composition, outputDir.newFile().getPath()); transformer.start(composition, outputDir.newFile().getPath());
@ -567,7 +571,11 @@ public final class SequenceExportTest {
Transformer transformer = Transformer transformer =
new TestTransformerBuilder(context).setMuxerFactory(muxerFactory).build(); new TestTransformerBuilder(context).setMuxerFactory(muxerFactory).build();
EditedMediaItemSequence sequence = EditedMediaItemSequence sequence =
new EditedMediaItemSequence.Builder().addGap(300_000).addGap(200_000).build(); new EditedMediaItemSequence.Builder()
.addGap(300_000)
.addGap(200_000)
.setForceAudioTrack(true)
.build();
Composition composition = new Composition.Builder(sequence).build(); Composition composition = new Composition.Builder(sequence).build();
transformer.start(composition, outputDir.newFile().getPath()); transformer.start(composition, outputDir.newFile().getPath());
@ -614,6 +622,7 @@ public final class SequenceExportTest {
.addGap(200_000) .addGap(200_000)
.addGap(500_000) .addGap(500_000)
.addItem(audioItem) .addItem(audioItem)
.setForceAudioTrack(true)
.build(); .build();
Composition composition = new Composition.Builder(sequence).build(); Composition composition = new Composition.Builder(sequence).build();
@ -709,6 +718,7 @@ public final class SequenceExportTest {
.addItem(firstAudioItem) .addItem(firstAudioItem)
.addGap(200_000) .addGap(200_000)
.addItem(secondAudioItem) .addItem(secondAudioItem)
.setForceAudioTrack(true)
.build(); .build();
Composition composition = new Composition.Builder(sequence).build(); Composition composition = new Composition.Builder(sequence).build();