Set MediaItem's image duration for image export

This field will become mandatory

PiperOrigin-RevId: 678656879
This commit is contained in:
kimvde 2024-09-25 05:20:13 -07:00 committed by Copybara-Service
parent f83d2b1392
commit eaec7a4a61
8 changed files with 69 additions and 53 deletions

View File

@ -121,6 +121,8 @@ import org.json.JSONObject;
/** An {@link Activity} that exports and plays media using {@link Transformer}. */
public final class TransformerActivity extends AppCompatActivity {
private static final String TAG = "TransformerActivity";
private static final int IMAGE_DURATION_MS = 5_000;
private static final int IMAGE_FRAME_RATE_FPS = 30;
private static int LOAD_CONTROL_MIN_BUFFER_MS = 5_000;
private static int LOAD_CONTROL_MAX_BUFFER_MS = 5_000;
@ -267,7 +269,8 @@ public final class TransformerActivity extends AppCompatActivity {
}
private MediaItem createMediaItem(@Nullable Bundle bundle, Uri uri) {
MediaItem.Builder mediaItemBuilder = new MediaItem.Builder().setUri(uri);
MediaItem.Builder mediaItemBuilder =
new MediaItem.Builder().setUri(uri).setImageDurationMs(IMAGE_DURATION_MS);
if (bundle != null) {
long trimStartMs =
bundle.getLong(ConfigurationActivity.TRIM_START_MS, /* defaultValue= */ C.TIME_UNSET);
@ -359,7 +362,7 @@ public final class TransformerActivity extends AppCompatActivity {
private Composition createComposition(MediaItem mediaItem, @Nullable Bundle bundle) {
EditedMediaItem.Builder editedMediaItemBuilder = new EditedMediaItem.Builder(mediaItem);
// For image inputs. Automatically ignored if input is audio/video.
editedMediaItemBuilder.setDurationUs(5_000_000).setFrameRate(30);
editedMediaItemBuilder.setFrameRate(IMAGE_FRAME_RATE_FPS);
if (bundle != null) {
ImmutableList<AudioProcessor> audioProcessors = createAudioProcessorsFromBundle(bundle);
ImmutableList<Effect> videoEffects = createVideoEffectsFromBundle(bundle);

View File

@ -105,7 +105,7 @@ import java.util.Objects;
protected final Effects effects;
private final String uri;
protected final String uri;
public ItemConfig(
String uri,
@ -120,20 +120,7 @@ import java.util.Objects;
this.effects = effects;
}
public final EditedMediaItem build() {
EditedMediaItem.Builder builder =
new EditedMediaItem.Builder(MediaItem.fromUri(uri)).setEffects(effects);
onBuild(builder);
return builder.build();
}
/**
* Called when an {@link EditedMediaItem} is being {@linkplain #build() built}.
*
* @param builder The {@link EditedMediaItem.Builder} to optionally modify before the item is
* built.
*/
protected abstract void onBuild(EditedMediaItem.Builder builder);
protected abstract EditedMediaItem build();
@Override
public String toString() {
@ -178,8 +165,13 @@ import java.util.Objects;
}
@Override
protected void onBuild(EditedMediaItem.Builder builder) {
builder.setFrameRate(frameRate).setDurationUs(durationUs);
protected EditedMediaItem build() {
MediaItem mediaItem =
new MediaItem.Builder().setUri(uri).setImageDurationMs(Util.usToMs(durationUs)).build();
return new EditedMediaItem.Builder(mediaItem)
.setEffects(effects)
.setFrameRate(frameRate)
.build();
}
}
@ -194,8 +186,11 @@ import java.util.Objects;
}
@Override
protected void onBuild(EditedMediaItem.Builder builder) {
builder.setRemoveAudio(true);
protected EditedMediaItem build() {
return new EditedMediaItem.Builder(MediaItem.fromUri(uri))
.setEffects(effects)
.setRemoveAudio(true)
.build();
}
}

View File

@ -102,10 +102,10 @@ public final class SequenceEffectTestUtil {
* effects} applied.
*/
public static EditedMediaItem oneFrameFromImage(String uri, List<Effect> effects) {
return new EditedMediaItem.Builder(MediaItem.fromUri(uri))
// 50ms for a 20-fps video is one frame.
return new EditedMediaItem.Builder(
new MediaItem.Builder().setUri(uri).setImageDurationMs(50).build())
.setFrameRate(20)
.setDurationUs(50_000)
.setEffects(
new Effects(/* audioProcessors= */ ImmutableList.of(), ImmutableList.copyOf(effects)))
.build();

View File

@ -145,8 +145,8 @@ public class TransformerEndToEndTest {
ImmutableList.of(RgbFilter.createInvertedFilter())))
.build();
EditedMediaItem imageItem =
new EditedMediaItem.Builder(MediaItem.fromUri(JPG_ASSET.uri))
.setDurationUs(1_500_000)
new EditedMediaItem.Builder(
new MediaItem.Builder().setUri(JPG_ASSET.uri).setImageDurationMs(1500).build())
.setFrameRate(30)
.build();
@ -209,8 +209,8 @@ public class TransformerEndToEndTest {
/* inputFormat= */ MP4_ASSET.videoFormat,
/* outputFormat= */ MP4_ASSET.videoFormat);
EditedMediaItem imageItem =
new EditedMediaItem.Builder(MediaItem.fromUri(JPG_ASSET.uri))
.setDurationUs(500_000)
new EditedMediaItem.Builder(
new MediaItem.Builder().setUri(JPG_ASSET.uri).setImageDurationMs(500).build())
.setFrameRate(30)
.build();
@ -251,9 +251,13 @@ public class TransformerEndToEndTest {
ImmutableList<Effect> videoEffects = ImmutableList.of(Presentation.createForHeight(480));
Effects effects = new Effects(/* audioProcessors= */ ImmutableList.of(), videoEffects);
int expectedFrameCount = 40;
MediaItem mediaItem =
new MediaItem.Builder()
.setUri(PNG_ASSET.uri)
.setImageDurationMs(C.MILLIS_PER_SECOND)
.build();
EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(MediaItem.fromUri(PNG_ASSET.uri))
.setDurationUs(C.MICROS_PER_SECOND)
new EditedMediaItem.Builder(mediaItem)
.setFrameRate(expectedFrameCount)
.setEffects(effects)
.build();
@ -275,8 +279,11 @@ public class TransformerEndToEndTest {
Transformer transformer = new Transformer.Builder(context).build();
int expectedFrameCount = 40;
EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(MediaItem.fromUri(PNG_ASSET.uri))
.setDurationUs(C.MICROS_PER_SECOND)
new EditedMediaItem.Builder(
new MediaItem.Builder()
.setUri(PNG_ASSET.uri)
.setImageDurationMs(C.MILLIS_PER_SECOND)
.build())
.setFrameRate(expectedFrameCount)
.build();
ExportTestResult result =
@ -299,8 +306,11 @@ public class TransformerEndToEndTest {
Effects effects = new Effects(/* audioProcessors= */ ImmutableList.of(), videoEffects);
int expectedFrameCount = 40;
EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(MediaItem.fromUri(WEBP_LARGE.uri))
.setDurationUs(C.MICROS_PER_SECOND)
new EditedMediaItem.Builder(
new MediaItem.Builder()
.setUri(WEBP_LARGE.uri)
.setImageDurationMs(C.MILLIS_PER_SECOND)
.build())
.setFrameRate(expectedFrameCount)
.setEffects(effects)
.build();
@ -516,14 +526,14 @@ public class TransformerEndToEndTest {
.build();
EditedMediaItem image1 =
new EditedMediaItem.Builder(MediaItem.fromUri(PNG_ASSET.uri))
.setDurationUs(100_000)
new EditedMediaItem.Builder(
new MediaItem.Builder().setUri(PNG_ASSET.uri).setImageDurationMs(100).build())
.setFrameRate(30)
.build();
int image1FrameCount = 3;
EditedMediaItem image2 =
new EditedMediaItem.Builder(MediaItem.fromUri(JPG_ASSET.uri))
.setDurationUs(200_000)
new EditedMediaItem.Builder(
new MediaItem.Builder().setUri(JPG_ASSET.uri).setImageDurationMs(200).build())
.setFrameRate(30)
.build();
int image2FrameCount = 6;
@ -1459,8 +1469,8 @@ public class TransformerEndToEndTest {
audioEditedMediaItem, audioEditedMediaItem, audioEditedMediaItem)
.build();
EditedMediaItem imageEditedMediaItem =
new EditedMediaItem.Builder(MediaItem.fromUri(PNG_ASSET.uri))
.setDurationUs(1_000_000)
new EditedMediaItem.Builder(
new MediaItem.Builder().setUri(PNG_ASSET.uri).setImageDurationMs(1000).build())
.setFrameRate(30)
.build();
EditedMediaItemSequence loopingImageSequence =
@ -1496,8 +1506,8 @@ public class TransformerEndToEndTest {
EditedMediaItemSequence audioSequence =
new EditedMediaItemSequence.Builder(audioEditedMediaItem).build();
EditedMediaItem imageEditedMediaItem =
new EditedMediaItem.Builder(MediaItem.fromUri(PNG_ASSET.uri))
.setDurationUs(1_050_000)
new EditedMediaItem.Builder(
new MediaItem.Builder().setUri(PNG_ASSET.uri).setImageDurationMs(1050).build())
.setFrameRate(20)
.build();
EditedMediaItemSequence loopingImageSequence =

View File

@ -17,7 +17,6 @@
package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Util.msToUs;
import static androidx.media3.test.utils.BitmapPixelTestUtil.MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_LUMA;
import static androidx.media3.test.utils.BitmapPixelTestUtil.getBitmapAveragePixelAbsoluteDifferenceArgb8888;
import static androidx.media3.test.utils.BitmapPixelTestUtil.maybeSaveTestBitmap;
@ -286,9 +285,9 @@ public final class TransformerMultiSequenceCompositionTest {
}
private static EditedMediaItem editedMediaItemOfOneFrameImage(String uri, List<Effect> effects) {
return new EditedMediaItem.Builder(MediaItem.fromUri(uri))
return new EditedMediaItem.Builder(
new MediaItem.Builder().setUri(uri).setImageDurationMs(ONE_FRAME_DURATION_MS).build())
.setRemoveAudio(true)
.setDurationUs(msToUs(ONE_FRAME_DURATION_MS))
.setFrameRate((int) (1000 / ONE_FRAME_DURATION_MS))
.setEffects(
new Effects(/* audioProcessors= */ ImmutableList.of(), ImmutableList.copyOf(effects)))

View File

@ -352,9 +352,12 @@ public final class TransformerSequenceEffectTest {
Composition composition =
createComposition(
/* presentation= */ null,
new EditedMediaItem.Builder(MediaItem.fromUri(PNG_ASSET_LINES_1080P.uri))
new EditedMediaItem.Builder(
new MediaItem.Builder()
.setUri(PNG_ASSET_LINES_1080P.uri)
.setImageDurationMs(C.MILLIS_PER_SECOND / 4)
.build())
.setFrameRate(30)
.setDurationUs(C.MICROS_PER_SECOND / 4)
.build());
// Some devices need a very high bitrate to avoid encoding artifacts.
int bitrate = 30_000_000;
@ -417,9 +420,12 @@ public final class TransformerSequenceEffectTest {
Composition composition =
createComposition(
/* presentation= */ null,
new EditedMediaItem.Builder(MediaItem.fromUri(PNG_ASSET_LINES_1080P.uri))
new EditedMediaItem.Builder(
new MediaItem.Builder()
.setUri(PNG_ASSET_LINES_1080P.uri)
.setImageDurationMs(C.MILLIS_PER_SECOND / 4)
.build())
.setFrameRate(30)
.setDurationUs(C.MICROS_PER_SECOND / 4)
.setEffects(
new Effects(
ImmutableList.of(),

View File

@ -130,9 +130,12 @@ public class TranscodeSpeedTest {
// This test uses ULTRA_HDR_URI_STRING because it's high resolution.
// Ultra HDR gainmap is ignored.
EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(MediaItem.fromUri(JPG_ULTRA_HDR_ASSET.uri))
new EditedMediaItem.Builder(
new MediaItem.Builder()
.setUri(JPG_ULTRA_HDR_ASSET.uri)
.setImageDurationMs(isHighPerformance ? 45_000 : 15_000)
.build())
.setFrameRate(30)
.setDurationUs(isHighPerformance ? 45_000_000 : 15_000_000)
.setEffects(
new Effects(
/* audioProcessors= */ ImmutableList.of(),

View File

@ -167,8 +167,8 @@ public class ImageAssetLoaderTest {
private static AssetLoader getAssetLoader(AssetLoader.Listener listener, String uri) {
Context context = ApplicationProvider.getApplicationContext();
EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(MediaItem.fromUri(uri))
.setDurationUs(1_000_000)
new EditedMediaItem.Builder(
new MediaItem.Builder().setUri(uri).setImageDurationMs(1000).build())
.setFrameRate(30)
.build();
return new ImageAssetLoader.Factory(