Skip to content

Commit 6efb759

Browse files
authored
[camera_android_camerax] Re-land "Force new Surface for each SurfaceRequest" (#9760)
> [!IMPORTANT] > **(DONE ✅ in flutter/flutter#172384 > flutter/flutter#169899 must be rolled into packages AND be in Flutter stable for this to land. I will try to cherry-pick this change into 3.32 stable once the 3.35 beta is cut. > [!NOTE] > Several files are not changed but simply formatted as a result of the bumped Flutter/Dart versions for `camera_android_camerax` that include flutter/flutter#171703. I will comment on these files for clarity. Re-lands #9360. Since timing for `Surface` requests cannot be guaranteed, request a new `Surface` from `SurfaceProducer.getSurface` each time a `Surface` is requested for rendering the camera preview to. Fixes flutter/flutter#155294. Fixes flutter/flutter#169506. ## Pre-Review Checklist **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. [^1]: Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling.
1 parent 091b87d commit 6efb759

File tree

14 files changed

+2542
-2392
lines changed

14 files changed

+2542
-2392
lines changed

packages/camera/camera_android_camerax/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 0.6.20
2+
3+
* Fixes pausing and resuming the camera preview.
4+
* Updates minimum supported SDK version to Flutter 3.32.8/Dart 3.8.1.
5+
16
## 0.6.19+1
27

38
* Fixes incorrect camera switching by selecting a camera via its CameraInfo.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public void onSurfaceCleanup() {
121121
// Provide surface.
122122
surfaceProducer.setSize(
123123
request.getResolution().getWidth(), request.getResolution().getHeight());
124-
Surface flutterSurface = surfaceProducer.getSurface();
124+
Surface flutterSurface = surfaceProducer.getForcedNewSurface();
125125
request.provideSurface(
126126
flutterSurface,
127127
Executors.newSingleThreadExecutor(),

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ TextureRegistry getTextureRegistry() {
103103
ArgumentCaptor.forClass(TextureRegistry.SurfaceProducer.Callback.class);
104104

105105
when(mockSurfaceRequest.getResolution()).thenReturn(new Size(5, 6));
106-
when(mockSurfaceProducer.getSurface()).thenReturn(mock(Surface.class));
106+
when(mockSurfaceProducer.getForcedNewSurface()).thenReturn(mock(Surface.class));
107107

108108
final Preview.SurfaceProvider previewSurfaceProvider =
109109
api.createSurfaceProvider(mockSurfaceProducer, mockSystemServicesManager);
@@ -155,7 +155,7 @@ TextureRegistry getTextureRegistry() {
155155

156156
when(mockSurfaceRequest.getResolution())
157157
.thenReturn(new Size(resolutionWidth, resolutionHeight));
158-
when(mockSurfaceProducer.getSurface()).thenReturn(mockSurface);
158+
when(mockSurfaceProducer.getForcedNewSurface()).thenReturn(mockSurface);
159159

160160
final ArgumentCaptor<Surface> surfaceCaptor = ArgumentCaptor.forClass(Surface.class);
161161
final ArgumentCaptor<Consumer<SurfaceRequest.Result>> consumerCaptor =

packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart

Lines changed: 45 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -289,12 +289,14 @@ class AndroidCameraCameraX extends CameraPlatform {
289289
// Determine the lens direction by filtering the CameraInfo
290290
// TODO(gmackall): replace this with call to CameraInfo.getLensFacing when changes containing that method are available
291291
if ((await proxy
292-
.newCameraSelector(requireLensFacing: LensFacing.back)
293-
.filter(<CameraInfo>[cameraInfo])).isNotEmpty) {
292+
.newCameraSelector(requireLensFacing: LensFacing.back)
293+
.filter(<CameraInfo>[cameraInfo]))
294+
.isNotEmpty) {
294295
cameraLensDirection = CameraLensDirection.back;
295296
} else if ((await proxy
296-
.newCameraSelector(requireLensFacing: LensFacing.front)
297-
.filter(<CameraInfo>[cameraInfo])).isNotEmpty) {
297+
.newCameraSelector(requireLensFacing: LensFacing.front)
298+
.filter(<CameraInfo>[cameraInfo]))
299+
.isNotEmpty) {
298300
cameraLensDirection = CameraLensDirection.front;
299301
} else {
300302
//Skip this CameraInfo as its lens direction is unknown
@@ -439,13 +441,13 @@ class AndroidCameraCameraX extends CameraPlatform {
439441
.toDouble();
440442

441443
sensorOrientationDegrees = cameraDescription.sensorOrientation.toDouble();
442-
_handlesCropAndRotation =
443-
await preview!.surfaceProducerHandlesCropAndRotation();
444+
_handlesCropAndRotation = await preview!
445+
.surfaceProducerHandlesCropAndRotation();
444446
_initialDeviceOrientation = _deserializeDeviceOrientation(
445447
await deviceOrientationManager.getUiOrientation(),
446448
);
447-
_initialDefaultDisplayRotation =
448-
await deviceOrientationManager.getDefaultDisplayRotation();
449+
_initialDefaultDisplayRotation = await deviceOrientationManager
450+
.getDefaultDisplayRotation();
449451

450452
return flutterSurfaceTextureId;
451453
}
@@ -476,8 +478,8 @@ class AndroidCameraCameraX extends CameraPlatform {
476478
);
477479
}
478480

479-
final ResolutionInfo previewResolutionInfo =
480-
(await preview!.getResolutionInfo())!;
481+
final ResolutionInfo previewResolutionInfo = (await preview!
482+
.getResolutionInfo())!;
481483

482484
// Mark auto-focus, auto-exposure and setting points for focus & exposure
483485
// as available operations as CameraX does its best across devices to
@@ -647,10 +649,9 @@ class AndroidCameraCameraX extends CameraPlatform {
647649
case FocusMode.auto:
648650
// Determine auto-focus point to restore, if any. We do not restore
649651
// default auto-focus point if set previously to lock focus.
650-
final MeteringPoint? unLockedFocusPoint =
651-
_defaultFocusPointLocked
652-
? null
653-
: currentFocusMeteringAction!.meteringPointsAf.first;
652+
final MeteringPoint? unLockedFocusPoint = _defaultFocusPointLocked
653+
? null
654+
: currentFocusMeteringAction!.meteringPointsAf.first;
654655
_defaultFocusPointLocked = false;
655656
autoFocusPoint = unLockedFocusPoint;
656657
disableAutoCancel = false;
@@ -661,10 +662,9 @@ class AndroidCameraCameraX extends CameraPlatform {
661662
if (currentFocusMeteringAction != null) {
662663
final List<MeteringPoint> possibleCurrentAfPoints =
663664
currentFocusMeteringAction!.meteringPointsAf;
664-
lockedFocusPoint =
665-
possibleCurrentAfPoints.isEmpty
666-
? null
667-
: possibleCurrentAfPoints.first;
665+
lockedFocusPoint = possibleCurrentAfPoints.isEmpty
666+
? null
667+
: possibleCurrentAfPoints.first;
668668
}
669669

670670
// If there isn't, lock center of entire sensor area by default.
@@ -1494,13 +1494,12 @@ class AndroidCameraCameraX extends CameraPlatform {
14941494
);
14951495
final ResolutionFilter resolutionFilter = proxy
14961496
.createWithOnePreferredSizeResolutionFilter(preferredSize: boundSize);
1497-
final AspectRatioStrategy? aspectRatioStrategy =
1498-
aspectRatio == null
1499-
? null
1500-
: proxy.newAspectRatioStrategy(
1501-
preferredAspectRatio: aspectRatio,
1502-
fallbackRule: AspectRatioStrategyFallbackRule.auto,
1503-
);
1497+
final AspectRatioStrategy? aspectRatioStrategy = aspectRatio == null
1498+
? null
1499+
: proxy.newAspectRatioStrategy(
1500+
preferredAspectRatio: aspectRatio,
1501+
fallbackRule: AspectRatioStrategyFallbackRule.auto,
1502+
);
15041503
return proxy.newResolutionSelector(
15051504
resolutionStrategy: resolutionStrategy,
15061505
resolutionFilter: resolutionFilter,
@@ -1617,17 +1616,17 @@ class AndroidCameraCameraX extends CameraPlatform {
16171616
// Remove metering point with specified meteringMode from current focus
16181617
// and metering action, as only one focus or exposure point may be set
16191618
// at once in this plugin.
1620-
final List<(MeteringPoint, MeteringMode)> newMeteringPointInfos =
1621-
originalMeteringPoints
1622-
.where(
1623-
((MeteringPoint, MeteringMode) meteringPointInfo) =>
1624-
// meteringPointInfo may technically include points without a
1625-
// mode specified, but this logic is safe because this plugin
1626-
// only uses points that explicitly have mode
1627-
// FocusMeteringAction.flagAe or FocusMeteringAction.flagAf.
1628-
meteringPointInfo.$2 != meteringMode,
1629-
)
1630-
.toList();
1619+
final List<(MeteringPoint, MeteringMode)>
1620+
newMeteringPointInfos = originalMeteringPoints
1621+
.where(
1622+
((MeteringPoint, MeteringMode) meteringPointInfo) =>
1623+
// meteringPointInfo may technically include points without a
1624+
// mode specified, but this logic is safe because this plugin
1625+
// only uses points that explicitly have mode
1626+
// FocusMeteringAction.flagAe or FocusMeteringAction.flagAf.
1627+
meteringPointInfo.$2 != meteringMode,
1628+
)
1629+
.toList();
16311630

16321631
if (newMeteringPointInfos.isEmpty) {
16331632
// If no other metering points were specified, cancel any previously
@@ -1664,17 +1663,16 @@ class AndroidCameraCameraX extends CameraPlatform {
16641663
final Iterable<(MeteringPoint, MeteringMode)> originalMeteringPoints =
16651664
_combineMeteringPoints(currentFocusMeteringAction!);
16661665

1667-
newMeteringPointInfos =
1668-
originalMeteringPoints
1669-
.where(
1670-
((MeteringPoint, MeteringMode) meteringPointInfo) =>
1671-
// meteringPointInfo may technically include points without a
1672-
// mode specified, but this logic is safe because this plugin
1673-
// only uses points that explicitly have mode
1674-
// FocusMeteringAction.flagAe or FocusMeteringAction.flagAf.
1675-
meteringPointInfo.$2 != meteringMode,
1676-
)
1677-
.toList();
1666+
newMeteringPointInfos = originalMeteringPoints
1667+
.where(
1668+
((MeteringPoint, MeteringMode) meteringPointInfo) =>
1669+
// meteringPointInfo may technically include points without a
1670+
// mode specified, but this logic is safe because this plugin
1671+
// only uses points that explicitly have mode
1672+
// FocusMeteringAction.flagAe or FocusMeteringAction.flagAf.
1673+
meteringPointInfo.$2 != meteringMode,
1674+
)
1675+
.toList();
16781676
}
16791677

16801678
newMeteringPointInfos.add((meteringPoint, meteringMode));

packages/camera/camera_android_camerax/lib/src/camerax_library.dart

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,15 @@ void setUpGenerics({
3737
);
3838

3939
camerax.CameraInfo.pigeon_setUpMessageHandlers(
40-
pigeon_newInstance: (
41-
int sensorRotationDegrees,
42-
camerax.ExposureState exposureState,
43-
) {
44-
return CameraInfo.detached(
45-
sensorRotationDegrees: sensorRotationDegrees,
46-
exposureState: exposureState,
47-
pigeon_binaryMessenger: pigeonBinaryMessenger,
48-
pigeon_instanceManager: pigeonInstanceManager,
49-
);
50-
},
40+
pigeon_newInstance:
41+
(int sensorRotationDegrees, camerax.ExposureState exposureState) {
42+
return CameraInfo.detached(
43+
sensorRotationDegrees: sensorRotationDegrees,
44+
exposureState: exposureState,
45+
pigeon_binaryMessenger: pigeonBinaryMessenger,
46+
pigeon_instanceManager: pigeonInstanceManager,
47+
);
48+
},
5149
);
5250
}
5351

packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,8 +384,8 @@ class PigeonInstanceManager {
384384
final PigeonInternalProxyApiBaseClass? strongInstance =
385385
_strongInstances[identifier];
386386
if (strongInstance != null) {
387-
final PigeonInternalProxyApiBaseClass copy =
388-
strongInstance.pigeon_copy();
387+
final PigeonInternalProxyApiBaseClass copy = strongInstance
388+
.pigeon_copy();
389389
_identifiers[copy] = identifier;
390390
_weakInstances[identifier] =
391391
WeakReference<PigeonInternalProxyApiBaseClass>(copy);

packages/camera/camera_android_camerax/lib/src/image_reader_rotated_preview.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,9 @@ final class _ImageReaderRotatedPreviewState
7878
late StreamSubscription<DeviceOrientation> deviceOrientationSubscription;
7979

8080
Future<int> _getCurrentDefaultDisplayRotationDegrees() async {
81-
final int currentDefaultDisplayRotationQuarterTurns =
82-
await widget.deviceOrientationManager.getDefaultDisplayRotation();
81+
final int currentDefaultDisplayRotationQuarterTurns = await widget
82+
.deviceOrientationManager
83+
.getDefaultDisplayRotation();
8384
return getQuarterTurnsFromSurfaceRotationConstant(
8485
currentDefaultDisplayRotationQuarterTurns,
8586
) *

packages/camera/camera_android_camerax/lib/src/rotated_preview_utils.dart

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@ int getQuarterTurnsFromSurfaceRotationConstant(int surfaceRotationConstant) {
1515
Surface.rotation90 => 3,
1616
Surface.rotation180 => 2,
1717
Surface.rotation270 => 1,
18-
int() =>
19-
throw ArgumentError(
20-
'$surfaceRotationConstant is an unknown Surface rotation constant, so counter-clockwise quarter turns cannot be determined.',
21-
),
18+
int() => throw ArgumentError(
19+
'$surfaceRotationConstant is an unknown Surface rotation constant, so counter-clockwise quarter turns cannot be determined.',
20+
),
2221
};
2322
}
2423

packages/camera/camera_android_camerax/lib/src/surface_texture_rotated_preview.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,9 @@ final class _SurfaceTextureRotatedPreviewState
5757
late Future<int> defaultDisplayRotationQuarterTurns;
5858

5959
Future<int> _getCurrentDefaultDisplayRotationQuarterTurns() async {
60-
final int currentDefaultDisplayRotationQuarterTurns =
61-
await widget.deviceOrientationManager.getDefaultDisplayRotation();
60+
final int currentDefaultDisplayRotationQuarterTurns = await widget
61+
.deviceOrientationManager
62+
.getDefaultDisplayRotation();
6263
return getQuarterTurnsFromSurfaceRotationConstant(
6364
currentDefaultDisplayRotationQuarterTurns,
6465
);

packages/camera/camera_android_camerax/pubspec.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ 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.19+1
5+
version: 0.6.20
66

77
environment:
8-
sdk: ^3.7.0
9-
flutter: ">=3.29.0"
8+
sdk: ^3.8.1
9+
flutter: ">=3.32.8"
1010

1111
flutter:
1212
plugin:

0 commit comments

Comments
 (0)