Allow setting a GlObjectsProvider on CompositionPlayer

PiperOrigin-RevId: 756300618
This commit is contained in:
claincly 2025-05-08 07:20:17 -07:00 committed by Copybara-Service
parent fd24449e7e
commit e6af3e49ca
2 changed files with 94 additions and 4 deletions

View File

@ -20,10 +20,14 @@ import static androidx.media3.transformer.AndroidTestUtil.JPG_SINGLE_PIXEL_ASSET
import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET; import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static com.google.common.util.concurrent.Futures.immediateFuture; import static com.google.common.util.concurrent.Futures.immediateFuture;
import static org.junit.Assert.assertThrows;
import android.app.Instrumentation; import android.app.Instrumentation;
import android.content.Context; import android.content.Context;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.util.Pair; import android.util.Pair;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import android.view.SurfaceView; import android.view.SurfaceView;
@ -33,8 +37,11 @@ import androidx.media3.common.ColorInfo;
import androidx.media3.common.DebugViewProvider; import androidx.media3.common.DebugViewProvider;
import androidx.media3.common.Effect; import androidx.media3.common.Effect;
import androidx.media3.common.Format; import androidx.media3.common.Format;
import androidx.media3.common.GlObjectsProvider;
import androidx.media3.common.GlTextureInfo;
import androidx.media3.common.MediaItem; import androidx.media3.common.MediaItem;
import androidx.media3.common.MimeTypes; import androidx.media3.common.MimeTypes;
import androidx.media3.common.PlaybackException;
import androidx.media3.common.VideoCompositorSettings; import androidx.media3.common.VideoCompositorSettings;
import androidx.media3.common.VideoGraph; import androidx.media3.common.VideoGraph;
import androidx.media3.common.audio.AudioProcessor; import androidx.media3.common.audio.AudioProcessor;
@ -449,6 +456,66 @@ public class CompositionPlayerTest {
listener.waitUntilPlayerEnded(); listener.waitUntilPlayerEnded();
} }
@Test
public void setGlObjectsProvider_withFailingImplementation_throws() {
PlayerTestListener listener = new PlayerTestListener(TEST_TIMEOUT_MS);
EditedMediaItem video =
new EditedMediaItem.Builder(MediaItem.fromUri(MP4_ASSET.uri))
.setDurationUs(MP4_ASSET.videoDurationUs)
.build();
instrumentation.runOnMainSync(
() -> {
compositionPlayer =
new CompositionPlayer.Builder(applicationContext)
.setGlObjectsProvider(
new GlObjectsProvider() {
@Override
public EGLContext createEglContext(
EGLDisplay eglDisplay, int openGlVersion, int[] configAttributes) {
throw new UnsupportedOperationException();
}
@Override
public EGLSurface createEglSurface(
EGLDisplay eglDisplay,
Object surface,
@C.ColorTransfer int colorTransfer,
boolean isEncoderInputSurface) {
throw new UnsupportedOperationException();
}
@Override
public EGLSurface createFocusedPlaceholderEglSurface(
EGLContext eglContext, EGLDisplay eglDisplay) {
throw new UnsupportedOperationException();
}
@Override
public GlTextureInfo createBuffersForTexture(
int texId, int width, int height) {
throw new UnsupportedOperationException();
}
@Override
public void release(EGLDisplay eglDisplay) {
throw new UnsupportedOperationException();
}
})
.build();
// Set a surface on the player even though there is no UI on this test. We need a surface
// otherwise the player will skip/drop video frames.
compositionPlayer.setVideoSurfaceView(surfaceView);
compositionPlayer.addListener(listener);
compositionPlayer.setComposition(
new Composition.Builder(new EditedMediaItemSequence.Builder(video).build()).build());
compositionPlayer.prepare();
compositionPlayer.play();
});
assertThrows(PlaybackException.class, listener::waitUntilPlayerEnded);
}
@Test @Test
public void release_videoGraphWrapperFailsDuringRelease_playerDoesNotRaiseError() public void release_videoGraphWrapperFailsDuringRelease_playerDoesNotRaiseError()
throws Exception { throws Exception {

View File

@ -38,6 +38,7 @@ import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.Effect; import androidx.media3.common.Effect;
import androidx.media3.common.GlObjectsProvider;
import androidx.media3.common.MediaItem; import androidx.media3.common.MediaItem;
import androidx.media3.common.PlaybackException; import androidx.media3.common.PlaybackException;
import androidx.media3.common.Player; import androidx.media3.common.Player;
@ -53,6 +54,7 @@ import androidx.media3.common.util.Log;
import androidx.media3.common.util.Size; import androidx.media3.common.util.Size;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import androidx.media3.effect.DefaultGlObjectsProvider;
import androidx.media3.effect.DefaultVideoFrameProcessor; import androidx.media3.effect.DefaultVideoFrameProcessor;
import androidx.media3.effect.SingleInputVideoGraph; import androidx.media3.effect.SingleInputVideoGraph;
import androidx.media3.effect.TimestampAdjustment; import androidx.media3.effect.TimestampAdjustment;
@ -128,6 +130,8 @@ public final class CompositionPlayer extends SimpleBasePlayer
private boolean videoPrewarmingEnabled; private boolean videoPrewarmingEnabled;
private Clock clock; private Clock clock;
private VideoGraph.@MonotonicNonNull Factory videoGraphFactory; private VideoGraph.@MonotonicNonNull Factory videoGraphFactory;
private @MonotonicNonNull GlObjectsProvider glObjectsProvider;
private boolean enableReplayableCache; private boolean enableReplayableCache;
private boolean built; private boolean built;
@ -250,6 +254,22 @@ public final class CompositionPlayer extends SimpleBasePlayer
return this; return this;
} }
/**
* Sets the {@link GlObjectsProvider} to be used by the effect processing pipeline.
*
* <p>Setting a {@link GlObjectsProvider} is no-op if a {@link VideoGraph.Factory} is
* {@linkplain #setVideoGraphFactory set}. By default, a {@link DefaultGlObjectsProvider} is
* used.
*
* @param glObjectsProvider The {@link GlObjectsProvider}.
* @return This builder, for convenience.
*/
@CanIgnoreReturnValue
public Builder setGlObjectsProvider(GlObjectsProvider glObjectsProvider) {
this.glObjectsProvider = glObjectsProvider;
return this;
}
/** /**
* Sets whether to enable replayable cache. * Sets whether to enable replayable cache.
* *
@ -281,11 +301,14 @@ public final class CompositionPlayer extends SimpleBasePlayer
audioSink = new DefaultAudioSink.Builder(context).build(); audioSink = new DefaultAudioSink.Builder(context).build();
} }
if (videoGraphFactory == null) { if (videoGraphFactory == null) {
DefaultVideoFrameProcessor.Factory.Builder videoFrameProcessorFactoryBuilder =
new DefaultVideoFrameProcessor.Factory.Builder()
.setEnableReplayableCache(enableReplayableCache);
if (glObjectsProvider != null) {
videoFrameProcessorFactoryBuilder.setGlObjectsProvider(glObjectsProvider);
}
videoGraphFactory = videoGraphFactory =
new SingleInputVideoGraph.Factory( new SingleInputVideoGraph.Factory(videoFrameProcessorFactoryBuilder.build());
new DefaultVideoFrameProcessor.Factory.Builder()
.setEnableReplayableCache(enableReplayableCache)
.build());
} }
CompositionPlayer compositionPlayer = new CompositionPlayer(this); CompositionPlayer compositionPlayer = new CompositionPlayer(this);
AnalyticsCollector analyticsCollector = new DefaultAnalyticsCollector(clock); AnalyticsCollector analyticsCollector = new DefaultAnalyticsCollector(clock);