[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: https://github.com/flutter/flutter/issues/126823
This commit is contained in:
Braden Bagby
2023-09-29 13:45:12 -06:00
committed by GitHub
parent eac45de20d
commit 8483ac23a3
4 changed files with 61 additions and 7 deletions

View File

@ -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.

View File

@ -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;
@ -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(
@ -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.
@ -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);
}
}
@ -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);
}
}

View File

@ -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"

View File

@ -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(