Skip to content

[camera,video_player] replace onSurfaceDestroyed with onSurfaceCleanup #9316

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/camera/camera_android_camerax/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.6.17+1

* Replaces deprecated `onSurfaceDestroyed` with `onSurfaceCleanup`.

## 0.6.17

* Replaces `BroadcastReceiver` usage with an `OrientationEventListener` to detect changes in device
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,7 @@ public void onSurfaceAvailable() {
}

@Override
// TODO(bparrishMines): Replace with onSurfaceCleanup once available on stable. See
// https://github.com/flutter/flutter/issues/161256.
@SuppressWarnings({"deprecation", "removal"})
public void onSurfaceDestroyed() {
public void onSurfaceCleanup() {
// Invalidate the SurfaceRequest so that CameraX knows to to make a new request
// for a surface.
request.invalidate();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ TextureRegistry getTextureRegistry() {

final TextureRegistry.SurfaceProducer.Callback callback = callbackCaptor.getValue();

// Verify callback's onSurfaceDestroyed invalidates SurfaceRequest.
simulateSurfaceDestruction(callback);
// Verify callback's onSurfaceCleanup invalidates SurfaceRequest.
simulateSurfaceCleanup(callback);
verify(mockSurfaceRequest).invalidate();

reset(mockSurfaceRequest);
Expand Down Expand Up @@ -286,7 +286,7 @@ TextureRegistry getTextureRegistry() {
// see https://github.com/flutter/flutter/issues/16125. This separate method only exists to scope
// the suppression.
@SuppressWarnings({"deprecation", "removal"})
void simulateSurfaceDestruction(TextureRegistry.SurfaceProducer.Callback producerLifecycle) {
producerLifecycle.onSurfaceDestroyed();
void simulateSurfaceCleanup(TextureRegistry.SurfaceProducer.Callback producerLifecycle) {
producerLifecycle.onSurfaceCleanup();
}
}
2 changes: 1 addition & 1 deletion packages/camera/camera_android_camerax/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: camera_android_camerax
description: Android implementation of the camera plugin using the CameraX library.
repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
version: 0.6.17
version: 0.6.17+1

environment:
sdk: ^3.7.0
Expand Down
5 changes: 5 additions & 0 deletions packages/video_player/video_player_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 2.8.5

* Replaces deprecated `onSurfaceDestroyed` with `onSurfaceCleanup`.
* Restores background playback support.

## 2.8.4

* Fixes incorrect width/height swap ([bug](https://github.com/flutter/flutter/issues/166097)). The swap was originally required for the uncorrected width/height of `Format` but was mistakenly retained after [switching to `VideoSize`](https://github.com/flutter/packages/pull/6535), which already accounts for rotation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

public abstract class ExoPlayerEventListener implements Player.Listener {
private boolean isBuffering = false;
private boolean isInitialized;
private boolean isInitialized = false;
protected final ExoPlayer exoPlayer;
protected final VideoPlayerCallbacks events;

Expand Down Expand Up @@ -42,10 +42,9 @@ public int getDegrees() {
}

public ExoPlayerEventListener(
@NonNull ExoPlayer exoPlayer, @NonNull VideoPlayerCallbacks events, boolean initialized) {
@NonNull ExoPlayer exoPlayer, @NonNull VideoPlayerCallbacks events) {
this.exoPlayer = exoPlayer;
this.events = events;
this.isInitialized = initialized;
}

private void setBuffering(boolean buffering) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@
* <p>It provides methods to control playback, adjust volume, and handle seeking.
*/
public abstract class VideoPlayer {
@NonNull private final ExoPlayerProvider exoPlayerProvider;
@NonNull private final MediaItem mediaItem;
@NonNull private final VideoPlayerOptions options;
@NonNull protected final VideoPlayerCallbacks videoPlayerEvents;
@Nullable protected final SurfaceProducer surfaceProducer;
@NonNull protected ExoPlayer exoPlayer;
Expand All @@ -47,22 +44,12 @@ public VideoPlayer(
@Nullable SurfaceProducer surfaceProducer,
@NonNull ExoPlayerProvider exoPlayerProvider) {
this.videoPlayerEvents = events;
this.mediaItem = mediaItem;
this.options = options;
this.exoPlayerProvider = exoPlayerProvider;
this.surfaceProducer = surfaceProducer;
this.exoPlayer = createVideoPlayer();
}

@NonNull
protected ExoPlayer createVideoPlayer() {
ExoPlayer exoPlayer = exoPlayerProvider.get();
exoPlayer = exoPlayerProvider.get();
exoPlayer.setMediaItem(mediaItem);
exoPlayer.prepare();
exoPlayer.addListener(createExoPlayerEventListener(exoPlayer, surfaceProducer));
setAudioAttributes(exoPlayer, options.mixWithOthers);

return exoPlayer;
}

@NonNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import androidx.annotation.NonNull;
import androidx.annotation.OptIn;
import androidx.annotation.VisibleForTesting;
import androidx.media3.common.Format;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.ExoPlayer;
Expand All @@ -15,15 +14,9 @@
import java.util.Objects;

public final class PlatformViewExoPlayerEventListener extends ExoPlayerEventListener {
@VisibleForTesting
public PlatformViewExoPlayerEventListener(
@NonNull ExoPlayer exoPlayer, @NonNull VideoPlayerCallbacks events) {
this(exoPlayer, events, false);
}

public PlatformViewExoPlayerEventListener(
@NonNull ExoPlayer exoPlayer, @NonNull VideoPlayerCallbacks events, boolean initialized) {
super(exoPlayer, events, initialized);
super(exoPlayer, events);
}

@OptIn(markerClass = UnstableApi.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ public static PlatformViewVideoPlayer create(
@Override
protected ExoPlayerEventListener createExoPlayerEventListener(
@NonNull ExoPlayer exoPlayer, @Nullable SurfaceProducer surfaceProducer) {
// Platform view video player does not suspend and re-create the exoPlayer, hence initialized
// is always false. It also does not require a reference to the SurfaceProducer.
return new PlatformViewExoPlayerEventListener(exoPlayer, videoPlayerEvents, false);
return new PlatformViewExoPlayerEventListener(exoPlayer, videoPlayerEvents);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.OptIn;
import androidx.annotation.VisibleForTesting;
import androidx.media3.common.Format;
import androidx.media3.common.VideoSize;
import androidx.media3.exoplayer.ExoPlayer;
Expand All @@ -18,20 +17,11 @@
public final class TextureExoPlayerEventListener extends ExoPlayerEventListener {
private boolean surfaceProducerHandlesCropAndRotation;

@VisibleForTesting
public TextureExoPlayerEventListener(
@NonNull ExoPlayer exoPlayer,
@NonNull VideoPlayerCallbacks events,
boolean surfaceProducerHandlesCropAndRotation) {
this(exoPlayer, events, surfaceProducerHandlesCropAndRotation, false);
}

public TextureExoPlayerEventListener(
@NonNull ExoPlayer exoPlayer,
@NonNull VideoPlayerCallbacks events,
boolean surfaceProducerHandlesCropAndRotation,
boolean initialized) {
super(exoPlayer, events, initialized);
super(exoPlayer, events);
this.surfaceProducerHandlesCropAndRotation = surfaceProducerHandlesCropAndRotation;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
package io.flutter.plugins.videoplayer.texture;

import android.content.Context;
import android.view.Surface;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.media3.common.MediaItem;
import androidx.media3.exoplayer.ExoPlayer;
import io.flutter.plugins.videoplayer.ExoPlayerEventListener;
import io.flutter.plugins.videoplayer.ExoPlayerState;
import io.flutter.plugins.videoplayer.VideoAsset;
import io.flutter.plugins.videoplayer.VideoPlayer;
import io.flutter.plugins.videoplayer.VideoPlayerCallbacks;
Expand All @@ -27,8 +27,8 @@
* the texture.
*/
public final class TextureVideoPlayer extends VideoPlayer implements SurfaceProducer.Callback {
@Nullable private ExoPlayerState savedStateDuring;

// True when the ExoPlayer instance has a null surface.
private boolean needsSurface = true;
/**
* Creates a texture video player.
*
Expand Down Expand Up @@ -70,7 +70,9 @@ public TextureVideoPlayer(

surfaceProducer.setCallback(this);

this.exoPlayer.setVideoSurface(surfaceProducer.getSurface());
Surface surface = surfaceProducer.getSurface();
this.exoPlayer.setVideoSurface(surface);
needsSurface = surface == null;
}

@NonNull
Expand All @@ -83,44 +85,31 @@ protected ExoPlayerEventListener createExoPlayerEventListener(
}
boolean surfaceProducerHandlesCropAndRotation = surfaceProducer.handlesCropAndRotation();
return new TextureExoPlayerEventListener(
exoPlayer,
videoPlayerEvents,
surfaceProducerHandlesCropAndRotation,
playerHasBeenSuspended());
exoPlayer, videoPlayerEvents, surfaceProducerHandlesCropAndRotation);
}

@RestrictTo(RestrictTo.Scope.LIBRARY)
public void onSurfaceAvailable() {
if (savedStateDuring != null) {
exoPlayer = createVideoPlayer();
if (needsSurface) {
// TextureVideoPlayer must always set a surfaceProducer.
assert surfaceProducer != null;
exoPlayer.setVideoSurface(surfaceProducer.getSurface());
savedStateDuring.restore(exoPlayer);
savedStateDuring = null;
needsSurface = false;
}
}

@RestrictTo(RestrictTo.Scope.LIBRARY)
// TODO(bparrishMines): Replace with onSurfaceCleanup once available on stable. See
// https://github.com/flutter/flutter/issues/161256.
@SuppressWarnings({"deprecation", "removal"})
public void onSurfaceDestroyed() {
// Intentionally do not call pause/stop here, because the surface has already been released
// at this point (see https://github.com/flutter/flutter/issues/156451).
savedStateDuring = ExoPlayerState.save(exoPlayer);
exoPlayer.release();
}

private boolean playerHasBeenSuspended() {
return savedStateDuring != null;
public void onSurfaceCleanup() {
exoPlayer.setVideoSurface(null);
needsSurface = true;
}

public void dispose() {
// Super must be called first to ensure the player is released before the surface.
super.dispose();

// TextureVideoPlayer must always set a surfaceProducer.
assert surfaceProducer != null;
surfaceProducer.release();
// TODO(matanlurey): Remove when embedder no longer calls-back once released.
// https://github.com/flutter/flutter/issues/156434.
surfaceProducer.setCallback(null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public final class ExoPlayerEventListenerTest {
*/
private static final class TestExoPlayerEventListener extends ExoPlayerEventListener {
public TestExoPlayerEventListener(ExoPlayer exoPlayer, VideoPlayerCallbacks callbacks) {
super(exoPlayer, callbacks, false);
super(exoPlayer, callbacks);
}

@Override
Expand Down
Loading