From 47cee977cb091e1b5b32f9d3e082bb8f10015637 Mon Sep 17 00:00:00 2001 From: Camille Simon Date: Thu, 29 May 2025 09:46:09 -0700 Subject: [PATCH 1/6] logging for debugging --- .../flutter/plugins/camerax/PreviewProxyApi.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java index 8fedd79f245..cb7f2506d38 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java @@ -15,6 +15,8 @@ import java.util.Map; import java.util.concurrent.Executors; +import android.util.Log; + /** * ProxyApi implementation for {@link Preview}. This class may handle instantiating native object * instances that are attached to a Dart instance or handle method calls on the associated native @@ -94,6 +96,9 @@ public void setTargetRotation(Preview pigeonInstance, long rotation) { pigeonInstance.setTargetRotation((int) rotation); } + int surfaceRequestNum = 0; + int resultNum = 0; + @NonNull Preview.SurfaceProvider createSurfaceProvider( @NonNull TextureRegistry.SurfaceProducer surfaceProducer, @@ -117,6 +122,7 @@ public void onSurfaceCleanup() { }); // Provide surface. + surfaceProducer.invalidateSurface(); surfaceProducer.setSize( request.getResolution().getWidth(), request.getResolution().getHeight()); Surface flutterSurface = surfaceProducer.getSurface(); @@ -124,6 +130,11 @@ public void onSurfaceCleanup() { flutterSurface, Executors.newSingleThreadExecutor(), (result) -> { + Log.e("CAMILLE RESULT #:", "--------------------------------------------------------------"); + Log.e("CAMILLE RESULT #:", Integer.toString(resultNum)); + Log.e("CAMILLE SURFACE HASHCODE:", Integer.toString(flutterSurface.hashCode())); + Log.e("CAMILLE RESULT #:", "--------------------------------------------------------------"); + resultNum++; // See // https://developer.android.com/reference/androidx/camera/core/SurfaceRequest.Result // for documentation. @@ -142,6 +153,11 @@ public void onSurfaceCleanup() { systemServicesManager.onCameraError(getProvideSurfaceErrorDescription(resultCode)); } }); + Log.e("CAMILLE RESULT #:", "--------------------------------------------------------------"); + Log.e("CAMILLE SURFACE REQUEST #:", Integer.toString(surfaceRequestNum)); + Log.e("CAMILLE SURFACE HASHCODE:", Integer.toString(flutterSurface.hashCode())); + Log.e("CAMILLE RESULT #:", "--------------------------------------------------------------"); + surfaceRequestNum++; }; } From c73110fcbac4a905ddedde1c7db90ef7b802ea15 Mon Sep 17 00:00:00 2001 From: Camille Simon Date: Thu, 29 May 2025 10:32:45 -0700 Subject: [PATCH 2/6] debugging logging pt 2 --- .../plugins/camerax/ProcessCameraProviderProxyApi.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderProxyApi.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderProxyApi.java index 86e62ec661a..547939676ac 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderProxyApi.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderProxyApi.java @@ -18,6 +18,8 @@ import kotlin.Unit; import kotlin.jvm.functions.Function1; +import android.util.Log; + /** * ProxyApi implementation for {@link ProcessCameraProvider}. This class may handle instantiating * native object instances that are attached to a Dart instance or handle method calls on the @@ -64,6 +66,7 @@ public Camera bindToLifecycle( @NonNull ProcessCameraProvider pigeonInstance, @NonNull CameraSelector cameraSelector, @NonNull List useCases) { + Log.e("CAMILLE"," BIND CALLED!!!!!!!!!!!!!!!****************************"); final LifecycleOwner lifecycleOwner = getPigeonRegistrar().getLifecycleOwner(); if (lifecycleOwner != null) { return pigeonInstance.bindToLifecycle( @@ -82,6 +85,7 @@ public boolean isBound(ProcessCameraProvider pigeonInstance, @NonNull UseCase us @Override public void unbind( ProcessCameraProvider pigeonInstance, @NonNull List useCases) { + Log.e("CAMILLE"," UNBIND CALLED!!!!!!!!!!!!!!!****************************"); pigeonInstance.unbind(useCases.toArray(new UseCase[0])); } From 8d5223dc8a2d9c032376cf4c597b715801f1f25e Mon Sep 17 00:00:00 2001 From: Camille Simon Date: Mon, 2 Jun 2025 15:27:54 -0700 Subject: [PATCH 3/6] remove logs and add comment --- .../plugins/camerax/PreviewProxyApi.java | 18 +++--------------- .../camerax/ProcessCameraProviderProxyApi.java | 4 ---- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java index cb7f2506d38..04877454088 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java @@ -15,8 +15,6 @@ import java.util.Map; import java.util.concurrent.Executors; -import android.util.Log; - /** * ProxyApi implementation for {@link Preview}. This class may handle instantiating native object * instances that are attached to a Dart instance or handle method calls on the associated native @@ -96,9 +94,6 @@ public void setTargetRotation(Preview pigeonInstance, long rotation) { pigeonInstance.setTargetRotation((int) rotation); } - int surfaceRequestNum = 0; - int resultNum = 0; - @NonNull Preview.SurfaceProvider createSurfaceProvider( @NonNull TextureRegistry.SurfaceProducer surfaceProducer, @@ -130,11 +125,9 @@ public void onSurfaceCleanup() { flutterSurface, Executors.newSingleThreadExecutor(), (result) -> { - Log.e("CAMILLE RESULT #:", "--------------------------------------------------------------"); - Log.e("CAMILLE RESULT #:", Integer.toString(resultNum)); - Log.e("CAMILLE SURFACE HASHCODE:", Integer.toString(flutterSurface.hashCode())); - Log.e("CAMILLE RESULT #:", "--------------------------------------------------------------"); - resultNum++; + // RACE CONDITION EXPLANATION: Sometimes, this callback is called after the next SurfaceRequest comes in, + // making a call on line 123 to get a `Surface`, which is the same `Surface` that was (potentially) + // successfully used and should not be re-used. // See // https://developer.android.com/reference/androidx/camera/core/SurfaceRequest.Result // for documentation. @@ -153,11 +146,6 @@ public void onSurfaceCleanup() { systemServicesManager.onCameraError(getProvideSurfaceErrorDescription(resultCode)); } }); - Log.e("CAMILLE RESULT #:", "--------------------------------------------------------------"); - Log.e("CAMILLE SURFACE REQUEST #:", Integer.toString(surfaceRequestNum)); - Log.e("CAMILLE SURFACE HASHCODE:", Integer.toString(flutterSurface.hashCode())); - Log.e("CAMILLE RESULT #:", "--------------------------------------------------------------"); - surfaceRequestNum++; }; } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderProxyApi.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderProxyApi.java index 547939676ac..86e62ec661a 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderProxyApi.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderProxyApi.java @@ -18,8 +18,6 @@ import kotlin.Unit; import kotlin.jvm.functions.Function1; -import android.util.Log; - /** * ProxyApi implementation for {@link ProcessCameraProvider}. This class may handle instantiating * native object instances that are attached to a Dart instance or handle method calls on the @@ -66,7 +64,6 @@ public Camera bindToLifecycle( @NonNull ProcessCameraProvider pigeonInstance, @NonNull CameraSelector cameraSelector, @NonNull List useCases) { - Log.e("CAMILLE"," BIND CALLED!!!!!!!!!!!!!!!****************************"); final LifecycleOwner lifecycleOwner = getPigeonRegistrar().getLifecycleOwner(); if (lifecycleOwner != null) { return pigeonInstance.bindToLifecycle( @@ -85,7 +82,6 @@ public boolean isBound(ProcessCameraProvider pigeonInstance, @NonNull UseCase us @Override public void unbind( ProcessCameraProvider pigeonInstance, @NonNull List useCases) { - Log.e("CAMILLE"," UNBIND CALLED!!!!!!!!!!!!!!!****************************"); pigeonInstance.unbind(useCases.toArray(new UseCase[0])); } From 0b4dfa0c3d086754e7a7fdad9735aedafa0db162 Mon Sep 17 00:00:00 2001 From: Camille Simon Date: Mon, 16 Jun 2025 13:47:37 -0700 Subject: [PATCH 4/6] update wip --- .../java/io/flutter/plugins/camerax/PreviewProxyApi.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java index 04877454088..d3c140ff2c7 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java @@ -117,17 +117,18 @@ public void onSurfaceCleanup() { }); // Provide surface. - surfaceProducer.invalidateSurface(); surfaceProducer.setSize( request.getResolution().getWidth(), request.getResolution().getHeight()); - Surface flutterSurface = surfaceProducer.getSurface(); + Surface flutterSurface = surfaceProducer.getSurface(true); request.provideSurface( flutterSurface, Executors.newSingleThreadExecutor(), (result) -> { - // RACE CONDITION EXPLANATION: Sometimes, this callback is called after the next SurfaceRequest comes in, - // making a call on line 123 to get a `Surface`, which is the same `Surface` that was (potentially) + // RACE CONDITION EXPLANATION for https://github.com/flutter/flutter/pull/169899: + // Sometimes, this callback is called after the next SurfaceRequest comes in, + // making a call on line 122 to get a `Surface`, which is the same `Surface` that was (potentially) // successfully used and should not be re-used. + // // See // https://developer.android.com/reference/androidx/camera/core/SurfaceRequest.Result // for documentation. From f8cb2948ec52e0bc9409924fd9f3ffda96612377 Mon Sep 17 00:00:00 2001 From: Camille Simon Date: Fri, 20 Jun 2025 09:24:43 -0700 Subject: [PATCH 5/6] update pr based on engine changes + add tests --- .../java/io/flutter/plugins/camerax/PreviewProxyApi.java | 7 +------ .../test/java/io/flutter/plugins/camerax/PreviewTest.java | 4 ++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java index d3c140ff2c7..47e36d4107e 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java @@ -119,16 +119,11 @@ public void onSurfaceCleanup() { // Provide surface. surfaceProducer.setSize( request.getResolution().getWidth(), request.getResolution().getHeight()); - Surface flutterSurface = surfaceProducer.getSurface(true); + Surface flutterSurface = surfaceProducer.getForcedNewSurface(); request.provideSurface( flutterSurface, Executors.newSingleThreadExecutor(), (result) -> { - // RACE CONDITION EXPLANATION for https://github.com/flutter/flutter/pull/169899: - // Sometimes, this callback is called after the next SurfaceRequest comes in, - // making a call on line 122 to get a `Surface`, which is the same `Surface` that was (potentially) - // successfully used and should not be re-used. - // // See // https://developer.android.com/reference/androidx/camera/core/SurfaceRequest.Result // for documentation. diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java index 5c3f3578a24..45b6be96375 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java @@ -103,7 +103,7 @@ TextureRegistry getTextureRegistry() { ArgumentCaptor.forClass(TextureRegistry.SurfaceProducer.Callback.class); when(mockSurfaceRequest.getResolution()).thenReturn(new Size(5, 6)); - when(mockSurfaceProducer.getSurface()).thenReturn(mock(Surface.class)); + when(mockSurfaceProducer.getForcedNewSurface()).thenReturn(mock(Surface.class)); final Preview.SurfaceProvider previewSurfaceProvider = api.createSurfaceProvider(mockSurfaceProducer, mockSystemServicesManager); @@ -155,7 +155,7 @@ TextureRegistry getTextureRegistry() { when(mockSurfaceRequest.getResolution()) .thenReturn(new Size(resolutionWidth, resolutionHeight)); - when(mockSurfaceProducer.getSurface()).thenReturn(mockSurface); + when(mockSurfaceProducer.getForcedNewSurface()).thenReturn(mockSurface); final ArgumentCaptor surfaceCaptor = ArgumentCaptor.forClass(Surface.class); final ArgumentCaptor> consumerCaptor = From 7918cbc98a4ce78c8bd0254d4c94190e6eb3fac1 Mon Sep 17 00:00:00 2001 From: Camille Simon Date: Fri, 20 Jun 2025 09:49:48 -0700 Subject: [PATCH 6/6] bump version --- packages/camera/camera_android_camerax/CHANGELOG.md | 5 +++++ packages/camera/camera_android_camerax/pubspec.yaml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 5f9c3c2a36c..d90e7f1244e 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.6.19 + +* Ensures a new surface is provided every timethat one is requested to render the camera preview into + to fix pausing and resuming the preview. + ## 0.6.18+3 * Fixes incorrect camera preview mirroring for front cameras of devices using the Impeller backend. diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index 0bf7e8a986a..09d8688b323 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -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.18+3 +version: 0.6.19 environment: sdk: ^3.7.0