Skip to content

Commit 1a90429

Browse files
authored
[camerax] Implement Image Streaming (#3454)
Implements image streaming with all required image data, see [here](flutter/flutter#113792). Fixes flutter/flutter#120463. Fixes flutter/flutter#113792 along the way. Special shoutout to @reidbaker for pair programming on this!
1 parent 124e330 commit 1a90429

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+3812
-239
lines changed

packages/camera/camera_android_camerax/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@
1818
* Implements image capture.
1919
* Fixes cast of CameraInfo to fix integration test failure.
2020
* Updates internal Java InstanceManager to only stop finalization callbacks when stopped.
21+
* Implements image streaming.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.plugins.camerax;
6+
7+
import androidx.annotation.NonNull;
8+
import androidx.annotation.VisibleForTesting;
9+
import androidx.camera.core.ImageAnalysis;
10+
import androidx.camera.core.ImageProxy;
11+
import io.flutter.plugin.common.BinaryMessenger;
12+
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.AnalyzerFlutterApi;
13+
import java.util.Objects;
14+
15+
/**
16+
* Flutter API implementation for {@link ImageAnalysis.Analyzer}.
17+
*
18+
* <p>This class may handle adding native instances that are attached to a Dart instance or passing
19+
* arguments of callbacks methods to a Dart instance.
20+
*/
21+
public class AnalyzerFlutterApiImpl {
22+
private final BinaryMessenger binaryMessenger;
23+
private final InstanceManager instanceManager;
24+
private AnalyzerFlutterApi api;
25+
26+
/**
27+
* Constructs a {@link AnalyzerFlutterApiImpl}.
28+
*
29+
* @param binaryMessenger used to communicate with Dart over asynchronous messages
30+
* @param instanceManager maintains instances stored to communicate with attached Dart objects
31+
*/
32+
public AnalyzerFlutterApiImpl(
33+
@NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) {
34+
this.binaryMessenger = binaryMessenger;
35+
this.instanceManager = instanceManager;
36+
api = new AnalyzerFlutterApi(binaryMessenger);
37+
}
38+
39+
/**
40+
* Stores the {@link ImageAnalysis.Analyzer} instance and notifies Dart to create and store a new
41+
* {@code Analyzer} instance that is attached to this one. If {@code instance} has already been
42+
* added, this method does nothing.
43+
*/
44+
public void create(
45+
@NonNull ImageAnalysis.Analyzer instance, @NonNull AnalyzerFlutterApi.Reply<Void> callback) {
46+
if (!instanceManager.containsInstance(instance)) {
47+
api.create(instanceManager.addHostCreatedInstance(instance), callback);
48+
}
49+
}
50+
51+
/**
52+
* Sends a message to Dart to call {@code Analyzer.analyze} on the Dart object representing
53+
* `instance`.
54+
*/
55+
public void analyze(
56+
@NonNull ImageAnalysis.Analyzer analyzerInstance,
57+
@NonNull ImageProxy imageProxyInstance,
58+
@NonNull AnalyzerFlutterApi.Reply<Void> callback) {
59+
api.analyze(
60+
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(analyzerInstance)),
61+
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(imageProxyInstance)),
62+
callback);
63+
}
64+
65+
/**
66+
* Sets the Flutter API used to send messages to Dart.
67+
*
68+
* <p>This is only visible for testing.
69+
*/
70+
@VisibleForTesting
71+
void setApi(@NonNull AnalyzerFlutterApi api) {
72+
this.api = api;
73+
}
74+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.plugins.camerax;
6+
7+
import androidx.annotation.NonNull;
8+
import androidx.annotation.VisibleForTesting;
9+
import androidx.camera.core.ImageAnalysis;
10+
import androidx.camera.core.ImageProxy;
11+
import io.flutter.plugin.common.BinaryMessenger;
12+
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.AnalyzerHostApi;
13+
14+
/**
15+
* Host API implementation for {@link ImageAnalysis.Analyzer}.
16+
*
17+
* <p>This class may handle instantiating and adding native object instances that are attached to a
18+
* Dart instance or handle method calls on the associated native class or an instance of the class.
19+
*/
20+
public class AnalyzerHostApiImpl implements AnalyzerHostApi {
21+
private final BinaryMessenger binaryMessenger;
22+
private final InstanceManager instanceManager;
23+
private final AnalyzerProxy proxy;
24+
25+
/** Proxy for constructors and static method of {@link ImageAnalysis.Analyzer}. */
26+
@VisibleForTesting
27+
public static class AnalyzerProxy {
28+
29+
/** Creates an instance of {@link AnalyzerImpl}. */
30+
@NonNull
31+
public AnalyzerImpl create(
32+
@NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) {
33+
return new AnalyzerImpl(binaryMessenger, instanceManager);
34+
}
35+
}
36+
37+
/**
38+
* Implementation of {@link ImageAnalysis.Analyzer} that passes arguments of callback methods to
39+
* Dart.
40+
*/
41+
public static class AnalyzerImpl implements ImageAnalysis.Analyzer {
42+
private BinaryMessenger binaryMessenger;
43+
private InstanceManager instanceManager;
44+
private AnalyzerFlutterApiImpl api;
45+
46+
@VisibleForTesting @NonNull public ImageProxyFlutterApiImpl imageProxyApi;
47+
48+
/**
49+
* Constructs an instance of {@link ImageAnalysis.Analyzer} that passes arguments of callbacks
50+
* methods to Dart.
51+
*/
52+
public AnalyzerImpl(
53+
@NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) {
54+
super();
55+
this.binaryMessenger = binaryMessenger;
56+
this.instanceManager = instanceManager;
57+
api = new AnalyzerFlutterApiImpl(binaryMessenger, instanceManager);
58+
imageProxyApi = new ImageProxyFlutterApiImpl(binaryMessenger, instanceManager);
59+
}
60+
61+
@Override
62+
public void analyze(@NonNull ImageProxy imageProxy) {
63+
Long imageFormat = Long.valueOf(imageProxy.getFormat());
64+
Long imageHeight = Long.valueOf(imageProxy.getHeight());
65+
Long imageWidth = Long.valueOf(imageProxy.getWidth());
66+
imageProxyApi.create(imageProxy, imageFormat, imageHeight, imageWidth, reply -> {});
67+
68+
api.analyze(this, imageProxy, reply -> {});
69+
}
70+
71+
/**
72+
* Flutter API used to send messages back to Dart.
73+
*
74+
* <p>This is only visible for testing.
75+
*/
76+
@VisibleForTesting
77+
void setApi(@NonNull AnalyzerFlutterApiImpl api) {
78+
this.api = api;
79+
}
80+
}
81+
82+
/**
83+
* Constructs a {@link AnalyzerHostApiImpl}.
84+
*
85+
* @param binaryMessenger used to communicate with Dart over asynchronous messages
86+
* @param instanceManager maintains instances stored to communicate with attached Dart objects
87+
*/
88+
public AnalyzerHostApiImpl(
89+
@NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) {
90+
this(binaryMessenger, instanceManager, new AnalyzerProxy());
91+
}
92+
93+
/**
94+
* Constructs a {@link AnalyzerHostApiImpl}.
95+
*
96+
* @param binaryMessenger used to communicate with Dart over asynchronous messages
97+
* @param instanceManager maintains instances stored to communicate with attached Dart objects
98+
* @param proxy proxy for constructors and static method of {@link ImageAnalysis.Analyzer}
99+
*/
100+
@VisibleForTesting
101+
AnalyzerHostApiImpl(
102+
@NonNull BinaryMessenger binaryMessenger,
103+
@NonNull InstanceManager instanceManager,
104+
@NonNull AnalyzerProxy proxy) {
105+
this.binaryMessenger = binaryMessenger;
106+
this.instanceManager = instanceManager;
107+
this.proxy = proxy;
108+
}
109+
110+
/**
111+
* Creates an {@link AnalyzerProxy} that represents an {@link ImageAnalysis.Analyzer} instance
112+
* with the specified identifier.
113+
*/
114+
@Override
115+
public void create(@NonNull Long identifier) {
116+
instanceManager.addDartCreatedInstance(
117+
proxy.create(binaryMessenger, instanceManager), identifier);
118+
}
119+
}

packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public final class CameraAndroidCameraxPlugin implements FlutterPlugin, Activity
1818
private InstanceManager instanceManager;
1919
private FlutterPluginBinding pluginBinding;
2020
private ProcessCameraProviderHostApiImpl processCameraProviderHostApi;
21+
private ImageAnalysisHostApiImpl imageAnalysisHostApiImpl;
2122
private ImageCaptureHostApiImpl imageCaptureHostApi;
2223
public SystemServicesHostApiImpl systemServicesHostApi;
2324

@@ -56,6 +57,12 @@ void setUp(BinaryMessenger binaryMessenger, Context context, TextureRegistry tex
5657
binaryMessenger, new PreviewHostApiImpl(binaryMessenger, instanceManager, textureRegistry));
5758
imageCaptureHostApi = new ImageCaptureHostApiImpl(binaryMessenger, instanceManager, context);
5859
GeneratedCameraXLibrary.ImageCaptureHostApi.setup(binaryMessenger, imageCaptureHostApi);
60+
imageAnalysisHostApiImpl = new ImageAnalysisHostApiImpl(binaryMessenger, instanceManager);
61+
GeneratedCameraXLibrary.ImageAnalysisHostApi.setup(binaryMessenger, imageAnalysisHostApiImpl);
62+
GeneratedCameraXLibrary.AnalyzerHostApi.setup(
63+
binaryMessenger, new AnalyzerHostApiImpl(binaryMessenger, instanceManager));
64+
GeneratedCameraXLibrary.ImageProxyHostApi.setup(
65+
binaryMessenger, new ImageProxyHostApiImpl(binaryMessenger, instanceManager));
5966
}
6067

