From 74c06dc2f482d890cf7d5c05a07f3f89f2f7e1d2 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Mon, 8 Jul 2024 11:30:14 -0700 Subject: [PATCH] Add `SurfaceAssetLoader` This supports queueing input to Transformer via a `Surface`. PiperOrigin-RevId: 650318396 --- RELEASENOTES.md | 2 + .../media3/common/VideoFrameProcessor.java | 19 ++ .../androidx/media3/common/VideoGraph.java | 2 +- .../effect/DefaultVideoFrameProcessor.java | 29 +++ .../src/test/assets/media/jpeg/london-512.jpg | Bin 0 -> 52076 bytes .../test/utils/BitmapPixelTestUtil.java | 22 ++ .../transformer/SurfaceAssetLoaderTest.java | 138 ++++++++++++ .../media3/transformer/SampleConsumer.java | 12 + .../transformer/SequenceAssetLoader.java | 5 + .../transformer/SurfaceAssetLoader.java | 209 ++++++++++++++++++ .../VideoFrameProcessingWrapper.java | 26 ++- 11 files changed, 461 insertions(+), 3 deletions(-) create mode 100644 libraries/test_data/src/test/assets/media/jpeg/london-512.jpg create mode 100644 libraries/transformer/src/androidTest/java/androidx/media3/transformer/SurfaceAssetLoaderTest.java create mode 100644 libraries/transformer/src/main/java/androidx/media3/transformer/SurfaceAssetLoader.java diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 498877bcc9..105ed420a0 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -20,6 +20,8 @@ `PreloadMediaSource.PreloadControl` implementations to take actions when error occurs. * Transformer: + * Add `SurfaceAssetLoader`, which supports queueing video data to + Transformer via a `Surface`. * Track Selection: * Extractors: * Allow `Mp4Extractor` to identify H264 samples that are not used as diff --git a/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java b/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java index 574946c36b..8ab45f7d39 100644 --- a/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java +++ b/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java @@ -19,6 +19,7 @@ import static java.lang.annotation.ElementType.TYPE_USE; import android.content.Context; import android.graphics.Bitmap; +import android.graphics.SurfaceTexture; import android.opengl.EGLExt; import android.view.Surface; import androidx.annotation.IntDef; @@ -222,6 +223,14 @@ public interface VideoFrameProcessor { */ void setOnInputFrameProcessedListener(OnInputFrameProcessedListener listener); + /** + * Sets a listener that's called when the {@linkplain #getInputSurface() input surface} is ready + * to use. + */ + void setOnInputSurfaceReadyListener(Runnable listener); + + // TODO: b/351776002 - Call setDefaultBufferSize on the INPUT_TYPE_SURFACE path too and remove + // mentions of the method (which leak an implementation detail) throughout this file. /** * Returns the input {@link Surface}, where {@link VideoFrameProcessor} consumes input frames * from. @@ -230,6 +239,16 @@ public interface VideoFrameProcessor { * VideoFrameProcessor} until {@link #registerInputStream} is called with {@link * #INPUT_TYPE_SURFACE}. * + *

For streams with {@link #INPUT_TYPE_SURFACE}, the returned surface is ready to use + * immediately and will not have a {@linkplain SurfaceTexture#setDefaultBufferSize(int, int) + * default buffer size} set on it. This is suitable for configuring a {@link + * android.media.MediaCodec} decoder. + * + *

For streams with {@link #INPUT_TYPE_SURFACE_AUTOMATIC_FRAME_REGISTRATION}, set a listener + * for the surface becoming ready via {@link #setOnInputSurfaceReadyListener(Runnable)} and wait + * for the event before using the returned surface. This is suitable for use with non-decoder + * producers like media projection. + * * @throws UnsupportedOperationException If the {@code VideoFrameProcessor} does not accept * {@linkplain #INPUT_TYPE_SURFACE surface input}. */ diff --git a/libraries/common/src/main/java/androidx/media3/common/VideoGraph.java b/libraries/common/src/main/java/androidx/media3/common/VideoGraph.java index 7163068f53..8b82433736 100644 --- a/libraries/common/src/main/java/androidx/media3/common/VideoGraph.java +++ b/libraries/common/src/main/java/androidx/media3/common/VideoGraph.java @@ -20,7 +20,7 @@ import androidx.annotation.IntRange; import androidx.annotation.Nullable; import androidx.media3.common.util.UnstableApi; -/** Represents a graph for processing decoded video frames. */ +/** Represents a graph for processing raw video frames. */ @UnstableApi public interface VideoGraph { diff --git a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java index dcf06ddc7b..c726deb701 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java @@ -76,6 +76,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** * A {@link VideoFrameProcessor} implementation that applies {@link GlEffect} instances using OpenGL * on a background thread. + * + *

When using surface input ({@link #INPUT_TYPE_SURFACE} or {@link + * #INPUT_TYPE_SURFACE_AUTOMATIC_FRAME_REGISTRATION}) the surface's format must be supported for + * sampling as an external texture in OpenGL. When a {@link android.media.MediaCodec} decoder is + * writing to the input surface, the default SDR color format is supported. When an {@link + * android.media.ImageWriter} is writing to the input surface, {@link + * android.graphics.PixelFormat#RGBA_8888} is supported for SDR data. Support for other formats may + * be device-dependent. */ @UnstableApi public final class DefaultVideoFrameProcessor implements VideoFrameProcessor { @@ -448,6 +456,10 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor { @GuardedBy("lock") private boolean registeredFirstInputStream; + @GuardedBy("lock") + @Nullable + private Runnable onInputSurfaceReadyListener; + private final List activeEffects; private final Object lock; private final ColorInfo outputColorInfo; @@ -569,6 +581,17 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor { inputSwitcher.setOnInputFrameProcessedListener(listener); } + @Override + public void setOnInputSurfaceReadyListener(Runnable listener) { + synchronized (lock) { + if (inputStreamRegisteredCondition.isOpen()) { + listener.run(); + } else { + onInputSurfaceReadyListener = listener; + } + } + } + @Override public Surface getInputSurface() { return inputSwitcher.getInputSurface(); @@ -992,6 +1015,12 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor { inputSwitcher.switchToInput(inputStreamInfo.inputType, inputStreamInfo.frameInfo); inputStreamRegisteredCondition.open(); + synchronized (lock) { + if (onInputSurfaceReadyListener != null) { + onInputSurfaceReadyListener.run(); + onInputSurfaceReadyListener = null; + } + } listenerExecutor.execute( () -> listener.onInputStreamRegistered( diff --git a/libraries/test_data/src/test/assets/media/jpeg/london-512.jpg b/libraries/test_data/src/test/assets/media/jpeg/london-512.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e71cec58fe1288789562c964775142bfedc47b00 GIT binary patch literal 52076 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 literal 0 HcmV?d00001 diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/BitmapPixelTestUtil.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/BitmapPixelTestUtil.java index 0c87049e1f..75a02bee73 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/BitmapPixelTestUtil.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/BitmapPixelTestUtil.java @@ -205,6 +205,28 @@ public class BitmapPixelTestUtil { plane.getPixelStride()); } + /** + * Copies image data from the specified {@link Bitmap} into the {@link Image}, which must be an + * {@linkplain PixelFormat#RGBA_8888} image. + */ + public static void copyRbga8888BitmapToImage(Bitmap bitmap, Image image) { + assertThat(image.getPlanes()).hasLength(1); + assertThat(image.getFormat()).isEqualTo(PixelFormat.RGBA_8888); + Image.Plane imagePlane = image.getPlanes()[0]; + ByteBuffer imageBuffer = imagePlane.getBuffer(); + for (int y = 0; y < bitmap.getHeight(); y++) { + for (int x = 0; x < bitmap.getWidth(); x++) { + int imageBufferOffset = y * imagePlane.getRowStride() + x * imagePlane.getPixelStride(); + int argbPixel = bitmap.getPixel(x, y); + imageBuffer.position(imageBufferOffset); + imageBuffer.put((byte) ((argbPixel >> 16) & 0xFF)); + imageBuffer.put((byte) ((argbPixel >> 8) & 0xFF)); + imageBuffer.put((byte) (argbPixel & 0xFF)); + imageBuffer.put((byte) ((argbPixel >> 24) & 0xFF)); + } + } + } + public static Bitmap createArgb8888BitmapFromRgba8888ImageBuffer(ImageBuffer imageBuffer) { int[] colors = new int[imageBuffer.width * imageBuffer.height]; for (int y = 0; y < imageBuffer.height; y++) { diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/SurfaceAssetLoaderTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/SurfaceAssetLoaderTest.java new file mode 100644 index 0000000000..3755d0b6d7 --- /dev/null +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/SurfaceAssetLoaderTest.java @@ -0,0 +1,138 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.transformer; + +import static com.google.common.truth.Truth.assertThat; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.junit.Assume.assumeTrue; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.PixelFormat; +import android.media.Image; +import android.media.ImageWriter; +import android.os.Handler; +import android.os.Looper; +import android.view.Surface; +import androidx.media3.common.C; +import androidx.media3.common.ColorInfo; +import androidx.media3.common.Format; +import androidx.media3.common.MediaItem; +import androidx.media3.common.MimeTypes; +import androidx.media3.common.util.ConditionVariable; +import androidx.media3.common.util.Util; +import androidx.media3.test.utils.BitmapPixelTestUtil; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.junit.runner.RunWith; + +/** End to end instrumentation test for {@link SurfaceAssetLoader} using {@link Transformer}. */ +@RunWith(AndroidJUnit4.class) +public class SurfaceAssetLoaderTest { + + // TODO: b/351776005 - Add HDR-based test case(s). + + private static final String TEST_BITMAP_PATH = "media/jpeg/london-512.jpg"; + private static final long TIMEOUT_MS = 10_000L; // Set to avoid timing out on slow emulators. + + @Rule public final TestName testName = new TestName(); + + private final Context context = ApplicationProvider.getApplicationContext(); + + private String testId; + + @Before + public void setUpTestId() { + testId = testName.getMethodName(); + } + + @Test + public void encodingFromSurface_succeeds() throws Exception { + assumeTrue("ImageWriter with pixel format set requires API 29", Util.SDK_INT >= 29); + + SettableFuture surfaceAssetLoaderSettableFuture = SettableFuture.create(); + SettableFuture surfaceSettableFuture = SettableFuture.create(); + Transformer transformer = + new Transformer.Builder(context) + .setAssetLoaderFactory( + new SurfaceAssetLoader.Factory( + new SurfaceAssetLoader.Callback() { + @Override + public void onSurfaceAssetLoaderCreated( + SurfaceAssetLoader surfaceAssetLoader) { + surfaceAssetLoaderSettableFuture.set(surfaceAssetLoader); + } + + @Override + public void onSurfaceReady(Surface surface, EditedMediaItem editedMediaItem) { + surfaceSettableFuture.set(surface); + } + })) + .build(); + EditedMediaItem editedMediaItem = + new EditedMediaItem.Builder( + MediaItem.fromUri(SurfaceAssetLoader.MEDIA_ITEM_URI_SCHEME + ":")) + .build(); + ListenableFuture exportCompletionFuture = + new TransformerAndroidTestRunner.Builder(context, transformer) + .build() + .runAsync(testId, editedMediaItem); + SurfaceAssetLoader surfaceAssetLoader = + surfaceAssetLoaderSettableFuture.get(TIMEOUT_MS, MILLISECONDS); + Bitmap bitmap = BitmapPixelTestUtil.readBitmap(TEST_BITMAP_PATH); + surfaceAssetLoader.setContentFormat( + new Format.Builder() + .setSampleMimeType(MimeTypes.VIDEO_RAW) + .setWidth(bitmap.getWidth()) + .setHeight(bitmap.getHeight()) + .setColorInfo(ColorInfo.SRGB_BT709_FULL) + .build()); + Surface surface = surfaceSettableFuture.get(TIMEOUT_MS, MILLISECONDS); + + int inputFrameCount = 10; + try (ImageWriter imageWriter = + ImageWriter.newInstance(surface, /* maxImages= */ inputFrameCount, PixelFormat.RGBA_8888)) { + ConditionVariable readyForInputCondition = new ConditionVariable(); + imageWriter.setOnImageReleasedListener( + unusedImageWriter -> readyForInputCondition.open(), new Handler(Looper.getMainLooper())); + for (int i = 0; i < inputFrameCount; i++) { + Image image = imageWriter.dequeueInputImage(); + image.setTimestamp(i * C.NANOS_PER_SECOND / 30); + BitmapPixelTestUtil.copyRbga8888BitmapToImage(bitmap, image); + readyForInputCondition.close(); + imageWriter.queueInputImage(image); + // When frames are queued as fast as possible some can be dropped, so throttle input by + // blocking until the previous frame has been released by the downstream pipeline. + if (i > 0) { + assertThat(readyForInputCondition.block(TIMEOUT_MS)).isTrue(); + } + } + } + surfaceAssetLoader.signalEndOfInput(); + + ExportResult exportResult = exportCompletionFuture.get(); + assertThat(exportResult.videoFrameCount).isEqualTo(inputFrameCount); + assertThat(exportResult.width).isEqualTo(bitmap.getWidth()); + assertThat(exportResult.height).isEqualTo(bitmap.getHeight()); + assertThat(exportResult.durationMs).isEqualTo(300); + } +} diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/SampleConsumer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/SampleConsumer.java index 3ab19b26a7..14dbe55430 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/SampleConsumer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/SampleConsumer.java @@ -133,6 +133,18 @@ public interface SampleConsumer { throw new UnsupportedOperationException(); } + /** + * Sets a listener that's called when the {@linkplain #getInputSurface() input surface} has been + * configured with a default input size, if applicable. + * + *

Should only be used for raw video data when input is provided by the app to a surface. + * + * @param runnable Listener that's called when the input surface is ready. + */ + default void setOnInputSurfaceReadyListener(Runnable runnable) { + throw new UnsupportedOperationException(); + } + /** * Attempts to provide an input texture to the consumer. * diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/SequenceAssetLoader.java b/libraries/transformer/src/main/java/androidx/media3/transformer/SequenceAssetLoader.java index 5103f02e71..beb77fab79 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/SequenceAssetLoader.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/SequenceAssetLoader.java @@ -482,6 +482,11 @@ import java.util.concurrent.atomic.AtomicInteger; sampleConsumer.setOnInputFrameProcessedListener(listener); } + @Override + public void setOnInputSurfaceReadyListener(Runnable runnable) { + sampleConsumer.setOnInputSurfaceReadyListener(runnable); + } + @Override public @InputResult int queueInputTexture(int texId, long presentationTimeUs) { long globalTimestampUs = totalDurationUs + presentationTimeUs; diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/SurfaceAssetLoader.java b/libraries/transformer/src/main/java/androidx/media3/transformer/SurfaceAssetLoader.java new file mode 100644 index 0000000000..380521edf8 --- /dev/null +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/SurfaceAssetLoader.java @@ -0,0 +1,209 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.transformer; + +import static androidx.media3.common.util.Assertions.checkArgument; +import static androidx.media3.common.util.Assertions.checkNotNull; +import static androidx.media3.common.util.Assertions.checkState; +import static androidx.media3.transformer.ExportException.ERROR_CODE_UNSPECIFIED; +import static androidx.media3.transformer.Transformer.PROGRESS_STATE_NOT_STARTED; + +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.view.Surface; +import androidx.media3.common.C; +import androidx.media3.common.Format; +import androidx.media3.common.MediaItem; +import androidx.media3.common.MimeTypes; +import androidx.media3.common.util.UnstableApi; +import com.google.common.collect.ImmutableMap; +import java.util.Objects; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; + +/** + * Asset loader that outputs video data passed to its input {@link Surface}. + * + *

To use this asset loader, pass a callback to the {@linkplain SurfaceAssetLoader.Factory + * factory's} constructor to get access to the underlying asset loader and {@link Surface} to write + * to once they are ready. Then pass the factory to {@link + * Transformer.Builder#setAssetLoaderFactory(AssetLoader.Factory)}. + * + *

The media item passed to transformer must have a URI starting with the scheme {@link + * #MEDIA_ITEM_URI_SCHEME}. + * + *

Call {@link #signalEndOfInput()} when the input stream ends, which will cause the + * transformation to complete. + */ +@UnstableApi +public final class SurfaceAssetLoader implements AssetLoader { + + /** + * URI scheme for creating a {@link MediaItem} that signals that the media is provided from this + * asset loader. + */ + public static final String MEDIA_ITEM_URI_SCHEME = "transformer_surface_asset"; + + /** Callbacks for {@link SurfaceAssetLoader} events. */ + public interface Callback { + /** + * Called when the asset loader has been created. Pass the {@linkplain #setContentFormat(Format) + * content format} to the provided asset loader to trigger surface creation. May be called on + * any thread. + */ + void onSurfaceAssetLoaderCreated(SurfaceAssetLoader surfaceAssetLoader); + + /** + * Called when the input surface is ready to write to. May be called on any thread. + * + * @param surface The {@link Surface} to write to. + * @param editedMediaItem The {@link EditedMediaItem} used to create the associated {@link + * SurfaceAssetLoader}. + */ + void onSurfaceReady(Surface surface, EditedMediaItem editedMediaItem); + } + + /** Factory for {@link SurfaceAssetLoader} instances. */ + public static final class Factory implements AssetLoader.Factory { + + private final Callback callback; + + /** Creates a factory with the specified callback. */ + public Factory(Callback callback) { + this.callback = callback; + } + + @Override + public AssetLoader createAssetLoader( + EditedMediaItem editedMediaItem, + Looper looper, + AssetLoader.Listener listener, + CompositionSettings compositionSettings) { + Uri uri = checkNotNull(editedMediaItem.mediaItem.localConfiguration).uri; + checkState(checkNotNull(uri.getScheme()).equals(MEDIA_ITEM_URI_SCHEME)); + SurfaceAssetLoader surfaceAssetLoader = + new SurfaceAssetLoader(editedMediaItem, looper, listener, callback); + callback.onSurfaceAssetLoaderCreated(surfaceAssetLoader); + return surfaceAssetLoader; + } + } + + private final EditedMediaItem editedMediaItem; + private final AssetLoader.Listener listener; + private final Handler handler; + private final Callback callback; + + private @Transformer.ProgressState int progressState; + + private boolean isStarted; + private boolean isVideoEndOfStreamSignaled; + private @MonotonicNonNull SampleConsumer sampleConsumer; + private @MonotonicNonNull Format contentFormat; + + private SurfaceAssetLoader( + EditedMediaItem editedMediaItem, + Looper looper, + AssetLoader.Listener listener, + Callback callback) { + this.editedMediaItem = editedMediaItem; + this.listener = listener; + this.callback = callback; + handler = new Handler(looper); + progressState = PROGRESS_STATE_NOT_STARTED; + } + + /** + * Sets the video content format, which must have a raw video sample MIME type, width, height and + * color info. May be called on any thread. + */ + public void setContentFormat(Format contentFormat) { + checkArgument(Objects.equals(contentFormat.sampleMimeType, MimeTypes.VIDEO_RAW)); + checkArgument(contentFormat.width != Format.NO_VALUE); + checkArgument(contentFormat.height != Format.NO_VALUE); + checkArgument(checkNotNull(contentFormat.colorInfo).isDataSpaceValid()); + handler.post( + () -> { + this.contentFormat = contentFormat; + try { + maybeFinishPreparation(); + } catch (RuntimeException e) { + listener.onError(ExportException.createForAssetLoader(e, ERROR_CODE_UNSPECIFIED)); + } + }); + } + + /** Returns the {@link EditedMediaItem} being loaded by this instance. */ + public EditedMediaItem getEditedMediaItem() { + return editedMediaItem; + } + + /** Signals that no further input frames will be rendered. May be called on any thread. */ + public void signalEndOfInput() { + handler.post( + () -> { + try { + if (!isVideoEndOfStreamSignaled && sampleConsumer != null) { + isVideoEndOfStreamSignaled = true; + sampleConsumer.signalEndOfVideoInput(); + } + } catch (RuntimeException e) { + listener.onError(ExportException.createForAssetLoader(e, ERROR_CODE_UNSPECIFIED)); + } + }); + } + + // AssetLoader implementation. + + @Override + public void start() { + isStarted = true; + maybeFinishPreparation(); + } + + @Override + public @Transformer.ProgressState int getProgress(ProgressHolder progressHolder) { + return progressState; + } + + @Override + public ImmutableMap getDecoderNames() { + return ImmutableMap.of(); + } + + @Override + public void release() { + // Do nothing. + } + + private void maybeFinishPreparation() { + if (!isStarted || contentFormat == null) { + return; + } + listener.onTrackCount(1); + listener.onDurationUs(C.TIME_UNSET); + listener.onTrackAdded(contentFormat, SUPPORTED_OUTPUT_TYPE_DECODED); + try { + sampleConsumer = checkNotNull(listener.onOutputFormat(contentFormat)); + sampleConsumer.setOnInputSurfaceReadyListener( + () -> + callback.onSurfaceReady( + checkNotNull(sampleConsumer).getInputSurface(), editedMediaItem)); + } catch (ExportException e) { + listener.onError(e); + } + progressState = Transformer.PROGRESS_STATE_UNAVAILABLE; + } +} diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoFrameProcessingWrapper.java b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoFrameProcessingWrapper.java index 493b2ef6aa..e4fcb6bed2 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoFrameProcessingWrapper.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoFrameProcessingWrapper.java @@ -27,6 +27,7 @@ import androidx.annotation.Nullable; import androidx.media3.common.Effect; import androidx.media3.common.Format; import androidx.media3.common.FrameInfo; +import androidx.media3.common.MediaItem; import androidx.media3.common.MimeTypes; import androidx.media3.common.OnInputFrameProcessedListener; import androidx.media3.common.VideoFrameProcessor; @@ -60,11 +61,14 @@ import java.util.concurrent.atomic.AtomicLong; long durationUs, @Nullable Format decodedFormat, boolean isLast) { + boolean isSurfaceAssetLoaderMediaItem = isMediaItemForSurfaceAssetLoader(editedMediaItem); durationUs = editedMediaItem.getDurationAfterEffectsApplied(durationUs); if (decodedFormat != null) { Size decodedSize = getDecodedSize(decodedFormat); videoFrameProcessor.registerInputStream( - getInputType(checkNotNull(decodedFormat.sampleMimeType)), + isSurfaceAssetLoaderMediaItem + ? VideoFrameProcessor.INPUT_TYPE_SURFACE_AUTOMATIC_FRAME_REGISTRATION + : getInputTypeForMimeType(checkNotNull(decodedFormat.sampleMimeType)), createEffectListWithPresentation(editedMediaItem.effects.videoEffects, presentation), new FrameInfo.Builder( checkNotNull(decodedFormat.colorInfo), @@ -90,6 +94,11 @@ import java.util.concurrent.atomic.AtomicLong; videoFrameProcessor.setOnInputFrameProcessedListener(listener); } + @Override + public void setOnInputSurfaceReadyListener(Runnable runnable) { + videoFrameProcessor.setOnInputSurfaceReadyListener(runnable); + } + @Override public @InputResult int queueInputTexture(int texId, long presentationTimeUs) { return videoFrameProcessor.queueInputTexture(texId, presentationTimeUs) @@ -138,7 +147,7 @@ import java.util.concurrent.atomic.AtomicLong; return effectsWithPresentationBuilder.build(); } - private static @VideoFrameProcessor.InputType int getInputType(String sampleMimeType) { + private static @VideoFrameProcessor.InputType int getInputTypeForMimeType(String sampleMimeType) { if (MimeTypes.isImage(sampleMimeType)) { return INPUT_TYPE_BITMAP; } @@ -150,4 +159,17 @@ import java.util.concurrent.atomic.AtomicLong; } throw new IllegalArgumentException("MIME type not supported " + sampleMimeType); } + + private static boolean isMediaItemForSurfaceAssetLoader(EditedMediaItem editedMediaItem) { + @Nullable + MediaItem.LocalConfiguration localConfiguration = editedMediaItem.mediaItem.localConfiguration; + if (localConfiguration == null) { + return false; + } + @Nullable String scheme = localConfiguration.uri.getScheme(); + if (scheme == null) { + return false; + } + return scheme.equals(SurfaceAssetLoader.MEDIA_ITEM_URI_SCHEME); + } }