1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.camera.core;
18 
19 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_BACKPRESSURE_STRATEGY;
20 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_IMAGE_QUEUE_DEPTH;
21 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_IMAGE_READER_PROXY_PROVIDER;
22 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_ONE_PIXEL_SHIFT_ENABLED;
23 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_OUTPUT_IMAGE_FORMAT;
24 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_OUTPUT_IMAGE_ROTATION_ENABLED;
25 import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_DYNAMIC_RANGE;
26 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_CUSTOM_ORDERED_RESOLUTIONS;
27 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_DEFAULT_RESOLUTION;
28 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_MAX_RESOLUTION;
29 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR;
30 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_SUPPORTED_RESOLUTIONS;
31 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO;
32 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_RESOLUTION;
33 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_ROTATION;
34 import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_CONFIG_UNPACKER;
35 import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE;
36 import static androidx.camera.core.impl.UseCaseConfig.OPTION_DEFAULT_CAPTURE_CONFIG;
37 import static androidx.camera.core.impl.UseCaseConfig.OPTION_DEFAULT_SESSION_CONFIG;
38 import static androidx.camera.core.impl.UseCaseConfig.OPTION_HIGH_RESOLUTION_DISABLED;
39 import static androidx.camera.core.impl.UseCaseConfig.OPTION_SESSION_CONFIG_UNPACKER;
40 import static androidx.camera.core.impl.UseCaseConfig.OPTION_SURFACE_OCCUPANCY_PRIORITY;
41 import static androidx.camera.core.impl.UseCaseConfig.OPTION_TARGET_CLASS;
42 import static androidx.camera.core.impl.UseCaseConfig.OPTION_TARGET_NAME;
43 import static androidx.camera.core.impl.UseCaseConfig.OPTION_ZSL_DISABLED;
44 import static androidx.camera.core.internal.ThreadConfig.OPTION_BACKGROUND_EXECUTOR;
45 
46 import android.graphics.ImageFormat;
47 import android.graphics.Matrix;
48 import android.graphics.PixelFormat;
49 import android.graphics.Rect;
50 import android.media.CamcorderProfile;
51 import android.media.ImageReader;
52 import android.util.Pair;
53 import android.util.Size;
54 import android.view.Display;
55 import android.view.Surface;
56 import android.view.View;
57 
58 import androidx.annotation.GuardedBy;
59 import androidx.annotation.IntDef;
60 import androidx.annotation.RequiresApi;
61 import androidx.annotation.RestrictTo;
62 import androidx.annotation.RestrictTo.Scope;
63 import androidx.camera.core.impl.CameraInfoInternal;
64 import androidx.camera.core.impl.CameraInternal;
65 import androidx.camera.core.impl.CaptureConfig;
66 import androidx.camera.core.impl.Config;
67 import androidx.camera.core.impl.ConfigProvider;
68 import androidx.camera.core.impl.DeferrableSurface;
69 import androidx.camera.core.impl.ImageAnalysisConfig;
70 import androidx.camera.core.impl.ImageInputConfig;
71 import androidx.camera.core.impl.ImageOutputConfig;
72 import androidx.camera.core.impl.ImageOutputConfig.RotationValue;
73 import androidx.camera.core.impl.ImmediateSurface;
74 import androidx.camera.core.impl.MutableConfig;
75 import androidx.camera.core.impl.MutableOptionsBundle;
76 import androidx.camera.core.impl.OptionsBundle;
77 import androidx.camera.core.impl.SessionConfig;
78 import androidx.camera.core.impl.StreamSpec;
79 import androidx.camera.core.impl.UseCaseConfig;
80 import androidx.camera.core.impl.UseCaseConfigFactory;
81 import androidx.camera.core.impl.utils.Threads;
82 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
83 import androidx.camera.core.internal.TargetConfig;
84 import androidx.camera.core.internal.ThreadConfig;
85 import androidx.camera.core.internal.compat.quirk.OnePixelShiftQuirk;
86 import androidx.camera.core.internal.utils.SizeUtil;
87 import androidx.camera.core.resolutionselector.AspectRatioStrategy;
88 import androidx.camera.core.resolutionselector.ResolutionSelector;
89 import androidx.camera.core.resolutionselector.ResolutionStrategy;
90 import androidx.core.util.Preconditions;
91 import androidx.lifecycle.LifecycleOwner;
92 
93 import org.jspecify.annotations.NonNull;
94 import org.jspecify.annotations.Nullable;
95 
96 import java.lang.annotation.Retention;
97 import java.lang.annotation.RetentionPolicy;
98 import java.util.ArrayList;
99 import java.util.List;
100 import java.util.Objects;
101 import java.util.UUID;
102 import java.util.concurrent.Executor;
103 
104 /**
105  * A use case providing CPU accessible images for an app to perform image analysis on.
106  *
107  * <p>ImageAnalysis acquires images from the camera via an {@link ImageReader}. Each image
108  * is provided to an {@link ImageAnalysis.Analyzer} function which can be implemented by application
109  * code, where it can access image data for application analysis via an {@link ImageProxy}.
110  *
111  * <p>The application is responsible for calling {@link ImageProxy#close()} to close the image.
112  * Failing to close the image will cause future images to be stalled or dropped depending on the
113  * backpressure strategy.
114  */
115 public final class ImageAnalysis extends UseCase {
116 
117     ////////////////////////////////////////////////////////////////////////////////////////////
118     // [UseCase lifetime constant] - Stays constant for the lifetime of the UseCase. Which means
119     // they could be created in the constructor.
120     ////////////////////////////////////////////////////////////////////////////////////////////
121 
122     /**
123      * Only deliver the latest image to the analyzer, dropping images as they arrive.
124      *
125      * <p>This strategy ignores the value set by {@link Builder#setImageQueueDepth(int)}.
126      * Only one image will be delivered for analysis at a time. If more images are produced
127      * while that image is being analyzed, they will be dropped and not queued for delivery.
128      * Once the image being analyzed is closed by calling {@link ImageProxy#close()}, the
129      * next latest image will be delivered.
130      *
131      * <p>Internally this strategy may make use of an internal {@link Executor} to receive
132      * and drop images from the producer. A performance-tuned executor will be created
133      * internally unless one is explicitly provided by
134      * {@link Builder#setBackgroundExecutor(Executor)}. In order to
135      * ensure smooth operation of this backpressure strategy, any user supplied
136      * {@link Executor} must be able to quickly respond to tasks posted to it, so setting
137      * the executor manually should only be considered in advanced use cases.
138      *
139      * @see Builder#setBackgroundExecutor(Executor)
140      */
141     public static final int STRATEGY_KEEP_ONLY_LATEST = 0;
142     /**
143      * Block the producer from generating new images.
144      *
145      * <p>Once the producer has produced the number of images equal to the image queue depth,
146      * and none have been closed, the producer will stop producing images. Note that images
147      * may be queued internally and not be delivered to the analyzer until the last delivered
148      * image has been closed with {@link ImageProxy#close()}. These internally queued images
149      * will count towards the total number of images that the producer can provide at any one
150      * time.
151      *
152      * <p>When the producer stops producing images, it may also stop producing images for
153      * other use cases, such as {@link Preview}, so it is important for the analyzer to keep
154      * up with frame rate, <i>on average</i>. Failure to keep up with frame rate may lead to
155      * jank in the frame stream and a diminished user experience. If more time is needed for
156      * analysis on <i>some</i> frames, consider increasing the image queue depth with
157      * {@link Builder#setImageQueueDepth(int)}.
158      *
159      * @see Builder#setImageQueueDepth(int)
160      */
161     public static final int STRATEGY_BLOCK_PRODUCER = 1;
162 
163     /**
164      * Images sent to the analyzer will have YUV format.
165      *
166      * <p>All {@link ImageProxy} sent to {@link Analyzer#analyze(ImageProxy)} will have
167      * format {@link android.graphics.ImageFormat#YUV_420_888}
168      *
169      * @see Builder#setOutputImageFormat(int)
170      */
171     public static final int OUTPUT_IMAGE_FORMAT_YUV_420_888 = 1;
172 
173     /**
174      * Images sent to the analyzer will have RGBA format.
175      *
176      * <p>All {@link ImageProxy} sent to {@link Analyzer#analyze(ImageProxy)} will have
177      * format {@link android.graphics.PixelFormat#RGBA_8888}
178      *
179      * <p>The output order is a single-plane with the order of R, G, B, A in increasing byte index
180      * in the {@link java.nio.ByteBuffer}. The {@link java.nio.ByteBuffer} is retrieved from
181      * {@link ImageProxy.PlaneProxy#getBuffer()}.
182      *
183      * @see Builder#setOutputImageFormat(int)
184      */
185     public static final int OUTPUT_IMAGE_FORMAT_RGBA_8888 = 2;
186 
187     /**
188      * Images sent to the analyzer will be formatted in NV21.
189      *
190      * <p>All {@link ImageProxy} sent to {@link Analyzer#analyze(ImageProxy)} will be in
191      * {@link ImageFormat#YUV_420_888} format with their image data formatted in NV21.
192      *
193      * <p>The output {@link ImageProxy} has three planes with the order of Y, U, V. The pixel
194      * stride of U or V planes are 2. The byte buffer pointer position of V plane will be ahead
195      * of the position of the U plane. Applications can directly read the <code>plane[2]</code>
196      * to get all the VU interleaved data.
197      *
198      * <p>Due to limitations on some Android devices in producing images in NV21 format, the
199      * {@link android.media.Image} object obtained from {@link ImageProxy#getImage()} will be the
200      * original image produced by the camera  capture pipeline. This may result in discrepancies
201      * between the  {@link android.media.Image} and the {@link ImageProxy}, such as:
202      *
203      * <ul>
204      * <li>Plane data may differ.
205      * <li>Width and height may differ.
206      * <li>Other properties may also differ.
207      * </ul>
208      *
209      * <p>Developers should be aware of these potential differences and use the properties from the
210      * {@link ImageProxy} when necessary.
211      *
212      * @see Builder#setOutputImageFormat(int)
213      */
214     public static final int OUTPUT_IMAGE_FORMAT_NV21 = 3;
215 
216     /**
217      * Provides a static configuration with implementation-agnostic options.
218      */
219     @RestrictTo(Scope.LIBRARY_GROUP)
220     public static final Defaults DEFAULT_CONFIG = new Defaults();
221     private static final String TAG = "ImageAnalysis";
222     // ImageReader depth for KEEP_ONLY_LATEST mode.
223     private static final int NON_BLOCKING_IMAGE_DEPTH = 4;
224     @BackpressureStrategy
225     private static final int DEFAULT_BACKPRESSURE_STRATEGY = STRATEGY_KEEP_ONLY_LATEST;
226     private static final int DEFAULT_IMAGE_QUEUE_DEPTH = 6;
227     // Default to YUV_420_888 format for output.
228     private static final int DEFAULT_OUTPUT_IMAGE_FORMAT = OUTPUT_IMAGE_FORMAT_YUV_420_888;
229     // One pixel shift for YUV.
230     private static final Boolean DEFAULT_ONE_PIXEL_SHIFT_ENABLED = null;
231     // Default to disabled for rotation.
232     private static final boolean DEFAULT_OUTPUT_IMAGE_ROTATION_ENABLED = false;
233     private final Object mAnalysisLock = new Object();
234 
235     @GuardedBy("mAnalysisLock")
236     @SuppressWarnings("WeakerAccess") /* synthetic access */
237     ImageAnalysisAbstractAnalyzer mImageAnalysisAbstractAnalyzer;
238 
239     ////////////////////////////////////////////////////////////////////////////////////////////
240     // [UseCase lifetime dynamic] - Dynamic variables which could change during anytime during
241     // the UseCase lifetime.
242     ////////////////////////////////////////////////////////////////////////////////////////////
243 
244     @GuardedBy("mAnalysisLock")
245     private Executor mSubscribedAnalyzerExecutor;
246     @GuardedBy("mAnalysisLock")
247     private ImageAnalysis.Analyzer mSubscribedAnalyzer;
248     @GuardedBy("mAnalysisLock")
249     private Rect mViewPortCropRect;
250     @GuardedBy("mAnalysisLock")
251     private Matrix mSensorToBufferTransformMatrix;
252 
253     ////////////////////////////////////////////////////////////////////////////////////////////
254     // [UseCase attached dynamic] - Can change but is only available when the UseCase is attached.
255     ////////////////////////////////////////////////////////////////////////////////////////////
256 
257     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
258     SessionConfig.Builder mSessionConfigBuilder;
259 
260     private @Nullable DeferrableSurface mDeferrableSurface;
261     private SessionConfig.@Nullable CloseableErrorListener mCloseableErrorListener;
262 
263     /**
264      * Creates a new image analysis use case from the given configuration.
265      *
266      * @param config for this use case instance
267      */
268     @SuppressWarnings("WeakerAccess")
ImageAnalysis(@onNull ImageAnalysisConfig config)269     ImageAnalysis(@NonNull ImageAnalysisConfig config) {
270         super(config);
271     }
272 
273     /**
274      * {@inheritDoc}
275      */
276     @RestrictTo(Scope.LIBRARY_GROUP)
277     @Override
onMergeConfig(@onNull CameraInfoInternal cameraInfo, UseCaseConfig.@NonNull Builder<?, ?, ?> builder)278     protected @NonNull UseCaseConfig<?> onMergeConfig(@NonNull CameraInfoInternal cameraInfo,
279             UseCaseConfig.@NonNull Builder<?, ?, ?> builder) {
280         // Override the target resolution with the value provided by the analyzer.
281         Size analyzerResolution;
282         synchronized (mAnalysisLock) {
283             analyzerResolution = mSubscribedAnalyzer != null
284                     ? mSubscribedAnalyzer.getDefaultTargetResolution() : null;
285         }
286 
287         if (analyzerResolution == null) {
288             return builder.getUseCaseConfig();
289         }
290 
291         int targetRotation = builder.getMutableConfig().retrieveOption(
292                 OPTION_TARGET_ROTATION, Surface.ROTATION_0);
293         // analyzerResolution is a size in the sensor coordinate system, but the legacy
294         // target resolution setting is in the view coordinate system. Flips the
295         // analyzerResolution according to the sensor rotation degrees.
296         if (cameraInfo.getSensorRotationDegrees(targetRotation) % 180 == 90) {
297             analyzerResolution = new Size(/* width= */ analyzerResolution.getHeight(),
298                     /* height= */ analyzerResolution.getWidth());
299         }
300 
301         // Merges the analyzerResolution as legacy target resolution setting so that it can take
302         // effect when running the legacy resolution selection logic flow.
303         if (!builder.getUseCaseConfig().containsOption(OPTION_TARGET_RESOLUTION)) {
304             builder.getMutableConfig().insertOption(OPTION_TARGET_RESOLUTION,
305                     analyzerResolution);
306         }
307 
308         // Merges the analyzerResolution to ResolutionSelector.
309         // Note: the input builder contains the configs that are merging result of default config
310         // and app config  (in UseCase#mergeConfigs()). Merging the analyzer default target
311         // resolution depends on the ResolutionSelector set by the app, therefore, need to check
312         // the ResolutionSelector retrieved from UseCase#getAppConfig() to determine how to merge
313         // it.
314         if (builder.getUseCaseConfig().containsOption(OPTION_RESOLUTION_SELECTOR)) {
315             ResolutionSelector appResolutionSelector =
316                     getAppConfig().retrieveOption(OPTION_RESOLUTION_SELECTOR, null);
317             // Creates a builder according to whether app has resolution selector setting or not.
318             ResolutionSelector.Builder resolutionSelectorBuilder =
319                     appResolutionSelector == null ? new ResolutionSelector.Builder()
320                             : ResolutionSelector.Builder.fromResolutionSelector(
321                                     appResolutionSelector);
322             // Sets a ResolutionStrategy matching to the analyzer default resolution when app
323             // doesn't have resolution strategy setting.
324             if (appResolutionSelector == null
325                     || appResolutionSelector.getResolutionStrategy() == null) {
326                 resolutionSelectorBuilder.setResolutionStrategy(
327                         new ResolutionStrategy(analyzerResolution,
328                                 ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER));
329             }
330             // Sets a ResolutionFilter to select the analyzer default resolution in priority only
331             // when the app doesn't have its own resolution selector setting. This can't be set when
332             // app has any ResolutionSelector setting. Otherwise, app might obtain an unexpected
333             // resolution for ImageAnalysis.
334             if (appResolutionSelector == null) {
335                 final Size analyzerResolutionFinal = analyzerResolution;
336                 resolutionSelectorBuilder.setResolutionFilter(
337                         (supportedSizes, rotationDegrees) -> {
338                             List<Size> resultList = new ArrayList<>(supportedSizes);
339                             if (resultList.contains(analyzerResolutionFinal)) {
340                                 resultList.remove(analyzerResolutionFinal);
341                                 resultList.add(0, analyzerResolutionFinal);
342                             }
343                             return resultList;
344                         }
345                 );
346             }
347             builder.getMutableConfig().insertOption(OPTION_RESOLUTION_SELECTOR,
348                     resolutionSelectorBuilder.build());
349         }
350 
351         return builder.getUseCaseConfig();
352     }
353 
354     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
createPipeline(@onNull String cameraId, @NonNull ImageAnalysisConfig config, @NonNull StreamSpec streamSpec)355     SessionConfig.Builder createPipeline(@NonNull String cameraId,
356             @NonNull ImageAnalysisConfig config, @NonNull StreamSpec streamSpec) {
357         Threads.checkMainThread();
358         Size resolution = streamSpec.getResolution();
359 
360         Executor backgroundExecutor = Preconditions.checkNotNull(config.getBackgroundExecutor(
361                 CameraXExecutors.highPriorityExecutor()));
362 
363         int imageQueueDepth =
364                 getBackpressureStrategy() == STRATEGY_BLOCK_PRODUCER ? getImageQueueDepth()
365                         : NON_BLOCKING_IMAGE_DEPTH;
366         SafeCloseImageReaderProxy imageReaderProxy;
367         if (config.getImageReaderProxyProvider() != null) {
368             imageReaderProxy = new SafeCloseImageReaderProxy(
369                     config.getImageReaderProxyProvider().newInstance(
370                             resolution.getWidth(), resolution.getHeight(), getImageFormat(),
371                             imageQueueDepth, 0));
372         } else {
373             imageReaderProxy =
374                     new SafeCloseImageReaderProxy(ImageReaderProxys.createIsolatedReader(
375                             resolution.getWidth(),
376                             resolution.getHeight(),
377                             getImageFormat(),
378                             imageQueueDepth));
379         }
380 
381         ImageAnalysisAbstractAnalyzer imageAnalysisAbstractAnalyzer;
382         synchronized (mAnalysisLock) {
383             recreateImageAnalysisAbstractAnalyzer();
384             imageAnalysisAbstractAnalyzer = mImageAnalysisAbstractAnalyzer;
385         }
386 
387         boolean flipWH = getCamera() != null ? isFlipWH(getCamera()) : false;
388         int width = flipWH ? resolution.getHeight() : resolution.getWidth();
389         int height = flipWH ? resolution.getWidth() : resolution.getHeight();
390         int format = getOutputImageFormat() == OUTPUT_IMAGE_FORMAT_RGBA_8888
391                 ? PixelFormat.RGBA_8888 : ImageFormat.YUV_420_888;
392 
393         boolean isYuv2Rgb = getImageFormat() == ImageFormat.YUV_420_888
394                 && getOutputImageFormat() == OUTPUT_IMAGE_FORMAT_RGBA_8888;
395         boolean isYuv2Nv21 = getImageFormat() == ImageFormat.YUV_420_888
396                 && getOutputImageFormat() == OUTPUT_IMAGE_FORMAT_NV21;
397         boolean isYuvRotationOrPixelShift = getImageFormat() == ImageFormat.YUV_420_888
398                 && ((getCamera() != null && getRelativeRotation(getCamera()) != 0)
399                 || Boolean.TRUE.equals(getOnePixelShiftEnabled()));
400 
401         // TODO(b/195021586): to support RGB format input for image analysis for devices already
402         // supporting RGB natively. The logic here will check if the specific configured size is
403         // available in RGB and if not, fall back to YUV-RGB conversion.
404         final SafeCloseImageReaderProxy processedImageReaderProxy =
405                 (isYuv2Rgb || (isYuvRotationOrPixelShift && !isYuv2Nv21))
406                         ? new SafeCloseImageReaderProxy(
407                         ImageReaderProxys.createIsolatedReader(
408                                 width,
409                                 height,
410                                 format,
411                                 imageReaderProxy.getMaxImages())) : null;
412         if (processedImageReaderProxy != null) {
413             imageAnalysisAbstractAnalyzer.setProcessedImageReaderProxy(processedImageReaderProxy);
414         }
415 
416         tryUpdateRelativeRotation();
417 
418         imageReaderProxy.setOnImageAvailableListener(imageAnalysisAbstractAnalyzer,
419                 backgroundExecutor);
420 
421         SessionConfig.Builder sessionConfigBuilder = SessionConfig.Builder.createFrom(config,
422                 streamSpec.getResolution());
423         if (streamSpec.getImplementationOptions() != null) {
424             sessionConfigBuilder.addImplementationOptions(streamSpec.getImplementationOptions());
425         }
426 
427         if (mDeferrableSurface != null) {
428             mDeferrableSurface.close();
429         }
430         mDeferrableSurface = new ImmediateSurface(imageReaderProxy.getSurface(), resolution,
431                 getImageFormat());
432         mDeferrableSurface.getTerminationFuture().addListener(
433                 () -> {
434                     imageReaderProxy.safeClose();
435                     if (processedImageReaderProxy != null) {
436                         processedImageReaderProxy.safeClose();
437                     }
438                 },
439                 CameraXExecutors.mainThreadExecutor());
440 
441         sessionConfigBuilder.setSessionType(streamSpec.getSessionType());
442         // Applies the AE fps range to the session config builder according to the stream spec and
443         // quirk values.
444         applyExpectedFrameRateRange(sessionConfigBuilder, streamSpec);
445 
446         sessionConfigBuilder.addSurface(mDeferrableSurface,
447                 streamSpec.getDynamicRange(),
448                 null,
449                 MirrorMode.MIRROR_MODE_UNSPECIFIED);
450 
451         if (mCloseableErrorListener != null) {
452             mCloseableErrorListener.close();
453         }
454         mCloseableErrorListener = new SessionConfig.CloseableErrorListener(
455                 (sessionConfig, error) -> {
456                     // Do nothing when the use case has been unbound.
457                     if (getCamera() == null) {
458                         return;
459                     }
460 
461                     clearPipeline();
462                     // Clear cache so app won't get a outdated image.
463                     imageAnalysisAbstractAnalyzer.clearCache();
464                     // Only reset the pipeline when the bound camera is the same.
465                     mSessionConfigBuilder = createPipeline(getCameraId(),
466                             (ImageAnalysisConfig) getCurrentConfig(),
467                             Preconditions.checkNotNull(getAttachedStreamSpec()));
468                     updateSessionConfig(List.of(mSessionConfigBuilder.build()));
469                     notifyReset();
470                 });
471 
472         sessionConfigBuilder.setErrorListener(mCloseableErrorListener);
473 
474         return sessionConfigBuilder;
475     }
476 
recreateImageAnalysisAbstractAnalyzer()477     private void recreateImageAnalysisAbstractAnalyzer() {
478         synchronized (mAnalysisLock) {
479             ImageAnalysisConfig config = (ImageAnalysisConfig) getCurrentConfig();
480 
481             if (config.getBackpressureStrategy(DEFAULT_BACKPRESSURE_STRATEGY)
482                     == STRATEGY_BLOCK_PRODUCER) {
483                 mImageAnalysisAbstractAnalyzer = new ImageAnalysisBlockingAnalyzer();
484             } else {
485                 mImageAnalysisAbstractAnalyzer = new ImageAnalysisNonBlockingAnalyzer(
486                         config.getBackgroundExecutor(CameraXExecutors.highPriorityExecutor()));
487             }
488             mImageAnalysisAbstractAnalyzer.setOutputImageFormat(getOutputImageFormat());
489             mImageAnalysisAbstractAnalyzer.setOutputImageRotationEnabled(
490                     isOutputImageRotationEnabled());
491 
492             CameraInternal cameraInternal = getCamera();
493 
494             // Flag to enable or disable one pixel shift. It will override the flag set by device
495             // info.
496             // If enabled, the workaround will be applied for all devices.
497             // If disabled, the workaround will be disabled for all devices.
498             // If not configured, the workaround will be applied to the problem devices only.
499             Boolean isOnePixelShiftEnabled = getOnePixelShiftEnabled();
500             boolean isOnePixelShiftIssueDevice = false;
501             if (cameraInternal != null) {
502                 isOnePixelShiftIssueDevice =
503                         cameraInternal.getCameraInfoInternal().getCameraQuirks().contains(
504                                 OnePixelShiftQuirk.class);
505             }
506             mImageAnalysisAbstractAnalyzer.setOnePixelShiftEnabled(
507                     isOnePixelShiftEnabled == null ? isOnePixelShiftIssueDevice
508                             : isOnePixelShiftEnabled);
509 
510             // Sets relative rotation
511             if (cameraInternal != null) {
512                 mImageAnalysisAbstractAnalyzer.setRelativeRotation(
513                         getRelativeRotation(cameraInternal));
514             }
515 
516             // Sets view port crop rect
517             if (mViewPortCropRect != null) {
518                 mImageAnalysisAbstractAnalyzer.setViewPortCropRect(mViewPortCropRect);
519             }
520 
521             // Sets sensor to buffer transform matrix
522             if (mSensorToBufferTransformMatrix != null) {
523                 mImageAnalysisAbstractAnalyzer.setSensorToBufferTransformMatrix(
524                         mSensorToBufferTransformMatrix);
525             }
526 
527             if (mSubscribedAnalyzerExecutor != null && mSubscribedAnalyzer != null) {
528                 mImageAnalysisAbstractAnalyzer.setAnalyzer(mSubscribedAnalyzerExecutor,
529                         mSubscribedAnalyzer);
530             }
531         }
532     }
533 
534     /**
535      * Clear the internal pipeline so that the pipeline can be set up again.
536      */
537     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
clearPipeline()538     void clearPipeline() {
539         Threads.checkMainThread();
540 
541         // Closes the old error listener
542         if (mCloseableErrorListener != null) {
543             mCloseableErrorListener.close();
544             mCloseableErrorListener = null;
545         }
546 
547         if (mDeferrableSurface != null) {
548             mDeferrableSurface.close();
549             mDeferrableSurface = null;
550         }
551     }
552 
553     /**
554      * Removes a previously set analyzer.
555      *
556      * <p>This will stop data from streaming to the {@link ImageAnalysis}.
557      */
clearAnalyzer()558     public void clearAnalyzer() {
559         synchronized (mAnalysisLock) {
560             if (mImageAnalysisAbstractAnalyzer != null) {
561                 mImageAnalysisAbstractAnalyzer.setAnalyzer(null, null);
562             }
563             if (mSubscribedAnalyzer != null) {
564                 notifyInactive();
565             }
566             mSubscribedAnalyzerExecutor = null;
567             mSubscribedAnalyzer = null;
568         }
569     }
570 
571     /**
572      * Returns the rotation of the intended target for images.
573      *
574      * <p>
575      * The rotation can be set when constructing an {@link ImageAnalysis} instance using
576      * {@link ImageAnalysis.Builder#setTargetRotation(int)}, or dynamically by calling
577      * {@link ImageAnalysis#setTargetRotation(int)}. If not set, the target rotation
578      * defaults to the value of {@link Display#getRotation()} of the default display at the time
579      * the use case is created. The use case is fully created once it has been attached to a camera.
580      * </p>
581      *
582      * @return The rotation of the intended target for images.
583      * @see ImageAnalysis#setTargetRotation(int)
584      */
585     @RotationValue
getTargetRotation()586     public int getTargetRotation() {
587         return getTargetRotationInternal();
588     }
589 
590     /**
591      * Sets the target rotation.
592      *
593      * <p>This adjust the {@link ImageInfo#getRotationDegrees()} of the {@link ImageProxy} passed
594      * to {@link Analyzer#analyze(ImageProxy)}. The rotation value of ImageInfo will be the
595      * rotation, which if applied to the output image, will make the image match target rotation
596      * specified here.
597      *
598      * <p>While rotation can also be set via {@link Builder#setTargetRotation(int)}, using
599      * {@link ImageAnalysis#setTargetRotation(int)} allows the target rotation to be set
600      * dynamically.
601      *
602      * <p>In general, it is best to use an {@link android.view.OrientationEventListener} to
603      * set the target rotation.  This way, the rotation output to the Analyzer will indicate
604      * which way is down for a given image.  This is important since display orientation may be
605      * locked by device default, user setting, or app configuration, and some devices may not
606      * transition to a reverse-portrait display orientation. In these cases, set target rotation
607      * dynamically according to the {@link android.view.OrientationEventListener}, without
608      * re-creating the use case. {@link UseCase#snapToSurfaceRotation(int)} is a helper function to
609      * convert the orientation of the {@link android.view.OrientationEventListener} to a rotation
610      * value. See {@link UseCase#snapToSurfaceRotation(int)} for more information and sample code.
611      *
612      * <p>When this function is called, value set by
613      * {@link ImageAnalysis.Builder#setTargetResolution(Size)} will be updated automatically to
614      * make sure the suitable resolution can be selected when the use case is bound.
615      *
616      * <p>If not set here or by configuration, the target rotation will default to the value of
617      * {@link Display#getRotation()} of the default display at the time the use case is bound. To
618      * return to the default value, set the value to
619      * <pre>{@code
620      * context.getSystemService(WindowManager.class).getDefaultDisplay().getRotation();
621      * }</pre>
622      *
623      * @param rotation Target rotation of the output image, expressed as one of
624      *                 {@link Surface#ROTATION_0}, {@link Surface#ROTATION_90},
625      *                 {@link Surface#ROTATION_180}, or {@link Surface#ROTATION_270}.
626      */
setTargetRotation(@otationValue int rotation)627     public void setTargetRotation(@RotationValue int rotation) {
628         if (setTargetRotationInternal(rotation)) {
629             tryUpdateRelativeRotation();
630         }
631     }
632 
633     /**
634      * Sets an analyzer to receive and analyze images.
635      *
636      * <p>Setting an analyzer will signal to the camera that it should begin sending data. The
637      * stream of data can be stopped by calling {@link #clearAnalyzer()}.
638      *
639      * <p>Applications can process or copy the image by implementing the {@link Analyzer}.  If
640      * frames should be skipped (no analysis), the analyzer function should return, instead of
641      * disconnecting the analyzer function completely.
642      *
643      * <p>Setting an analyzer function replaces any previous analyzer.  Only one analyzer can be
644      * set at any time.
645      *
646      * @param executor The executor in which the
647      *                 {@link ImageAnalysis.Analyzer#analyze(ImageProxy)} will be run.
648      * @param analyzer of the images.
649      */
setAnalyzer(@onNull Executor executor, @NonNull Analyzer analyzer)650     public void setAnalyzer(@NonNull Executor executor, @NonNull Analyzer analyzer) {
651         synchronized (mAnalysisLock) {
652             if (mImageAnalysisAbstractAnalyzer != null) {
653                 mImageAnalysisAbstractAnalyzer.setAnalyzer(executor,
654                         image -> analyzer.analyze(image));
655             }
656             if (mSubscribedAnalyzer == null) {
657                 notifyActive();
658             }
659             mSubscribedAnalyzerExecutor = executor;
660             mSubscribedAnalyzer = analyzer;
661         }
662     }
663 
664     /**
665      * {@inheritDoc}
666      */
667     @RestrictTo(Scope.LIBRARY_GROUP)
668     @Override
setViewPortCropRect(@onNull Rect viewPortCropRect)669     public void setViewPortCropRect(@NonNull Rect viewPortCropRect) {
670         super.setViewPortCropRect(viewPortCropRect);
671         synchronized (mAnalysisLock) {
672             if (mImageAnalysisAbstractAnalyzer != null) {
673                 mImageAnalysisAbstractAnalyzer.setViewPortCropRect(viewPortCropRect);
674             }
675             mViewPortCropRect = viewPortCropRect;
676         }
677     }
678 
679     /**
680      * {@inheritDoc}
681      */
682     @RestrictTo(Scope.LIBRARY_GROUP)
683     @Override
setSensorToBufferTransformMatrix(@onNull Matrix matrix)684     public void setSensorToBufferTransformMatrix(@NonNull Matrix matrix) {
685         super.setSensorToBufferTransformMatrix(matrix);
686         synchronized (mAnalysisLock) {
687             if (mImageAnalysisAbstractAnalyzer != null) {
688                 mImageAnalysisAbstractAnalyzer.setSensorToBufferTransformMatrix(matrix);
689             }
690             mSensorToBufferTransformMatrix = matrix;
691         }
692     }
693 
isFlipWH(@onNull CameraInternal cameraInternal)694     private boolean isFlipWH(@NonNull CameraInternal cameraInternal) {
695         return isOutputImageRotationEnabled()
696                 ? ((getRelativeRotation(cameraInternal) % 180) != 0) : false;
697     }
698 
699     /**
700      * Returns the mode with which images are acquired from the {@linkplain ImageReader image
701      * producer}.
702      *
703      * <p>
704      * The backpressure strategy is set when constructing an {@link ImageAnalysis} instance using
705      * {@link ImageAnalysis.Builder#setBackpressureStrategy(int)}. If not set, it defaults to
706      * {@link ImageAnalysis#STRATEGY_KEEP_ONLY_LATEST}.
707      * </p>
708      *
709      * @return The backpressure strategy applied to the image producer.
710      * @see ImageAnalysis.Builder#setBackpressureStrategy(int)
711      */
712     @BackpressureStrategy
getBackpressureStrategy()713     public int getBackpressureStrategy() {
714         return ((ImageAnalysisConfig) getCurrentConfig()).getBackpressureStrategy(
715                 DEFAULT_BACKPRESSURE_STRATEGY);
716     }
717 
718     /**
719      * Returns the executor that will be used for background tasks.
720      *
721      * @return The {@link Executor} provided to
722      * {@link ImageAnalysis.Builder#setBackgroundExecutor(Executor)}.
723      * If no Executor has been provided, then returns {@code null}
724      */
725     @ExperimentalUseCaseApi
getBackgroundExecutor()726     public @Nullable Executor getBackgroundExecutor() {
727         return ((ImageAnalysisConfig) getCurrentConfig())
728                 .getBackgroundExecutor(null);
729     }
730 
731     /**
732      * Returns the number of images available to the camera pipeline, including the image being
733      * analyzed, for the {@link #STRATEGY_BLOCK_PRODUCER} backpressure mode.
734      *
735      * <p>
736      * The image queue depth is set when constructing an {@link ImageAnalysis} instance using
737      * {@link ImageAnalysis.Builder#setImageQueueDepth(int)}. If not set, and this option is used
738      * by the backpressure strategy, the default will be a queue depth of 6 images.
739      * </p>
740      *
741      * @return The image queue depth for the {@link #STRATEGY_BLOCK_PRODUCER} backpressure mode.
742      * @see ImageAnalysis.Builder#setImageQueueDepth(int)
743      * @see ImageAnalysis.Builder#setBackpressureStrategy(int)
744      */
getImageQueueDepth()745     public int getImageQueueDepth() {
746         return ((ImageAnalysisConfig) getCurrentConfig()).getImageQueueDepth(
747                 DEFAULT_IMAGE_QUEUE_DEPTH);
748     }
749 
750     /**
751      * Gets output image format.
752      *
753      * <p>The returned image format will be
754      * {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_YUV_420_888},
755      * {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_RGBA_8888} or
756      * {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_NV21}.
757      *
758      * @return output image format.
759      * @see ImageAnalysis.Builder#setOutputImageFormat(int)
760      */
761     @ImageAnalysis.OutputImageFormat
getOutputImageFormat()762     public int getOutputImageFormat() {
763         return ((ImageAnalysisConfig) getCurrentConfig()).getOutputImageFormat(
764                 DEFAULT_OUTPUT_IMAGE_FORMAT);
765     }
766 
767     /**
768      * Checks if output image rotation is enabled. It returns false by default.
769      *
770      * @return true if enabled, false otherwise.
771      * @see ImageAnalysis.Builder#setOutputImageRotationEnabled(boolean)
772      */
isOutputImageRotationEnabled()773     public boolean isOutputImageRotationEnabled() {
774         return ((ImageAnalysisConfig) getCurrentConfig()).isOutputImageRotationEnabled(
775                 DEFAULT_OUTPUT_IMAGE_ROTATION_ENABLED);
776     }
777 
778     /**
779      *
780      */
781     @RestrictTo(Scope.LIBRARY_GROUP)
getOnePixelShiftEnabled()782     public @Nullable Boolean getOnePixelShiftEnabled() {
783         return ((ImageAnalysisConfig) getCurrentConfig()).getOnePixelShiftEnabled(
784                 DEFAULT_ONE_PIXEL_SHIFT_ENABLED);
785     }
786 
787     /**
788      * Gets resolution related information of the {@link ImageAnalysis}.
789      *
790      * <p>The returned {@link ResolutionInfo} will be expressed in the coordinates of the camera
791      * sensor. It will be the same as the resolution of the {@link ImageProxy} received from
792      * {@link ImageAnalysis.Analyzer#analyze}.
793      *
794      * <p>The resolution information might change if the use case is unbound and then rebound or
795      * {@link #setTargetRotation(int)} is called to change the target rotation setting. The
796      * application needs to call {@link #getResolutionInfo()} again to get the latest
797      * {@link ResolutionInfo} for the changes.
798      *
799      * @return the resolution information if the use case has been bound by the
800      * {@link androidx.camera.lifecycle.ProcessCameraProvider#bindToLifecycle(LifecycleOwner,
801      * CameraSelector, UseCase...)} API, or null if the use case is not bound yet.
802      */
getResolutionInfo()803     public @Nullable ResolutionInfo getResolutionInfo() {
804         return getResolutionInfoInternal();
805     }
806 
807     /**
808      * Returns the resolution selector setting.
809      *
810      * <p>This setting is set when constructing an ImageAnalysis using
811      * {@link Builder#setResolutionSelector(ResolutionSelector)}.
812      */
getResolutionSelector()813     public @Nullable ResolutionSelector getResolutionSelector() {
814         return ((ImageOutputConfig) getCurrentConfig()).getResolutionSelector(null);
815     }
816 
817     @Override
toString()818     public @NonNull String toString() {
819         return TAG + ":" + getName();
820     }
821 
822     /**
823      * {@inheritDoc}
824      */
825     @RestrictTo(Scope.LIBRARY_GROUP)
826     @Override
onUnbind()827     public void onUnbind() {
828         clearPipeline();
829         synchronized (mAnalysisLock) {
830             mImageAnalysisAbstractAnalyzer.detach();
831             mImageAnalysisAbstractAnalyzer = null;
832         }
833     }
834 
835     /**
836      * {@inheritDoc}
837      */
838     @RestrictTo(Scope.LIBRARY_GROUP)
839     @Override
getDefaultConfig(boolean applyDefaultConfig, @NonNull UseCaseConfigFactory factory)840     public @Nullable UseCaseConfig<?> getDefaultConfig(boolean applyDefaultConfig,
841             @NonNull UseCaseConfigFactory factory) {
842         Config captureConfig = factory.getConfig(
843                 DEFAULT_CONFIG.getConfig().getCaptureType(),
844                 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY);
845 
846         if (applyDefaultConfig) {
847             captureConfig = Config.mergeConfigs(captureConfig, DEFAULT_CONFIG.getConfig());
848         }
849 
850         return captureConfig == null ? null :
851                 getUseCaseConfigBuilder(captureConfig).getUseCaseConfig();
852     }
853 
854     /**
855      * {@inheritDoc}
856      */
857     @RestrictTo(Scope.LIBRARY_GROUP)
858     @Override
getUseCaseConfigBuilder(@onNull Config config)859     public UseCaseConfig.@NonNull Builder<?, ?, ?> getUseCaseConfigBuilder(@NonNull Config config) {
860         return ImageAnalysis.Builder.fromConfig(config);
861     }
862 
863     /**
864      * {@inheritDoc}
865      */
866     @Override
867     @RestrictTo(Scope.LIBRARY_GROUP)
onSuggestedStreamSpecUpdated( @onNull StreamSpec primaryStreamSpec, @Nullable StreamSpec secondaryStreamSpec)868     protected @NonNull StreamSpec onSuggestedStreamSpecUpdated(
869             @NonNull StreamSpec primaryStreamSpec,
870             @Nullable StreamSpec secondaryStreamSpec) {
871         final ImageAnalysisConfig config = (ImageAnalysisConfig) getCurrentConfig();
872 
873         mSessionConfigBuilder = createPipeline(getCameraId(), config,
874                 primaryStreamSpec);
875         updateSessionConfig(List.of(mSessionConfigBuilder.build()));
876 
877         return primaryStreamSpec;
878     }
879 
880     /**
881      * {@inheritDoc}
882      */
883     @Override
884     @RestrictTo(Scope.LIBRARY_GROUP)
onSuggestedStreamSpecImplementationOptionsUpdated( @onNull Config config)885     protected @NonNull StreamSpec onSuggestedStreamSpecImplementationOptionsUpdated(
886             @NonNull Config config) {
887         mSessionConfigBuilder.addImplementationOptions(config);
888         updateSessionConfig(List.of(mSessionConfigBuilder.build()));
889         return getAttachedStreamSpec().toBuilder().setImplementationOptions(config).build();
890     }
891 
892     /**
893      * Updates relative rotation if attached to a camera. No-op otherwise.
894      */
tryUpdateRelativeRotation()895     private void tryUpdateRelativeRotation() {
896         synchronized (mAnalysisLock) {
897             CameraInternal cameraInternal = getCamera();
898             if (cameraInternal != null) {
899                 mImageAnalysisAbstractAnalyzer.setRelativeRotation(
900                         getRelativeRotation(cameraInternal));
901             }
902         }
903     }
904 
905     /**
906      * How to apply backpressure to the source producing images for analysis.
907      *
908      * <p>Sometimes, images may be produced faster than they can be analyzed. Since images
909      * generally reserve a large portion of the device's memory, they cannot be buffered
910      * unbounded and indefinitely. The backpressure strategy defines how to deal with this scenario.
911      *
912      * <p>The receiver of the {@link ImageProxy} is responsible for explicitly closing the image
913      * by calling {@link ImageProxy#close()}. However, the image will only be valid when the
914      * ImageAnalysis instance is bound to a camera.
915      *
916      * @see Builder#setBackpressureStrategy(int)
917      */
918     @IntDef({STRATEGY_KEEP_ONLY_LATEST, STRATEGY_BLOCK_PRODUCER})
919     @Retention(RetentionPolicy.SOURCE)
920     @RestrictTo(Scope.LIBRARY_GROUP)
921     public @interface BackpressureStrategy {
922     }
923 
924     /**
925      * Supported output image format for image analysis.
926      *
927      * <p>The supported output image format
928      * is {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_YUV_420_888},
929      * {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_RGBA_8888} and
930      * {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_NV21}.
931      *
932      * <p>By default, {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_YUV_420_888} will be used.
933      *
934      * @see Builder#setOutputImageFormat(int)
935      */
936     @IntDef({OUTPUT_IMAGE_FORMAT_YUV_420_888, OUTPUT_IMAGE_FORMAT_RGBA_8888,
937             OUTPUT_IMAGE_FORMAT_NV21})
938     @Retention(RetentionPolicy.SOURCE)
939     @RestrictTo(Scope.LIBRARY_GROUP)
940     public @interface OutputImageFormat {
941     }
942 
943     /**
944      * Interface for analyzing images.
945      *
946      * <p>Implement Analyzer and pass it to {@link ImageAnalysis#setAnalyzer(Executor, Analyzer)}
947      * to receive images and perform custom processing by implementing the
948      * {@link ImageAnalysis.Analyzer#analyze(ImageProxy)} function.
949      */
950     public interface Analyzer {
951         /**
952          * Analyzes an image to produce a result.
953          *
954          * <p>This method is called once for each image from the camera, and called at the
955          * frame rate of the camera. Each analyze call is executed sequentially.
956          *
957          * <p>It is the responsibility of the application to close the image once done with it.
958          * If the images are not closed then it may block further images from being produced
959          * (causing the preview to stall) or drop images as determined by the configured
960          * backpressure strategy. The exact behavior is configurable via
961          * {@link ImageAnalysis.Builder#setBackpressureStrategy(int)}.
962          *
963          * <p>Images produced here will no longer be valid after the {@link ImageAnalysis}
964          * instance that produced it has been unbound from the camera.
965          *
966          * <p>The image provided has format {@link android.graphics.ImageFormat#YUV_420_888}.
967          *
968          * <p>The provided image is typically in the orientation of the sensor, meaning CameraX
969          * does not perform an internal rotation of the data.  The rotationDegrees parameter allows
970          * the analysis to understand the image orientation when processing or to apply a rotation.
971          * For example, if the
972          * {@linkplain ImageAnalysis#setTargetRotation(int) target rotation}) is natural
973          * orientation, rotationDegrees would be the rotation which would align the buffer
974          * data ordering to natural orientation.
975          *
976          * <p>Timestamps are in nanoseconds and monotonic and can be compared to timestamps from
977          * images produced from UseCases bound to the same camera instance.  More detail is
978          * available depending on the implementation.  For example with CameraX using a
979          * {@link androidx.camera.camera2} implementation additional detail can be found in
980          * {@link android.hardware.camera2.CameraDevice} documentation.
981          *
982          * @param image The image to analyze
983          * @see android.media.Image#getTimestamp()
984          * @see android.hardware.camera2.CaptureResult#SENSOR_TIMESTAMP
985          */
analyze(@onNull ImageProxy image)986         void analyze(@NonNull ImageProxy image);
987 
988         /**
989          * Implement this method to set a default target resolution for the {@link ImageAnalysis}.
990          *
991          * <p> Implement this method if the {@link Analyzer} requires a specific resolution to
992          * work. The return value will be used as the default target resolution for the
993          * {@link ImageAnalysis}. Return {@code null} if no falling back is needed. By default,
994          * this method returns {@code null}.
995          *
996          * <p> If the app does not set a target resolution for {@link ImageAnalysis}, then this
997          * value will be used as the target resolution. If the {@link ImageAnalysis} has set a
998          * target resolution, e.g. if {@link ImageAnalysis.Builder#setTargetResolution(Size)} is
999          * called, then the {@link ImageAnalysis} will use the app value over this value.
1000          *
1001          * <p> Note that this method is invoked by CameraX at the time of binding to lifecycle. In
1002          * order for this value to be effective, the {@link Analyzer} has to be set before
1003          * {@link ImageAnalysis} is bound to a lifecycle. Otherwise, the value will be ignored.
1004          *
1005          * @return the default resolution of {@link ImageAnalysis}, or {@code null} if no specific
1006          * resolution is needed.
1007          */
getDefaultTargetResolution()1008         default @Nullable Size getDefaultTargetResolution() {
1009             return null;
1010         }
1011 
1012         /**
1013          * Implement this method to return the target coordinate system.
1014          *
1015          * <p>The coordinates detected by analyzing camera frame usually needs to be transformed.
1016          * For example, in order to highlight a detected face, the app needs to transform the
1017          * bounding box from the {@link ImageAnalysis}'s coordinate system to the View's coordinate
1018          * system. This method allows the implementer to set a target coordinate system.
1019          *
1020          * <p>The value will be used by CameraX to calculate the transformation {@link Matrix} and
1021          * forward it to the {@link Analyzer} via {@link #updateTransform}. By default, this
1022          * method returns {@link ImageAnalysis#COORDINATE_SYSTEM_ORIGINAL}.
1023          *
1024          * <p>For now, camera-core only supports {@link ImageAnalysis#COORDINATE_SYSTEM_ORIGINAL},
1025          * please see libraries derived from camera-core, for example, camera-view.
1026          *
1027          * @see #updateTransform(Matrix)
1028          */
getTargetCoordinateSystem()1029         default int getTargetCoordinateSystem() {
1030             return COORDINATE_SYSTEM_ORIGINAL;
1031         }
1032 
1033         /**
1034          * Implement this method to receive the {@link Matrix} for coordinate transformation.
1035          *
1036          * <p>The value represents the transformation from the camera sensor to the target
1037          * coordinate system defined in {@link #getTargetCoordinateSystem()}. It should be used
1038          * by the implementation to transform the coordinates detected in the camera frame. For
1039          * example, the coordinates of the detected face.
1040          *
1041          * <p>If the value is {@code null}, it means that no valid transformation is available.
1042          * It could have different causes depending on the value of
1043          * {@link #getTargetCoordinateSystem()}:
1044          * <ul>
1045          *     <li> If the target coordinate system is {@link #COORDINATE_SYSTEM_ORIGINAL}, it is
1046          *     always invalid because in that case, the coordinate system depends on how the
1047          *     analysis algorithm processes the {@link ImageProxy}.
1048          *     <li> It is also invalid if the target coordinate system is not available, for example
1049          *     if the analyzer targets the viewfinder and the view finder is not visible in UI.
1050          * </ul>
1051          *
1052          * <p>This method is invoked whenever a new transformation is ready. For example, when
1053          * the view finder is first a launched as well as when it's resized.
1054          *
1055          * @see #getTargetCoordinateSystem()
1056          */
updateTransform(@ullable Matrix matrix)1057         default void updateTransform(@Nullable Matrix matrix) {
1058             // no-op
1059         }
1060     }
1061 
1062     /**
1063      * {@link ImageAnalysis.Analyzer} option for returning the original coordinates.
1064      *
1065      * <p>Use this option if no additional transformation is needed by the {@link Analyzer}
1066      * implementation. The coordinates returned by the {@link Analyzer} should be within (0, 0) -
1067      * (width, height) where width and height are the dimensions of the {@link ImageProxy}.
1068      *
1069      * <p>By using this option, CameraX will pass {@code null} to
1070      * {@link Analyzer#updateTransform(Matrix)}.
1071      */
1072     public static final int COORDINATE_SYSTEM_ORIGINAL = 0;
1073 
1074     /**
1075      * {@link ImageAnalysis.Analyzer} option for returning UI coordinates.
1076      *
1077      * <p>When the {@link ImageAnalysis.Analyzer} is configured with this option, it will receive a
1078      * {@link Matrix} that will receive a value that represents the transformation from camera
1079      * sensor to the {@link View}, which can be used for highlighting detected result in UI. For
1080      * example, laying over a bounding box on top of the detected face.
1081      *
1082      * <p>Note this option will only work with an artifact that displays the camera feed in UI.
1083      * Generally, this is used by higher-level libraries such as the CameraController API that
1084      * incorporates a viewfinder UI. It will not be effective when used with camera-core directly.
1085      *
1086      * @see ImageAnalysis.Analyzer
1087      */
1088     public static final int COORDINATE_SYSTEM_VIEW_REFERENCED = 1;
1089 
1090     /**
1091      * {@link ImageAnalysis.Analyzer} option for returning the sensor coordinates.
1092      *
1093      * <p>Use this option if the app wishes to get the detected objects in camera sensor
1094      * coordinates. The coordinates returned by the {@link Analyzer} should be within (left,
1095      * right) - (width, height), where the left, right, width and height are bounds of the camera
1096      * sensor's active array.
1097      *
1098      * <p>By using this option, CameraX will pass
1099      * {@link ImageInfo#getSensorToBufferTransformMatrix()}'s inverse to
1100      * {@link Analyzer#updateTransform}.
1101      */
1102     public static final int COORDINATE_SYSTEM_SENSOR = 2;
1103 
1104     /**
1105      * Provides a base static default configuration for the ImageAnalysis.
1106      *
1107      * <p>These values may be overridden by the implementation. They only provide a minimum set of
1108      * defaults that are implementation independent.
1109      */
1110     @RestrictTo(Scope.LIBRARY_GROUP)
1111     public static final class Defaults implements ConfigProvider<ImageAnalysisConfig> {
1112         private static final Size DEFAULT_TARGET_RESOLUTION = new Size(640, 480);
1113         private static final int DEFAULT_SURFACE_OCCUPANCY_PRIORITY = 1;
1114         private static final int DEFAULT_ASPECT_RATIO = AspectRatio.RATIO_4_3;
1115 
1116         /**
1117          * Explicitly setting the default dynamic range to SDR (rather than UNSPECIFIED) means
1118          * ImageAnalysis won't inherit dynamic ranges from other use cases.
1119          */
1120         // TODO(b/258099919): ImageAnalysis currently can't support HDR, so we don't expose the
1121         //  dynamic range setter and require SDR. We may want to get rid of this default once we
1122         //  can support tone-mapping from HDR -> SDR
1123         private static final DynamicRange DEFAULT_DYNAMIC_RANGE = DynamicRange.SDR;
1124 
1125         private static final ResolutionSelector DEFAULT_RESOLUTION_SELECTOR =
1126                 new ResolutionSelector.Builder().setAspectRatioStrategy(
1127                                 AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY)
1128                         .setResolutionStrategy(new ResolutionStrategy(SizeUtil.RESOLUTION_VGA,
1129                                 ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER))
1130                         .build();
1131 
1132         private static final ImageAnalysisConfig DEFAULT_CONFIG;
1133 
1134         static {
1135             Builder builder = new Builder()
1136                     .setDefaultResolution(DEFAULT_TARGET_RESOLUTION)
1137                     .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY)
1138                     .setTargetAspectRatio(DEFAULT_ASPECT_RATIO)
1139                     .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR)
1140                     .setDynamicRange(DEFAULT_DYNAMIC_RANGE);
1141 
1142             DEFAULT_CONFIG = builder.getUseCaseConfig();
1143         }
1144 
1145         @Override
getConfig()1146         public @NonNull ImageAnalysisConfig getConfig() {
1147             return DEFAULT_CONFIG;
1148         }
1149     }
1150 
1151     /** Builder for a {@link ImageAnalysis}. */
1152     @SuppressWarnings({"ObjectToString", "HiddenSuperclass"})
1153     public static final class Builder
1154             implements ImageOutputConfig.Builder<Builder>,
1155             ThreadConfig.Builder<Builder>,
1156             UseCaseConfig.Builder<ImageAnalysis, ImageAnalysisConfig, Builder>,
1157             ImageInputConfig.Builder<Builder> {
1158 
1159         private final MutableOptionsBundle mMutableConfig;
1160 
1161         /** Creates a new Builder object. */
Builder()1162         public Builder() {
1163             this(MutableOptionsBundle.create());
1164         }
1165 
Builder(MutableOptionsBundle mutableConfig)1166         private Builder(MutableOptionsBundle mutableConfig) {
1167             mMutableConfig = mutableConfig;
1168 
1169             Class<?> oldConfigClass =
1170                     mutableConfig.retrieveOption(TargetConfig.OPTION_TARGET_CLASS, null);
1171             if (oldConfigClass != null && !oldConfigClass.equals(ImageAnalysis.class)) {
1172                 throw new IllegalArgumentException(
1173                         "Invalid target class configuration for "
1174                                 + Builder.this
1175                                 + ": "
1176                                 + oldConfigClass);
1177             }
1178 
1179             setCaptureType(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS);
1180             setTargetClass(ImageAnalysis.class);
1181         }
1182 
1183         /**
1184          * Generates a Builder from another Config object.
1185          *
1186          * @param configuration An immutable configuration to pre-populate this builder.
1187          * @return The new Builder.
1188          */
1189         @RestrictTo(Scope.LIBRARY_GROUP)
fromConfig(@onNull Config configuration)1190         static @NonNull Builder fromConfig(@NonNull Config configuration) {
1191             return new Builder(MutableOptionsBundle.from(configuration));
1192         }
1193 
1194         /**
1195          * Generates a Builder from another Config object.
1196          *
1197          * @param configuration An immutable configuration to pre-populate this builder.
1198          * @return The new Builder.
1199          */
1200         @RestrictTo(Scope.LIBRARY_GROUP)
fromConfig(@onNull ImageAnalysisConfig configuration)1201         public static @NonNull Builder fromConfig(@NonNull ImageAnalysisConfig configuration) {
1202             return new Builder(MutableOptionsBundle.from(configuration));
1203         }
1204 
1205         /**
1206          * Sets the backpressure strategy to apply to the image producer to deal with scenarios
1207          * where images may be produced faster than they can be analyzed.
1208          *
1209          * <p>The available values are {@link #STRATEGY_BLOCK_PRODUCER} and
1210          * {@link #STRATEGY_KEEP_ONLY_LATEST}.
1211          *
1212          * <p>If not set, the backpressure strategy will default to
1213          * {@link #STRATEGY_KEEP_ONLY_LATEST}.
1214          *
1215          * @param strategy The strategy to use.
1216          * @return The current Builder.
1217          */
setBackpressureStrategy(@ackpressureStrategy int strategy)1218         public @NonNull Builder setBackpressureStrategy(@BackpressureStrategy int strategy) {
1219             getMutableConfig().insertOption(OPTION_BACKPRESSURE_STRATEGY, strategy);
1220             return this;
1221         }
1222 
1223         /**
1224          * Sets the number of images available to the camera pipeline for
1225          * {@link #STRATEGY_BLOCK_PRODUCER} mode.
1226          *
1227          * <p>The image queue depth is the number of images available to the camera to fill with
1228          * data. This includes the image currently being analyzed by {@link
1229          * ImageAnalysis.Analyzer#analyze(ImageProxy)}. Increasing the image queue depth
1230          * may make camera operation smoother, depending on the backpressure strategy, at
1231          * the cost of increased memory usage.
1232          *
1233          * <p>When the backpressure strategy is set to {@link #STRATEGY_BLOCK_PRODUCER},
1234          * increasing the image queue depth may make the camera pipeline run smoother on systems
1235          * under high load. However, the time spent analyzing an image should still be kept under
1236          * a single frame period for the current frame rate, <i>on average</i>, to avoid stalling
1237          * the camera pipeline.
1238          *
1239          * <p>The value only applies to {@link #STRATEGY_BLOCK_PRODUCER} mode.
1240          * For {@link #STRATEGY_KEEP_ONLY_LATEST} the value is ignored.
1241          *
1242          * <p>If not set, and this option is used by the selected backpressure strategy,
1243          * the default will be a queue depth of 6 images.
1244          *
1245          * @param depth The total number of images available to the camera.
1246          * @return The current Builder.
1247          */
setImageQueueDepth(int depth)1248         public @NonNull Builder setImageQueueDepth(int depth) {
1249             getMutableConfig().insertOption(OPTION_IMAGE_QUEUE_DEPTH, depth);
1250             return this;
1251         }
1252 
1253         /**
1254          * Sets output image format.
1255          *
1256          * <p>The supported output image format
1257          * is {@link OutputImageFormat#OUTPUT_IMAGE_FORMAT_YUV_420_888},
1258          * {@link OutputImageFormat#OUTPUT_IMAGE_FORMAT_RGBA_8888} and
1259          * {@link OutputImageFormat#OUTPUT_IMAGE_FORMAT_NV21}.
1260          *
1261          * <p>If not set, {@link OutputImageFormat#OUTPUT_IMAGE_FORMAT_YUV_420_888} will be used.
1262          *
1263          * Requesting {@link OutputImageFormat#OUTPUT_IMAGE_FORMAT_RGBA_8888} or
1264          * {@link OutputImageFormat#OUTPUT_IMAGE_FORMAT_NV21} will have extra overhead because
1265          * format conversion takes time.
1266          *
1267          * @param outputImageFormat The output image format.
1268          * @return The current Builder.
1269          */
setOutputImageFormat(@utputImageFormat int outputImageFormat)1270         public @NonNull Builder setOutputImageFormat(@OutputImageFormat int outputImageFormat) {
1271             getMutableConfig().insertOption(OPTION_OUTPUT_IMAGE_FORMAT, outputImageFormat);
1272             return this;
1273         }
1274 
1275         /**
1276          * Enable or disable output image rotation.
1277          *
1278          * <p>On API 22 and below, this API has no effect. User needs to handle the image rotation
1279          * based on the {@link ImageInfo#getRotationDegrees()}.
1280          *
1281          * <p>{@link ImageAnalysis#setTargetRotation(int)} is to adjust the rotation
1282          * degree information returned by {@link ImageInfo#getRotationDegrees()} based on
1283          * sensor rotation and user still needs to rotate the output image to achieve the target
1284          * rotation. Once this is enabled, user doesn't need to handle the rotation, the output
1285          * image will be a rotated {@link ImageProxy} and {@link ImageInfo#getRotationDegrees()}
1286          * will return 0.
1287          *
1288          * <p>Turning this on will add more processing overhead to every image analysis
1289          * frame. The average processing time is about 10-15ms for 640x480 image on a mid-range
1290          * device.
1291          *
1292          * By default, the rotation is disabled.
1293          *
1294          * @param outputImageRotationEnabled flag to enable or disable.
1295          * @return The current Builder.
1296          * @see
1297          * <a href="https://developer.android.com/training/camerax/orientation-rotation#imageanalysis">ImageAnalysis</a>
1298          */
1299         @RequiresApi(23)
setOutputImageRotationEnabled(boolean outputImageRotationEnabled)1300         public @NonNull Builder setOutputImageRotationEnabled(boolean outputImageRotationEnabled) {
1301             getMutableConfig().insertOption(OPTION_OUTPUT_IMAGE_ROTATION_ENABLED,
1302                     outputImageRotationEnabled);
1303             return this;
1304         }
1305 
1306         @RestrictTo(Scope.LIBRARY_GROUP)
setOnePixelShiftEnabled(boolean onePixelShiftEnabled)1307         public @NonNull Builder setOnePixelShiftEnabled(boolean onePixelShiftEnabled) {
1308             getMutableConfig().insertOption(OPTION_ONE_PIXEL_SHIFT_ENABLED,
1309                     Boolean.valueOf(onePixelShiftEnabled));
1310             return this;
1311         }
1312 
1313         /**
1314          * {@inheritDoc}
1315          */
1316         @RestrictTo(Scope.LIBRARY_GROUP)
1317         @Override
getMutableConfig()1318         public @NonNull MutableConfig getMutableConfig() {
1319             return mMutableConfig;
1320         }
1321 
1322         /**
1323          * {@inheritDoc}
1324          */
1325         @RestrictTo(Scope.LIBRARY_GROUP)
1326         @Override
getUseCaseConfig()1327         public @NonNull ImageAnalysisConfig getUseCaseConfig() {
1328             return new ImageAnalysisConfig(OptionsBundle.from(mMutableConfig));
1329         }
1330 
1331         /**
1332          * Builds an {@link ImageAnalysis} from the current state.
1333          *
1334          * @return A {@link ImageAnalysis} populated with the current state.
1335          * @throws IllegalArgumentException if attempting to set both target aspect ratio and
1336          *                                  target resolution.
1337          */
1338         @Override
build()1339         public @NonNull ImageAnalysis build() {
1340             ImageAnalysisConfig imageAnalysisConfig = getUseCaseConfig();
1341             ImageOutputConfig.validateConfig(imageAnalysisConfig);
1342             return new ImageAnalysis(imageAnalysisConfig);
1343         }
1344 
1345         // Implementations of TargetConfig.Builder default methods
1346 
1347         @RestrictTo(Scope.LIBRARY_GROUP)
1348         @Override
setTargetClass(@onNull Class<ImageAnalysis> targetClass)1349         public @NonNull Builder setTargetClass(@NonNull Class<ImageAnalysis> targetClass) {
1350             getMutableConfig().insertOption(OPTION_TARGET_CLASS, targetClass);
1351 
1352             // If no name is set yet, then generate a unique name
1353             if (null == getMutableConfig().retrieveOption(OPTION_TARGET_NAME, null)) {
1354                 String targetName = targetClass.getCanonicalName() + "-" + UUID.randomUUID();
1355                 setTargetName(targetName);
1356             }
1357 
1358             return this;
1359         }
1360 
1361         /**
1362          * Sets the name of the target object being configured, used only for debug logging.
1363          *
1364          * <p>The name should be a value that can uniquely identify an instance of the object being
1365          * configured.
1366          *
1367          * <p>If not set, the target name will default to a unique name automatically generated
1368          * with the class canonical name and random UUID.
1369          *
1370          * @param targetName A unique string identifier for the instance of the class being
1371          *                   configured.
1372          * @return the current Builder.
1373          */
1374         @Override
setTargetName(@onNull String targetName)1375         public @NonNull Builder setTargetName(@NonNull String targetName) {
1376             getMutableConfig().insertOption(OPTION_TARGET_NAME, targetName);
1377             return this;
1378         }
1379 
1380         /**
1381          * Sets the aspect ratio of the intended target for images from this configuration.
1382          *
1383          * <p>The aspect ratio is the ratio of width to height in the sensor orientation.
1384          *
1385          * <p>It is not allowed to set both target aspect ratio and target resolution on the same
1386          * use case. Attempting so will throw an IllegalArgumentException when building the Config.
1387          *
1388          * <p>The target aspect ratio is used as a hint when determining the resulting output aspect
1389          * ratio which may differ from the request, possibly due to device constraints.
1390          * Application code should check the resulting output's resolution and the resulting
1391          * aspect ratio may not be exactly as requested.
1392          *
1393          * <p>If not set, or {@link AspectRatio#RATIO_DEFAULT} is supplied, resolutions with
1394          * aspect ratio 4:3 will be considered in higher priority.
1395          *
1396          * @param aspectRatio The desired ImageAnalysis {@link AspectRatio}
1397          * @return The current Builder.
1398          * @deprecated use {@link ResolutionSelector} with {@link AspectRatioStrategy} to specify
1399          * the preferred aspect ratio settings instead.
1400          */
1401         @Override
1402         @Deprecated
setTargetAspectRatio(@spectRatio.Ratio int aspectRatio)1403         public @NonNull Builder setTargetAspectRatio(@AspectRatio.Ratio int aspectRatio) {
1404             if (aspectRatio == AspectRatio.RATIO_DEFAULT) {
1405                 aspectRatio = Defaults.DEFAULT_ASPECT_RATIO;
1406             }
1407             getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO, aspectRatio);
1408             return this;
1409         }
1410 
1411         /**
1412          * Sets the rotation of the intended target for images from this configuration.
1413          *
1414          * <p>This adjust the {@link ImageInfo#getRotationDegrees()} of the {@link ImageProxy}
1415          * passed to {@link Analyzer#analyze(ImageProxy)}. The rotation value of ImageInfo will
1416          * be the rotation, which if applied to the output image, will make the image match
1417          * target rotation specified here.
1418          *
1419          * <p>This is one of four valid values: {@link Surface#ROTATION_0}, {@link
1420          * Surface#ROTATION_90}, {@link Surface#ROTATION_180}, {@link Surface#ROTATION_270}.
1421          * Rotation values are relative to the "natural" rotation, {@link Surface#ROTATION_0}.
1422          *
1423          * <p>In general, it is best to additionally set the target rotation dynamically on the use
1424          * case. See {@link androidx.camera.core.ImageAnalysis#setTargetRotation(int)} for
1425          * additional documentation.
1426          *
1427          * <p>If not set, the target rotation will default to the value of
1428          * {@link android.view.Display#getRotation()} of the default display at the time the
1429          * use case is created. The use case is fully created once it has been attached to a camera.
1430          *
1431          * @param rotation The rotation of the intended target.
1432          * @return The current Builder.
1433          * @see androidx.camera.core.ImageAnalysis#setTargetRotation(int)
1434          * @see android.view.OrientationEventListener
1435          */
1436         @Override
setTargetRotation(@otationValue int rotation)1437         public @NonNull Builder setTargetRotation(@RotationValue int rotation) {
1438             getMutableConfig().insertOption(OPTION_TARGET_ROTATION, rotation);
1439             return this;
1440         }
1441 
1442         /**
1443          * setMirrorMode is not supported on ImageAnalysis.
1444          */
1445         @RestrictTo(Scope.LIBRARY_GROUP)
1446         @Override
setMirrorMode(@irrorMode.Mirror int mirrorMode)1447         public @NonNull Builder setMirrorMode(@MirrorMode.Mirror int mirrorMode) {
1448             throw new UnsupportedOperationException("setMirrorMode is not supported.");
1449         }
1450 
1451         /**
1452          * Sets the resolution of the intended target from this configuration.
1453          *
1454          * <p>The target resolution attempts to establish a minimum bound for the image resolution.
1455          * The actual image resolution will be the closest available resolution in size that is not
1456          * smaller than the target resolution, as determined by the Camera implementation. However,
1457          * if no resolution exists that is equal to or larger than the target resolution, the
1458          * nearest available resolution smaller than the target resolution will be chosen.
1459          * Resolutions with the same aspect ratio of the provided {@link Size} will be considered in
1460          * higher priority before resolutions of different aspect ratios.
1461          *
1462          * <p>It is not allowed to set both target aspect ratio and target resolution on the same
1463          * use case. Attempting so will throw an IllegalArgumentException when building the Config.
1464          *
1465          * <p>The resolution {@link Size} should be expressed in the coordinate frame after
1466          * rotating the supported sizes by the target rotation. For example, a device with
1467          * portrait natural orientation in natural target rotation requesting a portrait image
1468          * may specify 480x640, and the same device, rotated 90 degrees and targeting landscape
1469          * orientation may specify 640x480.
1470          *
1471          * <p>If not set, resolution of 640x480 will be selected to use in priority.
1472          *
1473          * <p>When using the <code>camera-camera2</code> CameraX implementation, which resolution
1474          * will be finally selected will depend on the camera device's hardware level and the
1475          * bound use cases combination. The device hardware level information can be retrieved by
1476          * {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
1477          * from the interop class
1478          * {@link androidx.camera.camera2.interop.Camera2CameraInfo#getCameraCharacteristic(CameraCharacteristics.Key)}.
1479          * A <code>LIMITED-level</code> above device can support a <code>RECORD</code> size
1480          * resolution for {@link ImageAnalysis} when it is bound together with {@link Preview}
1481          * and {@link ImageCapture}. The trade-off is the selected resolution for the
1482          * {@link ImageCapture} will also be restricted by the <code>RECORD</code> size. To
1483          * successfully select a <code>RECORD</code> size resolution for {@link ImageAnalysis}, a
1484          * <code>RECORD</code> size target resolution should be set on both {@link ImageCapture}
1485          * and {@link ImageAnalysis}. This indicates that the application clearly understand the
1486          * trade-off and prefer the {@link ImageAnalysis} to have a larger resolution rather than
1487          * the {@link ImageCapture} to have a <code>MAXIMUM</code> size resolution. For the
1488          * definitions of <code>RECORD</code>, <code>MAXIMUM</code> sizes and more details see the
1489          * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#regular-capture">Regular capture</a>
1490          * section in {@link android.hardware.camera2.CameraDevice}'s. The <code>RECORD</code>
1491          * size refers to the camera device's maximum supported recording resolution, as
1492          * determined by {@link CamcorderProfile}. The <code>MAXIMUM</code> size refers to the
1493          * camera device's maximum output resolution for that format or target from
1494          * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes}.
1495          *
1496          * @param resolution The target resolution to choose from supported output sizes list.
1497          * @return The current Builder.
1498          * @deprecated use {@link ResolutionSelector} with {@link ResolutionStrategy} to specify
1499          * the preferred resolution settings instead.
1500          */
1501         @Override
1502         @Deprecated
setTargetResolution(@onNull Size resolution)1503         public @NonNull Builder setTargetResolution(@NonNull Size resolution) {
1504             getMutableConfig()
1505                     .insertOption(ImageOutputConfig.OPTION_TARGET_RESOLUTION, resolution);
1506             return this;
1507         }
1508 
1509         /**
1510          * Sets the default resolution of the intended target from this configuration.
1511          *
1512          * @param resolution The default resolution to choose from supported output sizes list.
1513          * @return The current Builder.
1514          */
1515         @RestrictTo(Scope.LIBRARY_GROUP)
1516         @Override
setDefaultResolution(@onNull Size resolution)1517         public @NonNull Builder setDefaultResolution(@NonNull Size resolution) {
1518             getMutableConfig().insertOption(OPTION_DEFAULT_RESOLUTION,
1519                     resolution);
1520             return this;
1521         }
1522 
1523         @RestrictTo(Scope.LIBRARY_GROUP)
1524         @Override
setMaxResolution(@onNull Size resolution)1525         public @NonNull Builder setMaxResolution(@NonNull Size resolution) {
1526             getMutableConfig().insertOption(OPTION_MAX_RESOLUTION, resolution);
1527             return this;
1528         }
1529 
1530         @RestrictTo(Scope.LIBRARY_GROUP)
1531         @Override
setSupportedResolutions( @onNull List<Pair<Integer, Size[]>> resolutions)1532         public @NonNull Builder setSupportedResolutions(
1533                 @NonNull List<Pair<Integer, Size[]>> resolutions) {
1534             getMutableConfig().insertOption(OPTION_SUPPORTED_RESOLUTIONS, resolutions);
1535             return this;
1536         }
1537 
1538         @RestrictTo(Scope.LIBRARY_GROUP)
1539         @Override
setCustomOrderedResolutions(@onNull List<Size> resolutions)1540         public @NonNull Builder setCustomOrderedResolutions(@NonNull List<Size> resolutions) {
1541             getMutableConfig().insertOption(OPTION_CUSTOM_ORDERED_RESOLUTIONS, resolutions);
1542             return this;
1543         }
1544 
1545         /**
1546          * Sets the resolution selector to select the preferred supported resolution.
1547          *
1548          * <p>ImageAnalysis has a default {@link ResolutionStrategy} with bound size as 640x480
1549          * and fallback rule of {@link ResolutionStrategy#FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER}.
1550          * Applications can override this default strategy with a different resolution strategy.
1551          *
1552          * <p>When using the {@code camera-camera2} CameraX implementation, which resolution is
1553          * finally selected depends on the camera device's hardware level, capabilities and the
1554          * bound use cases combination. The device hardware level and capabilities information
1555          * can be retrieved via the interop class
1556          * {@link androidx.camera.camera2.interop.Camera2CameraInfo#getCameraCharacteristic(android.hardware.camera2.CameraCharacteristics.Key)}
1557          * with
1558          * {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} and
1559          * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}.
1560          *
1561          * <p>A {@code LIMITED-level} above device can support a {@code RECORD} size resolution
1562          * for {@link ImageAnalysis} when it is bound together with {@link Preview} and
1563          * {@link ImageCapture}. The trade-off is the selected resolution for the
1564          * {@link ImageCapture} is also restricted by the {@code RECORD} size. To successfully
1565          * select a {@code RECORD} size resolution for {@link ImageAnalysis}, a
1566          * {@link ResolutionStrategy} of selecting {@code RECORD} size resolution should be set
1567          * on both {@link ImageCapture} and {@link ImageAnalysis}. This indicates that the
1568          * application clearly understand the trade-off and prefer the {@link ImageAnalysis} to
1569          * have a larger resolution rather than the {@link ImageCapture} to have a {@code MAXIMUM
1570          * } size resolution. For the definitions of {@code RECORD}, {@code MAXIMUM} sizes and
1571          * more details see the
1572          * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#regular-capture">Regular capture</a>
1573          * section in {@link android.hardware.camera2.CameraDevice}'s. The {@code RECORD} size
1574          * refers to the camera device's maximum supported recording resolution, as determined by
1575          * {@link CamcorderProfile}. The {@code MAXIMUM} size refers to the camera device's
1576          * maximum output resolution for that format or target from
1577          * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes}.
1578          *
1579          * <p>The existing {@link #setTargetResolution(Size)} and
1580          * {@link #setTargetAspectRatio(int)} APIs are deprecated and are not compatible with
1581          * {@link #setResolutionSelector(ResolutionSelector)}. Calling either of these APIs
1582          * together with {@link #setResolutionSelector(ResolutionSelector)} will result in an
1583          * {@link IllegalArgumentException} being thrown when you attempt to build the
1584          * {@link ImageAnalysis} instance.
1585          *
1586          * @return The current Builder.
1587          */
1588         @Override
setResolutionSelector( @onNull ResolutionSelector resolutionSelector)1589         public @NonNull Builder setResolutionSelector(
1590                 @NonNull ResolutionSelector resolutionSelector) {
1591             getMutableConfig().insertOption(OPTION_RESOLUTION_SELECTOR, resolutionSelector);
1592             return this;
1593         }
1594 
1595         // Implementations of ThreadConfig.Builder default methods
1596 
1597         /**
1598          * Sets the default executor that will be used for background tasks.
1599          *
1600          * <p>If not set, the background executor will default to an automatically generated
1601          * {@link Executor}.
1602          *
1603          * @param executor The executor which will be used for background tasks.
1604          * @return the current Builder.
1605          */
1606         @Override
setBackgroundExecutor(@onNull Executor executor)1607         public @NonNull Builder setBackgroundExecutor(@NonNull Executor executor) {
1608             getMutableConfig().insertOption(OPTION_BACKGROUND_EXECUTOR, executor);
1609             return this;
1610         }
1611 
1612         // Implementations of UseCaseConfig.Builder default methods
1613 
1614         @RestrictTo(Scope.LIBRARY_GROUP)
1615         @Override
setDefaultSessionConfig(@onNull SessionConfig sessionConfig)1616         public @NonNull Builder setDefaultSessionConfig(@NonNull SessionConfig sessionConfig) {
1617             getMutableConfig().insertOption(OPTION_DEFAULT_SESSION_CONFIG, sessionConfig);
1618             return this;
1619         }
1620 
1621         @RestrictTo(Scope.LIBRARY_GROUP)
1622         @Override
setDefaultCaptureConfig(@onNull CaptureConfig captureConfig)1623         public @NonNull Builder setDefaultCaptureConfig(@NonNull CaptureConfig captureConfig) {
1624             getMutableConfig().insertOption(OPTION_DEFAULT_CAPTURE_CONFIG, captureConfig);
1625             return this;
1626         }
1627 
1628         @RestrictTo(Scope.LIBRARY_GROUP)
1629         @Override
setSessionOptionUnpacker( SessionConfig.@onNull OptionUnpacker optionUnpacker)1630         public @NonNull Builder setSessionOptionUnpacker(
1631                 SessionConfig.@NonNull OptionUnpacker optionUnpacker) {
1632             getMutableConfig().insertOption(OPTION_SESSION_CONFIG_UNPACKER, optionUnpacker);
1633             return this;
1634         }
1635 
1636         @RestrictTo(Scope.LIBRARY_GROUP)
1637         @Override
setCaptureOptionUnpacker( CaptureConfig.@onNull OptionUnpacker optionUnpacker)1638         public @NonNull Builder setCaptureOptionUnpacker(
1639                 CaptureConfig.@NonNull OptionUnpacker optionUnpacker) {
1640             getMutableConfig().insertOption(OPTION_CAPTURE_CONFIG_UNPACKER, optionUnpacker);
1641             return this;
1642         }
1643 
1644         @RestrictTo(Scope.LIBRARY_GROUP)
1645         @Override
setSurfaceOccupancyPriority(int priority)1646         public @NonNull Builder setSurfaceOccupancyPriority(int priority) {
1647             getMutableConfig().insertOption(OPTION_SURFACE_OCCUPANCY_PRIORITY, priority);
1648             return this;
1649         }
1650 
1651         @RestrictTo(Scope.LIBRARY_GROUP)
setImageReaderProxyProvider( @onNull ImageReaderProxyProvider imageReaderProxyProvider)1652         public @NonNull Builder setImageReaderProxyProvider(
1653                 @NonNull ImageReaderProxyProvider imageReaderProxyProvider) {
1654             getMutableConfig().insertOption(OPTION_IMAGE_READER_PROXY_PROVIDER,
1655                     imageReaderProxyProvider);
1656             return this;
1657         }
1658 
1659         @RestrictTo(Scope.LIBRARY_GROUP)
1660         @Override
setZslDisabled(boolean disabled)1661         public @NonNull Builder setZslDisabled(boolean disabled) {
1662             getMutableConfig().insertOption(OPTION_ZSL_DISABLED, disabled);
1663             return this;
1664         }
1665 
1666         @RestrictTo(Scope.LIBRARY_GROUP)
1667         @Override
setHighResolutionDisabled(boolean disabled)1668         public @NonNull Builder setHighResolutionDisabled(boolean disabled) {
1669             getMutableConfig().insertOption(OPTION_HIGH_RESOLUTION_DISABLED, disabled);
1670             return this;
1671         }
1672 
1673         @RestrictTo(Scope.LIBRARY_GROUP)
1674         @Override
setCaptureType( UseCaseConfigFactory.@onNull CaptureType captureType)1675         public @NonNull Builder setCaptureType(
1676                 UseCaseConfigFactory.@NonNull CaptureType captureType) {
1677             getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType);
1678             return this;
1679         }
1680 
1681         // Implementations of ImageInputConfig.Builder default methods
1682 
1683         /**
1684          * Sets the {@link DynamicRange}.
1685          *
1686          * <p>This is currently only exposed to internally set the dynamic range to SDR.
1687          *
1688          * @return The current Builder.
1689          * @see DynamicRange
1690          */
1691         @RestrictTo(Scope.LIBRARY)
1692         @Override
setDynamicRange(@onNull DynamicRange dynamicRange)1693         public @NonNull Builder setDynamicRange(@NonNull DynamicRange dynamicRange) {
1694             // TODO(b/258099919): ImageAnalysis currently can't support HDR, so we require SDR.
1695             //  It's possible to support other DynamicRanges through tone-mapping or by exposing
1696             //  other ImageReader formats, such as YCBCR_P010.
1697             if (!Objects.equals(DynamicRange.SDR, dynamicRange)) {
1698                 throw new UnsupportedOperationException(
1699                         "ImageAnalysis currently only supports SDR");
1700             }
1701             getMutableConfig().insertOption(OPTION_INPUT_DYNAMIC_RANGE, dynamicRange);
1702             return this;
1703         }
1704     }
1705 }
1706