6168
@Override
@@ -113,5 +120,8 @@ public void updateContext(Context context) {
113120
if (imageCaptureHostApi != null) {
114121
processCameraProviderHostApi.setContext(context);
115122
}
123+
if (imageAnalysisHostApiImpl != null) {
124+
imageAnalysisHostApiImpl.setContext(context);
125+
}
116126
}
117127
}

packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,27 @@
66

77
import android.app.Activity;
88
import android.graphics.SurfaceTexture;
9+
import android.util.Size;
910
import android.view.Surface;
1011
import androidx.annotation.NonNull;
1112
import androidx.camera.core.CameraSelector;
13+
import androidx.camera.core.ImageAnalysis;
1214
import androidx.camera.core.ImageCapture;
1315
import androidx.camera.core.Preview;
1416
import io.flutter.plugin.common.BinaryMessenger;
17+
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ResolutionInfo;
1518
import java.io.File;
1619

1720
/** Utility class used to create CameraX-related objects primarily for testing purposes. */
1821
public class CameraXProxy {
22+
/**
23+
* Converts a {@link ResolutionInfo} instance to a {@link Size} for setting the target resolution
24+
* of {@link UseCase}s.
25+
*/
26+
public static Size sizeFromResolution(@NonNull ResolutionInfo resolutionInfo) {
27+
return new Size(resolutionInfo.getWidth().intValue(), resolutionInfo.getHeight().intValue());
28+
}
29+
1930
public CameraSelector.Builder createCameraSelectorBuilder() {
2031
return new CameraSelector.Builder();
2132
}
@@ -58,7 +69,20 @@ public ImageCapture.Builder createImageCaptureBuilder() {
5869
/**
5970
* Creates an {@link ImageCapture.OutputFileOptions} to configure where to save a captured image.
6071
*/
72+
@NonNull
6173
public ImageCapture.OutputFileOptions createImageCaptureOutputFileOptions(@NonNull File file) {
6274
return new ImageCapture.OutputFileOptions.Builder(file).build();
6375
}
76+
77+
/** Creates an instance of {@link ImageAnalysis.Builder}. */
78+
@NonNull
79+
public ImageAnalysis.Builder createImageAnalysisBuilder() {
80+
return new ImageAnalysis.Builder();
81+
}
82+
83+
/** Creates an array of {@code byte}s with the size provided. */
84+
@NonNull
85+
public byte[] getBytesFromBuffer(int size) {
86+
return new byte[size];
87+
}
6488
}

0 commit comments

Comments
 (0)