Skip to content

Commit 47d5eb2

Browse files
authored
Migrate CameraX from SurfaceTexture to SurfaceProducer. (#6462)
_**WIP**: We do not plan to land this PR until the next stable release (>= April 3rd 2024)_. Work towards flutter/flutter#145930. ## Details Migrates uses of `createSurfaceTexture` to `createSurfaceProducer`, which is intended to have no change in behavior, but _does_ change the backend rendering path, so it will require more testing (and we're also open to minor API renames or changes before it becomes stable). ## Background Android plugins previously requested a `SurfaceTexture` from the Android embedder, and used that to produce a `Surface` to render external textures on (i.e. `video_player`). This worked because 100% of Flutter applications on Android used OpenGLES (via our Skia backend), and `SurfaceTexture` is actually an (opaque) OpenGLES-texture. Starting soon (roughly ~Q3, this is not a guarantee and just an estimate), Flutter on Android will start to use our new Impeller graphics backend, which on newer devices (`>= API_VERSION_28`), will default to the Vulkan, _not_ OpenGLES. In other words, `SurfaceTexture` will cease to work (it is possible, but non-trivial, to map an OpenGLES texture over to Vulkan). After consultation with the Android team, they helped us understand that vending `SurfaceTexture` (the _consumer-side_ API) was never the right abstraction, and we should have been vending the _producer-side_ API, or `Surface` directly. The new `SurfaceProducer` API is exactly that - it generates a `Surface`, and similar to our platform view strategy, picks the "right" _consumer-side_ implementation details _for_ the user/plugin packages. The new `SurfaceProducer` API has 2 possible rendering types (as an implementation detail): - `SurfaceTexture`, for older OpenGLES devices, which works exactly as it does today. - `ImageReader`, for newer OpenGLES _or_ Vulkan devices. These are some subtle nuances in how these two APIs work differently (one example: flutter/flutter#144407), but our theory at this point is we don't expect these changes to be observed by any users, and we have other ideas if necessary. > [!NOTE] > These invariants are [tested on CI in `flutter/engine`](https://github.com/flutter/engine/tree/main/testing/scenario_app/android#ci-configuration). Points of contact: - @matanlurey or @jonahwilliams (Flutter Engine) - @johnmccutchan or @reidbaker (Flutter on Android)
1 parent 058851b commit 47d5eb2

File tree

5 files changed

+33
-38
lines changed

5 files changed

+33
-38
lines changed

packages/camera/camera_android_camerax/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.6.5+4
2+
3+
* [Supports Impeller](https://docs.flutter.dev/release/breaking-changes/android-surface-plugins).
4+
15
## 0.6.5+3
26

37
* Updates minimum supported SDK version to Flutter 3.22/Dart 3.4.

packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55
package io.flutter.plugins.camerax;
66

77
import android.app.Activity;
8-
import android.graphics.SurfaceTexture;
98
import android.util.Size;
10-
import android.view.Surface;
119
import androidx.annotation.NonNull;
1210
import androidx.camera.core.CameraSelector;
1311
import androidx.camera.core.ImageAnalysis;
@@ -51,11 +49,6 @@ public class CameraXProxy {
5149
return new Preview.Builder();
5250
}
5351

54-
/** Creates a {@link Surface} instance from the specified {@link SurfaceTexture}. */
55-
public @NonNull Surface createSurface(@NonNull SurfaceTexture surfaceTexture) {
56-
return new Surface(surfaceTexture);
57-
}
58-
5952
/**
6053
* Creates an instance of the {@link SystemServicesFlutterApiImpl}.
6154
*

packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
package io.flutter.plugins.camerax;
66

7-
import android.graphics.SurfaceTexture;
87
import android.util.Size;
98
import android.view.Surface;
109
import androidx.annotation.NonNull;
@@ -25,7 +24,7 @@ public class PreviewHostApiImpl implements PreviewHostApi {
2524
private final TextureRegistry textureRegistry;
2625

2726
@VisibleForTesting public @NonNull CameraXProxy cameraXProxy = new CameraXProxy();
28-
@VisibleForTesting public @Nullable TextureRegistry.SurfaceTextureEntry flutterSurfaceTexture;
27+
@VisibleForTesting public @Nullable TextureRegistry.SurfaceProducer flutterSurfaceProducer;
2928

3029
public PreviewHostApiImpl(
3130
@NonNull BinaryMessenger binaryMessenger,
@@ -62,12 +61,11 @@ public void create(
6261
@Override
6362
public @NonNull Long setSurfaceProvider(@NonNull Long identifier) {
6463
Preview preview = getPreviewInstance(identifier);
65-
flutterSurfaceTexture = textureRegistry.createSurfaceTexture();
66-
SurfaceTexture surfaceTexture = flutterSurfaceTexture.surfaceTexture();
67-
Preview.SurfaceProvider surfaceProvider = createSurfaceProvider(surfaceTexture);
64+
flutterSurfaceProducer = textureRegistry.createSurfaceProducer();
65+
Preview.SurfaceProvider surfaceProvider = createSurfaceProvider(flutterSurfaceProducer);
6866
preview.setSurfaceProvider(surfaceProvider);
6967

70-
return flutterSurfaceTexture.id();
68+
return flutterSurfaceProducer.id();
7169
}
7270

7371
/**
@@ -76,13 +74,13 @@ public void create(
7674
*/
7775
@VisibleForTesting
7876
public @NonNull Preview.SurfaceProvider createSurfaceProvider(
79-
@NonNull SurfaceTexture surfaceTexture) {
77+
@NonNull TextureRegistry.SurfaceProducer surfaceProducer) {
8078
return new Preview.SurfaceProvider() {
8179
@Override
8280
public void onSurfaceRequested(@NonNull SurfaceRequest request) {
83-
surfaceTexture.setDefaultBufferSize(
81+
surfaceProducer.setSize(
8482
request.getResolution().getWidth(), request.getResolution().getHeight());
85-
Surface flutterSurface = cameraXProxy.createSurface(surfaceTexture);
83+
Surface flutterSurface = surfaceProducer.getSurface();
8684
request.provideSurface(
8785
flutterSurface,
8886
Executors.newSingleThreadExecutor(),
@@ -133,8 +131,8 @@ String getProvideSurfaceErrorDescription(int resultCode) {
133131
*/
134132
@Override
135133
public void releaseFlutterSurfaceTexture() {
136-
if (flutterSurfaceTexture != null) {
137-
flutterSurfaceTexture.release();
134+
if (flutterSurfaceProducer != null) {
135+
flutterSurfaceProducer.release();
138136
}
139137
}
140138

packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import static org.mockito.Mockito.verify;
1414
import static org.mockito.Mockito.when;
1515

16-
import android.graphics.SurfaceTexture;
1716
import android.util.Size;
1817
import android.view.Surface;
1918
import androidx.camera.core.Preview;
@@ -86,59 +85,60 @@ public void create_createsPreviewWithCorrectConfiguration() {
8685
public void setSurfaceProviderTest_createsSurfaceProviderAndReturnsTextureEntryId() {
8786
final PreviewHostApiImpl previewHostApi =
8887
spy(new PreviewHostApiImpl(mockBinaryMessenger, testInstanceManager, mockTextureRegistry));
89-
final TextureRegistry.SurfaceTextureEntry mockSurfaceTextureEntry =
90-
mock(TextureRegistry.SurfaceTextureEntry.class);
91-
final SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class);
88+
final TextureRegistry.SurfaceProducer mockSurfaceProducer =
89+
mock(TextureRegistry.SurfaceProducer.class);
9290
final Long previewIdentifier = 5L;
93-
final Long surfaceTextureEntryId = 120L;
91+
final Long surfaceProducerEntryId = 120L;
9492

9593
previewHostApi.cameraXProxy = mockCameraXProxy;
9694
testInstanceManager.addDartCreatedInstance(mockPreview, previewIdentifier);
9795

98-
when(mockTextureRegistry.createSurfaceTexture()).thenReturn(mockSurfaceTextureEntry);
99-
when(mockSurfaceTextureEntry.surfaceTexture()).thenReturn(mockSurfaceTexture);
100-
when(mockSurfaceTextureEntry.id()).thenReturn(surfaceTextureEntryId);
96+
when(mockTextureRegistry.createSurfaceProducer()).thenReturn(mockSurfaceProducer);
97+
when(mockSurfaceProducer.id()).thenReturn(surfaceProducerEntryId);
10198

10299
final ArgumentCaptor<Preview.SurfaceProvider> surfaceProviderCaptor =
103100
ArgumentCaptor.forClass(Preview.SurfaceProvider.class);
104-
final ArgumentCaptor<Surface> surfaceCaptor = ArgumentCaptor.forClass(Surface.class);
105101

106102
// Test that surface provider was set and the surface texture ID was returned.
107-
assertEquals(previewHostApi.setSurfaceProvider(previewIdentifier), surfaceTextureEntryId);
103+
assertEquals(previewHostApi.setSurfaceProvider(previewIdentifier), surfaceProducerEntryId);
108104
verify(mockPreview).setSurfaceProvider(surfaceProviderCaptor.capture());
109-
verify(previewHostApi).createSurfaceProvider(mockSurfaceTexture);
105+
verify(previewHostApi).createSurfaceProvider(mockSurfaceProducer);
110106
}
111107

112108
@Test
113109
public void createSurfaceProvider_createsExpectedPreviewSurfaceProvider() {
114110
final PreviewHostApiImpl previewHostApi =
115111
new PreviewHostApiImpl(mockBinaryMessenger, testInstanceManager, mockTextureRegistry);
116-
final SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class);
112+
final TextureRegistry.SurfaceProducer mockSurfaceProducer =
113+
mock(TextureRegistry.SurfaceProducer.class);
117114
final Surface mockSurface = mock(Surface.class);
118115
final SurfaceRequest mockSurfaceRequest = mock(SurfaceRequest.class);
119116
final SurfaceRequest.Result mockSurfaceRequestResult = mock(SurfaceRequest.Result.class);
120117
final SystemServicesFlutterApiImpl mockSystemServicesFlutterApi =
121118
mock(SystemServicesFlutterApiImpl.class);
122119
final int resolutionWidth = 200;
123120
final int resolutionHeight = 500;
121+
final Long surfaceProducerEntryId = 120L;
124122

125123
previewHostApi.cameraXProxy = mockCameraXProxy;
126-
when(mockCameraXProxy.createSurface(mockSurfaceTexture)).thenReturn(mockSurface);
127124
when(mockSurfaceRequest.getResolution())
128125
.thenReturn(new Size(resolutionWidth, resolutionHeight));
129126
when(mockCameraXProxy.createSystemServicesFlutterApiImpl(mockBinaryMessenger))
130127
.thenReturn(mockSystemServicesFlutterApi);
128+
when(mockTextureRegistry.createSurfaceProducer()).thenReturn(mockSurfaceProducer);
129+
when(mockSurfaceProducer.id()).thenReturn(surfaceProducerEntryId);
130+
when(mockSurfaceProducer.getSurface()).thenReturn(mockSurface);
131131

132132
final ArgumentCaptor<Surface> surfaceCaptor = ArgumentCaptor.forClass(Surface.class);
133133
@SuppressWarnings("unchecked")
134134
final ArgumentCaptor<Consumer<SurfaceRequest.Result>> consumerCaptor =
135135
ArgumentCaptor.forClass(Consumer.class);
136136

137137
Preview.SurfaceProvider previewSurfaceProvider =
138-
previewHostApi.createSurfaceProvider(mockSurfaceTexture);
138+
previewHostApi.createSurfaceProvider(mockSurfaceProducer);
139139
previewSurfaceProvider.onSurfaceRequested(mockSurfaceRequest);
140140

141-
verify(mockSurfaceTexture).setDefaultBufferSize(resolutionWidth, resolutionHeight);
141+
verify(mockSurfaceProducer).setSize(resolutionWidth, resolutionHeight);
142142
verify(mockSurfaceRequest)
143143
.provideSurface(surfaceCaptor.capture(), any(Executor.class), consumerCaptor.capture());
144144

@@ -189,13 +189,13 @@ public void createSurfaceProvider_createsExpectedPreviewSurfaceProvider() {
189189
public void releaseFlutterSurfaceTexture_makesCallToReleaseFlutterSurfaceTexture() {
190190
final PreviewHostApiImpl previewHostApi =
191191
new PreviewHostApiImpl(mockBinaryMessenger, testInstanceManager, mockTextureRegistry);
192-
final TextureRegistry.SurfaceTextureEntry mockSurfaceTextureEntry =
193-
mock(TextureRegistry.SurfaceTextureEntry.class);
192+
final TextureRegistry.SurfaceProducer mockSurfaceProducer =
193+
mock(TextureRegistry.SurfaceProducer.class);
194194

195-
previewHostApi.flutterSurfaceTexture = mockSurfaceTextureEntry;
195+
previewHostApi.flutterSurfaceProducer = mockSurfaceProducer;
196196

197197
previewHostApi.releaseFlutterSurfaceTexture();
198-
verify(mockSurfaceTextureEntry).release();
198+
verify(mockSurfaceProducer).release();
199199
}
200200

201201
@Test

packages/camera/camera_android_camerax/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: camera_android_camerax
22
description: Android implementation of the camera plugin using the CameraX library.
33
repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
5-
version: 0.6.5+3
5+
version: 0.6.5+4
66

77
environment:
88
sdk: ^3.4.0

0 commit comments

Comments
 (0)