Skip to content

Commit 8483ac2

Browse files
authored
[camera] Dispose resources correctly on setDescription (#4003)
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
1 parent eac45de commit 8483ac2

File tree

4 files changed

+61
-7
lines changed

4 files changed

+61
-7
lines changed

packages/camera/camera/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 0.10.5+5
2+
3+
* Fixes bug where old camera resources were not disposed when switching between camera descriptions.
4+
* Fixes bug where _deviceOrientationSubscription was recreated every time the camera description was
5+
changed.
6+
17
## 0.10.5+4
28

39
* Adds pub topics to package metadata.

packages/camera/camera/lib/src/camera_controller.dart

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,10 @@ class CameraController extends ValueNotifier<CameraValue> {
265265

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

@@ -294,11 +297,15 @@ class CameraController extends ValueNotifier<CameraValue> {
294297
'initialize was called on a disposed CameraController',
295298
);
296299
}
300+
301+
final Completer<void> initializeCompleter = Completer<void>();
302+
_initializeFuture = initializeCompleter.future;
303+
297304
try {
298305
final Completer<CameraInitializedEvent> initializeCompleter =
299306
Completer<CameraInitializedEvent>();
300307

301-
_deviceOrientationSubscription = CameraPlatform.instance
308+
_deviceOrientationSubscription ??= CameraPlatform.instance
302309
.onDeviceOrientationChanged()
303310
.listen((DeviceOrientationChangedEvent event) {
304311
value = value.copyWith(
@@ -343,9 +350,9 @@ class CameraController extends ValueNotifier<CameraValue> {
343350
);
344351
} on PlatformException catch (e) {
345352
throw CameraException(e.code, e.message);
353+
} finally {
354+
initializeCompleter.complete();
346355
}
347-
348-
_initCalled = true;
349356
}
350357

351358
/// Prepare the capture session for video recording.
@@ -402,6 +409,11 @@ class CameraController extends ValueNotifier<CameraValue> {
402409
await CameraPlatform.instance.setDescriptionWhileRecording(description);
403410
value = value.copyWith(description: description);
404411
} else {
412+
if (_initializeFuture != null) {
413+
await _initializeFuture;
414+
await CameraPlatform.instance.dispose(_cameraId);
415+
}
416+
405417
await _initializeWithDescription(description);
406418
}
407419
}
@@ -841,8 +853,8 @@ class CameraController extends ValueNotifier<CameraValue> {
841853
_unawaited(_deviceOrientationSubscription?.cancel());
842854
_isDisposed = true;
843855
super.dispose();
844-
if (_initCalled != null) {
845-
await _initCalled;
856+
if (_initializeFuture != null) {
857+
await _initializeFuture;
846858
await CameraPlatform.instance.dispose(_cameraId);
847859
}
848860
}

packages/camera/camera/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing
44
Dart.
55
repository: https://github.com/flutter/packages/tree/main/packages/camera/camera
66
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
7-
version: 0.10.5+4
7+
version: 0.10.5+5
88

99
environment:
1010
sdk: ">=2.19.0 <4.0.0"

packages/camera/camera/test/camera_test.dart

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,42 @@ void main() {
192192
.called(1);
193193
});
194194

195+
test('setDescription waits for initialize before calling dispose',
196+
() async {
197+
final CameraController cameraController = CameraController(
198+
const CameraDescription(
199+
name: 'cam',
200+
lensDirection: CameraLensDirection.back,
201+
sensorOrientation: 90,
202+
),
203+
ResolutionPreset.max,
204+
imageFormatGroup: ImageFormatGroup.bgra8888,
205+
);
206+
207+
final Completer<void> initializeCompleter = Completer<void>();
208+
when(CameraPlatform.instance.initializeCamera(
209+
mockInitializeCamera,
210+
imageFormatGroup: ImageFormatGroup.bgra8888,
211+
)).thenAnswer(
212+
(_) => initializeCompleter.future,
213+
);
214+
215+
unawaited(cameraController.initialize());
216+
217+
final Future<void> setDescriptionFuture = cameraController.setDescription(
218+
const CameraDescription(
219+
name: 'cam2',
220+
lensDirection: CameraLensDirection.front,
221+
sensorOrientation: 90),
222+
);
223+
verifyNever(CameraPlatform.instance.dispose(mockInitializeCamera));
224+
225+
initializeCompleter.complete();
226+
227+
await setDescriptionFuture;
228+
verify(CameraPlatform.instance.dispose(mockInitializeCamera));
229+
});
230+
195231
test('prepareForVideoRecording() calls $CameraPlatform ', () async {
196232
final CameraController cameraController = CameraController(
197233
const CameraDescription(

0 commit comments

Comments
 (0)