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 android.graphics.ImageFormat.JPEG; 20 import static android.graphics.ImageFormat.JPEG_R; 21 import static android.graphics.ImageFormat.RAW_SENSOR; 22 23 import static androidx.camera.core.CameraEffect.IMAGE_CAPTURE; 24 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_BUFFER_FORMAT; 25 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_CAPTURE_CONFIG_UNPACKER; 26 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_DEFAULT_CAPTURE_CONFIG; 27 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_DEFAULT_SESSION_CONFIG; 28 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_FLASH_MODE; 29 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_FLASH_TYPE; 30 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_IMAGE_CAPTURE_MODE; 31 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_IMAGE_READER_PROXY_PROVIDER; 32 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_IO_EXECUTOR; 33 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_JPEG_COMPRESSION_QUALITY; 34 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_MAX_RESOLUTION; 35 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_OUTPUT_FORMAT; 36 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_POSTVIEW_ENABLED; 37 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_POSTVIEW_RESOLUTION_SELECTOR; 38 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_SCREEN_FLASH; 39 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_SESSION_CONFIG_UNPACKER; 40 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_SUPPORTED_RESOLUTIONS; 41 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_SURFACE_OCCUPANCY_PRIORITY; 42 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_TARGET_ASPECT_RATIO; 43 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_TARGET_CLASS; 44 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_TARGET_NAME; 45 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_TARGET_RESOLUTION; 46 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_TARGET_ROTATION; 47 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_USE_SOFTWARE_JPEG_ENCODER; 48 import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_DYNAMIC_RANGE; 49 import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_FORMAT; 50 import static androidx.camera.core.impl.ImageInputConfig.OPTION_SECONDARY_INPUT_FORMAT; 51 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_CUSTOM_ORDERED_RESOLUTIONS; 52 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR; 53 import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE; 54 import static androidx.camera.core.impl.UseCaseConfig.OPTION_HIGH_RESOLUTION_DISABLED; 55 import static androidx.camera.core.impl.UseCaseConfig.OPTION_ZSL_DISABLED; 56 import static androidx.camera.core.impl.utils.Threads.checkMainThread; 57 import static androidx.camera.core.impl.utils.TransformUtils.is90or270; 58 import static androidx.camera.core.internal.utils.ImageUtil.computeCropRectFromAspectRatio; 59 import static androidx.camera.core.internal.utils.ImageUtil.isAspectRatioValid; 60 import static androidx.core.util.Preconditions.checkNotNull; 61 import static androidx.core.util.Preconditions.checkState; 62 63 import static java.util.Objects.requireNonNull; 64 65 import android.content.ContentResolver; 66 import android.content.ContentValues; 67 import android.graphics.Bitmap; 68 import android.graphics.ImageFormat; 69 import android.graphics.Rect; 70 import android.hardware.camera2.CameraCharacteristics; 71 import android.location.Location; 72 import android.media.Image; 73 import android.media.ImageReader; 74 import android.net.Uri; 75 import android.os.Build; 76 import android.os.Looper; 77 import android.provider.MediaStore; 78 import android.util.Log; 79 import android.util.Pair; 80 import android.util.Rational; 81 import android.util.Size; 82 import android.view.Display; 83 import android.view.Surface; 84 85 import androidx.annotation.GuardedBy; 86 import androidx.annotation.IntDef; 87 import androidx.annotation.IntRange; 88 import androidx.annotation.MainThread; 89 import androidx.annotation.OptIn; 90 import androidx.annotation.RestrictTo; 91 import androidx.annotation.RestrictTo.Scope; 92 import androidx.annotation.UiThread; 93 import androidx.annotation.VisibleForTesting; 94 import androidx.camera.core.imagecapture.ImageCaptureControl; 95 import androidx.camera.core.imagecapture.ImagePipeline; 96 import androidx.camera.core.imagecapture.PostviewSettings; 97 import androidx.camera.core.imagecapture.TakePictureManager; 98 import androidx.camera.core.imagecapture.TakePictureRequest; 99 import androidx.camera.core.impl.AdapterCameraInfo; 100 import androidx.camera.core.impl.CameraConfig; 101 import androidx.camera.core.impl.CameraInfoInternal; 102 import androidx.camera.core.impl.CameraInternal; 103 import androidx.camera.core.impl.CaptureConfig; 104 import androidx.camera.core.impl.Config; 105 import androidx.camera.core.impl.ConfigProvider; 106 import androidx.camera.core.impl.ImageCaptureConfig; 107 import androidx.camera.core.impl.ImageInputConfig; 108 import androidx.camera.core.impl.ImageOutputConfig; 109 import androidx.camera.core.impl.ImageOutputConfig.RotationValue; 110 import androidx.camera.core.impl.ImageReaderProxy; 111 import androidx.camera.core.impl.MutableConfig; 112 import androidx.camera.core.impl.MutableOptionsBundle; 113 import androidx.camera.core.impl.OptionsBundle; 114 import androidx.camera.core.impl.SessionConfig; 115 import androidx.camera.core.impl.SessionProcessor; 116 import androidx.camera.core.impl.StreamSpec; 117 import androidx.camera.core.impl.UseCaseConfig; 118 import androidx.camera.core.impl.UseCaseConfigFactory; 119 import androidx.camera.core.impl.utils.CameraOrientationUtil; 120 import androidx.camera.core.impl.utils.CompareSizesByArea; 121 import androidx.camera.core.impl.utils.executor.CameraXExecutors; 122 import androidx.camera.core.impl.utils.futures.Futures; 123 import androidx.camera.core.internal.IoConfig; 124 import androidx.camera.core.internal.ScreenFlashWrapper; 125 import androidx.camera.core.internal.SupportedOutputSizesSorter; 126 import androidx.camera.core.internal.TargetConfig; 127 import androidx.camera.core.internal.compat.quirk.SoftwareJpegEncodingPreferredQuirk; 128 import androidx.camera.core.internal.compat.workaround.ExifRotationAvailability; 129 import androidx.camera.core.internal.utils.ImageUtil; 130 import androidx.camera.core.resolutionselector.AspectRatioStrategy; 131 import androidx.camera.core.resolutionselector.ResolutionSelector; 132 import androidx.camera.core.resolutionselector.ResolutionStrategy; 133 import androidx.core.util.Preconditions; 134 import androidx.lifecycle.LifecycleOwner; 135 136 import com.google.common.util.concurrent.ListenableFuture; 137 138 import org.jspecify.annotations.NonNull; 139 import org.jspecify.annotations.Nullable; 140 141 import java.io.File; 142 import java.io.OutputStream; 143 import java.lang.annotation.ElementType; 144 import java.lang.annotation.Retention; 145 import java.lang.annotation.RetentionPolicy; 146 import java.lang.annotation.Target; 147 import java.util.Collections; 148 import java.util.HashSet; 149 import java.util.List; 150 import java.util.Map; 151 import java.util.Objects; 152 import java.util.Set; 153 import java.util.UUID; 154 import java.util.concurrent.Executor; 155 import java.util.concurrent.atomic.AtomicReference; 156 157 /** 158 * A use case for taking a picture. 159 * 160 * <p>This class is designed for basic picture taking. It provides takePicture() functions to take 161 * a picture to memory or save to a file, and provides image metadata. Pictures are taken in 162 * automatic mode after focus has converged. The flash mode can additionally be set by the 163 * application. 164 * 165 * <p>TakePicture returns immediately and a listener is called to provide the results after the 166 * capture completes. Multiple calls to takePicture will take pictures sequentially starting 167 * after the previous picture is captured. 168 * 169 * <p>Note that focus and exposure metering regions can be controlled via {@link Preview}. 170 * 171 * <p>When capturing to memory, the captured image is made available through an {@link ImageProxy} 172 * via an {@link ImageCapture.OnImageCapturedCallback}. 173 */ 174 @SuppressWarnings("unused") 175 public final class ImageCapture extends UseCase { 176 177 //////////////////////////////////////////////////////////////////////////////////////////// 178 // [UseCase lifetime constant] - Stays constant for the lifetime of the UseCase. Which means 179 // they could be created in the constructor. 180 //////////////////////////////////////////////////////////////////////////////////////////// 181 182 /** 183 * An unknown error occurred. 184 * 185 * <p>See message parameter in onError callback or log for more details. 186 */ 187 public static final int ERROR_UNKNOWN = 0; 188 /** 189 * An error occurred while attempting to read or write a file, such as when saving an image 190 * to a File. 191 */ 192 public static final int ERROR_FILE_IO = 1; 193 194 /** 195 * An error reported by camera framework indicating the capture request is failed. 196 */ 197 public static final int ERROR_CAPTURE_FAILED = 2; 198 199 /** 200 * An error indicating the request cannot be done due to camera is closed. 201 */ 202 public static final int ERROR_CAMERA_CLOSED = 3; 203 204 /** 205 * An error indicating this ImageCapture is not bound to a valid camera. 206 */ 207 public static final int ERROR_INVALID_CAMERA = 4; 208 209 /** 210 * Optimizes capture pipeline to prioritize image quality over latency. When the capture 211 * mode is set to MAX_QUALITY, images may take longer to capture. 212 */ 213 public static final int CAPTURE_MODE_MAXIMIZE_QUALITY = 0; 214 /** 215 * Optimizes capture pipeline to prioritize latency over image quality. When the capture 216 * mode is set to MIN_LATENCY, images may capture faster but the image quality may be 217 * reduced. 218 */ 219 public static final int CAPTURE_MODE_MINIMIZE_LATENCY = 1; 220 /** 221 * Optimizes capture pipeline to have better latency while keeping good image quality. When 222 * the capture mode is set to ZERO_SHUTTER_LAG, the latency between the shutter button is 223 * clicked and the picture is taken is expected to be minimized, compared with other capture 224 * modes. 225 * 226 * <p> ZERO_SHUTTER_LAG mode is aiming to provide the minimum latency for instant capture. It 227 * caches intermediate results and deliver the one with the closest timestamp when 228 * {@link ImageCapture#takePicture(OutputFileOptions, Executor, OnImageSavedCallback)} 229 * is invoked. 230 * 231 * <p> {@link CameraInfo#isZslSupported()} can be used to query the device capability to 232 * support this mode or not. However, this mode also depends on use cases configuration and 233 * flash mode settings. If VideoCapture is bound or flash mode is not OFF or 234 * OEM Extension is ON, this mode will be disabled automatically. 235 */ 236 @ExperimentalZeroShutterLag 237 public static final int CAPTURE_MODE_ZERO_SHUTTER_LAG = 2; 238 239 private static final int FLASH_MODE_UNKNOWN = -1; 240 /** 241 * Auto flash. The flash will be used according to the camera system's determination when taking 242 * a picture. 243 */ 244 public static final int FLASH_MODE_AUTO = 0; 245 /** Always flash. The flash will always be used when taking a picture. */ 246 public static final int FLASH_MODE_ON = 1; 247 /** No flash. The flash will never be used when taking a picture. */ 248 public static final int FLASH_MODE_OFF = 2; 249 /** 250 * Screen flash. Display screen brightness will be used as alternative to flash when taking 251 * a picture with front camera. 252 * 253 * <p> This flash mode can be set via {@link #setFlashMode(int)} after setting a non-null 254 * {@link ScreenFlash} instance with {@link #setScreenFlash(ScreenFlash)}. 255 * This mode will always invoke all the necessary operations for a screen flash image capture, 256 * i.e. it is similar to {@link #FLASH_MODE_ON}, not {@link #FLASH_MODE_AUTO}. 257 * 258 * <p> The following code snippet shows an example implementation of how this flash mode can be 259 * set to an {@link ImageCapture} instance. 260 * <pre>{@code 261 * imageCapture.setScreenFlash(new ImageCapture.ScreenFlash() { 262 * @Override 263 * public void apply(long expirationTimeMillis, 264 * @NonNull ScreenFlashListener screenFlashListener) { 265 * whiteColorOverlayView.setVisibility(View.VISIBLE); 266 * maximizeScreenBrightness(); 267 * screenFlashListener.onCompleted(); 268 * } 269 * 270 * @Override 271 * public void clear() { 272 * restoreScreenBrightness(); 273 * whiteColorOverlayView.setVisibility(View.INVISIBLE); 274 * } 275 * }); 276 * 277 * imageCapture.setFlashMode(ImageCapture.FLASH_MODE_SCREEN); 278 * }</pre> 279 * 280 * @see #setFlashMode(int) 281 */ 282 public static final int FLASH_MODE_SCREEN = 3; 283 284 /** The timeout in seconds within which screen flash UI changes have to be completed. */ 285 @RestrictTo(Scope.LIBRARY_GROUP) 286 public static final long SCREEN_FLASH_UI_APPLY_TIMEOUT_SECONDS = 3; 287 288 /** 289 * When flash is required for taking a picture, a normal one shot flash will be used. 290 */ 291 @RestrictTo(Scope.LIBRARY_GROUP) 292 public static final int FLASH_TYPE_ONE_SHOT_FLASH = 0; 293 /** 294 * When flash is required for taking a picture, torch will be used as flash. 295 */ 296 @RestrictTo(Scope.LIBRARY_GROUP) 297 public static final int FLASH_TYPE_USE_TORCH_AS_FLASH = 1; 298 299 /** 300 * Captures 8-bit standard dynamic range (SDR) images using the {@link ImageFormat#JPEG} 301 * image format. 302 */ 303 public static final int OUTPUT_FORMAT_JPEG = 0; 304 305 /** 306 * Captures Ultra HDR compressed images using the {@link ImageFormat#JPEG_R} image format. 307 * 308 * <p>This format is backward compatible with SDR JPEG images and supports HDR rendering of 309 * content. This means that on older apps or devices, images appear seamlessly as regular JPEG; 310 * on apps and devices that have been updated to fully support the format, images appear as HDR. 311 * 312 * <p>For more information see 313 * <a href="https://developer.android.com/media/grow/ultra-hdr">Support Ultra HDR</a>. 314 */ 315 public static final int OUTPUT_FORMAT_JPEG_ULTRA_HDR = 1; 316 317 /** 318 * Captures raw images in the {@link ImageFormat#RAW_SENSOR} image format. 319 */ 320 public static final int OUTPUT_FORMAT_RAW = 2; 321 322 /** 323 * Captures raw images in the {@link ImageFormat#RAW_SENSOR} and {@link ImageFormat#JPEG} 324 * image formats. 325 */ 326 public static final int OUTPUT_FORMAT_RAW_JPEG = 3; 327 328 /** 329 * Provides a static configuration with implementation-agnostic options. 330 */ 331 @RestrictTo(Scope.LIBRARY_GROUP) 332 public static final Defaults DEFAULT_CONFIG = new Defaults(); 333 private static final String TAG = "ImageCapture"; 334 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ 335 private static final int MAX_IMAGES = 2; 336 // TODO(b/149336664) Move the quality to a compatibility class when there is a per device case. 337 private static final byte JPEG_QUALITY_MAXIMIZE_QUALITY_MODE = 100; 338 private static final byte JPEG_QUALITY_MINIMIZE_LATENCY_MODE = 95; 339 @CaptureMode 340 private static final int DEFAULT_CAPTURE_MODE = CAPTURE_MODE_MINIMIZE_LATENCY; 341 @FlashMode 342 private static final int DEFAULT_FLASH_MODE = FLASH_MODE_OFF; 343 344 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ 345 static final ExifRotationAvailability EXIF_ROTATION_AVAILABILITY = 346 new ExifRotationAvailability(); 347 348 private final ImageReaderProxy.OnImageAvailableListener mClosingListener = imageReader -> { 349 try (ImageProxy image = imageReader.acquireLatestImage()) { 350 Log.d(TAG, "Discarding ImageProxy which was inadvertently acquired: " + image); 351 } catch (IllegalStateException e) { 352 Log.e(TAG, "Failed to acquire latest image.", e); 353 } 354 }; 355 356 @CaptureMode 357 private final int mCaptureMode; 358 359 @GuardedBy("mLockedFlashMode") 360 private final AtomicReference<Integer> mLockedFlashMode = new AtomicReference<>(null); 361 362 @FlashType 363 private final int mFlashType; 364 365 //////////////////////////////////////////////////////////////////////////////////////////// 366 // [UseCase lifetime dynamic] - Dynamic variables which could change during anytime during 367 // the UseCase lifetime. 368 //////////////////////////////////////////////////////////////////////////////////////////// 369 370 /** Current flash mode. */ 371 @GuardedBy("mLockedFlashMode") 372 @FlashMode 373 private int mFlashMode = FLASH_MODE_UNKNOWN; 374 private Rational mCropAspectRatio = null; 375 private @NonNull ScreenFlashWrapper mScreenFlashWrapper; 376 377 //////////////////////////////////////////////////////////////////////////////////////////// 378 // [UseCase attached dynamic] - Can change but is only available when the UseCase is attached. 379 //////////////////////////////////////////////////////////////////////////////////////////// 380 381 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ 382 SessionConfig.Builder mSessionConfigBuilder; 383 384 private @Nullable ImagePipeline mImagePipeline; 385 private @Nullable TakePictureManager mTakePictureManager; 386 private SessionConfig.@Nullable CloseableErrorListener mCloseableErrorListener; 387 388 /** 389 * Creates a new image capture use case from the given configuration. 390 * 391 * @param userConfig for this use case instance 392 * @throws IllegalArgumentException if the configuration is invalid. 393 */ ImageCapture(@onNull ImageCaptureConfig userConfig)394 ImageCapture(@NonNull ImageCaptureConfig userConfig) { 395 super(userConfig); 396 397 ImageCaptureConfig useCaseConfig = (ImageCaptureConfig) getCurrentConfig(); 398 399 if (useCaseConfig.containsOption(OPTION_IMAGE_CAPTURE_MODE)) { 400 mCaptureMode = useCaseConfig.getCaptureMode(); 401 } else { 402 mCaptureMode = DEFAULT_CAPTURE_MODE; 403 } 404 405 mFlashType = useCaseConfig.getFlashType(FLASH_TYPE_ONE_SHOT_FLASH); 406 mScreenFlashWrapper = ScreenFlashWrapper.from(useCaseConfig.getScreenFlash()); 407 } 408 isSessionProcessorEnabledInCurrentCamera()409 private boolean isSessionProcessorEnabledInCurrentCamera() { 410 if (getCamera() == null) { 411 return false; 412 } 413 414 CameraConfig cameraConfig = getCamera().getExtendedConfig(); 415 return cameraConfig.getSessionProcessor(null) != null; 416 } 417 418 /** 419 * {@inheritDoc} 420 */ 421 @RestrictTo(Scope.LIBRARY_GROUP) 422 @Override getDefaultConfig(boolean applyDefaultConfig, @NonNull UseCaseConfigFactory factory)423 public @Nullable UseCaseConfig<?> getDefaultConfig(boolean applyDefaultConfig, 424 @NonNull UseCaseConfigFactory factory) { 425 Config captureConfig = factory.getConfig( 426 DEFAULT_CONFIG.getConfig().getCaptureType(), 427 getCaptureMode()); 428 429 if (applyDefaultConfig) { 430 captureConfig = Config.mergeConfigs(captureConfig, DEFAULT_CONFIG.getConfig()); 431 } 432 433 return captureConfig == null ? null : 434 getUseCaseConfigBuilder(captureConfig).getUseCaseConfig(); 435 } 436 437 /** 438 * {@inheritDoc} 439 */ 440 @RestrictTo(Scope.LIBRARY_GROUP) 441 @Override getUseCaseConfigBuilder(@onNull Config config)442 public UseCaseConfig.@NonNull Builder<?, ?, ?> getUseCaseConfigBuilder(@NonNull Config config) { 443 return Builder.fromConfig(config); 444 } 445 446 /** 447 * {@inheritDoc} 448 */ 449 @RestrictTo(Scope.LIBRARY_GROUP) 450 @Override onMergeConfig(@onNull CameraInfoInternal cameraInfo, UseCaseConfig.@NonNull Builder<?, ?, ?> builder)451 protected @NonNull UseCaseConfig<?> onMergeConfig(@NonNull CameraInfoInternal cameraInfo, 452 UseCaseConfig.@NonNull Builder<?, ?, ?> builder) { 453 if (cameraInfo.getCameraQuirks().contains(SoftwareJpegEncodingPreferredQuirk.class)) { 454 // Request software JPEG encoder if quirk exists on this device, and the software JPEG 455 // option has not already been explicitly set. 456 if (Boolean.FALSE.equals(builder.getMutableConfig().retrieveOption( 457 OPTION_USE_SOFTWARE_JPEG_ENCODER, true))) { 458 Logger.w(TAG, "Device quirk suggests software JPEG encoder, but it has been " 459 + "explicitly disabled."); 460 } else { 461 Logger.i(TAG, "Requesting software JPEG due to device quirk."); 462 builder.getMutableConfig().insertOption(OPTION_USE_SOFTWARE_JPEG_ENCODER, true); 463 } 464 } 465 466 // If software JPEG is requested, disable if it is incompatible. 467 boolean useSoftwareJpeg = enforceSoftwareJpegConstraints(builder.getMutableConfig()); 468 469 // Update the input format base on the other options set (mainly whether processing 470 // is done) 471 Integer bufferFormat = builder.getMutableConfig().retrieveOption(OPTION_BUFFER_FORMAT, 472 null); 473 if (bufferFormat != null) { 474 Preconditions.checkArgument(!(isSessionProcessorEnabledInCurrentCamera() 475 && bufferFormat != JPEG), 476 "Cannot set non-JPEG buffer format with Extensions enabled."); 477 builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT, 478 useSoftwareJpeg ? ImageFormat.YUV_420_888 : bufferFormat); 479 } else { 480 if (isOutputFormatRaw(builder.getMutableConfig())) { 481 builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT, RAW_SENSOR); 482 } else if (isOutputFormatRawJpeg(builder.getMutableConfig())) { 483 builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT, RAW_SENSOR); 484 builder.getMutableConfig().insertOption(OPTION_SECONDARY_INPUT_FORMAT, JPEG); 485 } else if (isOutputFormatUltraHdr(builder.getMutableConfig())) { 486 builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT, JPEG_R); 487 builder.getMutableConfig().insertOption(OPTION_INPUT_DYNAMIC_RANGE, 488 DynamicRange.UNSPECIFIED); 489 } else if (useSoftwareJpeg) { 490 builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT, 491 ImageFormat.YUV_420_888); 492 } else { 493 List<Pair<Integer, Size[]>> supportedSizes = 494 builder.getMutableConfig().retrieveOption(OPTION_SUPPORTED_RESOLUTIONS, 495 null); 496 if (supportedSizes == null) { 497 builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT, JPEG); 498 } else { 499 // Use Jpeg first if supported. 500 if (isImageFormatSupported(supportedSizes, JPEG)) { 501 builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT, 502 JPEG); 503 } else if (isImageFormatSupported(supportedSizes, ImageFormat.YUV_420_888)) { 504 builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT, 505 ImageFormat.YUV_420_888); 506 } 507 } 508 } 509 } 510 return builder.getUseCaseConfig(); 511 } 512 isImageFormatSupported(List<Pair<Integer, Size[]>> supportedSizes, int imageFormat)513 private static boolean isImageFormatSupported(List<Pair<Integer, Size[]>> supportedSizes, 514 int imageFormat) { 515 if (supportedSizes == null) { 516 return false; 517 } 518 for (Pair<Integer, Size[]> supportedSize : supportedSizes) { 519 if (supportedSize.first.equals(imageFormat)) { 520 return true; 521 } 522 } 523 return false; 524 } 525 isOutputFormatUltraHdr(@onNull MutableConfig config)526 private static boolean isOutputFormatUltraHdr(@NonNull MutableConfig config) { 527 return Objects.equals(config.retrieveOption(OPTION_OUTPUT_FORMAT, null), 528 OUTPUT_FORMAT_JPEG_ULTRA_HDR); 529 } 530 isOutputFormatRaw(@onNull MutableConfig config)531 private static boolean isOutputFormatRaw(@NonNull MutableConfig config) { 532 return Objects.equals(config.retrieveOption(OPTION_OUTPUT_FORMAT, null), 533 OUTPUT_FORMAT_RAW); 534 } 535 isOutputFormatRawJpeg(@onNull MutableConfig config)536 private static boolean isOutputFormatRawJpeg(@NonNull MutableConfig config) { 537 return Objects.equals(config.retrieveOption(OPTION_OUTPUT_FORMAT, null), 538 OUTPUT_FORMAT_RAW_JPEG); 539 } 540 541 /** 542 * Configures flash mode to CameraControlInternal once it is ready. 543 */ 544 @RestrictTo(Scope.LIBRARY_GROUP) 545 @Override onCameraControlReady()546 public void onCameraControlReady() { 547 Logger.d(TAG, "onCameraControlReady"); 548 trySetFlashModeToCameraControl(); 549 setScreenFlashToCameraControl(); 550 } 551 getCameraLens()552 private @CameraSelector.LensFacing int getCameraLens() { 553 Camera camera = getCamera(); 554 if (camera != null) { 555 return camera.getCameraInfo().getLensFacing(); 556 } 557 return CameraSelector.LENS_FACING_UNKNOWN; 558 } 559 560 /** 561 * Get the flash mode. 562 * 563 * @return the flashMode. Value is {@link #FLASH_MODE_AUTO}, {@link #FLASH_MODE_ON}, 564 * {@link #FLASH_MODE_SCREEN}, or {@link #FLASH_MODE_OFF}. 565 */ 566 @FlashMode getFlashMode()567 public int getFlashMode() { 568 synchronized (mLockedFlashMode) { 569 return mFlashMode != FLASH_MODE_UNKNOWN ? mFlashMode 570 : ((ImageCaptureConfig) getCurrentConfig()).getFlashMode(DEFAULT_FLASH_MODE); 571 } 572 } 573 574 /** 575 * Set the flash mode. 576 * 577 * <p>The flash control for the subsequent photo capture requests. Applications can check if 578 * there is a flash unit via {@link CameraInfo#hasFlashUnit()} and update UI component if 579 * necessary. If there is no flash unit and {@code flashMode} is not {@link #FLASH_MODE_SCREEN}, 580 * then calling this API will take no effect for the subsequent photo capture requests and 581 * they will act like {@link #FLASH_MODE_OFF}. 582 * 583 * <p>When the torch is enabled via {@link CameraControl#enableTorch(boolean)}, the torch 584 * will remain enabled during photo capture regardless of flashMode setting. When 585 * the torch is disabled, flash will function as specified by {@code setFlashMode(int)}. 586 * 587 * <p>On some LEGACY devices like Samsung A3, taking pictures with {@link #FLASH_MODE_AUTO} 588 * mode could cause a crash. To workaround this CameraX will disable the auto flash behavior 589 * internally on devices that have this issue. 590 * 591 * <p>If {@link #FLASH_MODE_SCREEN} is set, a {@link ScreenFlash} implementation 592 * must be set via {@link #setScreenFlash(ScreenFlash)} before calling this 593 * API. Trying to use {@link #FLASH_MODE_SCREEN} without a {@code ScreenFlash} instance set or 594 * with a non-front camera will result in an {@link IllegalArgumentException}. It is the 595 * application's responsibility to change flashMode while switching the camera in case it 596 * leads to a non-supported case (e.g. switching to rear camera while FLASH_MODE_SCREEN is 597 * still on). 598 * 599 * @param flashMode the flash mode. Value is {@link #FLASH_MODE_AUTO}, {@link #FLASH_MODE_ON}, 600 * {@link #FLASH_MODE_SCREEN} or {@link #FLASH_MODE_OFF}. 601 * 602 * @throws IllegalArgumentException If flash mode is invalid or FLASH_MODE_SCREEN is used 603 * without a {@code ScreenFlash} instance or front camera. 604 */ setFlashMode(@lashMode int flashMode)605 public void setFlashMode(@FlashMode int flashMode) { 606 Logger.d(TAG, "setFlashMode: flashMode = " + flashMode); 607 608 if (flashMode != FLASH_MODE_AUTO && flashMode != FLASH_MODE_ON 609 && flashMode != FLASH_MODE_OFF) { 610 if (flashMode == FLASH_MODE_SCREEN) { 611 if (mScreenFlashWrapper.getBaseScreenFlash() == null) { 612 throw new IllegalArgumentException("ScreenFlash not set for FLASH_MODE_SCREEN"); 613 } 614 615 if (getCamera() != null && getCameraLens() != CameraSelector.LENS_FACING_FRONT) { 616 throw new IllegalArgumentException( 617 "Not a front camera despite setting FLASH_MODE_SCREEN"); 618 } 619 } else { 620 throw new IllegalArgumentException("Invalid flash mode: " + flashMode); 621 } 622 } 623 624 synchronized (mLockedFlashMode) { 625 mFlashMode = flashMode; 626 trySetFlashModeToCameraControl(); 627 } 628 } 629 630 /** 631 * Sets {@link ScreenFlash} for subsequent photo capture requests. 632 * 633 * <p>The calling of this API will take effect for {@link #FLASH_MODE_SCREEN} only 634 * and the {@code screenFlash} instance will be ignored for other flash modes. 635 * 636 * <p>If the implementation provided by the user is no longer valid (e.g. due to any 637 * {@link android.app.Activity} or {@link android.view.View} reference used in the 638 * implementation becoming invalid), user needs to re-set a new valid {@code ScreenFlash} or 639 * clear the previous one with {@code setScreenFlash(null)}, whichever appropriate. 640 * 641 * @param screenFlash A {@link ScreenFlash} implementation that is used to 642 * notify API users when app side changes need to be done. This 643 * will replace the previous {@code ScreenFlash} instance set 644 * with this method. 645 */ setScreenFlash(@ullable ScreenFlash screenFlash)646 public void setScreenFlash(@Nullable ScreenFlash screenFlash) { 647 mScreenFlashWrapper = ScreenFlashWrapper.from(screenFlash); 648 setScreenFlashToCameraControl(); 649 } 650 651 /** 652 * Returns the {@link ScreenFlash} instance currently set, null if none. 653 */ getScreenFlash()654 public @Nullable ScreenFlash getScreenFlash() { 655 return mScreenFlashWrapper.getBaseScreenFlash(); 656 } 657 setScreenFlashToCameraControl()658 private void setScreenFlashToCameraControl() { 659 setScreenFlashToCameraControl(mScreenFlashWrapper); 660 } 661 setScreenFlashToCameraControl(ImageCapture.@ullable ScreenFlash screenFlash)662 private void setScreenFlashToCameraControl(ImageCapture.@Nullable ScreenFlash screenFlash) { 663 getCameraControl().setScreenFlash(screenFlash); 664 } 665 666 /** 667 * Sets target cropping aspect ratio for output image. 668 * 669 * <p>This aspect ratio is orientation-dependent. It should be expressed in the coordinate 670 * frame after rotating the image by the target rotation. 671 * 672 * <p>This sets the cropping rectangle returned by {@link ImageProxy#getCropRect()} returned 673 * from {@link ImageCapture#takePicture(Executor, OnImageCapturedCallback)}. 674 * 675 * <p>For example, assume the {@code aspectRatio} of 3x4. If an image has a resolution of 676 * 480x640 after applying the target rotation, then the output {@link ImageProxy} of 677 * {@link ImageCapture#takePicture(Executor, OnImageCapturedCallback)} would have a cropping 678 * rectangle of 480x640 after applying the rotation degrees. However, if an image has a 679 * resolution of 640x480 after applying the target rotation, then the cropping rectangle 680 * of the output {@link ImageProxy} would be 360x480 after applying the rotation degrees. 681 * 682 * <p>This crops the saved image when calling 683 * {@link ImageCapture#takePicture(OutputFileOptions, Executor, OnImageSavedCallback)}. Note 684 * that the cropping will introduce an additional latency. 685 * 686 * <p>Cropping occurs around the center of the image and as though it were in the target 687 * rotation. For example, assume the {@code aspectRatio} of 3x4. If an image has a resolution 688 * of 480x640 after applying the target rotation, then the saved output image would be 689 * 480x640 after applying the EXIF orientation value. However, if an image has a resolution 690 * of 640x480 after applying the target rotation, then the saved output image would be 691 * 360x480 after applying the EXIF orientation value. 692 * 693 * <p>This setting value will be automatically updated to match the new target rotation value 694 * when {@link ImageCapture#setTargetRotation(int)} is called. 695 * 696 * @param aspectRatio New target aspect ratio. 697 */ setCropAspectRatio(@onNull Rational aspectRatio)698 public void setCropAspectRatio(@NonNull Rational aspectRatio) { 699 mCropAspectRatio = aspectRatio; 700 } 701 702 /** 703 * Returns the desired rotation of the output image. 704 * 705 * <p>The rotation can be set prior to constructing an ImageCapture using 706 * {@link ImageCapture.Builder#setTargetRotation(int)} or dynamically by calling 707 * {@link ImageCapture#setTargetRotation(int)}. The rotation of an image taken is 708 * determined by the rotation value set at the time image capture is initiated, such as when 709 * calling {@link #takePicture(Executor, OnImageCapturedCallback)}. 710 * 711 * <p>If no target rotation is set by the application, it is set to the value of 712 * {@link Display#getRotation()} of the default display at the time the use case is 713 * created. The use case is fully created once it has been attached to a camera. 714 * 715 * @return The rotation of the intended target. 716 */ 717 @RotationValue getTargetRotation()718 public int getTargetRotation() { 719 return getTargetRotationInternal(); 720 } 721 722 /** 723 * Sets the desired rotation of the output image. 724 * 725 * <p>This will affect the EXIF rotation metadata in images saved by takePicture calls and the 726 * {@link ImageInfo#getRotationDegrees()} value of the {@link ImageProxy} returned by 727 * {@link OnImageCapturedCallback}. These will be set to be the rotation, which if applied to 728 * the output image data, will make the image match target rotation specified here. 729 * 730 * <p>While rotation can also be set via {@link Builder#setTargetRotation(int)}, using 731 * {@code setTargetRotation(int)} allows the target rotation to be set dynamically. 732 * 733 * <p>In general, it is best to use an {@link android.view.OrientationEventListener} to 734 * set the target rotation. This way, the rotation output will indicate which way is down for 735 * a given image. This is important since display orientation may be locked by device 736 * default, user setting, or app configuration, and some devices may not transition to a 737 * reverse-portrait display orientation. In these cases, set target rotation dynamically 738 * according to the {@link android.view.OrientationEventListener}, without re-creating the 739 * use case. {@link UseCase#snapToSurfaceRotation(int)} is a helper function to convert the 740 * orientation of the {@link android.view.OrientationEventListener} to a rotation value. 741 * See {@link UseCase#snapToSurfaceRotation(int)} for more information and sample code. 742 * 743 * <p>When this function is called, value set by 744 * {@link ImageCapture.Builder#setTargetResolution(Size)} will be updated automatically to make 745 * sure the suitable resolution can be selected when the use case is bound. Value set by 746 * {@link ImageCapture#setCropAspectRatio(Rational)} will also be updated automatically to 747 * make sure the output image is cropped into expected aspect ratio. 748 * 749 * <p>If no target rotation is set by the application, it is set to the value of 750 * {@link Display#getRotation()} of the default display at the time the use case is bound. To 751 * return to the default value, set the value to 752 * <pre>{@code 753 * context.getSystemService(WindowManager.class).getDefaultDisplay().getRotation(); 754 * }</pre> 755 * 756 * <p>takePicture uses the target rotation at the time it begins executing (which may be delayed 757 * waiting on a previous takePicture call to complete). 758 * 759 * @param rotation Target rotation of the output image, expressed as one of 760 * {@link Surface#ROTATION_0}, {@link Surface#ROTATION_90}, 761 * {@link Surface#ROTATION_180}, or {@link Surface#ROTATION_270}. 762 */ setTargetRotation(@otationValue int rotation)763 public void setTargetRotation(@RotationValue int rotation) { 764 int oldRotation = getTargetRotation(); 765 766 if (setTargetRotationInternal(rotation)) { 767 // For the crop aspect ratio value, the numerator and denominator of original setting 768 // value will be swapped then set back. It is an orientation-dependent value that will 769 // be used to crop ImageCapture's output image. 770 if (mCropAspectRatio != null) { 771 int oldRotationDegrees = CameraOrientationUtil.surfaceRotationToDegrees( 772 oldRotation); 773 int newRotationDegrees = CameraOrientationUtil.surfaceRotationToDegrees(rotation); 774 mCropAspectRatio = ImageUtil.getRotatedAspectRatio( 775 Math.abs(newRotationDegrees - oldRotationDegrees), mCropAspectRatio); 776 } 777 778 // TODO(b/122846516): Update session configuration and possibly reconfigure session. 779 } 780 } 781 782 /** 783 * Returns the set capture mode. 784 * 785 * <p>This is set when constructing an ImageCapture using 786 * {@link ImageCapture.Builder#setCaptureMode(int)}. This is static for an instance of 787 * ImageCapture. 788 */ 789 @CaptureMode getCaptureMode()790 public int getCaptureMode() { 791 return mCaptureMode; 792 } 793 794 /** 795 * Returns the JPEG quality setting. 796 * 797 * <p>This is set when constructing an ImageCapture using 798 * {@link ImageCapture.Builder#setJpegQuality(int)}. If not set, a default value will be set 799 * according to the capture mode setting. JPEG compression quality 95 is set for 800 * {@link #CAPTURE_MODE_MINIMIZE_LATENCY} and 100 is set for 801 * {@link #CAPTURE_MODE_MAXIMIZE_QUALITY}. This is static for an instance of ImageCapture. 802 */ 803 @IntRange(from = 1, to = 100) getJpegQuality()804 public int getJpegQuality() { 805 return getJpegQualityInternal(); 806 } 807 808 /** 809 * Gets selected resolution information of the {@link ImageCapture}. 810 * 811 * <p>The returned {@link ResolutionInfo} will be expressed in the coordinates of the camera 812 * sensor. Note that the resolution might not be the same as the resolution of the received 813 * image by calling {@link #takePicture} because the received image might have been rotated 814 * to the upright orientation using the target rotation setting by the device. 815 * 816 * <p>The resolution information might change if the use case is unbound and then rebound, 817 * {@link #setTargetRotation(int)} is called to change the target rotation setting, or 818 * {@link #setCropAspectRatio(Rational)} is called to change the crop aspect ratio setting. 819 * The application needs to call {@code getResolutionInfo()} again to get the latest 820 * {@link ResolutionInfo} for the changes. 821 * 822 * @return the resolution information if the use case has been bound by the 823 * {@link androidx.camera.lifecycle.ProcessCameraProvider#bindToLifecycle(LifecycleOwner 824 *, CameraSelector, UseCase...)} API, or null if the use case is not bound yet. 825 */ getResolutionInfo()826 public @Nullable ResolutionInfo getResolutionInfo() { 827 return getResolutionInfoInternal(); 828 } 829 830 /** 831 * {@inheritDoc} 832 */ 833 @RestrictTo(Scope.LIBRARY_GROUP) 834 @Override getResolutionInfoInternal()835 protected @Nullable ResolutionInfo getResolutionInfoInternal() { 836 CameraInternal camera = getCamera(); 837 Size resolution = getAttachedSurfaceResolution(); 838 839 if (camera == null || resolution == null) { 840 return null; 841 } 842 843 Rect cropRect = getViewPortCropRect(); 844 845 Rational cropAspectRatio = mCropAspectRatio; 846 847 if (cropRect == null) { 848 if (cropAspectRatio != null) { 849 cropRect = ImageUtil.computeCropRectFromAspectRatio(resolution, cropAspectRatio); 850 } else { 851 cropRect = new Rect(0, 0, resolution.getWidth(), resolution.getHeight()); 852 } 853 } 854 855 int rotationDegrees = getRelativeRotation(camera); 856 857 return new ResolutionInfo(resolution, requireNonNull(cropRect), rotationDegrees); 858 } 859 860 /** 861 * Returns the resolution selector setting. 862 * 863 * <p>This setting is set when constructing an ImageCapture using 864 * {@link Builder#setResolutionSelector(ResolutionSelector)}. 865 */ getResolutionSelector()866 public @Nullable ResolutionSelector getResolutionSelector() { 867 return ((ImageOutputConfig) getCurrentConfig()).getResolutionSelector(null); 868 } 869 870 /** 871 * Returns the output format setting. 872 * 873 * <p>If the output format was not provided to 874 * {@link ImageCapture.Builder#setOutputFormat(int)}, this will return the default of 875 * {@link #OUTPUT_FORMAT_JPEG}. 876 * 877 * @return the output format set for this {@code ImageCapture} use case. 878 * 879 * @see ImageCapture.Builder#setOutputFormat(int) 880 */ 881 @OutputFormat getOutputFormat()882 public int getOutputFormat() { 883 return checkNotNull(getCurrentConfig().retrieveOption(OPTION_OUTPUT_FORMAT, 884 Defaults.DEFAULT_OUTPUT_FORMAT)); 885 } 886 887 /** 888 * Captures a new still image for in memory access. 889 * 890 * <p>The listener is responsible for calling {@link Image#close()} on the returned image. 891 * 892 * <p>For simultaneous image capture with {@link #OUTPUT_FORMAT_RAW_JPEG}, the 893 * {@link OnImageCapturedCallback#onCaptureSuccess(ImageProxy)} will be triggered twice, one for 894 * {@link ImageFormat#RAW_SENSOR} and the other for {@link ImageFormat#JPEG}. The order of the 895 * callbacks for which image format is triggered first is not guaranteed. 896 * 897 * @param executor The executor in which the callback methods will be run. 898 * @param callback Callback to be invoked for the newly captured image. 899 * 900 * @throws IllegalArgumentException If {@link ImageCapture#FLASH_MODE_SCREEN} is used without a 901 * non-null {@code ScreenFlash} instance set. 902 */ takePicture(@onNull Executor executor, final @NonNull OnImageCapturedCallback callback)903 public void takePicture(@NonNull Executor executor, 904 final @NonNull OnImageCapturedCallback callback) { 905 if (Looper.getMainLooper() != Looper.myLooper()) { 906 CameraXExecutors.mainThreadExecutor().execute(() -> takePicture(executor, callback)); 907 return; 908 } 909 910 takePictureInternal(executor, callback, /*onDiskCallback=*/null, 911 /*outputFileOptions=*/null, /*secondaryOutputFileOptions=*/null); 912 } 913 914 /** 915 * Captures a new still image and saves to a file along with application specified metadata. 916 * 917 * <p> If the {@link ImageCapture} is in a {@link UseCaseGroup} where {@link ViewPort} is 918 * set, or {@link #setCropAspectRatio} is used, the image may be cropped before saving to 919 * disk which causes an additional latency. 920 * 921 * @param outputFileOptions Options to store the newly captured image. 922 * @param executor The executor in which the callback methods will be run. 923 * @param imageSavedCallback Callback to be called for the newly captured image. 924 * 925 * @throws IllegalArgumentException If {@link ImageCapture#FLASH_MODE_SCREEN} is used without a 926 * a non-null {@code ScreenFlash} instance set. Also if 927 * {@link ImageCapture#OUTPUT_FORMAT_RAW_JPEG} is used. 928 * @see ViewPort 929 */ takePicture( final @NonNull OutputFileOptions outputFileOptions, final @NonNull Executor executor, final @NonNull OnImageSavedCallback imageSavedCallback)930 public void takePicture( 931 final @NonNull OutputFileOptions outputFileOptions, 932 final @NonNull Executor executor, 933 final @NonNull OnImageSavedCallback imageSavedCallback) { 934 if (Looper.getMainLooper() != Looper.myLooper()) { 935 CameraXExecutors.mainThreadExecutor().execute(() -> takePicture( 936 outputFileOptions, executor, imageSavedCallback)); 937 return; 938 } 939 940 takePictureInternal(executor, /*inMemoryCallback=*/null, imageSavedCallback, 941 outputFileOptions, /*secondaryOutputFileOptions=*/null); 942 } 943 944 /** 945 * Captures two still images simultaneously and saves to a file along with application 946 * specified metadata. 947 * 948 * <p>Currently only {@link #OUTPUT_FORMAT_RAW_JPEG} is supporting simultaneous image capture. 949 * It needs two {@link OutputFileOptions}, the first one is used for 950 * {@link ImageFormat#RAW_SENSOR} image and the second one is for {@link ImageFormat#JPEG}. The 951 * order of the callbacks for which image format is triggered first is not guaranteed. Check 952 * with {@link OutputFileResults#getImageFormat()} in 953 * {@link OnImageSavedCallback#onImageSaved(OutputFileResults)} for the image format. 954 * 955 * @param rawOutputFileOptions Options to store the newly captured raw image. 956 * @param jpegOutputFileOptions Options to store the newly captured jpeg image. 957 * @param executor The executor in which the callback methods will be run. 958 * @param imageSavedCallback Callback to be called for the newly captured image. 959 * 960 * @throws IllegalArgumentException If {@link ImageCapture#FLASH_MODE_SCREEN} is used without a 961 * a non-null {@code ScreenFlash} instance set. Also if 962 * non-{@link ImageCapture#OUTPUT_FORMAT_RAW_JPEG} format is 963 * used. 964 */ takePicture( final @NonNull OutputFileOptions rawOutputFileOptions, final @NonNull OutputFileOptions jpegOutputFileOptions, final @NonNull Executor executor, final @NonNull OnImageSavedCallback imageSavedCallback)965 public void takePicture( 966 final @NonNull OutputFileOptions rawOutputFileOptions, 967 final @NonNull OutputFileOptions jpegOutputFileOptions, 968 final @NonNull Executor executor, 969 final @NonNull OnImageSavedCallback imageSavedCallback) { 970 if (Looper.getMainLooper() != Looper.myLooper()) { 971 CameraXExecutors.mainThreadExecutor().execute( 972 () -> takePicture(rawOutputFileOptions, jpegOutputFileOptions, 973 executor, imageSavedCallback)); 974 return; 975 } 976 takePictureInternal(executor, /*inMemoryCallback=*/null, imageSavedCallback, 977 rawOutputFileOptions, jpegOutputFileOptions); 978 } 979 980 /** 981 * Returns {@link ImageCaptureCapabilities} to query ImageCapture capability of the given 982 * {@link CameraInfo}. 983 * 984 * <p>Some capabilities are only exposed on Extensions-enabled cameras. To get the correct 985 * capabilities when Extensions are enabled, you need to pass the {@link CameraInfo} from the 986 * Extensions-enabled {@link Camera} instance. To do this, use the {@link CameraSelector} 987 * instance retrieved from 988 * {@link androidx.camera.extensions.ExtensionsManager#getExtensionEnabledCameraSelector(CameraSelector, int)} 989 * to invoke {@link androidx.camera.lifecycle.ProcessCameraProvider#bindToLifecycle} where 990 * you can skip use cases arguments if you'd like to query it before opening the camera. Then, 991 * use the returned {@link Camera} to get the {@link CameraInfo} instance. 992 * 993 * <p>>The following code snippet demonstrates how to enable postview: 994 * 995 * <pre>{@code 996 * CameraSelector extensionCameraSelector = 997 * extensionsManager.getExtensionEnabledCameraSelector(cameraSelector, ExtensionMode.NIGHT); 998 * Camera camera = cameraProvider.bindToLifecycle(activity, extensionCameraSelector); 999 * ImageCaptureCapabilities capabilities = 1000 * ImageCapture.getImageCaptureCapabilities(camera.getCameraInfo()); 1001 * ImageCapture imageCapture = new ImageCapture.Builder() 1002 * .setPostviewEnabled(capabilities.isPostviewSupported()) 1003 * .build(); 1004 * }}</pre> 1005 * 1006 * @return {@link ImageCaptureCapabilities} 1007 */ getImageCaptureCapabilities( @onNull CameraInfo cameraInfo)1008 public static @NonNull ImageCaptureCapabilities getImageCaptureCapabilities( 1009 @NonNull CameraInfo cameraInfo) { 1010 return new ImageCaptureCapabilitiesImpl(cameraInfo); 1011 } 1012 1013 private static class ImageCaptureCapabilitiesImpl implements ImageCaptureCapabilities { 1014 private final CameraInfo mCameraInfo; ImageCaptureCapabilitiesImpl(@onNull CameraInfo cameraInfo)1015 ImageCaptureCapabilitiesImpl(@NonNull CameraInfo cameraInfo) { 1016 mCameraInfo = cameraInfo; 1017 } 1018 1019 @Override isPostviewSupported()1020 public boolean isPostviewSupported() { 1021 if (mCameraInfo instanceof CameraInfoInternal) { 1022 return ((CameraInfoInternal) mCameraInfo).isPostviewSupported(); 1023 } 1024 return false; 1025 } 1026 1027 @Override isCaptureProcessProgressSupported()1028 public boolean isCaptureProcessProgressSupported() { 1029 if (mCameraInfo instanceof CameraInfoInternal) { 1030 return ((CameraInfoInternal) mCameraInfo).isCaptureProcessProgressSupported(); 1031 } 1032 return false; 1033 } 1034 1035 @Override getSupportedOutputFormats()1036 public @NonNull Set<@OutputFormat Integer> getSupportedOutputFormats() { 1037 Set<Integer> outputFormatsFromAdapterCameraInfo = 1038 getSupportedOutputFormatsFromAdapterCameraInfo(); 1039 1040 if (outputFormatsFromAdapterCameraInfo != null) { 1041 return outputFormatsFromAdapterCameraInfo; 1042 } 1043 1044 Set<Integer> formats = new HashSet<>(); 1045 formats.add(OUTPUT_FORMAT_JPEG); 1046 if (isUltraHdrSupported()) { 1047 formats.add(OUTPUT_FORMAT_JPEG_ULTRA_HDR); 1048 } 1049 1050 if (isRawSupported()) { 1051 formats.add(OUTPUT_FORMAT_RAW); 1052 formats.add(OUTPUT_FORMAT_RAW_JPEG); 1053 } 1054 1055 return formats; 1056 } 1057 isUltraHdrSupported()1058 private boolean isUltraHdrSupported() { 1059 if (mCameraInfo instanceof CameraInfoInternal) { 1060 CameraInfoInternal cameraInfoInternal = (CameraInfoInternal) mCameraInfo; 1061 return cameraInfoInternal.getSupportedOutputFormats().contains(JPEG_R); 1062 } 1063 1064 return false; 1065 } 1066 isRawSupported()1067 private boolean isRawSupported() { 1068 if (mCameraInfo instanceof CameraInfoInternal) { 1069 CameraInfoInternal cameraInfoInternal = (CameraInfoInternal) mCameraInfo; 1070 return cameraInfoInternal.getSupportedOutputFormats().contains(RAW_SENSOR); 1071 } 1072 1073 return false; 1074 } 1075 getSupportedOutputFormatsFromAdapterCameraInfo()1076 private @Nullable Set<Integer> getSupportedOutputFormatsFromAdapterCameraInfo() { 1077 if (!(mCameraInfo instanceof AdapterCameraInfo)) { 1078 return null; 1079 } 1080 1081 // If AdapterCameraInfo is used to query the capabilities, retrieves the supported 1082 // output formats from the CameraConfig associated with it. 1083 CameraConfig cameraConfig = ((AdapterCameraInfo) mCameraInfo).getCameraConfig(); 1084 UseCaseConfigFactory useCaseConfigFactory = cameraConfig.getUseCaseConfigFactory(); 1085 Config useCaseConfig = useCaseConfigFactory.getConfig( 1086 UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE, DEFAULT_CAPTURE_MODE); 1087 1088 if (useCaseConfig == null || !useCaseConfig.containsOption( 1089 OPTION_SUPPORTED_RESOLUTIONS)) { 1090 return null; 1091 } 1092 1093 Set<Integer> formats = new HashSet<>(); 1094 // JPEG output format should always be supported for ImageCapture. 1095 formats.add(OUTPUT_FORMAT_JPEG); 1096 for (Pair<Integer, Size[]> formatSizesPair : useCaseConfig.retrieveOption( 1097 OPTION_SUPPORTED_RESOLUTIONS)) { 1098 if (formatSizesPair.first == ImageFormat.JPEG_R) { 1099 formats.add(OUTPUT_FORMAT_JPEG_ULTRA_HDR); 1100 // Besides JPEG, only JPEG_ULTRA_HDR can be supported for now from the 1101 // adapter camera info. Breaks the for-loop when it has been confirmed that 1102 // JPEG_ULTRA_HDR can be supported. 1103 break; 1104 } 1105 } 1106 1107 return formats; 1108 } 1109 } 1110 computeDispatchCropRect(@ullable Rect viewPortCropRect, @Nullable Rational cropAspectRatio, int rotationDegrees, @NonNull Size dispatchResolution, int dispatchRotationDegrees)1111 static @NonNull Rect computeDispatchCropRect(@Nullable Rect viewPortCropRect, 1112 @Nullable Rational cropAspectRatio, int rotationDegrees, 1113 @NonNull Size dispatchResolution, int dispatchRotationDegrees) { 1114 if (viewPortCropRect != null) { 1115 return ImageUtil.computeCropRectFromDispatchInfo(viewPortCropRect, rotationDegrees, 1116 dispatchResolution, dispatchRotationDegrees); 1117 } else if (cropAspectRatio != null) { 1118 // Fall back to crop aspect ratio if view port is not available. 1119 Rational aspectRatio = cropAspectRatio; 1120 if ((dispatchRotationDegrees % 180) != 0) { 1121 aspectRatio = new Rational( 1122 /* invert the ratio numerator=*/ cropAspectRatio.getDenominator(), 1123 /* invert the ratio denominator=*/ cropAspectRatio.getNumerator()); 1124 } 1125 if (ImageUtil.isAspectRatioValid(dispatchResolution, aspectRatio)) { 1126 return requireNonNull( 1127 ImageUtil.computeCropRectFromAspectRatio(dispatchResolution, aspectRatio)); 1128 } 1129 } 1130 1131 return new Rect(0, 0, dispatchResolution.getWidth(), dispatchResolution.getHeight()); 1132 } 1133 1134 /** 1135 * {@inheritDoc} 1136 */ 1137 @RestrictTo(Scope.LIBRARY_GROUP) 1138 @UiThread 1139 @Override onStateDetached()1140 public void onStateDetached() { 1141 abortImageCaptureRequests(); 1142 } 1143 1144 @UiThread abortImageCaptureRequests()1145 private void abortImageCaptureRequests() { 1146 // Camera2CapturePipeline ScreenFlash#clear event may come a bit later due to 1147 // thread-hopping or listener invocation delay. When all requests are aborted anyway, we can 1148 // complete all pending tasks earlier and ignore any that comes from user/camera-camera2. 1149 mScreenFlashWrapper.completePendingTasks(); 1150 1151 if (mTakePictureManager != null) { 1152 mTakePictureManager.abortRequests(); 1153 } 1154 } 1155 lockFlashMode()1156 void lockFlashMode() { 1157 synchronized (mLockedFlashMode) { 1158 if (mLockedFlashMode.get() != null) { 1159 // FlashMode is locked. 1160 return; 1161 } 1162 mLockedFlashMode.set(getFlashMode()); 1163 } 1164 } 1165 unlockFlashMode()1166 void unlockFlashMode() { 1167 synchronized (mLockedFlashMode) { 1168 Integer lockedFlashMode = mLockedFlashMode.getAndSet(null); 1169 if (lockedFlashMode == null) { 1170 // FlashMode is not locked yet. 1171 return; 1172 } 1173 if (lockedFlashMode != getFlashMode()) { 1174 // Flash Mode is changed during lock session. 1175 trySetFlashModeToCameraControl(); 1176 } 1177 } 1178 } 1179 trySetFlashModeToCameraControl()1180 private void trySetFlashModeToCameraControl() { 1181 synchronized (mLockedFlashMode) { 1182 if (mLockedFlashMode.get() != null) { 1183 // Flash Mode is locked. 1184 return; 1185 } 1186 getCameraControl().setFlashMode(getFlashMode()); 1187 } 1188 } 1189 1190 /** 1191 * Gets the JPEG quality based on {@link #mCaptureMode}. 1192 * 1193 * <p> Range is 1-100; larger is higher quality. 1194 * 1195 * @return Compression quality of the captured JPEG image. 1196 */ 1197 @OptIn(markerClass = ExperimentalZeroShutterLag.class) 1198 @IntRange(from = 1, to = 100) getJpegQualityInternal()1199 private int getJpegQualityInternal() { 1200 ImageCaptureConfig imageCaptureConfig = (ImageCaptureConfig) getCurrentConfig(); 1201 1202 if (imageCaptureConfig.containsOption(OPTION_JPEG_COMPRESSION_QUALITY)) { 1203 return imageCaptureConfig.getJpegQuality(); 1204 } 1205 1206 switch (mCaptureMode) { 1207 case CAPTURE_MODE_MAXIMIZE_QUALITY: 1208 return JPEG_QUALITY_MAXIMIZE_QUALITY_MODE; 1209 case CAPTURE_MODE_MINIMIZE_LATENCY: 1210 case CAPTURE_MODE_ZERO_SHUTTER_LAG: 1211 return JPEG_QUALITY_MINIMIZE_LATENCY_MODE; 1212 default: 1213 throw new IllegalStateException("CaptureMode " + mCaptureMode + " is invalid"); 1214 } 1215 } 1216 1217 @Override toString()1218 public @NonNull String toString() { 1219 return TAG + ":" + getName(); 1220 } 1221 1222 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ 1223 @ImageCaptureError getError(Throwable throwable)1224 static int getError(Throwable throwable) { 1225 if (throwable instanceof CameraClosedException) { 1226 return ERROR_CAMERA_CLOSED; 1227 } else if (throwable instanceof ImageCaptureException) { 1228 return ((ImageCaptureException) throwable).getImageCaptureError(); 1229 } else { 1230 return ERROR_UNKNOWN; 1231 } 1232 } 1233 1234 /** 1235 * Disables software JPEG if it is requested and the provided config and/or device 1236 * characteristics are incompatible. 1237 * 1238 * @return {@code true} if software JPEG will be used after applying constraints. 1239 */ enforceSoftwareJpegConstraints(@onNull MutableConfig mutableConfig)1240 boolean enforceSoftwareJpegConstraints(@NonNull MutableConfig mutableConfig) { 1241 if (Boolean.TRUE.equals( 1242 mutableConfig.retrieveOption(OPTION_USE_SOFTWARE_JPEG_ENCODER, false))) { 1243 boolean supported = true; 1244 if (isSessionProcessorEnabledInCurrentCamera()) { 1245 // SessionProcessor requires JPEG input format so it is incompatible with SW Jpeg. 1246 Logger.w(TAG, "Software JPEG cannot be used with Extensions."); 1247 supported = false; 1248 } 1249 Integer bufferFormat = mutableConfig.retrieveOption(OPTION_BUFFER_FORMAT, null); 1250 if (bufferFormat != null && bufferFormat != JPEG) { 1251 Logger.w(TAG, "Software JPEG cannot be used with non-JPEG output buffer format."); 1252 supported = false; 1253 } 1254 1255 if (!supported) { 1256 Logger.w(TAG, "Unable to support software JPEG. Disabling."); 1257 mutableConfig.insertOption(OPTION_USE_SOFTWARE_JPEG_ENCODER, false); 1258 } 1259 1260 return supported; 1261 } 1262 return false; 1263 } 1264 1265 /** 1266 * {@inheritDoc} 1267 */ 1268 @RestrictTo(Scope.LIBRARY_GROUP) 1269 @Override onUnbind()1270 public void onUnbind() { 1271 abortImageCaptureRequests(); 1272 clearPipeline(); 1273 setScreenFlashToCameraControl(null); 1274 } 1275 1276 /** 1277 * {@inheritDoc} 1278 */ 1279 @Override 1280 @RestrictTo(Scope.LIBRARY_GROUP) onBind()1281 public void onBind() { 1282 CameraInternal camera = getCamera(); 1283 checkNotNull(camera, "Attached camera cannot be null"); 1284 1285 if (getFlashMode() == FLASH_MODE_SCREEN 1286 && getCameraLens() != CameraSelector.LENS_FACING_FRONT) { 1287 throw new IllegalArgumentException( 1288 "Not a front camera despite setting FLASH_MODE_SCREEN in ImageCapture"); 1289 } 1290 } 1291 getSessionProcessor()1292 private @Nullable SessionProcessor getSessionProcessor() { 1293 CameraConfig cameraConfig = getCamera().getExtendedConfig(); 1294 return cameraConfig.getSessionProcessor(null); 1295 } 1296 1297 /** 1298 * {@inheritDoc} 1299 */ 1300 @Override 1301 @RestrictTo(Scope.LIBRARY_GROUP) onSuggestedStreamSpecUpdated( @onNull StreamSpec primaryStreamSpec, @Nullable StreamSpec secondaryStreamSpec)1302 protected @NonNull StreamSpec onSuggestedStreamSpecUpdated( 1303 @NonNull StreamSpec primaryStreamSpec, 1304 @Nullable StreamSpec secondaryStreamSpec) { 1305 mSessionConfigBuilder = createPipeline(getCameraId(), 1306 (ImageCaptureConfig) getCurrentConfig(), primaryStreamSpec); 1307 1308 updateSessionConfig(List.of(mSessionConfigBuilder.build())); 1309 1310 // In order to speed up the take picture process, notifyActive at an early stage to 1311 // attach the session capture callback to repeating and get capture result all the time. 1312 notifyActive(); 1313 return primaryStreamSpec; 1314 } 1315 1316 /** 1317 * {@inheritDoc} 1318 */ 1319 @Override 1320 @RestrictTo(Scope.LIBRARY_GROUP) onSuggestedStreamSpecImplementationOptionsUpdated( @onNull Config config)1321 protected @NonNull StreamSpec onSuggestedStreamSpecImplementationOptionsUpdated( 1322 @NonNull Config config) { 1323 mSessionConfigBuilder.addImplementationOptions(config); 1324 updateSessionConfig(List.of(mSessionConfigBuilder.build())); 1325 return getAttachedStreamSpec().toBuilder().setImplementationOptions(config).build(); 1326 } 1327 1328 /** 1329 * An {@link ImageCaptureControl} implementation to control this {@link ImageCapture} instance. 1330 */ 1331 private final ImageCaptureControl mImageCaptureControl = new ImageCaptureControl() { 1332 1333 @MainThread 1334 @Override 1335 public void lockFlashMode() { 1336 ImageCapture.this.lockFlashMode(); 1337 } 1338 1339 @MainThread 1340 @Override 1341 public void unlockFlashMode() { 1342 ImageCapture.this.unlockFlashMode(); 1343 } 1344 1345 @MainThread 1346 @Override 1347 public @NonNull ListenableFuture<Void> submitStillCaptureRequests( 1348 @NonNull List<CaptureConfig> captureConfigs) { 1349 return ImageCapture.this.submitStillCaptureRequest(captureConfigs); 1350 } 1351 }; 1352 1353 /** 1354 * Creates the pipeline for both capture request configuration and image post-processing. 1355 */ 1356 @OptIn(markerClass = {ExperimentalZeroShutterLag.class}) 1357 @MainThread createPipeline(@onNull String cameraId, @NonNull ImageCaptureConfig config, @NonNull StreamSpec streamSpec)1358 private SessionConfig.Builder createPipeline(@NonNull String cameraId, 1359 @NonNull ImageCaptureConfig config, @NonNull StreamSpec streamSpec) { 1360 checkMainThread(); 1361 Log.d(TAG, String.format("createPipeline(cameraId: %s, streamSpec: %s)", 1362 cameraId, streamSpec)); 1363 Size resolution = streamSpec.getResolution(); 1364 boolean isVirtualCamera = !requireNonNull(getCamera()).getHasTransform(); 1365 if (mImagePipeline != null) { 1366 checkState(isVirtualCamera); 1367 // On LEGACY devices, when the app is backgrounded, it will trigger StreamSharing's 1368 // SessionConfig error callback and recreate children pipeline. 1369 mImagePipeline.close(); 1370 } 1371 1372 Set<Integer> supportedOutputFormats = getImageCaptureCapabilities( 1373 getCamera().getCameraInfo()).getSupportedOutputFormats(); 1374 Preconditions.checkArgument(supportedOutputFormats.contains(getOutputFormat()), 1375 "The specified output format (" + getOutputFormat() 1376 + ") is not supported by current configuration. Supported output formats: " 1377 + supportedOutputFormats); 1378 1379 PostviewSettings postviewSettings = isPostviewEnabled() ? calculatePostviewSettings( 1380 resolution) : null; 1381 1382 CameraCharacteristics cameraCharacteristics = null; 1383 if (getCamera() != null) { 1384 try { 1385 Object obj = getCamera().getCameraInfoInternal().getCameraCharacteristics(); 1386 if (obj instanceof CameraCharacteristics) { 1387 cameraCharacteristics = (CameraCharacteristics) obj; 1388 } 1389 } catch (Exception e) { 1390 Log.e(TAG, "getCameraCharacteristics failed", e); 1391 } 1392 } 1393 1394 mImagePipeline = new ImagePipeline(config, resolution, cameraCharacteristics, getEffect(), 1395 isVirtualCamera, postviewSettings); 1396 1397 if (mTakePictureManager == null) { 1398 // mTakePictureManager is reused when the Surface is reset. 1399 mTakePictureManager = getCurrentConfig().getTakePictureManagerProvider().newInstance( 1400 mImageCaptureControl); 1401 } 1402 mTakePictureManager.setImagePipeline(mImagePipeline); 1403 1404 SessionConfig.Builder sessionConfigBuilder = 1405 mImagePipeline.createSessionConfigBuilder(streamSpec.getResolution()); 1406 sessionConfigBuilder.setSessionType(streamSpec.getSessionType()); 1407 if (Build.VERSION.SDK_INT >= 23 1408 && getCaptureMode() == CAPTURE_MODE_ZERO_SHUTTER_LAG 1409 && !streamSpec.getZslDisabled()) { 1410 getCameraControl().addZslConfig(sessionConfigBuilder); 1411 } 1412 if (streamSpec.getImplementationOptions() != null) { 1413 sessionConfigBuilder.addImplementationOptions(streamSpec.getImplementationOptions()); 1414 } 1415 // Close the old error listener 1416 if (mCloseableErrorListener != null) { 1417 mCloseableErrorListener.close(); 1418 } 1419 mCloseableErrorListener = new SessionConfig.CloseableErrorListener( 1420 (sessionConfig, error) -> { 1421 // Do nothing when the use case has been unbound. 1422 if (getCamera() == null) { 1423 return; 1424 } 1425 1426 mTakePictureManager.pause(); 1427 clearPipeline(/*keepTakePictureManager=*/ true); 1428 mSessionConfigBuilder = createPipeline(getCameraId(), 1429 (ImageCaptureConfig) getCurrentConfig(), 1430 Preconditions.checkNotNull(getAttachedStreamSpec())); 1431 updateSessionConfig(List.of(mSessionConfigBuilder.build())); 1432 notifyReset(); 1433 mTakePictureManager.resume(); 1434 }); 1435 sessionConfigBuilder.setErrorListener(mCloseableErrorListener); 1436 return sessionConfigBuilder; 1437 } 1438 1439 /** 1440 * Calculates the best format and size to generate the postview output bitmap according to the 1441 * info retrieved from the use case config. 1442 * 1443 * @param targetResolution the target resolution of the still image 1444 * @return the settings for the postview, or <code>null</code> if no supported format or 1445 * output size can be found. 1446 */ calculatePostviewSettings(@onNull Size targetResolution)1447 private @Nullable PostviewSettings calculatePostviewSettings(@NonNull Size targetResolution) { 1448 SessionProcessor sessionProcessor = getSessionProcessor(); 1449 1450 // No session processor can be found which is necessary for supporting postview 1451 if (sessionProcessor == null) { 1452 return null; 1453 } 1454 1455 Map<Integer, List<Size>> formatSizesMap = sessionProcessor.getSupportedPostviewSize( 1456 targetResolution); 1457 1458 int format = ImageFormat.UNKNOWN; 1459 // Prefer YUV because it takes less time to decode to bitmap. 1460 if (isPostviewImageFormatSupported(formatSizesMap, ImageFormat.YUV_420_888)) { 1461 format = ImageFormat.YUV_420_888; 1462 } else if (isPostviewImageFormatSupported(formatSizesMap, ImageFormat.JPEG)) { 1463 format = ImageFormat.JPEG; 1464 } else if (isPostviewImageFormatSupported(formatSizesMap, ImageFormat.JPEG_R)) { 1465 format = ImageFormat.JPEG_R; 1466 } 1467 1468 // No supported postview image format can be found 1469 if (format == ImageFormat.UNKNOWN) { 1470 return null; 1471 } 1472 1473 List<Size> sizes = formatSizesMap.get(format); 1474 ResolutionSelector postviewSizeSelector = getCurrentConfig().retrieveOption( 1475 OPTION_POSTVIEW_RESOLUTION_SELECTOR, null); 1476 1477 if (postviewSizeSelector != null) { 1478 Collections.sort(sizes, new CompareSizesByArea(true)); 1479 CameraInternal camera = getCamera(); 1480 Rect sensorRect = camera.getCameraControlInternal().getSensorRect(); 1481 CameraInfoInternal cameraInfo = camera.getCameraInfoInternal(); 1482 Rational fullFov = new Rational(sensorRect.width(), sensorRect.height()); 1483 List<Size> result = 1484 SupportedOutputSizesSorter.sortSupportedOutputSizesByResolutionSelector( 1485 postviewSizeSelector, sizes, null, getTargetRotation(), fullFov, 1486 cameraInfo.getSensorRotationDegrees(), cameraInfo.getLensFacing()); 1487 1488 if (result.isEmpty()) { 1489 throw new IllegalArgumentException( 1490 "The postview ResolutionSelector cannot select a valid size for the " 1491 + "postview."); 1492 } 1493 return PostviewSettings.create(result.get(0), format); 1494 } else { 1495 return PostviewSettings.create(Collections.max(sizes, new CompareSizesByArea()), 1496 format); 1497 } 1498 } 1499 isPostviewImageFormatSupported(@onNull Map<Integer, List<Size>> formatSizesMap, int format)1500 private boolean isPostviewImageFormatSupported(@NonNull Map<Integer, List<Size>> formatSizesMap, 1501 int format) { 1502 return formatSizesMap.containsKey(format) && !formatSizesMap.get(format).isEmpty(); 1503 } 1504 1505 /** 1506 * Takes a picture with the new architecture. 1507 * 1508 * @throws IllegalArgumentException If {@link ImageCapture#FLASH_MODE_SCREEN} is used without a 1509 * non-null {@code ScreenFlash} instance set. 1510 */ 1511 @MainThread takePictureInternal(@onNull Executor executor, @Nullable OnImageCapturedCallback inMemoryCallback, @Nullable OnImageSavedCallback onDiskCallback, @Nullable OutputFileOptions outputFileOptions, @Nullable OutputFileOptions secondaryOutputFileOptions)1512 private void takePictureInternal(@NonNull Executor executor, 1513 @Nullable OnImageCapturedCallback inMemoryCallback, 1514 @Nullable OnImageSavedCallback onDiskCallback, 1515 @Nullable OutputFileOptions outputFileOptions, 1516 @Nullable OutputFileOptions secondaryOutputFileOptions) { 1517 checkMainThread(); 1518 if (getFlashMode() == ImageCapture.FLASH_MODE_SCREEN 1519 && mScreenFlashWrapper.getBaseScreenFlash() == null) { 1520 throw new IllegalArgumentException("ScreenFlash not set for FLASH_MODE_SCREEN"); 1521 } 1522 Log.d(TAG, "takePictureInternal"); 1523 CameraInternal camera = getCamera(); 1524 if (camera == null) { 1525 sendInvalidCameraError(executor, inMemoryCallback, onDiskCallback); 1526 return; 1527 } 1528 boolean isSimultaneousCapture = getCurrentConfig() 1529 .getSecondaryInputFormat() != ImageFormat.UNKNOWN; 1530 if (isSimultaneousCapture && secondaryOutputFileOptions == null) { 1531 throw new IllegalArgumentException( 1532 "Simultaneous capture RAW and JPEG needs two output file options"); 1533 } 1534 if (!isSimultaneousCapture && secondaryOutputFileOptions != null) { 1535 throw new IllegalArgumentException( 1536 "Non simultaneous capture cannot have two output file options"); 1537 } 1538 requireNonNull(mTakePictureManager).offerRequest(TakePictureRequest.of( 1539 executor, 1540 inMemoryCallback, 1541 onDiskCallback, 1542 outputFileOptions, 1543 secondaryOutputFileOptions, 1544 getTakePictureCropRect(), 1545 getSensorToBufferTransformMatrix(), 1546 getRelativeRotation(camera), 1547 getJpegQualityInternal(), 1548 getCaptureMode(), 1549 isSimultaneousCapture, 1550 mSessionConfigBuilder.getSingleCameraCaptureCallbacks())); 1551 } 1552 sendInvalidCameraError(@onNull Executor executor, @Nullable OnImageCapturedCallback inMemoryCallback, ImageCapture.@Nullable OnImageSavedCallback onDiskCallback)1553 private void sendInvalidCameraError(@NonNull Executor executor, 1554 @Nullable OnImageCapturedCallback inMemoryCallback, 1555 ImageCapture.@Nullable OnImageSavedCallback onDiskCallback) { 1556 ImageCaptureException exception = new ImageCaptureException(ERROR_INVALID_CAMERA, 1557 "Not bound to a valid Camera [" + ImageCapture.this + "]", null); 1558 if (inMemoryCallback != null) { 1559 inMemoryCallback.onError(exception); 1560 } else if (onDiskCallback != null) { 1561 onDiskCallback.onError(exception); 1562 } else { 1563 throw new IllegalArgumentException("Must have either in-memory or on-disk callback."); 1564 } 1565 } 1566 1567 /** 1568 * Calculates a snapshot of crop rect when app calls {@link #takePicture}. 1569 */ getTakePictureCropRect()1570 private @NonNull Rect getTakePictureCropRect() { 1571 Rect rect = getViewPortCropRect(); 1572 Size resolution = requireNonNull(getAttachedSurfaceResolution()); 1573 if (rect != null) { 1574 return rect; 1575 } else if (isAspectRatioValid(mCropAspectRatio)) { 1576 int rotationDegrees = getRelativeRotation(requireNonNull(getCamera())); 1577 Rational rotatedAspectRatio = new Rational( 1578 /* numerator= */ mCropAspectRatio.getDenominator(), 1579 /* denominator= */ mCropAspectRatio.getNumerator()); 1580 Rational sensorCropRatio = is90or270(rotationDegrees) 1581 ? rotatedAspectRatio : mCropAspectRatio; 1582 return requireNonNull(computeCropRectFromAspectRatio(resolution, sensorCropRatio)); 1583 } 1584 return new Rect(0, 0, resolution.getWidth(), resolution.getHeight()); 1585 } 1586 1587 /** 1588 * Clears the pipeline without keeping the {@link TakePictureManager}. 1589 */ 1590 @MainThread clearPipeline()1591 private void clearPipeline() { 1592 clearPipeline(/*keepTakePictureManager=*/false); 1593 } 1594 1595 /** 1596 * Clears the pipeline. 1597 */ 1598 @MainThread clearPipeline(boolean keepTakePictureManager)1599 private void clearPipeline(boolean keepTakePictureManager) { 1600 Log.d(TAG, "clearPipeline"); 1601 checkMainThread(); 1602 1603 // Close the old error listener 1604 if (mCloseableErrorListener != null) { 1605 mCloseableErrorListener.close(); 1606 mCloseableErrorListener = null; 1607 } 1608 1609 if (mImagePipeline != null) { 1610 mImagePipeline.close(); 1611 mImagePipeline = null; 1612 } 1613 // TODO: no need to abort requests when UseCase unbinds. Clean this up when the old 1614 // pipeline is removed. 1615 if (!keepTakePictureManager && mTakePictureManager != null) { 1616 mTakePictureManager.abortRequests(); 1617 mTakePictureManager = null; 1618 } 1619 // Always clear ZSL resources to release the RingBuffer. 1620 getCameraControl().clearZslConfig(); 1621 } 1622 1623 /** 1624 * Submits still capture requests with the current configurations. 1625 */ 1626 @MainThread submitStillCaptureRequest( @onNull List<CaptureConfig> captureConfigs)1627 ListenableFuture<Void> submitStillCaptureRequest( 1628 @NonNull List<CaptureConfig> captureConfigs) { 1629 checkMainThread(); 1630 return Futures.transform(getCameraControl().submitStillCaptureRequests( 1631 captureConfigs, mCaptureMode, mFlashType), 1632 input -> null, CameraXExecutors.directExecutor()); 1633 } 1634 1635 @VisibleForTesting isProcessingPipelineEnabled()1636 boolean isProcessingPipelineEnabled() { 1637 return mImagePipeline != null && mTakePictureManager != null; 1638 } 1639 1640 @VisibleForTesting getImagePipeline()1641 @Nullable ImagePipeline getImagePipeline() { 1642 return mImagePipeline; 1643 } 1644 1645 @VisibleForTesting getTakePictureManager()1646 @NonNull TakePictureManager getTakePictureManager() { 1647 return requireNonNull(mTakePictureManager); 1648 } 1649 1650 /** 1651 * @inheritDoc 1652 */ 1653 @RestrictTo(Scope.LIBRARY_GROUP) 1654 @Override getSupportedEffectTargets()1655 public @NonNull Set<Integer> getSupportedEffectTargets() { 1656 Set<Integer> targets = new HashSet<>(); 1657 targets.add(IMAGE_CAPTURE); 1658 return targets; 1659 } 1660 1661 /** 1662 * Returns an estimate of the capture and processing sequence duration based on the current 1663 * camera configuration and scene conditions. The value will vary as the scene and/or camera 1664 * configuration change. 1665 * 1666 * <p>The processing estimate can vary based on device processing load. 1667 * 1668 * <p>If this method returns 1669 * {@link ImageCaptureLatencyEstimate#UNDEFINED_IMAGE_CAPTURE_LATENCY}, it means that the 1670 * camera HAL doesn't provide latency information. In this case, this method will 1671 * consistently return {@link ImageCaptureLatencyEstimate#UNDEFINED_IMAGE_CAPTURE_LATENCY} 1672 * for the current camera configuration, as long as the UseCase and camera configuration 1673 * remain unchanged (e.g., extensions mode and camera settings are kept the same). 1674 */ getRealtimeCaptureLatencyEstimate()1675 public @NonNull ImageCaptureLatencyEstimate getRealtimeCaptureLatencyEstimate() { 1676 final CameraInternal camera = getCamera(); 1677 if (camera == null) { 1678 return ImageCaptureLatencyEstimate.UNDEFINED_IMAGE_CAPTURE_LATENCY; 1679 } 1680 1681 final CameraConfig config = camera.getExtendedConfig(); 1682 final SessionProcessor sessionProcessor = config.getSessionProcessor(null); 1683 final Pair<Long, Long> latencyEstimate = 1684 sessionProcessor != null ? sessionProcessor.getRealtimeCaptureLatency() : null; 1685 if (latencyEstimate == null) { 1686 return ImageCaptureLatencyEstimate.UNDEFINED_IMAGE_CAPTURE_LATENCY; 1687 } 1688 return new ImageCaptureLatencyEstimate(latencyEstimate.first, latencyEstimate.second); 1689 } 1690 1691 /** 1692 * Returns if postview is enabled or not. 1693 * 1694 * @see Builder#setPostviewEnabled(boolean) 1695 */ isPostviewEnabled()1696 public boolean isPostviewEnabled() { 1697 return getCurrentConfig().retrieveOption(OPTION_POSTVIEW_ENABLED, false); 1698 } 1699 1700 /** 1701 * Returns the {@link ResolutionSelector} used to select the postview size. 1702 * 1703 * @see Builder#setPostviewResolutionSelector(ResolutionSelector) 1704 */ getPostviewResolutionSelector()1705 public @Nullable ResolutionSelector getPostviewResolutionSelector() { 1706 return getCurrentConfig().retrieveOption(OPTION_POSTVIEW_RESOLUTION_SELECTOR, 1707 null); 1708 } 1709 1710 /** 1711 * Describes the error that occurred during an image capture operation (such as {@link 1712 * ImageCapture#takePicture(Executor, OnImageCapturedCallback)}). 1713 * 1714 * <p>This is a parameter sent to the error callback functions set in listeners such as {@link 1715 * ImageCapture.OnImageSavedCallback#onError(ImageCaptureException)}. 1716 */ 1717 @IntDef({ERROR_UNKNOWN, ERROR_FILE_IO, ERROR_CAPTURE_FAILED, ERROR_CAMERA_CLOSED, 1718 ERROR_INVALID_CAMERA}) 1719 @Retention(RetentionPolicy.SOURCE) 1720 @RestrictTo(Scope.LIBRARY_GROUP) 1721 public @interface ImageCaptureError { 1722 } 1723 1724 /** 1725 * Capture mode options for ImageCapture. A picture will always be taken regardless of 1726 * mode, and the mode will be used on devices that support it. 1727 */ 1728 @OptIn(markerClass = androidx.camera.core.ExperimentalZeroShutterLag.class) 1729 @IntDef({CAPTURE_MODE_MAXIMIZE_QUALITY, CAPTURE_MODE_MINIMIZE_LATENCY, 1730 CAPTURE_MODE_ZERO_SHUTTER_LAG}) 1731 @Retention(RetentionPolicy.SOURCE) 1732 @RestrictTo(Scope.LIBRARY_GROUP) 1733 public @interface CaptureMode { 1734 } 1735 1736 /** 1737 * The flash mode options when taking a picture using ImageCapture. 1738 * 1739 * <p>Applications can check if there is a flash unit via {@link CameraInfo#hasFlashUnit()} and 1740 * update UI component if necessary. If there is no flash unit, then the FlashMode set to 1741 * {@link #setFlashMode(int)} will take no effect for the subsequent photo capture requests 1742 * and they will act like {@link #FLASH_MODE_OFF}. 1743 * 1744 * <p>When the torch is enabled via {@link CameraControl#enableTorch(boolean)}, the torch 1745 * will remain enabled during photo capture regardless of flash mode setting. When 1746 * the torch is disabled, flash will function as specified by 1747 * {@link #setFlashMode(int)}. 1748 */ 1749 @IntDef({FLASH_MODE_UNKNOWN, FLASH_MODE_AUTO, FLASH_MODE_ON, FLASH_MODE_SCREEN, FLASH_MODE_OFF}) 1750 @Retention(RetentionPolicy.SOURCE) 1751 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 1752 public @interface FlashMode { 1753 } 1754 1755 /** 1756 * The flash type options when flash is required for taking a picture. 1757 */ 1758 @IntDef({FLASH_TYPE_ONE_SHOT_FLASH, FLASH_TYPE_USE_TORCH_AS_FLASH}) 1759 @Retention(RetentionPolicy.SOURCE) 1760 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 1761 public @interface FlashType { 1762 } 1763 1764 /** 1765 * The output format of the captured image. 1766 */ 1767 @Target({ElementType.TYPE_USE}) 1768 @IntDef({OUTPUT_FORMAT_JPEG, OUTPUT_FORMAT_JPEG_ULTRA_HDR, 1769 OUTPUT_FORMAT_RAW, OUTPUT_FORMAT_RAW_JPEG}) 1770 @Retention(RetentionPolicy.SOURCE) 1771 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 1772 public @interface OutputFormat { 1773 } 1774 1775 /** Listener containing callbacks for image file I/O events. */ 1776 public interface OnImageSavedCallback { 1777 /** 1778 * Called when the capture is started. 1779 * 1780 * <p>This callback is guaranteed to be called once and before 1781 * {@link #onImageSaved(OutputFileResults)} for the same invocation of 1782 * {@link #takePicture(OutputFileOptions, Executor, OnImageSavedCallback)}. 1783 * 1784 * <p>It's recommended to play shutter sound or trigger UI indicators of 1785 * capture when receiving this callback. 1786 */ onCaptureStarted()1787 default void onCaptureStarted() { 1788 } 1789 1790 /** Called when an image has been successfully saved. */ onImageSaved(@onNull OutputFileResults outputFileResults)1791 void onImageSaved(@NonNull OutputFileResults outputFileResults); 1792 1793 /** 1794 * Called when an error occurs while attempting to save an image. 1795 * 1796 * @param exception An {@link ImageCaptureException} that contains the type of error, the 1797 * error message and the throwable that caused it. 1798 */ onError(@onNull ImageCaptureException exception)1799 void onError(@NonNull ImageCaptureException exception); 1800 1801 /** 1802 * Callback to report the progress of the capture's processing. 1803 * 1804 * <p>To know in advanced if this callback will be invoked or not, check the 1805 * capabilities by {@link #getImageCaptureCapabilities(CameraInfo)} and 1806 * {@link ImageCaptureCapabilities#isCaptureProcessProgressSupported()}. If supported, 1807 * this callback will be called multiple times with monotonically increasing 1808 * values. At the minimum the callback will be called once with value 100 to 1809 * indicate the processing is finished. This callback will always be called before 1810 * {@link #onImageSaved(OutputFileResults)}. 1811 * 1812 * @param progress the progress ranging from 0 to 100. 1813 */ onCaptureProcessProgressed(int progress)1814 default void onCaptureProcessProgressed(int progress) { 1815 } 1816 1817 /** 1818 * Callback to notify that the postview bitmap is available. The postview is intended to be 1819 * shown on UI before the long-processing capture is completed in order to provide a 1820 * better UX. 1821 * 1822 * <p>The postview is only available when the 1823 * {@link ImageCaptureCapabilities#isPostviewSupported()} returns true for the specified 1824 * {@link CameraInfo} and applications must explicitly enable the postview using the 1825 * {@link Builder#setPostviewEnabled(boolean)}. This callback will be called before 1826 * {@link #onImageSaved(OutputFileResults)}. But if something goes wrong when processing 1827 * the postview, this callback method could be skipped. 1828 * 1829 * <p>The bitmap is rotated according to the target rotation set to the {@link ImageCapture} 1830 * to make it upright. If target rotation is not set, the display rotation is used. 1831 * 1832 * <p>See also {@link ImageCapture.Builder#setTargetRotation(int)} and 1833 * {@link #setTargetRotation(int)}. 1834 * 1835 * @param bitmap the postview bitmap. 1836 */ onPostviewBitmapAvailable(@onNull Bitmap bitmap)1837 default void onPostviewBitmapAvailable(@NonNull Bitmap bitmap) { 1838 } 1839 } 1840 1841 /** 1842 * Callback for image capture events. 1843 */ 1844 public abstract static class OnImageCapturedCallback { 1845 /** 1846 * Callback for when the camera has started exposing a frame. 1847 * 1848 * <p>This callback is guaranteed to be called once and before 1849 * {@link #onCaptureSuccess(ImageProxy)} for the same invocation of 1850 * {@link #takePicture(Executor, OnImageCapturedCallback)}. 1851 * 1852 * <p>It's recommended to play shutter sound or trigger UI indicators of 1853 * capture when receiving this callback. 1854 */ onCaptureStarted()1855 public void onCaptureStarted() { 1856 } 1857 1858 /** 1859 * Callback for when the image has been captured. 1860 * 1861 * <p>The application is responsible for calling {@link ImageProxy#close()} to close the 1862 * image. 1863 * 1864 * <p>The image is of format {@link ImageFormat#JPEG} or {@link ImageFormat#JPEG_R}, 1865 * queryable via {@link ImageProxy#getFormat()}. 1866 * 1867 * <p>The image is provided as captured by the underlying {@link ImageReader} without 1868 * rotation applied. The value in {@code image.getImageInfo().getRotationDegrees()} 1869 * describes the magnitude of clockwise rotation, which if applied to the image will make 1870 * it match the currently configured target rotation. 1871 * 1872 * <p>For example, if the current target rotation is set to the display rotation, 1873 * rotationDegrees is the rotation to apply to the image to match the display orientation. 1874 * A rotation of 90 degrees would mean rotating the image 90 degrees clockwise produces an 1875 * image that will match the display orientation. 1876 * 1877 * <p>See also {@link ImageCapture.Builder#setTargetRotation(int)} and 1878 * {@link #setTargetRotation(int)}. 1879 * 1880 * <p>Timestamps are in nanoseconds and monotonic and can be compared to timestamps from 1881 * images produced from UseCases bound to the same camera instance. More detail is 1882 * available depending on the implementation. For example with CameraX using a 1883 * {@link androidx.camera.camera2} implementation additional detail can be found in 1884 * {@link android.hardware.camera2.CameraDevice} documentation. 1885 * 1886 * @param image The captured image 1887 */ onCaptureSuccess(@onNull ImageProxy image)1888 public void onCaptureSuccess(@NonNull ImageProxy image) { 1889 } 1890 1891 /** 1892 * Callback for when an error occurred during image capture. 1893 * 1894 * @param exception An {@link ImageCaptureException} that contains the type of error, the 1895 * error message and the throwable that caused it. 1896 */ onError(final @NonNull ImageCaptureException exception)1897 public void onError(final @NonNull ImageCaptureException exception) { 1898 } 1899 1900 /** 1901 * Callback to report the progress of the capture's processing. 1902 * 1903 * <p>To know in advanced if this callback will be invoked or not, check the 1904 * capabilities by {@link #getImageCaptureCapabilities(CameraInfo)} and 1905 * {@link ImageCaptureCapabilities#isCaptureProcessProgressSupported()}. If supported, 1906 * this callback will be called multiple times with monotonically increasing 1907 * values. At the minimum the callback will be called once with value 100 to 1908 * indicate the processing is finished. This callback will always be called before 1909 * {@link #onCaptureSuccess(ImageProxy)}. 1910 * 1911 * @param progress the progress ranging from 0 to 100. 1912 */ onCaptureProcessProgressed(int progress)1913 public void onCaptureProcessProgressed(int progress) { 1914 } 1915 1916 /** 1917 * Callback to notify that the postview bitmap is available. The postview is intended to be 1918 * shown on UI before the long-processing capture is completed in order to provide a 1919 * better UX. 1920 * 1921 * <p>The postview is only available when the 1922 * {@link ImageCaptureCapabilities#isPostviewSupported()} returns true for the specified 1923 * {@link CameraInfo} and applications must explicitly enable the postview using the 1924 * {@link Builder#setPostviewEnabled(boolean)}. This callback will be called before 1925 * {@link #onCaptureSuccess(ImageProxy)}. But if something goes wrong when processing the 1926 * postview, this callback method could be skipped. 1927 * 1928 * <p>The bitmap is rotated according to the target rotation set to the {@link ImageCapture} 1929 * to make it upright. If target rotation is not set, the display rotation is used. 1930 * 1931 * <p>See also {@link ImageCapture.Builder#setTargetRotation(int)} and 1932 * {@link #setTargetRotation(int)}. 1933 * 1934 * @param bitmap the postview bitmap. 1935 1936 */ onPostviewBitmapAvailable(@onNull Bitmap bitmap)1937 public void onPostviewBitmapAvailable(@NonNull Bitmap bitmap) { 1938 } 1939 } 1940 1941 /** 1942 * Callback listener for discovering when the application has completed its changes for a 1943 * screen flash image capture. 1944 * 1945 * <p> For example, an application may change its UI to a full white screen with maximum 1946 * brightness for a proper screen flash capture. 1947 * 1948 * @see ScreenFlash#apply(long, ScreenFlashListener) 1949 */ 1950 public interface ScreenFlashListener { 1951 /** 1952 * Invoked by the application when it has completed its changes due to a screen flash 1953 * image capture. 1954 * 1955 * @see ScreenFlash#apply 1956 */ onCompleted()1957 void onCompleted(); 1958 } 1959 1960 /** 1961 * Interface to do the application changes required for screen flash operations. 1962 * 1963 * <p> Each {@link #apply} invocation will be followed up with a corresponding {@link #clear} 1964 * invocation. For each image capture, {@code #apply} and {@code #clear} will be invoked only 1965 * once. 1966 */ 1967 public interface ScreenFlash { 1968 /** 1969 * Applies the necessary application changes for a screen flash photo capture. 1970 * 1971 * <p>When the application UI needs to be changed for a successful photo capture with 1972 * screen flash feature, CameraX will invoke this method and wait for the application to 1973 * complete its changes. When this API is invoked, the application UI should utilize the 1974 * screen to provide extra light as an alternative to physical flash. For example, the 1975 * screen brightness can be maximized and screen color can be covered with some bright 1976 * color like white. 1977 * 1978 * <p>The parameter {@code expirationTimeMillis} is based on 1979 * {@link System#currentTimeMillis()}. It is at least 3 seconds later from the start of a 1980 * screen flash image capture operation. Until the timestamp of {@code expirationTimeMillis} 1981 * parameter, CameraX will wait for the application to notify the completion of the 1982 * application-side changes using the {@link ScreenFlashListener} parameter of this 1983 * method. Applications must call {@link ScreenFlashListener#onCompleted()} after their 1984 * UI changes are done so that CameraX is not unnecessarily waiting. If the application 1985 * does not call {@code ScreenFlashListener#onCompleted} before {@code 1986 * expirationTimeMillis}, CameraX will stop waiting and move forward with the subsequent 1987 * operations regardless. In such case, the application no longer needs to call {@code 1988 * ScreenFlashListener#onCompleted()}. If {@link #clear} has also been invoked while the 1989 * application is still doing the changes, it is the application's responsibility to 1990 * clear any UI change done after {@link #clear} has been invoked. 1991 * 1992 * <p>The following code snippet shows an example implementation of this API. 1993 * <pre>{@code 1994 * @Override 1995 * public void apply(long expirationTimeMillis, 1996 * @NonNull ScreenFlashListener screenFlashListener) { 1997 * // Enable top overlay to make screen color white 1998 * whiteColorOverlay.setVisible(true); 1999 * // Maximize screen brightness 2000 * maximizeScreenBrightness(); 2001 * screenFlashListener.onCompleted(); 2002 * }}</pre> 2003 * 2004 * @param expirationTimeMillis The timestamp after which CameraX will no longer listen 2005 * to {@code screenFlashListener}. 2006 * @param screenFlashListener Used to notify when UI changes have been applied. 2007 */ 2008 // ExecutorRegistration lint suppressed since this is called by app and CameraX supports 2009 // receiving the call on any thread. Adding executor will make it harder for apps. 2010 @SuppressWarnings("ExecutorRegistration") 2011 @UiThread apply(long expirationTimeMillis, @NonNull ScreenFlashListener screenFlashListener)2012 void apply(long expirationTimeMillis, @NonNull ScreenFlashListener screenFlashListener); 2013 2014 /** 2015 * Clears any application change done for screen flash operation, if required. 2016 * 2017 * <p>CameraX will invoke this method when a screen flash photo capture has been completed 2018 * and the application screen can be safely changed to a state not conforming to screen 2019 * flash photo capture. 2020 */ 2021 @UiThread clear()2022 void clear(); 2023 } 2024 2025 /** 2026 * Provides a base static default configuration for the ImageCapture 2027 * 2028 * <p>These values may be overridden by the implementation. They only provide a minimum set of 2029 * defaults that are implementation independent. 2030 */ 2031 @RestrictTo(Scope.LIBRARY_GROUP) 2032 public static final class Defaults 2033 implements ConfigProvider<ImageCaptureConfig> { 2034 private static final int DEFAULT_SURFACE_OCCUPANCY_PRIORITY = 4; 2035 private static final int DEFAULT_ASPECT_RATIO = AspectRatio.RATIO_4_3; 2036 private static final int DEFAULT_OUTPUT_FORMAT = OUTPUT_FORMAT_JPEG; 2037 2038 private static final ResolutionSelector DEFAULT_RESOLUTION_SELECTOR = 2039 new ResolutionSelector.Builder().setAspectRatioStrategy( 2040 AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY).setResolutionStrategy( 2041 ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY).build(); 2042 2043 private static final ImageCaptureConfig DEFAULT_CONFIG; 2044 // ImageCapture does not yet support HDR so we must default to SDR. This ensures it won't 2045 // choose an HDR format when other use cases have selected HDR. 2046 private static final DynamicRange DEFAULT_DYNAMIC_RANGE = DynamicRange.SDR; 2047 2048 static { 2049 Builder builder = new Builder() 2050 .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY) 2051 .setTargetAspectRatio(DEFAULT_ASPECT_RATIO) 2052 .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR) 2053 .setOutputFormat(DEFAULT_OUTPUT_FORMAT) 2054 .setDynamicRange(DEFAULT_DYNAMIC_RANGE); 2055 2056 DEFAULT_CONFIG = builder.getUseCaseConfig(); 2057 } 2058 2059 @Override getConfig()2060 public @NonNull ImageCaptureConfig getConfig() { 2061 return DEFAULT_CONFIG; 2062 } 2063 } 2064 2065 /** 2066 * Options for saving newly captured image. 2067 * 2068 * <p> this class is used to configure save location and metadata. Save location can be 2069 * either a {@link File}, {@link MediaStore} or a {@link OutputStream}. The metadata will be 2070 * stored with the saved image. For JPEG this will be included in the EXIF. 2071 */ 2072 public static final class OutputFileOptions { 2073 2074 private final @Nullable File mFile; 2075 private final @Nullable ContentResolver mContentResolver; 2076 private final @Nullable Uri mSaveCollection; 2077 private final @Nullable ContentValues mContentValues; 2078 private final @Nullable OutputStream mOutputStream; 2079 private final @NonNull Metadata mMetadata; 2080 OutputFileOptions(@ullable File file, @Nullable ContentResolver contentResolver, @Nullable Uri saveCollection, @Nullable ContentValues contentValues, @Nullable OutputStream outputStream, @Nullable Metadata metadata)2081 OutputFileOptions(@Nullable File file, 2082 @Nullable ContentResolver contentResolver, 2083 @Nullable Uri saveCollection, 2084 @Nullable ContentValues contentValues, 2085 @Nullable OutputStream outputStream, 2086 @Nullable Metadata metadata) { 2087 mFile = file; 2088 mContentResolver = contentResolver; 2089 mSaveCollection = saveCollection; 2090 mContentValues = contentValues; 2091 mOutputStream = outputStream; 2092 mMetadata = metadata == null ? new Metadata() : metadata; 2093 } 2094 2095 /** 2096 * 2097 */ 2098 @RestrictTo(Scope.LIBRARY_GROUP) getFile()2099 public @Nullable File getFile() { 2100 return mFile; 2101 } 2102 2103 /** 2104 * 2105 */ 2106 @RestrictTo(Scope.LIBRARY_GROUP) getContentResolver()2107 public @Nullable ContentResolver getContentResolver() { 2108 return mContentResolver; 2109 } 2110 2111 /** 2112 * 2113 */ 2114 @RestrictTo(Scope.LIBRARY_GROUP) getSaveCollection()2115 public @Nullable Uri getSaveCollection() { 2116 return mSaveCollection; 2117 } 2118 2119 /** 2120 * 2121 */ 2122 @RestrictTo(Scope.LIBRARY_GROUP) getContentValues()2123 public @Nullable ContentValues getContentValues() { 2124 return mContentValues; 2125 } 2126 2127 /** 2128 * 2129 */ 2130 @RestrictTo(Scope.LIBRARY_GROUP) getOutputStream()2131 public @Nullable OutputStream getOutputStream() { 2132 return mOutputStream; 2133 } 2134 2135 /** 2136 * Exposed internally so that CameraController can overwrite the flip horizontal flag for 2137 * front camera. External core API users shouldn't need this because they are the ones who 2138 * created the {@link Metadata}. 2139 */ 2140 @RestrictTo(Scope.LIBRARY_GROUP) getMetadata()2141 public @NonNull Metadata getMetadata() { 2142 return mMetadata; 2143 } 2144 2145 @Override toString()2146 public @NonNull String toString() { 2147 return "OutputFileOptions{" 2148 + "mFile=" + mFile + ", " 2149 + "mContentResolver=" + mContentResolver + ", " 2150 + "mSaveCollection=" + mSaveCollection + ", " 2151 + "mContentValues=" + mContentValues + ", " 2152 + "mOutputStream=" + mOutputStream + ", " 2153 + "mMetadata=" + mMetadata 2154 + "}"; 2155 } 2156 2157 /** 2158 * Builder class for {@link OutputFileOptions}. 2159 */ 2160 public static final class Builder { 2161 private @Nullable File mFile; 2162 private @Nullable ContentResolver mContentResolver; 2163 private @Nullable Uri mSaveCollection; 2164 private @Nullable ContentValues mContentValues; 2165 private @Nullable OutputStream mOutputStream; 2166 private @Nullable Metadata mMetadata; 2167 2168 /** 2169 * Creates options to write captured image to a {@link File}. 2170 * 2171 * @param file save location of the image. 2172 */ Builder(@onNull File file)2173 public Builder(@NonNull File file) { 2174 mFile = file; 2175 } 2176 2177 /** 2178 * Creates options to write captured image to {@link MediaStore}. 2179 * 2180 * Example: 2181 * 2182 * <pre>{@code 2183 * 2184 * ContentValues contentValues = new ContentValues(); 2185 * contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "NEW_IMAGE"); 2186 * contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg"); 2187 * 2188 * ImageCapture.OutputFileOptions options = new ImageCapture.OutputFileOptions.Builder( 2189 * getContentResolver(), 2190 * MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 2191 * contentValues).build(); 2192 * 2193 * }</pre> 2194 * 2195 * @param contentResolver to access {@link MediaStore} 2196 * @param saveCollection The URL of the table to insert into. 2197 * @param contentValues to be included in the created image file. 2198 */ Builder(@onNull ContentResolver contentResolver, @NonNull Uri saveCollection, @NonNull ContentValues contentValues)2199 public Builder(@NonNull ContentResolver contentResolver, 2200 @NonNull Uri saveCollection, 2201 @NonNull ContentValues contentValues) { 2202 mContentResolver = contentResolver; 2203 mSaveCollection = saveCollection; 2204 mContentValues = contentValues; 2205 } 2206 2207 /** 2208 * Creates options that write captured image to a {@link OutputStream}. 2209 * 2210 * @param outputStream save location of the image. 2211 */ Builder(@onNull OutputStream outputStream)2212 public Builder(@NonNull OutputStream outputStream) { 2213 mOutputStream = outputStream; 2214 } 2215 2216 /** 2217 * Sets the metadata to be stored with the saved image. 2218 * 2219 * <p> For JPEG this will be included in the EXIF. 2220 * 2221 * @param metadata Metadata to be stored with the saved image. For JPEG this will 2222 * be included in the EXIF. 2223 */ setMetadata(@onNull Metadata metadata)2224 public @NonNull Builder setMetadata(@NonNull Metadata metadata) { 2225 mMetadata = metadata; 2226 return this; 2227 } 2228 2229 /** 2230 * Builds {@link OutputFileOptions}. 2231 */ build()2232 public @NonNull OutputFileOptions build() { 2233 return new OutputFileOptions(mFile, mContentResolver, mSaveCollection, 2234 mContentValues, mOutputStream, mMetadata); 2235 } 2236 } 2237 } 2238 2239 /** 2240 * Info about the saved image file. 2241 */ 2242 public static class OutputFileResults { 2243 private final @Nullable Uri mSavedUri; 2244 2245 private final int mImageFormat; 2246 2247 @RestrictTo(Scope.LIBRARY_GROUP) OutputFileResults(@ullable Uri savedUri)2248 public OutputFileResults(@Nullable Uri savedUri) { 2249 this(savedUri, JPEG); 2250 } 2251 2252 @RestrictTo(Scope.LIBRARY_GROUP) OutputFileResults(@ullable Uri savedUri, int imageFormat)2253 public OutputFileResults(@Nullable Uri savedUri, int imageFormat) { 2254 mSavedUri = savedUri; 2255 mImageFormat = imageFormat; 2256 } 2257 2258 /** 2259 * Returns the {@link Uri} of the saved file. 2260 * 2261 * <p> Returns null if the {@link OutputFileOptions} is constructed with 2262 * {@link androidx.camera.core.ImageCapture.OutputFileOptions.Builder 2263 * #Builder(OutputStream)}. 2264 */ getSavedUri()2265 public @Nullable Uri getSavedUri() { 2266 return mSavedUri; 2267 } 2268 2269 /** 2270 * Returns the {@link ImageFormat} of the saved file. 2271 */ getImageFormat()2272 public int getImageFormat() { 2273 return mImageFormat; 2274 } 2275 } 2276 2277 /** Holder class for metadata that will be saved with captured images. */ 2278 public static final class Metadata { 2279 /** 2280 * Indicates a left-right mirroring (reflection). 2281 * 2282 * <p>The reflection is meant to be applied to the upright image (after rotation to the 2283 * target orientation). When saving the image to file, it is combined with the rotation 2284 * degrees, to generate the corresponding EXIF orientation value. 2285 */ 2286 private boolean mIsReversedHorizontal; 2287 2288 /** 2289 * Whether the mIsReversedHorizontal has been set by the app explicitly. 2290 */ 2291 private boolean mIsReversedHorizontalSet = false; 2292 2293 /** 2294 * Indicates an upside down mirroring, equivalent to a horizontal mirroring (reflection) 2295 * followed by a 180 degree rotation. 2296 * 2297 * <p>The reflection is meant to be applied to the upright image (after rotation to the 2298 * target orientation). When saving the image to file, it is combined with the rotation 2299 * degrees, to generate the corresponding EXIF orientation value. 2300 */ 2301 private boolean mIsReversedVertical; 2302 /** Data representing a geographic location. */ 2303 private @Nullable Location mLocation; 2304 2305 /** 2306 * Gets left-right mirroring of the capture. 2307 * 2308 * @return true if the capture is left-right mirrored. 2309 */ isReversedHorizontal()2310 public boolean isReversedHorizontal() { 2311 return mIsReversedHorizontal; 2312 } 2313 2314 /** 2315 * Returns true if {@link #setReversedHorizontal} has been called. 2316 * 2317 * <p> CameraController's default behavior is mirroring the picture when front camera is 2318 * used. This method is used to check if reverseHorizontal is set explicitly by the app. 2319 */ 2320 @RestrictTo(Scope.LIBRARY_GROUP) isReversedHorizontalSet()2321 public boolean isReversedHorizontalSet() { 2322 return mIsReversedHorizontalSet; 2323 } 2324 2325 /** 2326 * Sets left-right mirroring of the capture. 2327 * 2328 * @param isReversedHorizontal true if the capture is left-right mirrored. 2329 */ setReversedHorizontal(boolean isReversedHorizontal)2330 public void setReversedHorizontal(boolean isReversedHorizontal) { 2331 mIsReversedHorizontal = isReversedHorizontal; 2332 mIsReversedHorizontalSet = true; 2333 } 2334 2335 /** 2336 * Gets upside-down mirroring of the capture. 2337 * 2338 * @return true if the capture is upside-down. 2339 */ isReversedVertical()2340 public boolean isReversedVertical() { 2341 return mIsReversedVertical; 2342 } 2343 2344 /** 2345 * Sets upside-down mirroring of the capture. 2346 * 2347 * @param isReversedVertical true if the capture is upside-down. 2348 */ setReversedVertical(boolean isReversedVertical)2349 public void setReversedVertical(boolean isReversedVertical) { 2350 mIsReversedVertical = isReversedVertical; 2351 } 2352 2353 /** 2354 * Gets the geographic location of the capture. 2355 * 2356 * @return the geographic location. 2357 */ getLocation()2358 public @Nullable Location getLocation() { 2359 return mLocation; 2360 } 2361 2362 /** 2363 * Sets the geographic location of the capture. 2364 * 2365 * @param location the geographic location. 2366 */ setLocation(@ullable Location location)2367 public void setLocation(@Nullable Location location) { 2368 mLocation = location; 2369 } 2370 2371 @Override toString()2372 public @NonNull String toString() { 2373 return "Metadata{" 2374 + "mIsReversedHorizontal=" + mIsReversedHorizontal + ", " 2375 + "mIsReversedVertical=" + mIsReversedVertical + ", " 2376 + "mLocation=" + mLocation 2377 + "}"; 2378 } 2379 } 2380 2381 /** Builder for an {@link ImageCapture}. */ 2382 @SuppressWarnings({"ObjectToString", "unused", "HiddenSuperclass"}) 2383 public static final class Builder implements 2384 UseCaseConfig.Builder<ImageCapture, ImageCaptureConfig, Builder>, 2385 ImageOutputConfig.Builder<Builder>, 2386 IoConfig.Builder<Builder>, 2387 ImageInputConfig.Builder<Builder> { 2388 2389 private final MutableOptionsBundle mMutableConfig; 2390 2391 /** Creates a new Builder object. */ Builder()2392 public Builder() { 2393 this(MutableOptionsBundle.create()); 2394 } 2395 Builder(MutableOptionsBundle mutableConfig)2396 private Builder(MutableOptionsBundle mutableConfig) { 2397 mMutableConfig = mutableConfig; 2398 2399 Class<?> oldConfigClass = 2400 mutableConfig.retrieveOption(TargetConfig.OPTION_TARGET_CLASS, null); 2401 if (oldConfigClass != null && !oldConfigClass.equals(ImageCapture.class)) { 2402 throw new IllegalArgumentException( 2403 "Invalid target class configuration for " 2404 + Builder.this 2405 + ": " 2406 + oldConfigClass); 2407 } 2408 2409 setCaptureType(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE); 2410 setTargetClass(ImageCapture.class); 2411 } 2412 2413 /** 2414 * Generates a Builder from another Config object 2415 * 2416 * @param configuration An immutable configuration to pre-populate this builder. 2417 * @return The new Builder. 2418 */ 2419 @RestrictTo(Scope.LIBRARY_GROUP) fromConfig(@onNull Config configuration)2420 public static @NonNull Builder fromConfig(@NonNull Config configuration) { 2421 return new Builder(MutableOptionsBundle.from(configuration)); 2422 } 2423 2424 /** 2425 * Generates a Builder from another Config object 2426 * 2427 * @param configuration An immutable configuration to pre-populate this builder. 2428 * @return The new Builder. 2429 */ 2430 @RestrictTo(Scope.LIBRARY_GROUP) fromConfig(@onNull ImageCaptureConfig configuration)2431 static @NonNull Builder fromConfig(@NonNull ImageCaptureConfig configuration) { 2432 return new Builder(MutableOptionsBundle.from(configuration)); 2433 } 2434 2435 /** 2436 * {@inheritDoc} 2437 */ 2438 @RestrictTo(Scope.LIBRARY_GROUP) 2439 @Override getMutableConfig()2440 public @NonNull MutableConfig getMutableConfig() { 2441 return mMutableConfig; 2442 } 2443 2444 /** 2445 * {@inheritDoc} 2446 */ 2447 @RestrictTo(Scope.LIBRARY_GROUP) 2448 @Override getUseCaseConfig()2449 public @NonNull ImageCaptureConfig getUseCaseConfig() { 2450 return new ImageCaptureConfig(OptionsBundle.from(mMutableConfig)); 2451 } 2452 2453 /** 2454 * Builds an immutable {@link ImageCapture} from the current state. 2455 * 2456 * @return A {@link ImageCapture} populated with the current state. 2457 * @throws IllegalArgumentException if attempting to set both target aspect ratio and 2458 * target resolution, or attempting to set 2459 * {@link ImageCapture#FLASH_MODE_SCREEN} without 2460 * setting a non-null {@link ScreenFlash} instance. 2461 */ 2462 @Override build()2463 public @NonNull ImageCapture build() { 2464 // Update the input format base on the other options set (mainly whether processing 2465 // is done) 2466 Integer bufferFormat = getMutableConfig().retrieveOption(OPTION_BUFFER_FORMAT, null); 2467 if (bufferFormat != null) { 2468 getMutableConfig().insertOption(OPTION_INPUT_FORMAT, bufferFormat); 2469 } else { 2470 if (isOutputFormatRaw(getMutableConfig())) { 2471 getMutableConfig().insertOption(OPTION_INPUT_FORMAT, RAW_SENSOR); 2472 } else if (isOutputFormatRawJpeg(getMutableConfig())) { 2473 getMutableConfig().insertOption(OPTION_INPUT_FORMAT, RAW_SENSOR); 2474 getMutableConfig().insertOption(OPTION_SECONDARY_INPUT_FORMAT, JPEG); 2475 } else if (isOutputFormatUltraHdr(getMutableConfig())) { 2476 getMutableConfig().insertOption(OPTION_INPUT_FORMAT, JPEG_R); 2477 getMutableConfig().insertOption(OPTION_INPUT_DYNAMIC_RANGE, 2478 DynamicRange.UNSPECIFIED); 2479 } else { 2480 getMutableConfig().insertOption(OPTION_INPUT_FORMAT, JPEG); 2481 } 2482 } 2483 2484 ImageCaptureConfig imageCaptureConfig = getUseCaseConfig(); 2485 ImageOutputConfig.validateConfig(imageCaptureConfig); 2486 ImageCapture imageCapture = new ImageCapture(imageCaptureConfig); 2487 2488 // Makes the crop aspect ratio match the target resolution setting as what mentioned 2489 // in javadoc of setTargetResolution(). When the target resolution is set, {@link 2490 // ImageCapture#setCropAspectRatio(Rational)} will be automatically called to set 2491 // corresponding value. 2492 Size targetResolution = getMutableConfig().retrieveOption(OPTION_TARGET_RESOLUTION, 2493 null); 2494 if (targetResolution != null) { 2495 imageCapture.setCropAspectRatio(new Rational(targetResolution.getWidth(), 2496 targetResolution.getHeight())); 2497 } 2498 2499 checkNotNull(getMutableConfig().retrieveOption(OPTION_IO_EXECUTOR, 2500 CameraXExecutors.ioExecutor()), "The IO executor can't be null"); 2501 2502 if (getMutableConfig().containsOption(OPTION_FLASH_MODE)) { 2503 Integer flashMode = getMutableConfig().retrieveOption(OPTION_FLASH_MODE); 2504 2505 if (flashMode == null || (flashMode != FLASH_MODE_AUTO && flashMode != FLASH_MODE_ON 2506 && flashMode != FLASH_MODE_SCREEN && flashMode != FLASH_MODE_OFF)) { 2507 throw new IllegalArgumentException( 2508 "The flash mode is not allowed to set: " + flashMode); 2509 } 2510 2511 if (flashMode == FLASH_MODE_SCREEN) { 2512 if (getMutableConfig().retrieveOption(OPTION_SCREEN_FLASH, null) 2513 == null) { 2514 throw new IllegalArgumentException( 2515 "The flash mode is not allowed to set to FLASH_MODE_SCREEN " 2516 + "without setting ScreenFlash"); 2517 } 2518 } 2519 } 2520 2521 return imageCapture; 2522 } 2523 2524 /** 2525 * Sets the image capture mode. 2526 * 2527 * <p>Valid capture modes are {@link CaptureMode#CAPTURE_MODE_MINIMIZE_LATENCY}, which 2528 * prioritizes 2529 * latency over image quality, or {@link CaptureMode#CAPTURE_MODE_MAXIMIZE_QUALITY}, 2530 * which prioritizes 2531 * image quality over latency. 2532 * 2533 * <p>If not set, the capture mode will default to 2534 * {@link CaptureMode#CAPTURE_MODE_MINIMIZE_LATENCY}. 2535 * 2536 * @param captureMode The requested image capture mode. 2537 * @return The current Builder. 2538 */ setCaptureMode(@aptureMode int captureMode)2539 public @NonNull Builder setCaptureMode(@CaptureMode int captureMode) { 2540 getMutableConfig().insertOption(OPTION_IMAGE_CAPTURE_MODE, captureMode); 2541 return this; 2542 } 2543 2544 /** 2545 * Sets the flashMode. 2546 * 2547 * <p>If not set, the flash mode will default to {@link #FLASH_MODE_OFF}. 2548 * 2549 * <p>If set to {@link #FLASH_MODE_SCREEN}, a non-null {@link ScreenFlash} instance must 2550 * also be set with {@link #setScreenFlash(ScreenFlash)}. Otherwise, an 2551 * {@link IllegalArgumentException} will be thrown when {@link #build()} is invoked. 2552 * 2553 * <p>See {@link ImageCapture#setFlashMode(int)} for more information. 2554 * 2555 * @param flashMode The requested flash mode. Value is {@link #FLASH_MODE_AUTO}, 2556 * {@link #FLASH_MODE_ON}, {@link #FLASH_MODE_SCREEN}, or 2557 * {@link #FLASH_MODE_OFF}. 2558 * @return The current Builder. 2559 */ setFlashMode(@lashMode int flashMode)2560 public @NonNull Builder setFlashMode(@FlashMode int flashMode) { 2561 getMutableConfig().insertOption(OPTION_FLASH_MODE, flashMode); 2562 return this; 2563 } 2564 2565 /** 2566 * Sets the {@link ScreenFlash} instance necessary for screen flash operations. 2567 * 2568 * <p>If not set, the instance will be set to null and users will need to set it later 2569 * before calling {@link #setFlashMode(int)} with {@link #FLASH_MODE_SCREEN}. 2570 * 2571 * <p>See {@link ImageCapture#setScreenFlash(ScreenFlash)} for more 2572 * information. 2573 * 2574 * @param screenFlash The {@link ScreenFlash} to notify caller for the 2575 * UI side changes required for photo capture with 2576 * {@link #FLASH_MODE_SCREEN}. 2577 * @return The current Builder. 2578 */ setScreenFlash(@onNull ScreenFlash screenFlash)2579 public @NonNull Builder setScreenFlash(@NonNull ScreenFlash screenFlash) { 2580 getMutableConfig().insertOption(OPTION_SCREEN_FLASH, screenFlash); 2581 return this; 2582 } 2583 2584 /** 2585 * Sets the {@link ImageFormat} of the {@link ImageProxy} returned by the 2586 * {@link ImageCapture.OnImageCapturedCallback}. 2587 * 2588 * <p>Warning. This could lead to an invalid configuration as image format support is per 2589 * device. Also, setting the buffer format in conjuncture with image capture extensions will 2590 * result in an invalid configuration. In this case {@link 2591 * ImageCapture#ImageCapture(ImageCaptureConfig)} will throw an 2592 * {@link IllegalArgumentException}. 2593 * 2594 * @param bufferImageFormat The image format for captured images. 2595 * @return The current Builder. 2596 */ 2597 @RestrictTo(Scope.LIBRARY_GROUP) setBufferFormat(int bufferImageFormat)2598 public @NonNull Builder setBufferFormat(int bufferImageFormat) { 2599 getMutableConfig().insertOption(OPTION_BUFFER_FORMAT, bufferImageFormat); 2600 return this; 2601 } 2602 2603 @RestrictTo(Scope.LIBRARY_GROUP) 2604 @Override setSupportedResolutions( @onNull List<Pair<Integer, Size[]>> resolutions)2605 public @NonNull Builder setSupportedResolutions( 2606 @NonNull List<Pair<Integer, Size[]>> resolutions) { 2607 getMutableConfig().insertOption(OPTION_SUPPORTED_RESOLUTIONS, resolutions); 2608 return this; 2609 } 2610 2611 @RestrictTo(Scope.LIBRARY_GROUP) 2612 @Override setCustomOrderedResolutions(@onNull List<Size> resolutions)2613 public @NonNull Builder setCustomOrderedResolutions(@NonNull List<Size> resolutions) { 2614 getMutableConfig().insertOption(OPTION_CUSTOM_ORDERED_RESOLUTIONS, resolutions); 2615 return this; 2616 } 2617 2618 // Implementations of TargetConfig.Builder default methods 2619 2620 @RestrictTo(Scope.LIBRARY_GROUP) 2621 @Override setTargetClass(@onNull Class<ImageCapture> targetClass)2622 public @NonNull Builder setTargetClass(@NonNull Class<ImageCapture> targetClass) { 2623 getMutableConfig().insertOption(OPTION_TARGET_CLASS, targetClass); 2624 2625 // If no name is set yet, then generate a unique name 2626 if (null == getMutableConfig().retrieveOption(OPTION_TARGET_NAME, null)) { 2627 String targetName = targetClass.getCanonicalName() + "-" + UUID.randomUUID(); 2628 setTargetName(targetName); 2629 } 2630 2631 return this; 2632 } 2633 2634 /** 2635 * Sets the name of the target object being configured, used only for debug logging. 2636 * 2637 * <p>The name should be a value that can uniquely identify an instance of the object being 2638 * configured. 2639 * 2640 * <p>If not set, the target name will default to a unique name automatically generated 2641 * with the class canonical name and random UUID. 2642 * 2643 * @param targetName A unique string identifier for the instance of the class being 2644 * configured. 2645 * @return the current Builder. 2646 */ 2647 @Override setTargetName(@onNull String targetName)2648 public @NonNull Builder setTargetName(@NonNull String targetName) { 2649 getMutableConfig().insertOption(OPTION_TARGET_NAME, targetName); 2650 return this; 2651 } 2652 2653 // Implementations of ImageOutputConfig.Builder default methods 2654 2655 /** 2656 * Sets the aspect ratio of the intended target for images from this configuration. 2657 * 2658 * <p>The aspect ratio is the ratio of width to height in the sensor orientation. 2659 * 2660 * <p>It is not allowed to set both target aspect ratio and target resolution on the same 2661 * use case. Attempting so will throw an IllegalArgumentException when building the Config. 2662 * 2663 * <p>The target aspect ratio is used as a hint when determining the resulting output aspect 2664 * ratio which may differ from the request, possibly due to device constraints. 2665 * Application code should check the resulting output's resolution and the resulting 2666 * aspect ratio may not be exactly as requested. 2667 * 2668 * <p>If not set, or {@link AspectRatio#RATIO_DEFAULT} is supplied, resolutions with 2669 * aspect ratio 4:3 will be considered in higher priority. 2670 * 2671 * @param aspectRatio The desired ImageCapture {@link AspectRatio} 2672 * @return The current Builder. 2673 * @deprecated use {@link ResolutionSelector} with {@link AspectRatioStrategy} to specify 2674 * the preferred aspect ratio settings instead. 2675 */ 2676 @Override 2677 @Deprecated setTargetAspectRatio(@spectRatio.Ratio int aspectRatio)2678 public @NonNull Builder setTargetAspectRatio(@AspectRatio.Ratio int aspectRatio) { 2679 if (aspectRatio == AspectRatio.RATIO_DEFAULT) { 2680 aspectRatio = Defaults.DEFAULT_ASPECT_RATIO; 2681 } 2682 getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO, aspectRatio); 2683 return this; 2684 } 2685 2686 /** 2687 * Sets the rotation of the intended target for images from this configuration. 2688 * 2689 * <p>This will affect the EXIF rotation metadata in images saved by takePicture calls and 2690 * the {@link ImageInfo#getRotationDegrees()} value of the {@link ImageProxy} returned by 2691 * {@link OnImageCapturedCallback}. These will be set to be the rotation, which if 2692 * applied to the output image data, will make the image match the target rotation 2693 * specified here. 2694 * 2695 * <p>This is one of four valid values: {@link Surface#ROTATION_0}, {@link 2696 * Surface#ROTATION_90}, {@link Surface#ROTATION_180}, {@link Surface#ROTATION_270}. 2697 * Rotation values are relative to the "natural" rotation, {@link Surface#ROTATION_0}. 2698 * 2699 * <p>In general, it is best to additionally set the target rotation dynamically on the use 2700 * case. See {@link androidx.camera.core.ImageCapture#setTargetRotation(int)} for 2701 * additional documentation. 2702 * 2703 * <p>If not set, the target rotation will default to the value of 2704 * {@link android.view.Display#getRotation()} of the default display at the time the use 2705 * case is created. The use case is fully created once it has been attached to a camera. 2706 * 2707 * @param rotation The rotation of the intended target. 2708 * @return The current Builder. 2709 * @see androidx.camera.core.ImageCapture#setTargetRotation(int) 2710 * @see android.view.OrientationEventListener 2711 */ 2712 @Override setTargetRotation(@otationValue int rotation)2713 public @NonNull Builder setTargetRotation(@RotationValue int rotation) { 2714 getMutableConfig().insertOption(OPTION_TARGET_ROTATION, rotation); 2715 return this; 2716 } 2717 2718 /** 2719 * setMirrorMode is not supported on ImageCapture. 2720 */ 2721 @RestrictTo(Scope.LIBRARY_GROUP) 2722 @Override setMirrorMode(@irrorMode.Mirror int mirrorMode)2723 public @NonNull Builder setMirrorMode(@MirrorMode.Mirror int mirrorMode) { 2724 throw new UnsupportedOperationException("setMirrorMode is not supported."); 2725 } 2726 2727 /** 2728 * Sets the intended output target resolution. 2729 * 2730 * <p>The target resolution attempts to establish a minimum bound for the image resolution. 2731 * The actual image resolution will be the closest available resolution in size that is not 2732 * smaller than the target resolution, as determined by the Camera implementation. However, 2733 * if no resolution exists that is equal to or larger than the target resolution, the 2734 * nearest available resolution smaller than the target resolution will be chosen. 2735 * Resolutions with the same aspect ratio of the provided {@link Size} will be considered in 2736 * higher priority before resolutions of different aspect ratios. 2737 * 2738 * <p>It is not allowed to set both target aspect ratio and target resolution on the same 2739 * use case. Attempting so will throw an IllegalArgumentException when building the Config. 2740 * 2741 * <p>The resolution {@link Size} should be expressed in the coordinate frame after 2742 * rotating the supported sizes by the target rotation. For example, a device with 2743 * portrait natural orientation in natural target rotation requesting a portrait image 2744 * may specify 480x640, and the same device, rotated 90 degrees and targeting landscape 2745 * orientation may specify 640x480. 2746 * 2747 * <p>When the target resolution is set, 2748 * {@link ImageCapture#setCropAspectRatio(Rational)} will be automatically called to set 2749 * corresponding value. Such that the output image will be cropped into the desired 2750 * aspect ratio. 2751 * 2752 * <p>The maximum available resolution that could be selected for an {@link ImageCapture} 2753 * will depend on the camera device's capability. 2754 * 2755 * <p>If not set, the largest available resolution will be selected to use. Usually, 2756 * users will intend to get the largest still image that the camera device can support. 2757 * 2758 * <p>When using the <code>camera-camera2</code> CameraX implementation, which resolution 2759 * will be finally selected will depend on the camera device's hardware level and the 2760 * bound use cases combination. For more details see the guaranteed supported 2761 * configurations tables in {@link android.hardware.camera2.CameraDevice}'s 2762 * href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice 2763 * #regular-capture">Regular capture</a> section. 2764 * 2765 * @param resolution The target resolution to choose from supported output sizes list. 2766 * @return The current Builder. 2767 * @deprecated use {@link ResolutionSelector} with {@link ResolutionStrategy} to specify 2768 * the preferred resolution settings instead. 2769 */ 2770 @Override 2771 @Deprecated setTargetResolution(@onNull Size resolution)2772 public @NonNull Builder setTargetResolution(@NonNull Size resolution) { 2773 getMutableConfig().insertOption(OPTION_TARGET_RESOLUTION, resolution); 2774 return this; 2775 } 2776 2777 /** 2778 * Sets the default resolution of the intended target from this configuration. 2779 * 2780 * @param resolution The default resolution to choose from supported output sizes list. 2781 * @return The current Builder. 2782 */ 2783 @RestrictTo(Scope.LIBRARY_GROUP) 2784 @Override setDefaultResolution(@onNull Size resolution)2785 public @NonNull Builder setDefaultResolution(@NonNull Size resolution) { 2786 getMutableConfig().insertOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION, 2787 resolution); 2788 return this; 2789 } 2790 2791 @RestrictTo(Scope.LIBRARY_GROUP) 2792 @Override setMaxResolution(@onNull Size resolution)2793 public @NonNull Builder setMaxResolution(@NonNull Size resolution) { 2794 getMutableConfig().insertOption(OPTION_MAX_RESOLUTION, resolution); 2795 return this; 2796 } 2797 2798 /** 2799 * Sets the resolution selector to select the preferred supported resolution. 2800 * 2801 * <p>The default resolution strategy for ImageCapture is 2802 * {@link ResolutionStrategy#HIGHEST_AVAILABLE_STRATEGY}, which will select the largest 2803 * available resolution to use. Applications can override this default strategy with a 2804 * different resolution strategy. 2805 * 2806 * <p>The existing {@link #setTargetResolution(Size)} and 2807 * {@link #setTargetAspectRatio(int)} APIs are deprecated and are not compatible with 2808 * {@link #setResolutionSelector(ResolutionSelector)}. Calling either of these APIs 2809 * together with {@link #setResolutionSelector(ResolutionSelector)} will result in an 2810 * {@link IllegalArgumentException} being thrown when you attempt to build the 2811 * {@link ImageCapture} instance. 2812 * 2813 * @return The current Builder. 2814 */ 2815 @Override setResolutionSelector( @onNull ResolutionSelector resolutionSelector)2816 public @NonNull Builder setResolutionSelector( 2817 @NonNull ResolutionSelector resolutionSelector) { 2818 getMutableConfig().insertOption(OPTION_RESOLUTION_SELECTOR, resolutionSelector); 2819 return this; 2820 } 2821 2822 /** 2823 * Enables postview image generation. A postview image is a low-quality image 2824 * that's produced earlier during image capture than the final high-quality image, 2825 * and can be used as a thumbnail or placeholder until the final image is ready. 2826 * 2827 * <p>When the postview is available, 2828 * {@link OnImageCapturedCallback#onPostviewBitmapAvailable(Bitmap)} or 2829 * {@link OnImageSavedCallback#onPostviewBitmapAvailable(Bitmap)} will be called. 2830 * 2831 * <p>By default the largest available postview size that is smaller or equal to the 2832 * ImagaeCapture size will be used to configure the postview. The {@link ResolutionSelector} 2833 * can also be used to select a specific size via 2834 * {@link #setPostviewResolutionSelector(ResolutionSelector)}. 2835 * 2836 * <p>You can query the postview capability by invoking 2837 * {@link #getImageCaptureCapabilities(CameraInfo)}. If 2838 * {@link ImageCaptureCapabilities#isPostviewSupported()} returns false and you still 2839 * enable the postview, the postview image won't be generated. 2840 * 2841 * @param postviewEnabled whether postview is enabled or not 2842 * @return the current Builder. 2843 */ setPostviewEnabled(boolean postviewEnabled)2844 public @NonNull Builder setPostviewEnabled(boolean postviewEnabled) { 2845 getMutableConfig().insertOption(OPTION_POSTVIEW_ENABLED, 2846 postviewEnabled); 2847 return this; 2848 } 2849 2850 /** 2851 * Set the {@link ResolutionSelector} to select the postview size from the available 2852 * postview sizes. These available postview sizes are smaller or equal to the 2853 * ImageCapture size. You can implement the 2854 * {@link androidx.camera.core.resolutionselector.ResolutionFilter} and set it to the 2855 * {@link ResolutionSelector} to get the list of available sizes and determine which size 2856 * to use. 2857 * 2858 * <p>If no sizes can be selected using the given {@link ResolutionSelector}, it will throw 2859 * an {@link IllegalArgumentException} when {@code bindToLifecycle()} is invoked. 2860 * 2861 * @return the current Builder. 2862 */ setPostviewResolutionSelector( @onNull ResolutionSelector resolutionSelector)2863 public @NonNull Builder setPostviewResolutionSelector( 2864 @NonNull ResolutionSelector resolutionSelector) { 2865 getMutableConfig().insertOption(OPTION_POSTVIEW_RESOLUTION_SELECTOR, 2866 resolutionSelector); 2867 return this; 2868 } 2869 2870 @RestrictTo(Scope.LIBRARY_GROUP) setImageReaderProxyProvider( @onNull ImageReaderProxyProvider imageReaderProxyProvider)2871 public @NonNull Builder setImageReaderProxyProvider( 2872 @NonNull ImageReaderProxyProvider imageReaderProxyProvider) { 2873 getMutableConfig().insertOption(OPTION_IMAGE_READER_PROXY_PROVIDER, 2874 imageReaderProxyProvider); 2875 return this; 2876 } 2877 2878 @RestrictTo(Scope.LIBRARY_GROUP) setSoftwareJpegEncoderRequested(boolean requestSoftwareJpeg)2879 public @NonNull Builder setSoftwareJpegEncoderRequested(boolean requestSoftwareJpeg) { 2880 getMutableConfig().insertOption(OPTION_USE_SOFTWARE_JPEG_ENCODER, 2881 requestSoftwareJpeg); 2882 return this; 2883 } 2884 2885 /** 2886 * Sets the flashType. 2887 * 2888 * <p>If not set, the flash type will default to {@link #FLASH_TYPE_ONE_SHOT_FLASH}. 2889 * 2890 * @param flashType The requested flash mode. Value is {@link #FLASH_TYPE_ONE_SHOT_FLASH} 2891 * or {@link #FLASH_TYPE_USE_TORCH_AS_FLASH}. 2892 * @return The current Builder. 2893 */ 2894 @RestrictTo(Scope.LIBRARY_GROUP) setFlashType(@lashType int flashType)2895 public @NonNull Builder setFlashType(@FlashType int flashType) { 2896 getMutableConfig().insertOption(OPTION_FLASH_TYPE, flashType); 2897 return this; 2898 } 2899 2900 /** 2901 * Sets the output JPEG image compression quality. 2902 * 2903 * <p>This is used for the {@link ImageProxy} which is returned by 2904 * {@link #takePicture(Executor, OnImageCapturedCallback)} or the output JPEG image which 2905 * is saved by {@link #takePicture(OutputFileOptions, Executor, OnImageSavedCallback)}. 2906 * The saved JPEG image might be cropped according to the {@link ViewPort} setting or 2907 * the crop aspect ratio set by {@link #setCropAspectRatio(Rational)}. The JPEG quality 2908 * setting will also be used to compress the cropped output image. 2909 * 2910 * <p>If not set, a default value will be used according to the capture mode setting. 2911 * JPEG compression quality 95 is used for {@link #CAPTURE_MODE_MINIMIZE_LATENCY} and 100 2912 * is used for {@link #CAPTURE_MODE_MAXIMIZE_QUALITY}. 2913 * 2914 * @param jpegQuality The requested output JPEG image compression quality. The value must 2915 * be in range [1..100] which larger is higher quality. 2916 * @return The current Builder. 2917 * @throws IllegalArgumentException if the input value is not in range [1..100]. 2918 */ setJpegQuality(@ntRangefrom = 1, to = 100) int jpegQuality)2919 public @NonNull Builder setJpegQuality(@IntRange(from = 1, to = 100) int jpegQuality) { 2920 Preconditions.checkArgumentInRange(jpegQuality, 1, 100, "jpegQuality"); 2921 getMutableConfig().insertOption(OPTION_JPEG_COMPRESSION_QUALITY, 2922 jpegQuality); 2923 return this; 2924 } 2925 2926 // Implementations of IoConfig.Builder default methods 2927 2928 /** 2929 * Sets the default executor that will be used for IO tasks. 2930 * 2931 * <p> This executor will be used for any IO tasks specifically for ImageCapture, such as 2932 * {@link ImageCapture#takePicture(OutputFileOptions, Executor, 2933 * ImageCapture.OnImageSavedCallback)}. If no executor is set, then a default Executor 2934 * specifically for IO will be used instead. 2935 * 2936 * @param executor The executor which will be used for IO tasks. 2937 * @return the current Builder. 2938 */ 2939 @Override setIoExecutor(@onNull Executor executor)2940 public @NonNull Builder setIoExecutor(@NonNull Executor executor) { 2941 getMutableConfig().insertOption(OPTION_IO_EXECUTOR, executor); 2942 return this; 2943 } 2944 2945 // Implementations of UseCaseConfig.Builder default methods 2946 2947 @RestrictTo(Scope.LIBRARY_GROUP) 2948 @Override setDefaultSessionConfig(@onNull SessionConfig sessionConfig)2949 public @NonNull Builder setDefaultSessionConfig(@NonNull SessionConfig sessionConfig) { 2950 getMutableConfig().insertOption(OPTION_DEFAULT_SESSION_CONFIG, sessionConfig); 2951 return this; 2952 } 2953 2954 /** 2955 * Sets the output format of the captured image. 2956 * 2957 * <p>The supported output formats for capturing image depend on the capabilities of the 2958 * camera. The supported output formats of the camera can be queried using 2959 * {@link ImageCaptureCapabilities#getSupportedOutputFormats()}. 2960 * 2961 * <p>If not set, the output format will default to {@link #OUTPUT_FORMAT_JPEG}. 2962 * 2963 * <p>An {@link IllegalArgumentException} will be thrown when binding the UseCase if the 2964 * specified output format is not supported. Please note that the supported output formats 2965 * might be changed when Extensions is enabled. 2966 * 2967 * @param outputFormat The output image format. Value is {@link #OUTPUT_FORMAT_JPEG} or 2968 * {@link #OUTPUT_FORMAT_JPEG_ULTRA_HDR} or {@link #OUTPUT_FORMAT_RAW}. 2969 * @return The current Builder. 2970 * 2971 * @see OutputFormat 2972 * @see ImageCaptureCapabilities#getSupportedOutputFormats() 2973 */ setOutputFormat(@utputFormat int outputFormat)2974 public @NonNull Builder setOutputFormat(@OutputFormat int outputFormat) { 2975 getMutableConfig().insertOption(OPTION_OUTPUT_FORMAT, outputFormat); 2976 return this; 2977 } 2978 2979 @RestrictTo(Scope.LIBRARY_GROUP) 2980 @Override setDefaultCaptureConfig(@onNull CaptureConfig captureConfig)2981 public @NonNull Builder setDefaultCaptureConfig(@NonNull CaptureConfig captureConfig) { 2982 getMutableConfig().insertOption(OPTION_DEFAULT_CAPTURE_CONFIG, captureConfig); 2983 return this; 2984 } 2985 2986 @RestrictTo(Scope.LIBRARY_GROUP) 2987 @Override setSessionOptionUnpacker( SessionConfig.@onNull OptionUnpacker optionUnpacker)2988 public @NonNull Builder setSessionOptionUnpacker( 2989 SessionConfig.@NonNull OptionUnpacker optionUnpacker) { 2990 getMutableConfig().insertOption(OPTION_SESSION_CONFIG_UNPACKER, optionUnpacker); 2991 return this; 2992 } 2993 2994 @RestrictTo(Scope.LIBRARY_GROUP) 2995 @Override setCaptureOptionUnpacker( CaptureConfig.@onNull OptionUnpacker optionUnpacker)2996 public @NonNull Builder setCaptureOptionUnpacker( 2997 CaptureConfig.@NonNull OptionUnpacker optionUnpacker) { 2998 getMutableConfig().insertOption(OPTION_CAPTURE_CONFIG_UNPACKER, optionUnpacker); 2999 return this; 3000 } 3001 3002 @RestrictTo(Scope.LIBRARY_GROUP) 3003 @Override setSurfaceOccupancyPriority(int priority)3004 public @NonNull Builder setSurfaceOccupancyPriority(int priority) { 3005 getMutableConfig().insertOption(OPTION_SURFACE_OCCUPANCY_PRIORITY, priority); 3006 return this; 3007 } 3008 3009 /** 3010 * {@inheritDoc} 3011 */ 3012 @RestrictTo(Scope.LIBRARY_GROUP) 3013 @Override setZslDisabled(boolean disabled)3014 public @NonNull Builder setZslDisabled(boolean disabled) { 3015 getMutableConfig().insertOption(OPTION_ZSL_DISABLED, disabled); 3016 return this; 3017 } 3018 3019 /** 3020 * {@inheritDoc} 3021 */ 3022 @RestrictTo(Scope.LIBRARY_GROUP) 3023 @Override setHighResolutionDisabled(boolean disabled)3024 public @NonNull Builder setHighResolutionDisabled(boolean disabled) { 3025 getMutableConfig().insertOption(OPTION_HIGH_RESOLUTION_DISABLED, disabled); 3026 return this; 3027 } 3028 3029 /** 3030 * {@inheritDoc} 3031 */ 3032 @RestrictTo(Scope.LIBRARY_GROUP) 3033 @Override setCaptureType( UseCaseConfigFactory.@onNull CaptureType captureType)3034 public @NonNull Builder setCaptureType( 3035 UseCaseConfigFactory.@NonNull CaptureType captureType) { 3036 getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType); 3037 return this; 3038 } 3039 3040 // Implementations of ImageInputConfig.Builder default methods 3041 3042 /** 3043 * Sets the {@link DynamicRange}. 3044 * 3045 * <p>This is currently only exposed to internally set the dynamic range to SDR. 3046 * 3047 * @return The current Builder. 3048 * @see DynamicRange 3049 */ 3050 @RestrictTo(Scope.LIBRARY) 3051 @Override setDynamicRange(@onNull DynamicRange dynamicRange)3052 public @NonNull Builder setDynamicRange(@NonNull DynamicRange dynamicRange) { 3053 getMutableConfig().insertOption(OPTION_INPUT_DYNAMIC_RANGE, dynamicRange); 3054 return this; 3055 } 3056 } 3057 } 3058