Skip to content

Commit

Permalink
[camera] Dispose resources correctly on setDescription (#4003)
Browse files Browse the repository at this point in the history
setDescription for CameraController was not correctly handling resources. It would recreate the _deviceOrientationSubscription each time it was called, which caused the subscription to not be disposed correctly. It also was not disposing of the previous device camera

*List which issues are fixed by this PR. You must list at least one issue.*

Issue seen here: flutter/flutter#126823
  • Loading branch information
BradenBagby committed Sep 29, 2023
1 parent eac45de commit 8483ac2
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 7 deletions.
6 changes: 6 additions & 0 deletions packages/camera/camera/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.10.5+5

* Fixes bug where old camera resources were not disposed when switching between camera descriptions.
* Fixes bug where _deviceOrientationSubscription was recreated every time the camera description was
changed.

## 0.10.5+4

* Adds pub topics to package metadata.
Expand Down
24 changes: 18 additions & 6 deletions packages/camera/camera/lib/src/camera_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,10 @@ class CameraController extends ValueNotifier<CameraValue> {

bool _isDisposed = false;
StreamSubscription<CameraImageData>? _imageStreamSubscription;
FutureOr<bool>? _initCalled;
// A Future awaiting an attempt to initialize (e.g. after `initialize` was
// just called). If the controller has not been initialized at least once,
// this value is null.
Future<void>? _initializeFuture;
StreamSubscription<DeviceOrientationChangedEvent>?
_deviceOrientationSubscription;

Expand Down Expand Up @@ -294,11 +297,15 @@ class CameraController extends ValueNotifier<CameraValue> {
'initialize was called on a disposed CameraController',
);
}

final Completer<void> initializeCompleter = Completer<void>();
_initializeFuture = initializeCompleter.future;

try {
final Completer<CameraInitializedEvent> initializeCompleter =
Completer<CameraInitializedEvent>();

_deviceOrientationSubscription = CameraPlatform.instance
_deviceOrientationSubscription ??= CameraPlatform.instance
.onDeviceOrientationChanged()
.listen((DeviceOrientationChangedEvent event) {
value = value.copyWith(
Expand Down Expand Up @@ -343,9 +350,9 @@ class CameraController extends ValueNotifier<CameraValue> {
);
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
} finally {
initializeCompleter.complete();
}

_initCalled = true;
}

/// Prepare the capture session for video recording.
Expand Down Expand Up @@ -402,6 +409,11 @@ class CameraController extends ValueNotifier<CameraValue> {
await CameraPlatform.instance.setDescriptionWhileRecording(description);
value = value.copyWith(description: description);
} else {
if (_initializeFuture != null) {
await _initializeFuture;
await CameraPlatform.instance.dispose(_cameraId);
}

await _initializeWithDescription(description);
}
}
Expand Down Expand Up @@ -841,8 +853,8 @@ class CameraController extends ValueNotifier<CameraValue> {
_unawaited(_deviceOrientationSubscription?.cancel());
_isDisposed = true;
super.dispose();
if (_initCalled != null) {
await _initCalled;
if (_initializeFuture != null) {
await _initializeFuture;
await CameraPlatform.instance.dispose(_cameraId);
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/camera/camera/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing
Dart.
repository: https://github.com/flutter/packages/tree/main/packages/camera/camera
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
version: 0.10.5+4
version: 0.10.5+5

environment:
sdk: ">=2.19.0 <4.0.0"
Expand Down
36 changes: 36 additions & 0 deletions packages/camera/camera/test/camera_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,42 @@ void main() {
.called(1);
});

test('setDescription waits for initialize before calling dispose',
() async {
final CameraController cameraController = CameraController(
const CameraDescription(
name: 'cam',
lensDirection: CameraLensDirection.back,
sensorOrientation: 90,
),
ResolutionPreset.max,
imageFormatGroup: ImageFormatGroup.bgra8888,
);

final Completer<void> initializeCompleter = Completer<void>();
when(CameraPlatform.instance.initializeCamera(
mockInitializeCamera,
imageFormatGroup: ImageFormatGroup.bgra8888,
)).thenAnswer(
(_) => initializeCompleter.future,
);

unawaited(cameraController.initialize());

final Future<void> setDescriptionFuture = cameraController.setDescription(
const CameraDescription(
name: 'cam2',
lensDirection: CameraLensDirection.front,
sensorOrientation: 90),
);
verifyNever(CameraPlatform.instance.dispose(mockInitializeCamera));

initializeCompleter.complete();

await setDescriptionFuture;
verify(CameraPlatform.instance.dispose(mockInitializeCamera));
});

test('prepareForVideoRecording() calls $CameraPlatform ', () async {
final CameraController cameraController = CameraController(
const CameraDescription(
Expand Down

0 comments on commit 8483ac2

Please sign in to comment.