mirror of
https://github.com/flutter/packages.git
synced 2025-06-30 23:03:11 +08:00
[camerax] Fixes relistening to onStreamedFrameAvailable
's stream behavior (#4511)
Removes incorrect assumption causing image stream to stop emitting data after subscription to stream is canceled and then the stream is listened to again. Fixes https://github.com/flutter/flutter/issues/130005.
This commit is contained in:
@ -1,3 +1,7 @@
|
|||||||
|
## 0.5.0+11
|
||||||
|
|
||||||
|
* Fixes issue with image data not being emitted after relistening to stream returned by `onStreamedFrameAvailable`.
|
||||||
|
|
||||||
## 0.5.0+10
|
## 0.5.0+10
|
||||||
|
|
||||||
* Implements off, auto, and always flash mode configurations for image capture.
|
* Implements off, auto, and always flash mode configurations for image capture.
|
||||||
|
@ -605,12 +605,6 @@ class AndroidCameraCameraX extends CameraPlatform {
|
|||||||
/// Configures the [imageAnalysis] instance for image streaming and binds it
|
/// Configures the [imageAnalysis] instance for image streaming and binds it
|
||||||
/// to camera lifecycle controlled by the [processCameraProvider].
|
/// to camera lifecycle controlled by the [processCameraProvider].
|
||||||
Future<void> _configureAndBindImageAnalysisToLifecycle() async {
|
Future<void> _configureAndBindImageAnalysisToLifecycle() async {
|
||||||
if (imageAnalysis != null &&
|
|
||||||
await processCameraProvider!.isBound(imageAnalysis!)) {
|
|
||||||
// imageAnalysis already configured and bound to lifecycle.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create Analyzer that can read image data for image streaming.
|
// Create Analyzer that can read image data for image streaming.
|
||||||
final WeakReference<AndroidCameraCameraX> weakThis =
|
final WeakReference<AndroidCameraCameraX> weakThis =
|
||||||
WeakReference<AndroidCameraCameraX>(this);
|
WeakReference<AndroidCameraCameraX>(this);
|
||||||
@ -648,9 +642,14 @@ class AndroidCameraCameraX extends CameraPlatform {
|
|||||||
|
|
||||||
// TODO(camsim99): Support resolution configuration.
|
// TODO(camsim99): Support resolution configuration.
|
||||||
// Defaults to YUV_420_888 image format.
|
// Defaults to YUV_420_888 image format.
|
||||||
imageAnalysis = createImageAnalysis(null);
|
imageAnalysis ??= createImageAnalysis(null);
|
||||||
unawaited(imageAnalysis!.setAnalyzer(analyzer));
|
unawaited(imageAnalysis!.setAnalyzer(analyzer));
|
||||||
|
|
||||||
|
if (await processCameraProvider!.isBound(imageAnalysis!)) {
|
||||||
|
// No need to bind imageAnalysis to lifecycle again.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(camsim99): Reset live camera state observers here when
|
// TODO(camsim99): Reset live camera state observers here when
|
||||||
// https://github.com/flutter/packages/pull/3419 lands.
|
// https://github.com/flutter/packages/pull/3419 lands.
|
||||||
camera = await processCameraProvider!
|
camera = await processCameraProvider!
|
||||||
|
@ -2,7 +2,7 @@ name: camera_android_camerax
|
|||||||
description: Android implementation of the camera plugin using the CameraX library.
|
description: Android implementation of the camera plugin using the CameraX library.
|
||||||
repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax
|
repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax
|
||||||
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.5.0+10
|
version: 0.5.0+11
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.19.0 <4.0.0"
|
sdk: ">=2.19.0 <4.0.0"
|
||||||
|
@ -940,7 +940,47 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'onStreamedFrameAvaiable returns stream that responds expectedly to being listened to',
|
'onStreamedFrameAvailable emits CameraImageData when listened to after cancelation',
|
||||||
|
() async {
|
||||||
|
final FakeAndroidCameraCameraX camera =
|
||||||
|
FakeAndroidCameraCameraX(shouldCreateDetachedObjectForTesting: true);
|
||||||
|
final MockProcessCameraProvider mockProcessCameraProvider =
|
||||||
|
MockProcessCameraProvider();
|
||||||
|
final MockCamera mockCamera = MockCamera();
|
||||||
|
const int cameraId = 22;
|
||||||
|
|
||||||
|
camera.processCameraProvider = mockProcessCameraProvider;
|
||||||
|
camera.cameraSelector = MockCameraSelector();
|
||||||
|
|
||||||
|
when(mockProcessCameraProvider.bindToLifecycle(any, any))
|
||||||
|
.thenAnswer((_) => Future<Camera>.value(mockCamera));
|
||||||
|
when(mockCamera.getCameraInfo())
|
||||||
|
.thenAnswer((_) => Future<CameraInfo>.value(MockCameraInfo()));
|
||||||
|
|
||||||
|
final CameraImageData mockCameraImageData = MockCameraImageData();
|
||||||
|
final Stream<CameraImageData> imageStream =
|
||||||
|
camera.onStreamedFrameAvailable(cameraId);
|
||||||
|
|
||||||
|
// Listen to image stream.
|
||||||
|
final StreamSubscription<CameraImageData> imageStreamSubscription =
|
||||||
|
imageStream.listen((CameraImageData data) {});
|
||||||
|
|
||||||
|
// Cancel subscription to image stream.
|
||||||
|
await imageStreamSubscription.cancel();
|
||||||
|
final Stream<CameraImageData> imageStream2 =
|
||||||
|
camera.onStreamedFrameAvailable(cameraId);
|
||||||
|
|
||||||
|
// Listen to image stream again.
|
||||||
|
final StreamQueue<CameraImageData> streamQueue =
|
||||||
|
StreamQueue<CameraImageData>(imageStream2);
|
||||||
|
camera.cameraImageDataStreamController!.add(mockCameraImageData);
|
||||||
|
|
||||||
|
expect(await streamQueue.next, equals(mockCameraImageData));
|
||||||
|
await streamQueue.cancel();
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'onStreamedFrameAvailable returns stream that responds expectedly to being listened to',
|
||||||
() async {
|
() async {
|
||||||
final FakeAndroidCameraCameraX camera =
|
final FakeAndroidCameraCameraX camera =
|
||||||
FakeAndroidCameraCameraX(shouldCreateDetachedObjectForTesting: true);
|
FakeAndroidCameraCameraX(shouldCreateDetachedObjectForTesting: true);
|
||||||
@ -963,6 +1003,8 @@ void main() {
|
|||||||
camera.processCameraProvider = mockProcessCameraProvider;
|
camera.processCameraProvider = mockProcessCameraProvider;
|
||||||
camera.cameraSelector = mockCameraSelector;
|
camera.cameraSelector = mockCameraSelector;
|
||||||
|
|
||||||
|
when(mockProcessCameraProvider.isBound(camera.mockImageAnalysis))
|
||||||
|
.thenAnswer((_) async => Future<bool>.value(false));
|
||||||
when(mockProcessCameraProvider.bindToLifecycle(
|
when(mockProcessCameraProvider.bindToLifecycle(
|
||||||
mockCameraSelector, <UseCase>[camera.mockImageAnalysis]))
|
mockCameraSelector, <UseCase>[camera.mockImageAnalysis]))
|
||||||
.thenAnswer((_) async => mockCamera);
|
.thenAnswer((_) async => mockCamera);
|
||||||
@ -989,7 +1031,9 @@ void main() {
|
|||||||
final Analyzer capturedAnalyzer =
|
final Analyzer capturedAnalyzer =
|
||||||
verify(camera.mockImageAnalysis.setAnalyzer(captureAny)).captured.single
|
verify(camera.mockImageAnalysis.setAnalyzer(captureAny)).captured.single
|
||||||
as Analyzer;
|
as Analyzer;
|
||||||
verify(mockProcessCameraProvider.bindToLifecycle(
|
await untilCalled(
|
||||||
|
mockProcessCameraProvider.isBound(camera.mockImageAnalysis));
|
||||||
|
await untilCalled(mockProcessCameraProvider.bindToLifecycle(
|
||||||
mockCameraSelector, <UseCase>[camera.mockImageAnalysis]));
|
mockCameraSelector, <UseCase>[camera.mockImageAnalysis]));
|
||||||
|
|
||||||
await capturedAnalyzer.analyze(mockImageProxy);
|
await capturedAnalyzer.analyze(mockImageProxy);
|
||||||
@ -1011,7 +1055,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'onStreamedFrameAvaiable returns stream that responds expectedly to being canceled',
|
'onStreamedFrameAvailable returns stream that responds expectedly to being canceled',
|
||||||
() async {
|
() async {
|
||||||
final FakeAndroidCameraCameraX camera =
|
final FakeAndroidCameraCameraX camera =
|
||||||
FakeAndroidCameraCameraX(shouldCreateDetachedObjectForTesting: true);
|
FakeAndroidCameraCameraX(shouldCreateDetachedObjectForTesting: true);
|
||||||
|
Reference in New Issue
Block a user