Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[camerax] Fixes relistening to onStreamedFrameAvailable's stream behavior #4511

Merged
merged 21 commits into from
Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/camera/camera_android_camerax/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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

* Implements off, auto, and always flash mode configurations for image capture.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -605,12 +605,6 @@ class AndroidCameraCameraX extends CameraPlatform {
/// Configures the [imageAnalysis] instance for image streaming and binds it
/// to camera lifecycle controlled by the [processCameraProvider].
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.
final WeakReference<AndroidCameraCameraX> weakThis =
WeakReference<AndroidCameraCameraX>(this);
Expand Down Expand Up @@ -648,9 +642,14 @@ class AndroidCameraCameraX extends CameraPlatform {

// TODO(camsim99): Support resolution configuration.
// Defaults to YUV_420_888 image format.
imageAnalysis = createImageAnalysis(null);
imageAnalysis ??= createImageAnalysis(null);
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
// https://github.com/flutter/packages/pull/3419 lands.
camera = await processCameraProvider!
Expand Down
2 changes: 1 addition & 1 deletion packages/camera/camera_android_camerax/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: camera_android_camerax
description: Android implementation of the camera plugin using the CameraX library.
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
version: 0.5.0+10
version: 0.5.0+11

environment:
sdk: ">=2.19.0 <4.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -940,7 +940,47 @@ void main() {
});

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 {
final FakeAndroidCameraCameraX camera =
FakeAndroidCameraCameraX(shouldCreateDetachedObjectForTesting: true);
Expand All @@ -963,6 +1003,8 @@ void main() {
camera.processCameraProvider = mockProcessCameraProvider;
camera.cameraSelector = mockCameraSelector;

when(mockProcessCameraProvider.isBound(camera.mockImageAnalysis))
.thenAnswer((_) async => Future<bool>.value(false));
when(mockProcessCameraProvider.bindToLifecycle(
mockCameraSelector, <UseCase>[camera.mockImageAnalysis]))
.thenAnswer((_) async => mockCamera);
Expand All @@ -989,7 +1031,9 @@ void main() {
final Analyzer capturedAnalyzer =
verify(camera.mockImageAnalysis.setAnalyzer(captureAny)).captured.single
as Analyzer;
verify(mockProcessCameraProvider.bindToLifecycle(
await untilCalled(
mockProcessCameraProvider.isBound(camera.mockImageAnalysis));
await untilCalled(mockProcessCameraProvider.bindToLifecycle(
mockCameraSelector, <UseCase>[camera.mockImageAnalysis]));

await capturedAnalyzer.analyze(mockImageProxy);
Expand All @@ -1011,7 +1055,7 @@ void main() {
});

test(
'onStreamedFrameAvaiable returns stream that responds expectedly to being canceled',
'onStreamedFrameAvailable returns stream that responds expectedly to being canceled',
() async {
final FakeAndroidCameraCameraX camera =
FakeAndroidCameraCameraX(shouldCreateDetachedObjectForTesting: true);
Expand Down