diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java index 917563f754..6a101eff94 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/CompositingVideoSinkProvider.java @@ -18,6 +18,7 @@ package androidx.media3.exoplayer.video; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkStateNotNull; +import static java.lang.annotation.ElementType.TYPE_USE; import android.content.Context; import android.graphics.Bitmap; @@ -25,6 +26,7 @@ import android.os.Looper; import android.util.Pair; import android.view.Surface; import androidx.annotation.FloatRange; +import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.media3.common.C; import androidx.media3.common.ColorInfo; @@ -50,6 +52,10 @@ import androidx.media3.exoplayer.video.VideoFrameReleaseControl.FrameTimingEvalu import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; @@ -199,6 +205,16 @@ public final class CompositingVideoSinkProvider } } + @Documented + @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) + @IntDef({STATE_CREATED, STATE_INITIALIZED, STATE_RELEASED}) + private @interface State {} + + private static final int STATE_CREATED = 0; + private static final int STATE_INITIALIZED = 1; + private static final int STATE_RELEASED = 2; + private static final Executor NO_OP_EXECUTOR = runnable -> {}; private final Context context; @@ -217,7 +233,7 @@ public final class CompositingVideoSinkProvider private VideoSink.Listener listener; private Executor listenerExecutor; private int pendingFlushCount; - private boolean released; + private @State int state; private CompositingVideoSinkProvider(Builder builder) { this.context = builder.context; @@ -230,13 +246,14 @@ public final class CompositingVideoSinkProvider clock = Clock.DEFAULT; listener = VideoSink.Listener.NO_OP; listenerExecutor = NO_OP_EXECUTOR; + state = STATE_CREATED; } // VideoSinkProvider methods @Override public void initialize(Format sourceFormat) throws VideoSink.VideoSinkException { - checkState(!released && videoSinkImpl == null); + checkState(state == STATE_CREATED); checkStateNotNull(videoEffects); // Lazily initialize the handler here so it's initialized on the playback looper. @@ -272,18 +289,20 @@ public final class CompositingVideoSinkProvider throw new VideoSink.VideoSinkException(e, sourceFormat); } videoSinkImpl.setVideoEffects(checkNotNull(videoEffects)); + state = STATE_INITIALIZED; } @Override public boolean isInitialized() { - return videoSinkImpl != null; + return state == STATE_INITIALIZED; } @Override public void release() { - if (released) { + if (state == STATE_RELEASED) { return; } + if (handler != null) { handler.removeCallbacksAndMessages(/* token= */ null); } @@ -292,7 +311,7 @@ public final class CompositingVideoSinkProvider videoGraph.release(); } currentSurfaceAndSize = null; - released = true; + state = STATE_RELEASED; } @Override diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/CompositingVideoSinkProviderTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/CompositingVideoSinkProviderTest.java index d2ba7e4616..4c7473f33b 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/CompositingVideoSinkProviderTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/CompositingVideoSinkProviderTest.java @@ -86,6 +86,17 @@ public final class CompositingVideoSinkProviderTest { IllegalStateException.class, () -> provider.initialize(new Format.Builder().build())); } + @Test + public void isInitialized_afterRelease_returnsFalse() throws VideoSink.VideoSinkException { + CompositingVideoSinkProvider provider = createCompositingVideoSinkProvider(); + provider.setVideoEffects(ImmutableList.of()); + provider.initialize(new Format.Builder().build()); + + provider.release(); + + assertThat(provider.isInitialized()).isFalse(); + } + @Test public void initialize_afterRelease_throws() throws VideoSink.VideoSinkException { CompositingVideoSinkProvider provider = createCompositingVideoSinkProvider();