mirror of
https://github.com/flutter/packages.git
synced 2025-06-30 23:03:11 +08:00
[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:
@ -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
|
## 0.10.5+4
|
||||||
|
|
||||||
* Adds pub topics to package metadata.
|
* Adds pub topics to package metadata.
|
||||||
|
@ -265,7 +265,10 @@ class CameraController extends ValueNotifier<CameraValue> {
|
|||||||
|
|
||||||
bool _isDisposed = false;
|
bool _isDisposed = false;
|
||||||
StreamSubscription<CameraImageData>? _imageStreamSubscription;
|
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>?
|
StreamSubscription<DeviceOrientationChangedEvent>?
|
||||||
_deviceOrientationSubscription;
|
_deviceOrientationSubscription;
|
||||||
|
|
||||||
@ -294,11 +297,15 @@ class CameraController extends ValueNotifier<CameraValue> {
|
|||||||
'initialize was called on a disposed CameraController',
|
'initialize was called on a disposed CameraController',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Completer<void> initializeCompleter = Completer<void>();
|
||||||
|
_initializeFuture = initializeCompleter.future;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final Completer<CameraInitializedEvent> initializeCompleter =
|
final Completer<CameraInitializedEvent> initializeCompleter =
|
||||||
Completer<CameraInitializedEvent>();
|
Completer<CameraInitializedEvent>();
|
||||||
|
|
||||||
_deviceOrientationSubscription = CameraPlatform.instance
|
_deviceOrientationSubscription ??= CameraPlatform.instance
|
||||||
.onDeviceOrientationChanged()
|
.onDeviceOrientationChanged()
|
||||||
.listen((DeviceOrientationChangedEvent event) {
|
.listen((DeviceOrientationChangedEvent event) {
|
||||||
value = value.copyWith(
|
value = value.copyWith(
|
||||||
@ -343,9 +350,9 @@ class CameraController extends ValueNotifier<CameraValue> {
|
|||||||
);
|
);
|
||||||
} on PlatformException catch (e) {
|
} on PlatformException catch (e) {
|
||||||
throw CameraException(e.code, e.message);
|
throw CameraException(e.code, e.message);
|
||||||
|
} finally {
|
||||||
|
initializeCompleter.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
_initCalled = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepare the capture session for video recording.
|
/// Prepare the capture session for video recording.
|
||||||
@ -402,6 +409,11 @@ class CameraController extends ValueNotifier<CameraValue> {
|
|||||||
await CameraPlatform.instance.setDescriptionWhileRecording(description);
|
await CameraPlatform.instance.setDescriptionWhileRecording(description);
|
||||||
value = value.copyWith(description: description);
|
value = value.copyWith(description: description);
|
||||||
} else {
|
} else {
|
||||||
|
if (_initializeFuture != null) {
|
||||||
|
await _initializeFuture;
|
||||||
|
await CameraPlatform.instance.dispose(_cameraId);
|
||||||
|
}
|
||||||
|
|
||||||
await _initializeWithDescription(description);
|
await _initializeWithDescription(description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -841,8 +853,8 @@ class CameraController extends ValueNotifier<CameraValue> {
|
|||||||
_unawaited(_deviceOrientationSubscription?.cancel());
|
_unawaited(_deviceOrientationSubscription?.cancel());
|
||||||
_isDisposed = true;
|
_isDisposed = true;
|
||||||
super.dispose();
|
super.dispose();
|
||||||
if (_initCalled != null) {
|
if (_initializeFuture != null) {
|
||||||
await _initCalled;
|
await _initializeFuture;
|
||||||
await CameraPlatform.instance.dispose(_cameraId);
|
await CameraPlatform.instance.dispose(_cameraId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing
|
|||||||
Dart.
|
Dart.
|
||||||
repository: https://github.com/flutter/packages/tree/main/packages/camera/camera
|
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
|
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:
|
environment:
|
||||||
sdk: ">=2.19.0 <4.0.0"
|
sdk: ">=2.19.0 <4.0.0"
|
||||||
|
@ -192,6 +192,42 @@ void main() {
|
|||||||
.called(1);
|
.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 {
|
test('prepareForVideoRecording() calls $CameraPlatform ', () async {
|
||||||
final CameraController cameraController = CameraController(
|
final CameraController cameraController = CameraController(
|
||||||
const CameraDescription(
|
const CameraDescription(
|
||||||
|
Reference in New Issue
Block a user