1 /* 2 * Copyright 2020 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.view; 18 19 import static androidx.camera.core.impl.utils.Threads.checkMainThread; 20 import static androidx.camera.core.impl.utils.executor.CameraXExecutors.directExecutor; 21 import static androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor; 22 import static androidx.camera.core.impl.utils.futures.Futures.transform; 23 import static androidx.camera.view.internal.ScreenFlashUiInfo.ProviderType.PREVIEW_VIEW; 24 import static androidx.camera.view.internal.ScreenFlashUiInfo.ProviderType.SCREEN_FLASH_VIEW; 25 import static androidx.core.content.ContextCompat.getMainExecutor; 26 27 import android.Manifest; 28 import android.annotation.SuppressLint; 29 import android.content.Context; 30 import android.graphics.Matrix; 31 import android.graphics.PointF; 32 import android.hardware.camera2.CaptureResult; 33 import android.os.Build; 34 import android.os.Handler; 35 import android.os.Looper; 36 import android.util.Range; 37 import android.util.Rational; 38 import android.util.Size; 39 import android.view.Window; 40 41 import androidx.annotation.FloatRange; 42 import androidx.annotation.IntDef; 43 import androidx.annotation.IntRange; 44 import androidx.annotation.MainThread; 45 import androidx.annotation.OptIn; 46 import androidx.annotation.RequiresApi; 47 import androidx.annotation.RequiresPermission; 48 import androidx.annotation.RestrictTo; 49 import androidx.annotation.VisibleForTesting; 50 import androidx.camera.core.AspectRatio; 51 import androidx.camera.core.Camera; 52 import androidx.camera.core.CameraControl; 53 import androidx.camera.core.CameraEffect; 54 import androidx.camera.core.CameraInfo; 55 import androidx.camera.core.CameraInfoUnavailableException; 56 import androidx.camera.core.CameraSelector; 57 import androidx.camera.core.CameraUnavailableException; 58 import androidx.camera.core.DynamicRange; 59 import androidx.camera.core.FocusMeteringAction; 60 import androidx.camera.core.FocusMeteringResult; 61 import androidx.camera.core.ImageAnalysis; 62 import androidx.camera.core.ImageCapture; 63 import androidx.camera.core.ImageCapture.ScreenFlash; 64 import androidx.camera.core.ImageProxy; 65 import androidx.camera.core.InitializationException; 66 import androidx.camera.core.Logger; 67 import androidx.camera.core.MeteringPoint; 68 import androidx.camera.core.MeteringPointFactory; 69 import androidx.camera.core.MirrorMode; 70 import androidx.camera.core.Preview; 71 import androidx.camera.core.TorchState; 72 import androidx.camera.core.UseCase; 73 import androidx.camera.core.UseCaseGroup; 74 import androidx.camera.core.ViewPort; 75 import androidx.camera.core.ZoomState; 76 import androidx.camera.core.impl.ImageOutputConfig; 77 import androidx.camera.core.impl.StreamSpec; 78 import androidx.camera.core.impl.utils.CameraOrientationUtil; 79 import androidx.camera.core.impl.utils.ContextUtil; 80 import androidx.camera.core.impl.utils.LiveDataUtil; 81 import androidx.camera.core.impl.utils.Threads; 82 import androidx.camera.core.impl.utils.futures.FutureCallback; 83 import androidx.camera.core.impl.utils.futures.Futures; 84 import androidx.camera.core.resolutionselector.AspectRatioStrategy; 85 import androidx.camera.core.resolutionselector.ResolutionSelector; 86 import androidx.camera.core.resolutionselector.ResolutionStrategy; 87 import androidx.camera.lifecycle.ProcessCameraProvider; 88 import androidx.camera.video.FileDescriptorOutputOptions; 89 import androidx.camera.video.FileOutputOptions; 90 import androidx.camera.video.MediaStoreOutputOptions; 91 import androidx.camera.video.OutputOptions; 92 import androidx.camera.video.PendingRecording; 93 import androidx.camera.video.QualitySelector; 94 import androidx.camera.video.Recorder; 95 import androidx.camera.video.Recording; 96 import androidx.camera.video.VideoCapture; 97 import androidx.camera.video.VideoRecordEvent; 98 import androidx.camera.view.internal.ScreenFlashUiInfo; 99 import androidx.camera.view.video.AudioConfig; 100 import androidx.core.content.PermissionChecker; 101 import androidx.core.util.Consumer; 102 import androidx.core.util.Preconditions; 103 import androidx.lifecycle.LiveData; 104 import androidx.lifecycle.MutableLiveData; 105 106 import com.google.common.util.concurrent.ListenableFuture; 107 108 import org.jspecify.annotations.NonNull; 109 import org.jspecify.annotations.Nullable; 110 111 import java.lang.annotation.Retention; 112 import java.lang.annotation.RetentionPolicy; 113 import java.util.HashMap; 114 import java.util.HashSet; 115 import java.util.Map; 116 import java.util.Objects; 117 import java.util.Set; 118 import java.util.concurrent.Executor; 119 import java.util.concurrent.TimeUnit; 120 121 /** 122 * The abstract base camera controller class. 123 * 124 * <p> This a high level controller that provides most of the CameraX core features 125 * in a single class. It handles camera initialization, creates and configures {@link UseCase}s. 126 * It also listens to device motion sensor and set the target rotation for the use cases. 127 * 128 * <p> The controller is required to be used with a {@link PreviewView}. {@link PreviewView} 129 * provides the UI elements to display camera preview. The layout of the {@link PreviewView} is 130 * used to set the crop rect so the output from other use cases matches the preview display in a 131 * WYSIWYG way. The controller also listens to {@link PreviewView}'s touch events to handle 132 * tap-to-focus and pinch-to-zoom features. 133 * 134 * <p> This class provides features of 4 {@link UseCase}s: {@link Preview}, {@link ImageCapture}, 135 * {@link ImageAnalysis} and {@link VideoCapture}. {@link Preview} is required and always enabled. 136 * {@link ImageCapture} and {@link ImageAnalysis} are enabled by default. The video capture is 137 * disabled by default because it might affect other use cases, especially on lower end devices. 138 * It might be necessary to disable {@link ImageCapture} and/or {@link ImageAnalysis} before the 139 * video capture feature can be enabled. Disabling/enabling {@link UseCase}s freezes the preview for 140 * a short period of time. To avoid the glitch, the {@link UseCase}s need to be enabled/disabled 141 * before the controller is set on {@link PreviewView}. 142 */ 143 public abstract class CameraController { 144 145 private static final String TAG = "CameraController"; 146 147 // Externally visible error messages. 148 private static final String CAMERA_NOT_INITIALIZED = "Camera not initialized."; 149 private static final String PREVIEW_VIEW_NOT_ATTACHED = 150 "PreviewView not attached to CameraController."; 151 private static final String CAMERA_NOT_ATTACHED = "Use cases not attached to camera."; 152 private static final String IMAGE_CAPTURE_DISABLED = "ImageCapture disabled."; 153 private static final String VIDEO_CAPTURE_DISABLED = "VideoCapture disabled."; 154 private static final String VIDEO_RECORDING_UNFINISHED = "Recording video. Only one recording" 155 + " can be active at a time."; 156 157 // Auto focus is 1/6 of the area. 158 private static final float AF_SIZE = 1.0f / 6.0f; 159 private static final float AE_SIZE = AF_SIZE * 1.5f; 160 161 /** 162 * {@link ImageAnalysis.Analyzer} option for returning {@link PreviewView} coordinates. 163 * 164 * <p>When the {@link ImageAnalysis.Analyzer} is configured with this option, it will receive a 165 * {@link Matrix} that will receive a value that represents the transformation from camera 166 * sensor to the {@link PreviewView}, which can be used for highlighting detected result in 167 * {@link PreviewView}. For example, laying over a bounding box on top of the detected face. 168 * 169 * <p>Note this option only works if the {@link ImageAnalysis.Analyzer} is set via 170 * {@link CameraController#setImageAnalysisAnalyzer}. It will not be effective when used with 171 * camera-core directly. 172 * 173 * @see ImageAnalysis.Analyzer 174 * @deprecated Use {@link ImageAnalysis#COORDINATE_SYSTEM_VIEW_REFERENCED} instead. 175 */ 176 @Deprecated 177 public static final int COORDINATE_SYSTEM_VIEW_REFERENCED = 1; 178 179 /** 180 * No tap-to-focus action has been started by the end user. 181 */ 182 public static final int TAP_TO_FOCUS_NOT_STARTED = 0; 183 184 /** 185 * A tap-to-focus action has started but not completed. The app also gets notified with this 186 * state if a new action happens before the previous one could finish. 187 */ 188 public static final int TAP_TO_FOCUS_STARTED = 1; 189 190 /** 191 * The previous tap-to-focus action was completed successfully and the camera is focused. 192 */ 193 public static final int TAP_TO_FOCUS_FOCUSED = 2; 194 195 /** 196 * The previous tap-to-focus action was completed successfully but the camera is still 197 * unfocused, similar to the {@link CaptureResult#CONTROL_AF_STATE_NOT_FOCUSED_LOCKED} state. 198 * The end user might be able to get a better result by trying again with different camera 199 * distances and/or lighting. 200 */ 201 public static final int TAP_TO_FOCUS_NOT_FOCUSED = 3; 202 203 /** 204 * The previous tap-to-focus action was failed to complete. This is usually due to device 205 * limitations. 206 */ 207 public static final int TAP_TO_FOCUS_FAILED = 4; 208 209 /** 210 * Bitmask options to enable/disable use cases. 211 */ 212 @Retention(RetentionPolicy.SOURCE) 213 @RestrictTo(RestrictTo.Scope.LIBRARY) 214 @IntDef(flag = true, value = {IMAGE_CAPTURE, IMAGE_ANALYSIS, VIDEO_CAPTURE}) 215 public @interface UseCases { 216 } 217 218 /** 219 * Bitmask option to enable {@link ImageCapture}. In {@link #setEnabledUseCases}, if 220 * {@code (enabledUseCases & IMAGE_CAPTURE) != 0}, then controller will enable image capture 221 * features. 222 */ 223 public static final int IMAGE_CAPTURE = 1; 224 /** 225 * Bitmask option to enable {@link ImageAnalysis}. In {@link #setEnabledUseCases}, if 226 * {@code (enabledUseCases & IMAGE_ANALYSIS) != 0}, then controller will enable image 227 * analysis features. 228 */ 229 public static final int IMAGE_ANALYSIS = 1 << 1; 230 /** 231 * Bitmask option to enable video capture use case. In {@link #setEnabledUseCases}, if 232 * {@code (enabledUseCases & VIDEO_CAPTURE) != 0}, then controller will enable video capture 233 * features. 234 */ 235 public static final int VIDEO_CAPTURE = 1 << 2; 236 237 private static final ScreenFlash NO_OP_SCREEN_FLASH = 238 new ScreenFlash() { 239 @Override 240 public void apply(long expirationTimeMillis, 241 ImageCapture.@NonNull ScreenFlashListener screenFlashListener) { 242 screenFlashListener.onCompleted(); 243 } 244 245 @Override 246 public void clear() { 247 248 } 249 }; 250 251 CameraSelector mCameraSelector = CameraSelector.DEFAULT_BACK_CAMERA; 252 253 // By default, ImageCapture and ImageAnalysis are enabled. VideoCapture is disabled. 254 private int mEnabledUseCases = IMAGE_CAPTURE | IMAGE_ANALYSIS; 255 256 // CameraController and PreviewView hold reference to each other. The 2-way link is managed 257 // by PreviewView. 258 // Synthetic access 259 @SuppressWarnings("WeakerAccess") 260 @NonNull Preview mPreview; 261 262 @Nullable OutputSize mPreviewTargetSize; 263 @Nullable ResolutionSelector mPreviewResolutionSelector; 264 265 // Synthetic access 266 @SuppressWarnings("WeakerAccess") 267 @NonNull ImageCapture mImageCapture; 268 269 @Nullable OutputSize mImageCaptureTargetSize; 270 @Nullable ResolutionSelector mImageCaptureResolutionSelector; 271 272 @Nullable Executor mImageCaptureIoExecutor; 273 274 private @Nullable Executor mAnalysisExecutor; 275 276 private @Nullable Executor mAnalysisBackgroundExecutor; 277 278 private ImageAnalysis.@Nullable Analyzer mAnalysisAnalyzer; 279 280 @NonNull ImageAnalysis mImageAnalysis; 281 282 @Nullable OutputSize mImageAnalysisTargetSize; 283 @Nullable ResolutionSelector mImageAnalysisResolutionSelector; 284 285 @NonNull VideoCapture<Recorder> mVideoCapture; 286 287 @Nullable Recording mActiveRecording = null; 288 289 @NonNull Map<Consumer<VideoRecordEvent>, Recording> mRecordingMap = new HashMap<>(); 290 291 @NonNull QualitySelector mVideoCaptureQualitySelector = Recorder.DEFAULT_QUALITY_SELECTOR; 292 293 @MirrorMode.Mirror 294 private int mVideoCaptureMirrorMode = MirrorMode.MIRROR_MODE_OFF; 295 296 private @NonNull DynamicRange mVideoCaptureDynamicRange = DynamicRange.UNSPECIFIED; 297 298 private @NonNull DynamicRange mPreviewDynamicRange = DynamicRange.UNSPECIFIED; 299 300 private @NonNull Range<Integer> mVideoCaptureTargetFrameRate = 301 StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED; 302 303 // The latest bound camera. 304 // Synthetic access 305 @SuppressWarnings("WeakerAccess") 306 @Nullable Camera mCamera; 307 308 // Synthetic access 309 @SuppressWarnings("WeakerAccess") 310 @Nullable ProcessCameraProviderWrapper mCameraProvider; 311 312 // Synthetic access 313 @SuppressWarnings("WeakerAccess") 314 @Nullable ViewPort mViewPort; 315 316 // Synthetic access 317 @SuppressWarnings("WeakerAccess") 318 Preview.@Nullable SurfaceProvider mSurfaceProvider; 319 320 private final RotationProvider mRotationProvider; 321 322 @VisibleForTesting 323 final RotationProvider.@NonNull Listener mDeviceRotationListener; 324 325 private boolean mPinchToZoomEnabled = true; 326 private boolean mTapToFocusEnabled = true; 327 private FocusMeteringResultCallback mFocusMeteringResultCallback; 328 329 private final ForwardingLiveData<ZoomState> mZoomState = new ForwardingLiveData<>(); 330 private final ForwardingLiveData<Integer> mTorchState = new ForwardingLiveData<>(); 331 final MutableLiveData<TapToFocusInfo> mTapToFocusInfoState = new MutableLiveData<>( 332 new TapToFocusInfo(TAP_TO_FOCUS_NOT_STARTED, /* tapPoint = */ null)); 333 final LiveData<Integer> mTapToFocusState = LiveDataUtil.map(mTapToFocusInfoState, 334 TapToFocusInfo::getFocusState); 335 336 private final @NonNull PendingValue<Boolean> mPendingEnableTorch = new PendingValue<>(); 337 338 private final @NonNull PendingValue<Float> mPendingLinearZoom = new PendingValue<>(); 339 340 private final @NonNull PendingValue<Float> mPendingZoomRatio = new PendingValue<>(); 341 342 private final @NonNull Set<CameraEffect> mEffects = new HashSet<>(); 343 344 private final Context mAppContext; 345 346 private final @NonNull ListenableFuture<Void> mInitializationFuture; 347 348 private final Map<ScreenFlashUiInfo.ProviderType, ScreenFlashUiInfo> 349 mScreenFlashUiInfoMap = new HashMap<>(); 350 351 private long mTapToFocusAutoCancelDurationNanos = TimeUnit.MILLISECONDS.toNanos( 352 FocusMeteringAction.DEFAULT_AUTO_CANCEL_DURATION_MILLIS); 353 CameraController(@onNull Context context)354 CameraController(@NonNull Context context) { 355 this(context, transform(ProcessCameraProvider.getInstance(context), 356 ProcessCameraProviderWrapperImpl::new, directExecutor())); 357 } 358 CameraController(@onNull Context context, @NonNull ListenableFuture<ProcessCameraProviderWrapper> cameraProviderFuture)359 CameraController(@NonNull Context context, 360 @NonNull ListenableFuture<ProcessCameraProviderWrapper> cameraProviderFuture) { 361 mAppContext = ContextUtil.getApplicationContext(context); 362 mPreview = createPreview(); 363 mImageCapture = createImageCapture(null); 364 mImageAnalysis = createImageAnalysis(null, null, null); 365 mVideoCapture = createVideoCapture(); 366 367 // Wait for camera to be initialized before binding use cases. 368 mInitializationFuture = transform( 369 cameraProviderFuture, 370 provider -> { 371 mCameraProvider = provider; 372 unbindAllAndRecreate(); 373 startCameraAndTrackStates(); 374 return null; 375 }, mainThreadExecutor()); 376 377 // Listen for device rotation changes and set target rotation for non-preview use cases. 378 // The output of non-preview use cases need to be corrected in fixed landscape/portrait 379 // mode. 380 mRotationProvider = new RotationProvider(mAppContext); 381 mDeviceRotationListener = rotation -> { 382 mImageAnalysis.setTargetRotation(rotation); 383 mImageCapture.setTargetRotation(rotation); 384 mVideoCapture.setTargetRotation(rotation); 385 }; 386 } 387 388 /** 389 * Gets a {@link ListenableFuture} that completes when camera initialization completes. 390 * 391 * <p>This future may fail with an {@link InitializationException} and associated cause that 392 * can be retrieved by {@link Throwable#getCause()}. The cause will be a 393 * {@link CameraUnavailableException} if it fails to access any camera during initialization. 394 * 395 * <p>In the rare case that the future fails with {@link CameraUnavailableException}, the 396 * camera will become unusable. This could happen for various reasons, for example hardware 397 * failure or the camera being held by another process. If the failure is temporary, killing 398 * and restarting the app might fix the issue. 399 * 400 * <p>The initialization also try to bind use cases before completing the 401 * {@link ListenableFuture}. The {@link ListenableFuture} will complete successfully 402 * regardless of whether the use cases are ready to be bound, e.g. it will complete 403 * successfully even if the controller is not set on a {@link PreviewView}. However the 404 * {@link ListenableFuture} will fail if the enabled use cases are not supported by the 405 * current camera. 406 * 407 * @see ProcessCameraProvider#getInstance 408 */ getInitializationFuture()409 public @NonNull ListenableFuture<Void> getInitializationFuture() { 410 return mInitializationFuture; 411 } 412 413 /** 414 * Implemented by children to refresh after {@link UseCase} is changed. 415 * 416 * @throws IllegalStateException For invalid {@link UseCase} combinations. 417 * @throws RuntimeException For invalid {@link CameraEffect} combinations. 418 */ startCamera()419 abstract @Nullable Camera startCamera(); 420 isCameraInitialized()421 private boolean isCameraInitialized() { 422 return mCameraProvider != null; 423 } 424 isPreviewViewAttached()425 private boolean isPreviewViewAttached() { 426 return mSurfaceProvider != null && mViewPort != null; 427 } 428 isCameraAttached()429 private boolean isCameraAttached() { 430 return mCamera != null; 431 } 432 433 /** 434 * Enables or disables use cases. 435 * 436 * <p>Use cases need to be enabled before they can be used. By default, {@link #IMAGE_CAPTURE} 437 * and {@link #IMAGE_ANALYSIS} are enabled, and {@link #VIDEO_CAPTURE} is disabled. This is 438 * necessary because {@link #VIDEO_CAPTURE} may affect the available resolutions for other use 439 * cases, especially on lower end devices. 440 * 441 * <p>To make sure getting the maximum resolution, only enable {@link #VIDEO_CAPTURE} when 442 * shooting video. For example: 443 * 444 * <pre><code> 445 * // By default, image capture is enabled. Taking picture works. 446 * controller.takePicture(...); 447 * 448 * // Switch to video capture to shoot video. 449 * controller.setEnabledUseCases(VIDEO_CAPTURE); 450 * controller.startRecording(...); 451 * 452 * // Switch back to image capture and image analysis before taking another picture. 453 * controller.setEnabledUseCases(IMAGE_CAPTURE|IMAGE_ANALYSIS); 454 * controller.takePicture(...); 455 * 456 * </code></pre> 457 * 458 * @param enabledUseCases one or more of the following use cases, bitwise-OR-ed together: 459 * {@link #IMAGE_CAPTURE}, {@link #IMAGE_ANALYSIS} and/or {@link #VIDEO_CAPTURE}. 460 * @throws IllegalStateException If the current camera selector is unable to resolve a camera 461 * to be used for the enabled use cases. 462 * @see UseCase 463 * @see ImageCapture 464 * @see ImageAnalysis 465 */ 466 @MainThread setEnabledUseCases(@seCases int enabledUseCases)467 public void setEnabledUseCases(@UseCases int enabledUseCases) { 468 checkMainThread(); 469 if (enabledUseCases == mEnabledUseCases) { 470 return; 471 } 472 int oldEnabledUseCases = mEnabledUseCases; 473 mEnabledUseCases = enabledUseCases; 474 if (!isVideoCaptureEnabled() && isRecording()) { 475 stopRecording(); 476 } 477 startCameraAndTrackStates(() -> { 478 mEnabledUseCases = oldEnabledUseCases; 479 Logger.w(TAG, 480 "setEnabledUseCases: failed to enable use cases properly for enabledUseCases = " 481 + Integer.toBinaryString(enabledUseCases) 482 + ", restoring back previous values " + Integer.toBinaryString( 483 oldEnabledUseCases)); 484 }); 485 } 486 487 /** 488 * Checks if the given use case mask is enabled. 489 * 490 * @param useCaseMask One of the {@link #IMAGE_CAPTURE}, {@link #IMAGE_ANALYSIS} or 491 * {@link #VIDEO_CAPTURE}. 492 * @return {@code true} if the use case is enabled. 493 */ isUseCaseEnabled(int useCaseMask)494 private boolean isUseCaseEnabled(int useCaseMask) { 495 return (mEnabledUseCases & useCaseMask) != 0; 496 } 497 498 /** 499 * Configures the resolution on the config. 500 * 501 * <p>If the {@link ResolutionSelector} and the {@link OutputSize} are set at the same time, 502 * the {@link ResolutionSelector} takes precedence. 503 * 504 * <p>If the given {@link ResolutionSelector} is {@code null} and {@link OutputSize}, the 505 * {@link AspectRatioStrategy} will be override to match the {@link ViewPort}. 506 */ configureResolution(ImageOutputConfig.@onNull Builder<?> builder, @Nullable ResolutionSelector resolutionSelector, @Nullable OutputSize outputSize)507 private void configureResolution(ImageOutputConfig.@NonNull Builder<?> builder, 508 @Nullable ResolutionSelector resolutionSelector, @Nullable OutputSize outputSize) { 509 if (resolutionSelector != null) { 510 builder.setResolutionSelector(resolutionSelector); 511 } else if (outputSize != null) { 512 setTargetOutputSize(builder, outputSize); 513 } else if (mViewPort != null) { 514 // Override the aspect ratio strategy if viewport is set and there's no resolution 515 // selector explicitly set by the user. 516 AspectRatioStrategy aspectRatioStrategy = getViewportAspectRatioStrategy(mViewPort); 517 if (aspectRatioStrategy != null) { 518 builder.setResolutionSelector( 519 new ResolutionSelector.Builder().setAspectRatioStrategy( 520 aspectRatioStrategy).build()); 521 } 522 } 523 } 524 525 /** 526 * Sets the target aspect ratio or target resolution based on {@link OutputSize}. 527 */ setTargetOutputSize(ImageOutputConfig.@onNull Builder<?> builder, @Nullable OutputSize outputSize)528 private void setTargetOutputSize(ImageOutputConfig.@NonNull Builder<?> builder, 529 @Nullable OutputSize outputSize) { 530 if (outputSize == null) { 531 return; 532 } 533 if (outputSize.getResolution() != null) { 534 builder.setTargetResolution(outputSize.getResolution()); 535 } else if (outputSize.getAspectRatio() != OutputSize.UNASSIGNED_ASPECT_RATIO) { 536 builder.setTargetAspectRatio(outputSize.getAspectRatio()); 537 } else { 538 Logger.e(TAG, "Invalid target surface size. " + outputSize); 539 } 540 } 541 542 /** 543 * Checks if two {@link OutputSize} are equal. 544 */ isOutputSizeEqual( @ullable OutputSize currentSize, @Nullable OutputSize newSize)545 private boolean isOutputSizeEqual( 546 @Nullable OutputSize currentSize, 547 @Nullable OutputSize newSize) { 548 if (currentSize == newSize) { 549 return true; 550 } 551 return currentSize != null && currentSize.equals(newSize); 552 } 553 554 // ------------------ 555 // Preview use case. 556 // ------------------ 557 558 /** 559 * Internal API used by {@link PreviewView} to notify changes. 560 */ 561 @SuppressLint({"MissingPermission", "WrongConstant"}) 562 @MainThread attachPreviewSurface(Preview.@onNull SurfaceProvider surfaceProvider, @NonNull ViewPort viewPort)563 void attachPreviewSurface(Preview.@NonNull SurfaceProvider surfaceProvider, 564 @NonNull ViewPort viewPort) { 565 checkMainThread(); 566 if (mSurfaceProvider != surfaceProvider) { 567 mSurfaceProvider = surfaceProvider; 568 mPreview.setSurfaceProvider(surfaceProvider); 569 } 570 boolean shouldUpdateAspectRatio = mViewPort == null || getViewportAspectRatioStrategy( 571 viewPort) != getViewportAspectRatioStrategy(mViewPort); 572 mViewPort = viewPort; 573 startListeningToRotationEvents(); 574 if (shouldUpdateAspectRatio) { 575 unbindAllAndRecreate(); 576 } 577 startCameraAndTrackStates(); 578 } 579 580 /** 581 * Clears {@link PreviewView} to remove the UI reference. 582 */ 583 @MainThread clearPreviewSurface()584 void clearPreviewSurface() { 585 checkMainThread(); 586 if (mCameraProvider != null) { 587 // Preview is required. Unbind everything if Preview is down. 588 mCameraProvider.unbind(mPreview, mImageCapture, mImageAnalysis, mVideoCapture); 589 } 590 mPreview.setSurfaceProvider(null); 591 mCamera = null; 592 mSurfaceProvider = null; 593 mViewPort = null; 594 stopListeningToRotationEvents(); 595 } 596 startListeningToRotationEvents()597 private void startListeningToRotationEvents() { 598 mRotationProvider.addListener(mainThreadExecutor(), 599 mDeviceRotationListener); 600 } 601 stopListeningToRotationEvents()602 private void stopListeningToRotationEvents() { 603 mRotationProvider.removeListener(mDeviceRotationListener); 604 } 605 606 /** 607 * Sets the intended output size for {@link Preview}. 608 * 609 * <p>The value is used as a hint when determining the resolution and aspect ratio of the 610 * preview. The actual output may differ from the requested value due to device constraints. 611 * 612 * <p>When set to {@code null}, the output will be based on the default config of 613 * {@link Preview}. 614 * 615 * <p>Changing the value will reconfigure the camera which will cause additional latency. To 616 * avoid this, set the value before controller is bound to lifecycle. 617 * 618 * @param targetSize the intended output size for {@link Preview}. 619 * @see Preview.Builder#setTargetAspectRatio(int) 620 * @see Preview.Builder#setTargetResolution(Size) 621 * @deprecated Use {@link #setPreviewResolutionSelector(ResolutionSelector)} instead. 622 */ 623 @MainThread 624 @Deprecated setPreviewTargetSize(@ullable OutputSize targetSize)625 public void setPreviewTargetSize(@Nullable OutputSize targetSize) { 626 checkMainThread(); 627 if (isOutputSizeEqual(mPreviewTargetSize, targetSize)) { 628 return; 629 } 630 mPreviewTargetSize = targetSize; 631 unbindPreviewAndRecreate(); 632 startCameraAndTrackStates(); 633 } 634 635 /** 636 * Returns the intended output size for {@link Preview} set by 637 * {@link #setPreviewTargetSize(OutputSize)}, or {@code null} if not set. 638 * 639 * @deprecated Use {@link #getPreviewResolutionSelector()} instead. 640 */ 641 @MainThread 642 @Deprecated getPreviewTargetSize()643 public @Nullable OutputSize getPreviewTargetSize() { 644 checkMainThread(); 645 return mPreviewTargetSize; 646 } 647 648 /** 649 * Sets the {@link ResolutionSelector} for {@link Preview}. 650 * 651 * <p>CameraX uses this value as a hint to select the resolution for preview. The actual 652 * output may differ from the requested value due to device constraints. When set to 653 * {@code null}, CameraX will use the default config of {@link Preview}. By default, the 654 * selected resolution will be limited by the {@code PREVIEW} size which is defined as the best 655 * size match to the device's screen resolution, or to 1080p (1920x1080), whichever is smaller. 656 * 657 * <p>Changing the value will reconfigure the camera which will cause additional latency. To 658 * avoid this, set the value before controller is bound to lifecycle. 659 * 660 * @see Preview.Builder#setResolutionSelector(ResolutionSelector) 661 */ 662 @MainThread setPreviewResolutionSelector(@ullable ResolutionSelector resolutionSelector)663 public void setPreviewResolutionSelector(@Nullable ResolutionSelector resolutionSelector) { 664 checkMainThread(); 665 if (mPreviewResolutionSelector == resolutionSelector) { 666 return; 667 } 668 mPreviewResolutionSelector = resolutionSelector; 669 unbindPreviewAndRecreate(); 670 startCameraAndTrackStates(); 671 } 672 673 /** 674 * Returns the {@link ResolutionSelector} for {@link Preview}. 675 * 676 * <p>This method returns the value set by 677 * {@link #setPreviewResolutionSelector(ResolutionSelector)}. It returns {@code null} if 678 * the value has not been set. 679 */ 680 @MainThread getPreviewResolutionSelector()681 public @Nullable ResolutionSelector getPreviewResolutionSelector() { 682 checkMainThread(); 683 return mPreviewResolutionSelector; 684 } 685 686 /** 687 * Sets the {@link DynamicRange} for preview. 688 * 689 * <p>The dynamic range specifies how the range of colors, highlights and shadows that 690 * are displayed on a display. Some dynamic ranges will allow the preview to make full use of 691 * the extended range of brightness of a display. 692 * 693 * <p>The supported dynamic ranges of the camera can be queried using 694 * {@link CameraInfo#querySupportedDynamicRanges(Set)}. 695 * 696 * <p>It is possible to choose a high dynamic range (HDR) with unspecified encoding by providing 697 * {@link DynamicRange#HDR_UNSPECIFIED_10_BIT}. If the dynamic range is not provided, the 698 * default value is {@link DynamicRange#SDR}. 699 * 700 * @see Preview.Builder#setDynamicRange(DynamicRange) 701 * 702 * TODO: make this public in the next release. 703 */ 704 @MainThread 705 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) setPreviewDynamicRange(@onNull DynamicRange dynamicRange)706 public void setPreviewDynamicRange(@NonNull DynamicRange dynamicRange) { 707 checkMainThread(); 708 mPreviewDynamicRange = dynamicRange; 709 unbindPreviewAndRecreate(); 710 startCameraAndTrackStates(); 711 } 712 713 /** 714 * Gets the {@link DynamicRange} for preview. 715 * 716 * TODO: make this public in the next release. 717 */ 718 @MainThread 719 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) getPreviewDynamicRange()720 public @NonNull DynamicRange getPreviewDynamicRange() { 721 checkMainThread(); 722 return mPreviewDynamicRange; 723 } 724 725 /** 726 * Unbinds {@link Preview} and recreates with the latest parameters. 727 */ 728 @MainThread unbindPreviewAndRecreate()729 private void unbindPreviewAndRecreate() { 730 if (isCameraInitialized()) { 731 mCameraProvider.unbind(mPreview); 732 } 733 mPreview = createPreview(); 734 if (mSurfaceProvider != null) { 735 mPreview.setSurfaceProvider(mSurfaceProvider); 736 } 737 } 738 createPreview()739 private Preview createPreview() { 740 Preview.Builder builder = new Preview.Builder(); 741 configureResolution(builder, mPreviewResolutionSelector, mPreviewTargetSize); 742 builder.setDynamicRange(mPreviewDynamicRange); 743 return builder.build(); 744 } 745 746 // ---------------------- 747 // ImageCapture UseCase. 748 // ---------------------- 749 750 /** 751 * Checks if {@link ImageCapture} is enabled. 752 * 753 * <p>{@link ImageCapture} is enabled by default. It has to be enabled before 754 * {@link #takePicture} can be called. 755 * 756 * @see ImageCapture 757 */ 758 @MainThread isImageCaptureEnabled()759 public boolean isImageCaptureEnabled() { 760 checkMainThread(); 761 return isUseCaseEnabled(IMAGE_CAPTURE); 762 } 763 764 /** 765 * Gets the flash mode for {@link ImageCapture}. 766 * 767 * @return the flashMode. Value is {@link ImageCapture#FLASH_MODE_AUTO}, 768 * {@link ImageCapture#FLASH_MODE_ON}, or {@link ImageCapture#FLASH_MODE_OFF}. 769 * @see ImageCapture 770 */ 771 @MainThread 772 @ImageCapture.FlashMode getImageCaptureFlashMode()773 public int getImageCaptureFlashMode() { 774 checkMainThread(); 775 return mImageCapture.getFlashMode(); 776 } 777 778 /** 779 * Sets the flash mode for {@link ImageCapture}. 780 * 781 * <p>If not set, the flash mode will default to {@link ImageCapture#FLASH_MODE_OFF}. 782 * 783 * <p>If {@link ImageCapture#FLASH_MODE_SCREEN} is set, a valid {@link android.view.Window} 784 * instance must be set to a {@link PreviewView} or {@link ScreenFlashView} which this 785 * controller is set to. Trying to use {@link ImageCapture#FLASH_MODE_SCREEN} with a 786 * non-front camera or without setting a non-null window will be no-op. While switching the 787 * camera, it is the application's responsibility to change flash mode to the desired one if 788 * it leads to a no-op case (e.g. switching to rear camera while {@code FLASH_MODE_SCREEN} is 789 * still set). Otherwise, {@code FLASH_MODE_OFF} will be set. 790 * 791 * @param flashMode the flash mode for {@link ImageCapture}. 792 * @throws IllegalArgumentException If flash mode is invalid or 793 * {@link ImageCapture#FLASH_MODE_SCREEN} is used without a front camera. 794 * @see PreviewView#setScreenFlashWindow(Window) 795 * @see ScreenFlashView#setScreenFlashWindow(Window) 796 */ 797 @MainThread setImageCaptureFlashMode(@mageCapture.FlashMode int flashMode)798 public void setImageCaptureFlashMode(@ImageCapture.FlashMode int flashMode) { 799 checkMainThread(); 800 801 if (flashMode == ImageCapture.FLASH_MODE_SCREEN) { 802 Integer lensFacing = mCameraSelector.getLensFacing(); 803 if (lensFacing != null && lensFacing != CameraSelector.LENS_FACING_FRONT) { 804 throw new IllegalArgumentException( 805 "Not a front camera despite setting FLASH_MODE_SCREEN"); 806 } 807 808 updateScreenFlashToImageCapture(); 809 } 810 811 mImageCapture.setFlashMode(flashMode); 812 } 813 814 /** 815 * Internal API used by {@link PreviewView} and {@link ScreenFlashView} to provide a 816 * {@link ScreenFlash}. 817 */ 818 @RestrictTo(RestrictTo.Scope.LIBRARY) setScreenFlashUiInfo(@onNull ScreenFlashUiInfo screenFlashUiInfo)819 public void setScreenFlashUiInfo(@NonNull ScreenFlashUiInfo screenFlashUiInfo) { 820 ScreenFlashUiInfo previousInfo = getScreenFlashUiInfoByPriority(); 821 mScreenFlashUiInfoMap.put(screenFlashUiInfo.getProviderType(), screenFlashUiInfo); 822 ScreenFlashUiInfo prioritizedInfo = getScreenFlashUiInfoByPriority(); 823 if (prioritizedInfo != null && !prioritizedInfo.equals(previousInfo)) { 824 updateScreenFlashToImageCapture(); 825 } 826 } 827 828 /** 829 * Internal API used by {@link PreviewView} and {@link ScreenFlashView} to update screen 830 * flash mode to ImageCapture in case it's pending. 831 */ 832 @RestrictTo(RestrictTo.Scope.LIBRARY) updateScreenFlashToImageCapture()833 public void updateScreenFlashToImageCapture() { 834 ScreenFlashUiInfo screenFlashUiInfo = getScreenFlashUiInfoByPriority(); 835 836 if (screenFlashUiInfo == null) { 837 // PreviewView/ScreenFlashView may have not been attached yet, so setting a NO-OP 838 // ScreenFlash until one of the views is attached 839 Logger.d(TAG, "No ScreenFlash instance set yet, need to wait for " 840 + "controller to be set to either ScreenFlashView or PreviewView"); 841 mImageCapture.setScreenFlash(NO_OP_SCREEN_FLASH); 842 return; 843 } 844 845 mImageCapture.setScreenFlash(screenFlashUiInfo.getScreenFlash()); 846 Logger.d(TAG, "Set ScreenFlash instance to ImageCapture, provided by " 847 + screenFlashUiInfo.getProviderType().name()); 848 } 849 850 /** 851 * Returns a {@link ScreenFlashUiInfo} by prioritizing {@link ScreenFlashView} over 852 * {@link PreviewView}. 853 * 854 * <p>{@link PreviewView} always has a {@link ScreenFlashView} internally and does not know 855 * if user is using another {@link ScreenFlashView} themselves. This API prioritizes user's 856 * {@link ScreenFlashView} over the internal one in {@link PreviewView} and provides the 857 * {@link ScreenFlashUiInfo} accordingly. 858 */ 859 @RestrictTo(RestrictTo.Scope.LIBRARY) getScreenFlashUiInfoByPriority()860 public @Nullable ScreenFlashUiInfo getScreenFlashUiInfoByPriority() { 861 if (mScreenFlashUiInfoMap.get(SCREEN_FLASH_VIEW) != null) { 862 return mScreenFlashUiInfoMap.get(SCREEN_FLASH_VIEW); 863 } 864 if (mScreenFlashUiInfoMap.get(PREVIEW_VIEW) != null) { 865 return mScreenFlashUiInfoMap.get(PREVIEW_VIEW); 866 } 867 return null; 868 } 869 870 /** 871 * Captures a new still image and saves to a file along with application specified metadata. 872 * 873 * <p>The callback will be called only once for every invocation of this method. 874 * 875 * <p>By default, the saved image is mirrored to match the output of the preview if front 876 * camera is used. To override this behavior, the app needs to explicitly set the flag to 877 * {@code false} using {@link ImageCapture.Metadata#setReversedHorizontal} and 878 * {@link ImageCapture.OutputFileOptions.Builder#setMetadata}. 879 * 880 * <p>The saved image is cropped to match the aspect ratio of the {@link PreviewView}. To 881 * take a picture with the maximum available resolution, make sure that the 882 * {@link PreviewView}'s aspect ratio matches the max JPEG resolution supported by the camera. 883 * 884 * @param outputFileOptions Options to store the newly captured image. 885 * @param executor The executor in which the callback methods will be run. 886 * @param imageSavedCallback Callback to be called for the newly captured image. 887 * @throws IllegalStateException If {@link ImageCapture#FLASH_MODE_SCREEN} is set to the 888 * {@link CameraController}, but a non-null {@link Window} instance has not been set with 889 * {@link PreviewView#setScreenFlashWindow}. 890 * @see ImageCapture#takePicture( 891 * ImageCapture.OutputFileOptions, Executor, ImageCapture.OnImageSavedCallback) 892 */ 893 @MainThread takePicture( ImageCapture.@onNull OutputFileOptions outputFileOptions, @NonNull Executor executor, ImageCapture.@NonNull OnImageSavedCallback imageSavedCallback)894 public void takePicture( 895 ImageCapture.@NonNull OutputFileOptions outputFileOptions, 896 @NonNull Executor executor, 897 ImageCapture.@NonNull OnImageSavedCallback imageSavedCallback) { 898 checkMainThread(); 899 Preconditions.checkState(isCameraInitialized(), CAMERA_NOT_INITIALIZED); 900 Preconditions.checkState(isImageCaptureEnabled(), IMAGE_CAPTURE_DISABLED); 901 902 throwExceptionForInvalidScreenFlashCapture(); 903 904 updateMirroringFlagInOutputFileOptions(outputFileOptions); 905 mImageCapture.takePicture(outputFileOptions, executor, imageSavedCallback); 906 } 907 908 /** 909 * Updates {@link ImageCapture.OutputFileOptions} based on config. 910 * 911 * <p>Mirror the output image if front camera is used and if the flag is not set explicitly by 912 * the app. 913 */ 914 @VisibleForTesting 915 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) updateMirroringFlagInOutputFileOptions( ImageCapture.@onNull OutputFileOptions outputFileOptions)916 void updateMirroringFlagInOutputFileOptions( 917 ImageCapture.@NonNull OutputFileOptions outputFileOptions) { 918 if (mCameraSelector.getLensFacing() != null 919 && !outputFileOptions.getMetadata().isReversedHorizontalSet()) { 920 outputFileOptions.getMetadata().setReversedHorizontal( 921 mCameraSelector.getLensFacing() == CameraSelector.LENS_FACING_FRONT); 922 } 923 } 924 925 /** 926 * Captures a new still image for in memory access. 927 * 928 * <p>The listener is responsible for calling {@link ImageProxy#close()} on the returned image. 929 * 930 * @param executor The executor in which the callback methods will be run. 931 * @param callback Callback to be invoked for the newly captured image 932 * @throws IllegalStateException If {@link ImageCapture#FLASH_MODE_SCREEN} is set to the 933 * {@link CameraController}, but a non-null {@link Window} instance has not been set with 934 * {@link PreviewView#setScreenFlashWindow}. 935 * @see ImageCapture#takePicture(Executor, ImageCapture.OnImageCapturedCallback) 936 */ 937 @MainThread takePicture( @onNull Executor executor, ImageCapture.@NonNull OnImageCapturedCallback callback)938 public void takePicture( 939 @NonNull Executor executor, 940 ImageCapture.@NonNull OnImageCapturedCallback callback) { 941 checkMainThread(); 942 Preconditions.checkState(isCameraInitialized(), CAMERA_NOT_INITIALIZED); 943 Preconditions.checkState(isImageCaptureEnabled(), IMAGE_CAPTURE_DISABLED); 944 945 throwExceptionForInvalidScreenFlashCapture(); 946 947 mImageCapture.takePicture(executor, callback); 948 } 949 throwExceptionForInvalidScreenFlashCapture()950 private void throwExceptionForInvalidScreenFlashCapture() { 951 if (getImageCaptureFlashMode() == ImageCapture.FLASH_MODE_SCREEN && ( 952 getScreenFlashUiInfoByPriority() == null 953 || getScreenFlashUiInfoByPriority().getScreenFlash() == null)) { 954 // ScreenFlash instance won't be found at this point only if a non-null window was not 955 // set to PreviewView. 956 throw new IllegalStateException( 957 "No window set in PreviewView despite setting FLASH_MODE_SCREEN"); 958 } 959 } 960 961 /** 962 * Sets the image capture mode. 963 * 964 * <p>Valid capture modes are {@link ImageCapture.CaptureMode#CAPTURE_MODE_MINIMIZE_LATENCY}, 965 * which prioritizes latency over image quality, or 966 * {@link ImageCapture.CaptureMode#CAPTURE_MODE_MAXIMIZE_QUALITY}, 967 * which prioritizes image quality over latency. 968 * 969 * <p>Changing the value will reconfigure the camera which will cause additional latency. To 970 * avoid this, set the value before controller is bound to lifecycle. 971 * 972 * @param captureMode The requested image capture mode. 973 */ 974 @MainThread setImageCaptureMode(@mageCapture.CaptureMode int captureMode)975 public void setImageCaptureMode(@ImageCapture.CaptureMode int captureMode) { 976 checkMainThread(); 977 if (mImageCapture.getCaptureMode() == captureMode) { 978 return; 979 } 980 unbindImageCaptureAndRecreate(captureMode); 981 startCameraAndTrackStates(); 982 } 983 984 /** 985 * Returns the image capture mode. 986 * 987 * @see ImageCapture#getCaptureMode() 988 */ 989 @MainThread getImageCaptureMode()990 public int getImageCaptureMode() { 991 checkMainThread(); 992 return mImageCapture.getCaptureMode(); 993 } 994 995 /** 996 * Sets the intended image size for {@link ImageCapture}. 997 * 998 * <p>The value is used as a hint when determining the resolution and aspect ratio of the 999 * captured image. The actual output may differ from the requested value due to device 1000 * constraints. 1001 * 1002 * <p>When set to {@code null}, the output will be based on the default config of 1003 * {@link ImageCapture}. 1004 * 1005 * <p>Changing the value will reconfigure the camera which will cause additional latency. To 1006 * avoid this, set the value before controller is bound to lifecycle. 1007 * 1008 * @param targetSize The intended image size for {@link ImageCapture}. 1009 * @deprecated Use {@link #setImageCaptureResolutionSelector(ResolutionSelector)} instead. 1010 */ 1011 @MainThread 1012 @Deprecated setImageCaptureTargetSize(@ullable OutputSize targetSize)1013 public void setImageCaptureTargetSize(@Nullable OutputSize targetSize) { 1014 checkMainThread(); 1015 if (isOutputSizeEqual(mImageCaptureTargetSize, targetSize)) { 1016 return; 1017 } 1018 mImageCaptureTargetSize = targetSize; 1019 unbindImageCaptureAndRecreate(getImageCaptureMode()); 1020 startCameraAndTrackStates(); 1021 } 1022 1023 /** 1024 * Returns the intended output size for {@link ImageCapture} set by 1025 * {@link #setImageCaptureTargetSize(OutputSize)}, or {@code null} if not set. 1026 * 1027 * @deprecated Use {@link #getImageCaptureResolutionSelector()} instead. 1028 */ 1029 @Deprecated 1030 @MainThread getImageCaptureTargetSize()1031 public @Nullable OutputSize getImageCaptureTargetSize() { 1032 checkMainThread(); 1033 return mImageCaptureTargetSize; 1034 } 1035 1036 /** 1037 * Sets the {@link ResolutionSelector} for {@link ImageCapture}. 1038 * 1039 * <p>CameraX uses this value as a hint to select the resolution for captured images. The actual 1040 * output may differ from the requested value due to device constraints. When set to 1041 * {@code null}, CameraX will use the default config of {@link ImageCapture}. The default 1042 * resolution strategy for ImageCapture is 1043 * {@link ResolutionStrategy#HIGHEST_AVAILABLE_STRATEGY}, which will select the largest 1044 * available resolution to use. 1045 * 1046 * <p>Changing the value will reconfigure the camera which will cause additional latency. To 1047 * avoid this, set the value before controller is bound to lifecycle. 1048 * 1049 * @see ImageCapture.Builder#setResolutionSelector(ResolutionSelector) 1050 */ 1051 @MainThread setImageCaptureResolutionSelector(@ullable ResolutionSelector resolutionSelector)1052 public void setImageCaptureResolutionSelector(@Nullable ResolutionSelector resolutionSelector) { 1053 checkMainThread(); 1054 if (mImageCaptureResolutionSelector == resolutionSelector) { 1055 return; 1056 } 1057 mImageCaptureResolutionSelector = resolutionSelector; 1058 unbindImageCaptureAndRecreate(getImageCaptureMode()); 1059 startCameraAndTrackStates(); 1060 } 1061 1062 /** 1063 * Returns the {@link ResolutionSelector} for {@link ImageCapture}. 1064 * 1065 * <p>This method returns the value set by 1066 * {@link #setImageCaptureResolutionSelector} (ResolutionSelector)}. It returns {@code null} if 1067 * the value has not been set. 1068 */ 1069 @MainThread getImageCaptureResolutionSelector()1070 public @Nullable ResolutionSelector getImageCaptureResolutionSelector() { 1071 checkMainThread(); 1072 return mImageCaptureResolutionSelector; 1073 } 1074 1075 /** 1076 * Sets the default executor that will be used for {@link ImageCapture} IO tasks. 1077 * 1078 * <p>This executor will be used for any IO tasks specifically for {@link ImageCapture}, 1079 * such as {@link #takePicture(ImageCapture.OutputFileOptions, Executor, 1080 * ImageCapture.OnImageSavedCallback)}. If no executor is set, then a default Executor 1081 * specifically for IO will be used instead. 1082 * 1083 * <p>Changing the value will reconfigure the camera which will cause additional latency. 1084 * To avoid this, set the value before controller is bound to lifecycle. 1085 * 1086 * @param executor The executor which will be used for IO tasks. 1087 */ 1088 @MainThread setImageCaptureIoExecutor(@ullable Executor executor)1089 public void setImageCaptureIoExecutor(@Nullable Executor executor) { 1090 checkMainThread(); 1091 if (mImageCaptureIoExecutor == executor) { 1092 return; 1093 } 1094 mImageCaptureIoExecutor = executor; 1095 unbindImageCaptureAndRecreate(getImageCaptureMode()); 1096 startCameraAndTrackStates(); 1097 } 1098 1099 /** 1100 * Gets the default executor for {@link ImageCapture} IO tasks. 1101 */ 1102 @MainThread getImageCaptureIoExecutor()1103 public @Nullable Executor getImageCaptureIoExecutor() { 1104 checkMainThread(); 1105 return mImageCaptureIoExecutor; 1106 } 1107 1108 /** 1109 * Unbinds {@link ImageCapture} and recreates with the latest parameters. 1110 */ 1111 @MainThread unbindImageCaptureAndRecreate(Integer imageCaptureMode)1112 private void unbindImageCaptureAndRecreate(Integer imageCaptureMode) { 1113 if (isCameraInitialized()) { 1114 mCameraProvider.unbind(mImageCapture); 1115 } 1116 int flashMode = mImageCapture.getFlashMode(); 1117 mImageCapture = createImageCapture(imageCaptureMode); 1118 setImageCaptureFlashMode(flashMode); 1119 } 1120 createImageCapture(Integer imageCaptureMode)1121 private ImageCapture createImageCapture(Integer imageCaptureMode) { 1122 ImageCapture.Builder builder = new ImageCapture.Builder(); 1123 if (imageCaptureMode != null) { 1124 builder.setCaptureMode(imageCaptureMode); 1125 } 1126 configureResolution(builder, mImageCaptureResolutionSelector, mImageCaptureTargetSize); 1127 if (mImageCaptureIoExecutor != null) { 1128 builder.setIoExecutor(mImageCaptureIoExecutor); 1129 } 1130 1131 return builder.build(); 1132 } 1133 1134 // ----------------- 1135 // Image analysis 1136 // ----------------- 1137 1138 /** 1139 * Checks if {@link ImageAnalysis} is enabled. 1140 * 1141 * @see ImageAnalysis 1142 */ 1143 @MainThread isImageAnalysisEnabled()1144 public boolean isImageAnalysisEnabled() { 1145 checkMainThread(); 1146 return isUseCaseEnabled(IMAGE_ANALYSIS); 1147 } 1148 1149 /** 1150 * Sets an analyzer to receive and analyze images. 1151 * 1152 * <p>Applications can process or copy the image by implementing the 1153 * {@link ImageAnalysis.Analyzer}. The image needs to be closed by calling 1154 * {@link ImageProxy#close()} when the analyzing is done. 1155 * 1156 * <p>Setting an analyzer function replaces any previous analyzer. Only one analyzer can be 1157 * set at any time. 1158 * 1159 * <p>If the {@link ImageAnalysis.Analyzer#getTargetCoordinateSystem()} returns 1160 * {@link ImageAnalysis#COORDINATE_SYSTEM_VIEW_REFERENCED}, the analyzer will receive a 1161 * transformation via {@link ImageAnalysis.Analyzer#updateTransform} that converts 1162 * coordinates from the {@link ImageAnalysis}'s coordinate system to the {@link PreviewView}'s 1163 * coordinate system. 1164 * 1165 * <p>If the {@link ImageAnalysis.Analyzer#getDefaultTargetResolution()} returns a non-null 1166 * value, calling this method will reconfigure the camera which might cause additional 1167 * latency. To avoid this, set the value before controller is bound to the lifecycle. 1168 * 1169 * @param executor The executor in which the 1170 * {@link ImageAnalysis.Analyzer#analyze(ImageProxy)} will be run. 1171 * @param analyzer of the images. 1172 * @see ImageAnalysis#setAnalyzer(Executor, ImageAnalysis.Analyzer) 1173 */ 1174 @MainThread setImageAnalysisAnalyzer(@onNull Executor executor, ImageAnalysis.@NonNull Analyzer analyzer)1175 public void setImageAnalysisAnalyzer(@NonNull Executor executor, 1176 ImageAnalysis.@NonNull Analyzer analyzer) { 1177 checkMainThread(); 1178 if (mAnalysisAnalyzer == analyzer && mAnalysisExecutor == executor) { 1179 return; 1180 } 1181 ImageAnalysis.Analyzer oldAnalyzer = mAnalysisAnalyzer; 1182 mAnalysisExecutor = executor; 1183 mAnalysisAnalyzer = analyzer; 1184 mImageAnalysis.setAnalyzer(executor, analyzer); 1185 restartCameraIfAnalyzerResolutionChanged(oldAnalyzer, analyzer); 1186 } 1187 1188 /** 1189 * Removes a previously set analyzer. 1190 * 1191 * <p>This will stop data from streaming to the {@link ImageAnalysis}. 1192 * 1193 * <p>If the current {@link ImageAnalysis.Analyzer#getDefaultTargetResolution()} returns 1194 * non-null value, calling this method will reconfigure the camera which might cause additional 1195 * latency. To avoid this, call this method when the lifecycle is not active. 1196 * 1197 * @see ImageAnalysis#clearAnalyzer(). 1198 */ 1199 @MainThread clearImageAnalysisAnalyzer()1200 public void clearImageAnalysisAnalyzer() { 1201 checkMainThread(); 1202 ImageAnalysis.Analyzer oldAnalyzer = mAnalysisAnalyzer; 1203 mAnalysisExecutor = null; 1204 mAnalysisAnalyzer = null; 1205 mImageAnalysis.clearAnalyzer(); 1206 restartCameraIfAnalyzerResolutionChanged(oldAnalyzer, null); 1207 } 1208 restartCameraIfAnalyzerResolutionChanged( ImageAnalysis.@ullable Analyzer oldAnalyzer, ImageAnalysis.@Nullable Analyzer newAnalyzer)1209 private void restartCameraIfAnalyzerResolutionChanged( 1210 ImageAnalysis.@Nullable Analyzer oldAnalyzer, 1211 ImageAnalysis.@Nullable Analyzer newAnalyzer) { 1212 Size oldResolution = oldAnalyzer == null ? null : 1213 oldAnalyzer.getDefaultTargetResolution(); 1214 Size newResolution = newAnalyzer == null ? null : 1215 newAnalyzer.getDefaultTargetResolution(); 1216 if (!Objects.equals(oldResolution, newResolution)) { 1217 // Rebind ImageAnalysis to reconfigure target resolution. 1218 unbindImageAnalysisAndRecreate(mImageAnalysis.getBackpressureStrategy(), 1219 mImageAnalysis.getImageQueueDepth(), mImageAnalysis.getOutputImageFormat()); 1220 startCameraAndTrackStates(); 1221 } 1222 } 1223 1224 /** 1225 * Returns the mode with which images are acquired. 1226 * 1227 * <p>If not set, it defaults to {@link ImageAnalysis#STRATEGY_KEEP_ONLY_LATEST}. 1228 * 1229 * @return The backpressure strategy applied to the image producer. 1230 * @see ImageAnalysis.Builder#getBackpressureStrategy() 1231 */ 1232 @MainThread 1233 @ImageAnalysis.BackpressureStrategy getImageAnalysisBackpressureStrategy()1234 public int getImageAnalysisBackpressureStrategy() { 1235 checkMainThread(); 1236 return mImageAnalysis.getBackpressureStrategy(); 1237 } 1238 1239 /** 1240 * Sets the backpressure strategy to apply to the image producer to deal with scenarios 1241 * where images may be produced faster than they can be analyzed. 1242 * 1243 * <p>The available values are {@link ImageAnalysis#STRATEGY_BLOCK_PRODUCER} and 1244 * {@link ImageAnalysis#STRATEGY_KEEP_ONLY_LATEST}. If not set, the backpressure strategy 1245 * will default to {@link ImageAnalysis#STRATEGY_KEEP_ONLY_LATEST}. 1246 * 1247 * <p>Changing the value will reconfigure the camera which will cause additional latency. To 1248 * avoid this, set the value before controller is bound to lifecycle. 1249 * 1250 * @param strategy The strategy to use. 1251 * @see ImageAnalysis.Builder#setBackpressureStrategy(int) 1252 */ 1253 @MainThread setImageAnalysisBackpressureStrategy( @mageAnalysis.BackpressureStrategy int strategy)1254 public void setImageAnalysisBackpressureStrategy( 1255 @ImageAnalysis.BackpressureStrategy int strategy) { 1256 checkMainThread(); 1257 if (mImageAnalysis.getBackpressureStrategy() == strategy) { 1258 return; 1259 } 1260 1261 unbindImageAnalysisAndRecreate(strategy, mImageAnalysis.getImageQueueDepth(), 1262 mImageAnalysis.getOutputImageFormat()); 1263 startCameraAndTrackStates(); 1264 } 1265 1266 /** 1267 * Sets the image queue depth of {@link ImageAnalysis}. 1268 * 1269 * <p>This sets the number of images available in parallel to {@link ImageAnalysis.Analyzer}. 1270 * The value is only used if the backpressure strategy is 1271 * {@link ImageAnalysis.BackpressureStrategy#STRATEGY_BLOCK_PRODUCER}. 1272 * 1273 * <p>Changing the value will reconfigure the camera which will cause additional latency. To 1274 * avoid this, set the value before controller is bound to lifecycle. 1275 * 1276 * @param depth The total number of images available. 1277 * @see ImageAnalysis.Builder#setImageQueueDepth(int) 1278 */ 1279 @MainThread setImageAnalysisImageQueueDepth(int depth)1280 public void setImageAnalysisImageQueueDepth(int depth) { 1281 checkMainThread(); 1282 if (mImageAnalysis.getImageQueueDepth() == depth) { 1283 return; 1284 } 1285 unbindImageAnalysisAndRecreate(mImageAnalysis.getBackpressureStrategy(), depth, 1286 mImageAnalysis.getOutputImageFormat()); 1287 startCameraAndTrackStates(); 1288 } 1289 1290 /** 1291 * Gets the image queue depth of {@link ImageAnalysis}. 1292 * 1293 * @see ImageAnalysis#getImageQueueDepth() 1294 */ 1295 @MainThread getImageAnalysisImageQueueDepth()1296 public int getImageAnalysisImageQueueDepth() { 1297 checkMainThread(); 1298 return mImageAnalysis.getImageQueueDepth(); 1299 } 1300 1301 /** 1302 * Sets the intended output size for {@link ImageAnalysis}. 1303 * 1304 * <p>The value is used as a hint when determining the resolution and aspect ratio of 1305 * the output buffer. The actual output may differ from the requested value due to device 1306 * constraints. 1307 * 1308 * <p>When set to {@code null}, the output will be based on the default config of 1309 * {@link ImageAnalysis}. 1310 * 1311 * <p>Changing the value will reconfigure the camera which will cause additional latency. 1312 * To avoid this, set the value before controller is bound to lifecycle. 1313 * 1314 * @param targetSize The intended output size for {@link ImageAnalysis}. 1315 * @see ImageAnalysis.Builder#setTargetAspectRatio(int) 1316 * @see ImageAnalysis.Builder#setTargetResolution(Size) 1317 * @deprecated Use {@link #setImageAnalysisResolutionSelector(ResolutionSelector)} instead. 1318 */ 1319 @MainThread 1320 @Deprecated setImageAnalysisTargetSize(@ullable OutputSize targetSize)1321 public void setImageAnalysisTargetSize(@Nullable OutputSize targetSize) { 1322 checkMainThread(); 1323 if (isOutputSizeEqual(mImageAnalysisTargetSize, targetSize)) { 1324 return; 1325 } 1326 mImageAnalysisTargetSize = targetSize; 1327 unbindImageAnalysisAndRecreate( 1328 mImageAnalysis.getBackpressureStrategy(), 1329 mImageAnalysis.getImageQueueDepth(), 1330 mImageAnalysis.getOutputImageFormat()); 1331 startCameraAndTrackStates(); 1332 } 1333 1334 /** 1335 * Returns the intended output size for {@link ImageAnalysis} set by 1336 * {@link #setImageAnalysisTargetSize(OutputSize)}, or {@code null} if not set. 1337 * 1338 * @deprecated Use {@link #getImageAnalysisResolutionSelector()} instead. 1339 */ 1340 @MainThread 1341 @Deprecated getImageAnalysisTargetSize()1342 public @Nullable OutputSize getImageAnalysisTargetSize() { 1343 checkMainThread(); 1344 return mImageAnalysisTargetSize; 1345 } 1346 1347 /** 1348 * Sets the {@link ResolutionSelector} for {@link ImageAnalysis}. 1349 * 1350 * <p>CameraX uses this value as a hint to select the resolution for images. The actual 1351 * output may differ from the requested value due to device constraints. When set to 1352 * {@code null}, CameraX will use the default config of {@link ImageAnalysis}. ImageAnalysis has 1353 * a default {@link ResolutionStrategy} with bound size as 640x480 and fallback rule of 1354 * {@link ResolutionStrategy#FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER}. 1355 * 1356 * <p>Changing the value will reconfigure the camera which will cause additional latency. To 1357 * avoid this, set the value before controller is bound to lifecycle. 1358 * 1359 * @see ImageAnalysis.Builder#setResolutionSelector(ResolutionSelector) 1360 */ 1361 @MainThread setImageAnalysisResolutionSelector( @ullable ResolutionSelector resolutionSelector)1362 public void setImageAnalysisResolutionSelector( 1363 @Nullable ResolutionSelector resolutionSelector) { 1364 checkMainThread(); 1365 if (mImageAnalysisResolutionSelector == resolutionSelector) { 1366 return; 1367 } 1368 mImageAnalysisResolutionSelector = resolutionSelector; 1369 unbindImageAnalysisAndRecreate( 1370 mImageAnalysis.getBackpressureStrategy(), 1371 mImageAnalysis.getImageQueueDepth(), 1372 mImageAnalysis.getOutputImageFormat()); 1373 startCameraAndTrackStates(); 1374 } 1375 1376 /** 1377 * Returns the {@link ResolutionSelector} for {@link ImageAnalysis}. 1378 * 1379 * <p>This method returns the value set by 1380 * {@link #setImageAnalysisResolutionSelector(ResolutionSelector)}. It returns {@code null} if 1381 * the value has not been set. 1382 */ 1383 @MainThread getImageAnalysisResolutionSelector()1384 public @Nullable ResolutionSelector getImageAnalysisResolutionSelector() { 1385 checkMainThread(); 1386 return mImageAnalysisResolutionSelector; 1387 } 1388 1389 /** 1390 * Sets the executor that will be used for {@link ImageAnalysis} background tasks. 1391 * 1392 * <p>If not set, the background executor will default to an automatically generated 1393 * {@link Executor}. 1394 * 1395 * <p>Changing the value will reconfigure the camera, which will cause additional latency. To 1396 * avoid this, set the value before controller is bound to lifecycle. 1397 * 1398 * @param executor The executor for {@link ImageAnalysis} background tasks. 1399 * @see ImageAnalysis.Builder#setBackgroundExecutor(Executor) 1400 */ 1401 @MainThread setImageAnalysisBackgroundExecutor(@ullable Executor executor)1402 public void setImageAnalysisBackgroundExecutor(@Nullable Executor executor) { 1403 checkMainThread(); 1404 if (mAnalysisBackgroundExecutor == executor) { 1405 return; 1406 } 1407 mAnalysisBackgroundExecutor = executor; 1408 unbindImageAnalysisAndRecreate(mImageAnalysis.getBackpressureStrategy(), 1409 mImageAnalysis.getImageQueueDepth(), mImageAnalysis.getOutputImageFormat()); 1410 startCameraAndTrackStates(); 1411 } 1412 1413 /** 1414 * Gets the default executor for {@link ImageAnalysis} background tasks. 1415 * 1416 * @see ImageAnalysis.Builder#setBackgroundExecutor(Executor) 1417 */ 1418 @MainThread getImageAnalysisBackgroundExecutor()1419 public @Nullable Executor getImageAnalysisBackgroundExecutor() { 1420 checkMainThread(); 1421 return mAnalysisBackgroundExecutor; 1422 } 1423 1424 /** 1425 * Sets the output image format for {@link ImageAnalysis}. 1426 * 1427 * <p>The supported output image format 1428 * are {@link ImageAnalysis.OutputImageFormat#OUTPUT_IMAGE_FORMAT_YUV_420_888} and 1429 * {@link ImageAnalysis.OutputImageFormat#OUTPUT_IMAGE_FORMAT_RGBA_8888}. 1430 * 1431 * <p>If not set, {@link ImageAnalysis.OutputImageFormat#OUTPUT_IMAGE_FORMAT_YUV_420_888} 1432 * will be used. 1433 * 1434 * <p>Requesting {@link ImageAnalysis.OutputImageFormat#OUTPUT_IMAGE_FORMAT_RGBA_8888} or 1435 * {@link ImageAnalysis.OutputImageFormat#OUTPUT_IMAGE_FORMAT_NV21} causes extra overhead 1436 * because format conversion takes time. 1437 * 1438 * <p>Changing the value will reconfigure the camera, which may cause additional latency. To 1439 * avoid this, set the value before controller is bound to lifecycle. If the value is changed 1440 * when the camera is active, check the {@link ImageProxy#getFormat()} value to determine 1441 * when the new format takes effect. 1442 * 1443 * @see ImageAnalysis.Builder#setOutputImageFormat(int) 1444 * @see ImageAnalysis.Builder#getOutputImageFormat() 1445 * @see ImageAnalysis#getOutputImageFormat() 1446 */ 1447 @MainThread setImageAnalysisOutputImageFormat( @mageAnalysis.OutputImageFormat int imageAnalysisOutputImageFormat)1448 public void setImageAnalysisOutputImageFormat( 1449 @ImageAnalysis.OutputImageFormat int imageAnalysisOutputImageFormat) { 1450 checkMainThread(); 1451 if (imageAnalysisOutputImageFormat == mImageAnalysis.getOutputImageFormat()) { 1452 // No-op if the value is not changed. 1453 return; 1454 } 1455 unbindImageAnalysisAndRecreate(mImageAnalysis.getBackpressureStrategy(), 1456 mImageAnalysis.getImageQueueDepth(), imageAnalysisOutputImageFormat); 1457 } 1458 1459 /** 1460 * Gets the output image format for {@link ImageAnalysis}. 1461 * 1462 * <p>The returned image format can be either 1463 * {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_YUV_420_888}, 1464 * {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_RGBA_8888} or 1465 * {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_NV21}. 1466 * 1467 * @see ImageAnalysis.Builder#setOutputImageFormat(int) 1468 * @see ImageAnalysis.Builder#getOutputImageFormat() 1469 * @see ImageAnalysis#getOutputImageFormat() 1470 */ 1471 @MainThread 1472 @ImageAnalysis.OutputImageFormat getImageAnalysisOutputImageFormat()1473 public int getImageAnalysisOutputImageFormat() { 1474 checkMainThread(); 1475 return mImageAnalysis.getOutputImageFormat(); 1476 } 1477 1478 /** 1479 * Unbinds {@link ImageAnalysis} and recreates with the latest parameters. 1480 */ 1481 @MainThread unbindImageAnalysisAndRecreate(Integer strategy, Integer imageQueueDepth, Integer outputFormat)1482 private void unbindImageAnalysisAndRecreate(Integer strategy, Integer imageQueueDepth, 1483 Integer outputFormat) { 1484 checkMainThread(); 1485 if (isCameraInitialized()) { 1486 mCameraProvider.unbind(mImageAnalysis); 1487 } 1488 mImageAnalysis = createImageAnalysis(strategy, imageQueueDepth, outputFormat); 1489 if (mAnalysisExecutor != null && mAnalysisAnalyzer != null) { 1490 mImageAnalysis.setAnalyzer(mAnalysisExecutor, mAnalysisAnalyzer); 1491 } 1492 } 1493 createImageAnalysis(Integer strategy, Integer imageQueueDepth, Integer outputFormat)1494 private ImageAnalysis createImageAnalysis(Integer strategy, Integer imageQueueDepth, 1495 Integer outputFormat) { 1496 ImageAnalysis.Builder builder = new ImageAnalysis.Builder(); 1497 if (strategy != null) { 1498 builder.setBackpressureStrategy(strategy); 1499 } 1500 if (imageQueueDepth != null) { 1501 builder.setImageQueueDepth(imageQueueDepth); 1502 } 1503 if (outputFormat != null) { 1504 builder.setOutputImageFormat(outputFormat); 1505 } 1506 configureResolution(builder, mImageAnalysisResolutionSelector, mImageAnalysisTargetSize); 1507 if (mAnalysisBackgroundExecutor != null) { 1508 builder.setBackgroundExecutor(mAnalysisBackgroundExecutor); 1509 } 1510 1511 return builder.build(); 1512 } 1513 1514 @OptIn(markerClass = {TransformExperimental.class}) 1515 @MainThread updatePreviewViewTransform(@ullable Matrix matrix)1516 void updatePreviewViewTransform(@Nullable Matrix matrix) { 1517 checkMainThread(); 1518 if (mAnalysisAnalyzer == null) { 1519 return; 1520 } 1521 if (mAnalysisAnalyzer.getTargetCoordinateSystem() 1522 == ImageAnalysis.COORDINATE_SYSTEM_VIEW_REFERENCED) { 1523 mAnalysisAnalyzer.updateTransform(matrix); 1524 } 1525 } 1526 1527 // ----------------- 1528 // Video capture 1529 // ----------------- 1530 1531 /** 1532 * Checks if video capture is enabled. 1533 * 1534 * <p>Video capture is disabled by default. It has to be enabled before {@link #startRecording} 1535 * can be called. 1536 */ 1537 @MainThread isVideoCaptureEnabled()1538 public boolean isVideoCaptureEnabled() { 1539 checkMainThread(); 1540 return isUseCaseEnabled(VIDEO_CAPTURE); 1541 } 1542 1543 /** 1544 * Takes a video to a given file. 1545 * 1546 * <p>Only a single recording can be active at a time, so if {@link #isRecording()} is 1547 * {@code true}, this will throw an {@link IllegalStateException}. 1548 * 1549 * <p>Upon successfully starting the recording, a {@link VideoRecordEvent.Start} event will 1550 * be the first event sent to the provided event listener. 1551 * 1552 * <p>If errors occur while starting the recording, a {@link VideoRecordEvent.Finalize} event 1553 * will be the first event sent to the provided listener, and information about the error can 1554 * be found in that event's {@link VideoRecordEvent.Finalize#getError()} method. 1555 * 1556 * <p>Recording with audio requires the {@link android.Manifest.permission#RECORD_AUDIO} 1557 * permission; without it, starting a recording will fail with a {@link SecurityException}. 1558 * 1559 * @param outputOptions The options to store the newly captured video. 1560 * @param audioConfig The configuration of audio. 1561 * @param executor The executor that the event listener will be run on. 1562 * @param listener The event listener to handle video record events. 1563 * @return a {@link Recording} that provides controls for new active recordings. 1564 * @throws IllegalStateException if there is an unfinished active recording. 1565 * @throws SecurityException if the audio config specifies audio should be enabled but the 1566 * {@link android.Manifest.permission#RECORD_AUDIO} permission is denied. 1567 */ 1568 @SuppressLint("MissingPermission") 1569 @MainThread startRecording( @onNull FileOutputOptions outputOptions, @NonNull AudioConfig audioConfig, @NonNull Executor executor, @NonNull Consumer<VideoRecordEvent> listener)1570 public @NonNull Recording startRecording( 1571 @NonNull FileOutputOptions outputOptions, 1572 @NonNull AudioConfig audioConfig, 1573 @NonNull Executor executor, 1574 @NonNull Consumer<VideoRecordEvent> listener) { 1575 return startRecordingInternal(outputOptions, audioConfig, executor, listener); 1576 } 1577 1578 /** 1579 * Takes a video to a given file descriptor. 1580 * 1581 * <p>Currently, file descriptors as output destinations are not supported on pre-Android O 1582 * (API 26) devices. 1583 * 1584 * <p>Only a single recording can be active at a time, so if {@link #isRecording()} is true, 1585 * this will throw an {@link IllegalStateException}. 1586 * 1587 * <p>Upon successfully starting the recording, a {@link VideoRecordEvent.Start} event will 1588 * be the first event sent to the provided event listener. 1589 * 1590 * <p>If errors occur while starting the recording, a {@link VideoRecordEvent.Finalize} event 1591 * will be the first event sent to the provided listener, and information about the error can 1592 * be found in that event's {@link VideoRecordEvent.Finalize#getError()} method. 1593 * 1594 * <p>Recording with audio requires the {@link android.Manifest.permission#RECORD_AUDIO} 1595 * permission; without it, starting a recording will fail with a {@link SecurityException}. 1596 * 1597 * @param outputOptions The options to store the newly captured video. 1598 * @param audioConfig The configuration of audio. 1599 * @param executor The executor that the event listener will be run on. 1600 * @param listener The event listener to handle video record events. 1601 * @return a {@link Recording} that provides controls for new active recordings. 1602 * @throws IllegalStateException if there is an unfinished active recording. 1603 * @throws SecurityException if the audio config specifies audio should be enabled but the 1604 * {@link android.Manifest.permission#RECORD_AUDIO} permission is denied. 1605 */ 1606 @SuppressLint("MissingPermission") 1607 @RequiresApi(26) 1608 @MainThread startRecording( @onNull FileDescriptorOutputOptions outputOptions, @NonNull AudioConfig audioConfig, @NonNull Executor executor, @NonNull Consumer<VideoRecordEvent> listener)1609 public @NonNull Recording startRecording( 1610 @NonNull FileDescriptorOutputOptions outputOptions, 1611 @NonNull AudioConfig audioConfig, 1612 @NonNull Executor executor, 1613 @NonNull Consumer<VideoRecordEvent> listener) { 1614 return startRecordingInternal(outputOptions, audioConfig, executor, listener); 1615 } 1616 1617 /** 1618 * Takes a video to MediaStore. 1619 * 1620 * <p>Only a single recording can be active at a time, so if {@link #isRecording()} is 1621 * {@code true}, this will throw an {@link IllegalStateException}. 1622 * 1623 * <p>Upon successfully starting the recording, a {@link VideoRecordEvent.Start} event will 1624 * be the first event sent to the provided event listener. 1625 * 1626 * <p>If errors occur while starting the recording, a {@link VideoRecordEvent.Finalize} event 1627 * will be the first event sent to the provided listener, and information about the error can 1628 * be found in that event's {@link VideoRecordEvent.Finalize#getError()} method. 1629 * 1630 * <p>Recording with audio requires the {@link android.Manifest.permission#RECORD_AUDIO} 1631 * permission; without it, starting a recording will fail with a {@link SecurityException}. 1632 * 1633 * @param outputOptions The options to store the newly captured video. 1634 * @param audioConfig The configuration of audio. 1635 * @param executor The executor that the event listener will be run on. 1636 * @param listener The event listener to handle video record events. 1637 * @return a {@link Recording} that provides controls for new active recordings. 1638 * @throws IllegalStateException if there is an unfinished active recording. 1639 * @throws SecurityException if the audio config specifies audio should be enabled but the 1640 * {@link android.Manifest.permission#RECORD_AUDIO} permission is denied. 1641 */ 1642 @SuppressLint("MissingPermission") 1643 @MainThread startRecording( @onNull MediaStoreOutputOptions outputOptions, @NonNull AudioConfig audioConfig, @NonNull Executor executor, @NonNull Consumer<VideoRecordEvent> listener)1644 public @NonNull Recording startRecording( 1645 @NonNull MediaStoreOutputOptions outputOptions, 1646 @NonNull AudioConfig audioConfig, 1647 @NonNull Executor executor, 1648 @NonNull Consumer<VideoRecordEvent> listener) { 1649 return startRecordingInternal(outputOptions, audioConfig, executor, listener); 1650 } 1651 1652 @RequiresPermission(Manifest.permission.RECORD_AUDIO) 1653 @MainThread startRecordingInternal( @onNull OutputOptions outputOptions, @NonNull AudioConfig audioConfig, @NonNull Executor executor, @NonNull Consumer<VideoRecordEvent> listener)1654 private Recording startRecordingInternal( 1655 @NonNull OutputOptions outputOptions, 1656 @NonNull AudioConfig audioConfig, 1657 @NonNull Executor executor, 1658 @NonNull Consumer<VideoRecordEvent> listener) { 1659 checkMainThread(); 1660 Preconditions.checkState(isCameraInitialized(), CAMERA_NOT_INITIALIZED); 1661 Preconditions.checkState(isVideoCaptureEnabled(), VIDEO_CAPTURE_DISABLED); 1662 Preconditions.checkState(!isRecording(), VIDEO_RECORDING_UNFINISHED); 1663 1664 Consumer<VideoRecordEvent> wrappedListener = 1665 wrapListenerToDeactivateRecordingOnFinalized(listener); 1666 PendingRecording pendingRecording = prepareRecording(outputOptions); 1667 boolean isAudioEnabled = audioConfig.getAudioEnabled(); 1668 if (isAudioEnabled) { 1669 checkAudioPermissionGranted(); 1670 pendingRecording.withAudioEnabled(); 1671 } 1672 Recording recording = pendingRecording.start(executor, wrappedListener); 1673 setActiveRecording(recording, wrappedListener); 1674 1675 return recording; 1676 } 1677 checkAudioPermissionGranted()1678 private void checkAudioPermissionGranted() { 1679 int permissionState = PermissionChecker.checkSelfPermission(mAppContext, 1680 Manifest.permission.RECORD_AUDIO); 1681 if (permissionState == PermissionChecker.PERMISSION_DENIED) { 1682 throw new SecurityException("Attempted to start recording with audio, but " 1683 + "application does not have RECORD_AUDIO permission granted."); 1684 } 1685 } 1686 1687 /** 1688 * Generates a {@link PendingRecording} instance for starting a recording. 1689 * 1690 * <p>This method handles {@code prepareRecording()} methods for different output formats, 1691 * and makes {@link #startRecordingInternal(OutputOptions, AudioConfig, Executor, Consumer)} 1692 * only handle the general flow. 1693 * 1694 * <p>This method uses the parent class {@link OutputOptions} as the parameter. On the other 1695 * hand, the public {@code startRecording()} is overloaded with subclasses. The reason is to 1696 * enforce compile-time check for API levels. 1697 */ 1698 @MainThread prepareRecording(@onNull OutputOptions options)1699 private PendingRecording prepareRecording(@NonNull OutputOptions options) { 1700 Recorder recorder = mVideoCapture.getOutput(); 1701 if (options instanceof FileOutputOptions) { 1702 return recorder.prepareRecording(mAppContext, (FileOutputOptions) options); 1703 } else if (options instanceof FileDescriptorOutputOptions) { 1704 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { 1705 throw new UnsupportedOperationException( 1706 "File descriptors are not supported on pre-Android O (API 26) devices." 1707 ); 1708 } 1709 return recorder.prepareRecording(mAppContext, (FileDescriptorOutputOptions) options); 1710 } else if (options instanceof MediaStoreOutputOptions) { 1711 return recorder.prepareRecording(mAppContext, (MediaStoreOutputOptions) options); 1712 } else { 1713 throw new IllegalArgumentException("Unsupported OutputOptions type."); 1714 } 1715 } 1716 wrapListenerToDeactivateRecordingOnFinalized( final @NonNull Consumer<VideoRecordEvent> listener)1717 private Consumer<VideoRecordEvent> wrapListenerToDeactivateRecordingOnFinalized( 1718 final @NonNull Consumer<VideoRecordEvent> listener) { 1719 final Executor mainExecutor = getMainExecutor(mAppContext); 1720 1721 return new Consumer<VideoRecordEvent>() { 1722 @Override 1723 public void accept(VideoRecordEvent videoRecordEvent) { 1724 if (videoRecordEvent instanceof VideoRecordEvent.Finalize) { 1725 if (!Threads.isMainThread()) { 1726 // Post on main thread to ensure thread safety. 1727 mainExecutor.execute(() -> deactivateRecordingByListener(this)); 1728 } else { 1729 deactivateRecordingByListener(this); 1730 } 1731 } 1732 listener.accept(videoRecordEvent); 1733 } 1734 }; 1735 } 1736 1737 @MainThread 1738 void deactivateRecordingByListener(@NonNull Consumer<VideoRecordEvent> listener) { 1739 Recording recording = mRecordingMap.remove(listener); 1740 if (recording != null) { 1741 deactivateRecording(recording); 1742 } 1743 } 1744 1745 /** 1746 * Clears the active video recording reference if the recording to be deactivated matches. 1747 */ 1748 @MainThread 1749 private void deactivateRecording(@NonNull Recording recording) { 1750 if (mActiveRecording == recording) { 1751 mActiveRecording = null; 1752 } 1753 } 1754 1755 @MainThread 1756 private void setActiveRecording( 1757 @NonNull Recording recording, 1758 @NonNull Consumer<VideoRecordEvent> listener) { 1759 mRecordingMap.put(listener, recording); 1760 mActiveRecording = recording; 1761 } 1762 1763 /** 1764 * Stops an in-progress video recording. 1765 * 1766 * <p>Once the current recording has been stopped, the next recording can be started. 1767 * 1768 * <p>If the recording completes successfully, a {@link VideoRecordEvent.Finalize} event with 1769 * {@link VideoRecordEvent.Finalize#ERROR_NONE} will be sent to the provided listener. 1770 */ 1771 @MainThread 1772 private void stopRecording() { 1773 checkMainThread(); 1774 1775 if (mActiveRecording != null) { 1776 mActiveRecording.stop(); 1777 deactivateRecording(mActiveRecording); 1778 } 1779 } 1780 1781 /** 1782 * Returns whether there is an in-progress video recording. 1783 */ 1784 @MainThread 1785 public boolean isRecording() { 1786 checkMainThread(); 1787 return mActiveRecording != null && !mActiveRecording.isClosed(); 1788 } 1789 1790 /** 1791 * Sets the {@link QualitySelector} for {@link #VIDEO_CAPTURE}. 1792 * 1793 * <p>The provided quality selector is used to select the resolution of the recording 1794 * depending on the resolutions supported by the camera and codec capabilities. 1795 * 1796 * <p>If no quality selector is provided, the default is 1797 * {@link Recorder#DEFAULT_QUALITY_SELECTOR}. 1798 * 1799 * <p>Changing the value will reconfigure the camera which will cause video capture to stop. To 1800 * avoid this, set the value before controller is bound to lifecycle. 1801 * 1802 * @param qualitySelector The quality selector for {@link #VIDEO_CAPTURE}. 1803 * @see QualitySelector 1804 */ 1805 @MainThread 1806 public void setVideoCaptureQualitySelector(@NonNull QualitySelector qualitySelector) { 1807 checkMainThread(); 1808 mVideoCaptureQualitySelector = qualitySelector; 1809 unbindVideoAndRecreate(); 1810 startCameraAndTrackStates(); 1811 } 1812 1813 /** 1814 * Returns the {@link QualitySelector} for {@link #VIDEO_CAPTURE}. 1815 * 1816 * @return the {@link QualitySelector} provided to 1817 * {@link #setVideoCaptureQualitySelector(QualitySelector)} or the default value of 1818 * {@link Recorder#DEFAULT_QUALITY_SELECTOR} if no quality selector was provided. 1819 */ 1820 @MainThread 1821 public @NonNull QualitySelector getVideoCaptureQualitySelector() { 1822 checkMainThread(); 1823 return mVideoCaptureQualitySelector; 1824 } 1825 1826 /** 1827 * Sets the mirror mode for video capture. 1828 * 1829 * <p>Valid values include: {@link MirrorMode#MIRROR_MODE_OFF}, 1830 * {@link MirrorMode#MIRROR_MODE_ON} and {@link MirrorMode#MIRROR_MODE_ON_FRONT_ONLY}. 1831 * If not set, it defaults to {@link MirrorMode#MIRROR_MODE_OFF}. 1832 * 1833 * @see VideoCapture.Builder#setMirrorMode(int) 1834 */ 1835 @MainThread 1836 public void setVideoCaptureMirrorMode(@MirrorMode.Mirror int mirrorMode) { 1837 checkMainThread(); 1838 mVideoCaptureMirrorMode = mirrorMode; 1839 unbindVideoAndRecreate(); 1840 startCameraAndTrackStates(); 1841 } 1842 1843 /** 1844 * Gets the mirror mode for video capture. 1845 */ 1846 @MainThread 1847 @MirrorMode.Mirror 1848 public int getVideoCaptureMirrorMode() { 1849 checkMainThread(); 1850 return mVideoCaptureMirrorMode; 1851 } 1852 1853 /** 1854 * Sets the {@link DynamicRange} for video capture. 1855 * 1856 * <p>The dynamic range specifies how the range of colors, highlights and shadows that 1857 * are captured by the video producer are displayed on a display. Some dynamic ranges will 1858 * allow the video to make full use of the extended range of brightness of a display when 1859 * the video is played back. 1860 * 1861 * <p>The supported dynamic ranges for video capture can be queried through the 1862 * {@link androidx.camera.video.VideoCapabilities} returned by 1863 * {@link Recorder#getVideoCapabilities(CameraInfo)} via 1864 * {@link androidx.camera.video.VideoCapabilities#getSupportedDynamicRanges()}. 1865 * 1866 * <p>It is possible to choose a high dynamic range (HDR) with unspecified encoding by providing 1867 * {@link DynamicRange#HDR_UNSPECIFIED_10_BIT}. 1868 * 1869 * <p>If the dynamic range is not provided, the default value is {@link DynamicRange#SDR}. 1870 * 1871 * @see VideoCapture.Builder#setDynamicRange(DynamicRange) 1872 */ 1873 @MainThread 1874 public void setVideoCaptureDynamicRange(@NonNull DynamicRange dynamicRange) { 1875 checkMainThread(); 1876 mVideoCaptureDynamicRange = dynamicRange; 1877 unbindVideoAndRecreate(); 1878 startCameraAndTrackStates(); 1879 } 1880 1881 /** 1882 * Gets the {@link DynamicRange} for video capture. 1883 */ 1884 @MainThread 1885 public @NonNull DynamicRange getVideoCaptureDynamicRange() { 1886 checkMainThread(); 1887 return mVideoCaptureDynamicRange; 1888 } 1889 1890 /** 1891 * Sets the target frame rate range in frames per second for video capture. 1892 * 1893 * <p>This target will be used as a part of the heuristics for the algorithm that determines 1894 * the final frame rate range and resolution of all concurrently bound use cases. 1895 * 1896 * <p>It is not guaranteed that this target frame rate will be the final range, 1897 * as other use cases as well as frame rate restrictions of the device may affect the 1898 * outcome of the algorithm that chooses the actual frame rate. 1899 * 1900 * <p>By default, the value is {@link StreamSpec#FRAME_RATE_RANGE_UNSPECIFIED}. For supported 1901 * frame rates, see {@link CameraInfo#getSupportedFrameRateRanges()}. 1902 * 1903 * @see VideoCapture.Builder#setTargetFrameRate(Range) 1904 */ 1905 @MainThread 1906 public void setVideoCaptureTargetFrameRate(@NonNull Range<Integer> targetFrameRate) { 1907 checkMainThread(); 1908 mVideoCaptureTargetFrameRate = targetFrameRate; 1909 unbindVideoAndRecreate(); 1910 startCameraAndTrackStates(); 1911 } 1912 1913 /** 1914 * Gets the target frame rate in frames per second for video capture. 1915 */ 1916 @MainThread 1917 public @NonNull Range<Integer> getVideoCaptureTargetFrameRate() { 1918 checkMainThread(); 1919 return mVideoCaptureTargetFrameRate; 1920 } 1921 1922 /** 1923 * Unbinds VideoCapture and recreate with the latest parameters. 1924 */ 1925 @MainThread 1926 private void unbindVideoAndRecreate() { 1927 if (isCameraInitialized()) { 1928 mCameraProvider.unbind(mVideoCapture); 1929 } 1930 mVideoCapture = createVideoCapture(); 1931 } 1932 1933 private VideoCapture<Recorder> createVideoCapture() { 1934 Recorder.Builder videoRecorderBuilder = new Recorder.Builder().setQualitySelector( 1935 mVideoCaptureQualitySelector); 1936 if (mViewPort != null 1937 && mVideoCaptureQualitySelector == Recorder.DEFAULT_QUALITY_SELECTOR) { 1938 int aspectRatioInt = getViewportAspectRatioInt(mViewPort); 1939 if (aspectRatioInt != AspectRatio.RATIO_DEFAULT) { 1940 videoRecorderBuilder.setAspectRatio(aspectRatioInt); 1941 } 1942 } 1943 1944 return new VideoCapture.Builder<>(videoRecorderBuilder.build()) 1945 .setTargetFrameRate(mVideoCaptureTargetFrameRate) 1946 .setMirrorMode(mVideoCaptureMirrorMode) 1947 .setDynamicRange(mVideoCaptureDynamicRange) 1948 .build(); 1949 } 1950 1951 private @Nullable AspectRatioStrategy getViewportAspectRatioStrategy( 1952 @NonNull ViewPort viewPort) { 1953 int aspectRatioInt = getViewportAspectRatioInt(viewPort); 1954 if (aspectRatioInt != AspectRatio.RATIO_DEFAULT) { 1955 return new AspectRatioStrategy(aspectRatioInt, AspectRatioStrategy.FALLBACK_RULE_AUTO); 1956 } else { 1957 return null; 1958 } 1959 } 1960 1961 /** 1962 * If the aspect ratio of the viewport is one of the {@link AspectRatio.Ratio}, returns it, 1963 * otherwise returns {@link AspectRatio.Ratio#RATIO_DEFAULT}. 1964 */ 1965 @AspectRatio.Ratio 1966 private int getViewportAspectRatioInt(@NonNull ViewPort viewPort) { 1967 int surfaceRotationDegrees = 1968 viewPort == null ? 0 : CameraOrientationUtil.surfaceRotationToDegrees( 1969 viewPort.getRotation()); 1970 int sensorRotationDegrees = 1971 mCameraProvider == null ? 0 : mCameraProvider.getCameraInfo( 1972 mCameraSelector).getSensorRotationDegrees(); 1973 boolean isOppositeFacing = 1974 mCameraProvider == null ? true : mCameraProvider.getCameraInfo( 1975 mCameraSelector).getLensFacing() == CameraSelector.LENS_FACING_BACK; 1976 int relativeRotation = CameraOrientationUtil.getRelativeImageRotation( 1977 surfaceRotationDegrees, sensorRotationDegrees, isOppositeFacing); 1978 Rational aspectRatio = viewPort.getAspectRatio(); 1979 if (relativeRotation == 90 || relativeRotation == 270) { 1980 aspectRatio = new Rational(/* numerator= */ aspectRatio.getDenominator(), 1981 /* denominator= */ aspectRatio.getNumerator()); 1982 } 1983 1984 if (aspectRatio.equals(new Rational(4, 3))) { 1985 return AspectRatio.RATIO_4_3; 1986 } else if (aspectRatio.equals(new Rational(16, 9))) { 1987 return AspectRatio.RATIO_16_9; 1988 } else { 1989 return AspectRatio.RATIO_DEFAULT; 1990 } 1991 } 1992 1993 /** 1994 * Unbinds all the use cases and recreate with the latest parameters. 1995 */ 1996 @MainThread 1997 private void unbindAllAndRecreate() { 1998 unbindPreviewAndRecreate(); 1999 unbindImageCaptureAndRecreate(getImageCaptureMode()); 2000 unbindImageAnalysisAndRecreate(mImageAnalysis.getBackpressureStrategy(), 2001 mImageAnalysis.getImageQueueDepth(), mImageAnalysis.getOutputImageFormat()); 2002 unbindVideoAndRecreate(); 2003 } 2004 2005 // ----------------- 2006 // Camera control 2007 // ----------------- 2008 2009 /** 2010 * Sets the {@link CameraSelector}. 2011 * 2012 * <p>Calling this method with a {@link CameraSelector} that resolves to a different camera 2013 * will change the camera being used by the controller. If camera initialization is complete, 2014 * the controller will immediately rebind use cases with the new {@link CameraSelector}; 2015 * otherwise, the new {@link CameraSelector} will be used when the camera becomes ready. 2016 * 2017 * <p>The default value is {@link CameraSelector#DEFAULT_BACK_CAMERA}. 2018 * 2019 * @throws IllegalStateException If the provided camera selector is unable to resolve a camera 2020 * to be used for the enabled use cases. 2021 * @see CameraSelector 2022 */ 2023 @MainThread 2024 public void setCameraSelector(@NonNull CameraSelector cameraSelector) { 2025 checkMainThread(); 2026 if (mCameraSelector == cameraSelector) { 2027 return; 2028 } 2029 2030 Integer lensFacing = cameraSelector.getLensFacing(); 2031 if (mImageCapture.getFlashMode() == ImageCapture.FLASH_MODE_SCREEN && lensFacing != null 2032 && lensFacing != CameraSelector.LENS_FACING_FRONT) { 2033 throw new IllegalStateException("Not a front camera despite setting FLASH_MODE_SCREEN"); 2034 } 2035 2036 CameraSelector oldCameraSelector = mCameraSelector; 2037 mCameraSelector = cameraSelector; 2038 2039 if (mCameraProvider == null) { 2040 return; 2041 } 2042 mCameraProvider.unbind(mPreview, mImageCapture, mImageAnalysis, mVideoCapture); 2043 startCameraAndTrackStates(() -> mCameraSelector = oldCameraSelector); 2044 } 2045 2046 /** 2047 * Checks if the given {@link CameraSelector} can be resolved to a camera. 2048 * 2049 * <p>Use this method to check if the device has the given camera. 2050 * 2051 * <p>Only call this method after camera is initialized. e.g. after the {@link ListenableFuture} 2052 * from {@link #getInitializationFuture()} is finished. Calling it prematurely throws 2053 * {@link IllegalStateException}. Example: 2054 * 2055 * <pre><code> 2056 * controller.getInitializationFuture().addListener(() -> { 2057 * if (controller.hasCamera(cameraSelector)) { 2058 * controller.setCameraSelector(cameraSelector); 2059 * } else { 2060 * // Update UI if the camera is not available. 2061 * } 2062 * // Attach PreviewView after we know the camera is available. 2063 * previewView.setController(controller); 2064 * }, ContextCompat.getMainExecutor(requireContext())); 2065 * </code></pre> 2066 * 2067 * @return {@code true} if the {@link CameraSelector} can be resolved to a camera. 2068 * @throws IllegalStateException if the camera is not initialized. 2069 */ 2070 @MainThread 2071 public boolean hasCamera(@NonNull CameraSelector cameraSelector) { 2072 checkMainThread(); 2073 Preconditions.checkNotNull(cameraSelector); 2074 2075 if (mCameraProvider == null) { 2076 throw new IllegalStateException("Camera not initialized. Please wait for " 2077 + "the initialization future to finish. See #getInitializationFuture()."); 2078 } 2079 2080 try { 2081 return mCameraProvider.hasCamera(cameraSelector); 2082 } catch (CameraInfoUnavailableException e) { 2083 Logger.w(TAG, "Failed to check camera availability", e); 2084 return false; 2085 } 2086 } 2087 2088 /** 2089 * Gets the {@link CameraSelector}. 2090 * 2091 * <p>The default value is{@link CameraSelector#DEFAULT_BACK_CAMERA}. 2092 * 2093 * @see CameraSelector 2094 */ 2095 @MainThread 2096 public @NonNull CameraSelector getCameraSelector() { 2097 checkMainThread(); 2098 return mCameraSelector; 2099 } 2100 2101 /** 2102 * Returns whether pinch-to-zoom is enabled. 2103 * 2104 * <p>By default pinch-to-zoom is enabled. 2105 * 2106 * @return {@code true} if pinch-to-zoom is enabled. 2107 */ 2108 @MainThread 2109 public boolean isPinchToZoomEnabled() { 2110 checkMainThread(); 2111 return mPinchToZoomEnabled; 2112 } 2113 2114 /** 2115 * Enables/disables pinch-to-zoom. 2116 * 2117 * <p>Once enabled, end user can pinch on the {@link PreviewView} to zoom in/out if the bound 2118 * camera supports zooming. 2119 * 2120 * @param enabled {@code true} to enable pinch-to-zoom. 2121 */ 2122 @MainThread 2123 public void setPinchToZoomEnabled(boolean enabled) { 2124 checkMainThread(); 2125 mPinchToZoomEnabled = enabled; 2126 } 2127 2128 /** 2129 * Called by {@link PreviewView} for a pinch-to-zoom event. 2130 */ 2131 @SuppressWarnings("FutureReturnValueIgnored") 2132 void onPinchToZoom(float pinchToZoomScale) { 2133 if (!isCameraAttached()) { 2134 Logger.w(TAG, CAMERA_NOT_ATTACHED); 2135 return; 2136 } 2137 if (!mPinchToZoomEnabled) { 2138 Logger.d(TAG, "Pinch to zoom disabled."); 2139 return; 2140 } 2141 Logger.d(TAG, "Pinch to zoom with scale: " + pinchToZoomScale); 2142 2143 ZoomState zoomState = getZoomState().getValue(); 2144 if (zoomState == null) { 2145 return; 2146 } 2147 float clampedRatio = zoomState.getZoomRatio() * speedUpZoomBy2X(pinchToZoomScale); 2148 // Clamp the ratio with the zoom range. 2149 clampedRatio = Math.min(Math.max(clampedRatio, zoomState.getMinZoomRatio()), 2150 zoomState.getMaxZoomRatio()); 2151 setZoomRatio(clampedRatio); 2152 } 2153 2154 private float speedUpZoomBy2X(float scaleFactor) { 2155 if (scaleFactor > 1f) { 2156 return 1.0f + (scaleFactor - 1.0f) * 2; 2157 } else { 2158 return 1.0f - (1.0f - scaleFactor) * 2; 2159 } 2160 } 2161 2162 /** 2163 * Called by {@link PreviewView} for a tap-to-focus event. 2164 */ 2165 @SuppressWarnings("FutureReturnValueIgnored") 2166 void onTapToFocus(MeteringPointFactory meteringPointFactory, float x, float y) { 2167 if (!isCameraAttached()) { 2168 Logger.w(TAG, CAMERA_NOT_ATTACHED); 2169 return; 2170 } 2171 if (!mTapToFocusEnabled) { 2172 Logger.d(TAG, "Tap to focus disabled. "); 2173 return; 2174 } 2175 2176 PointF tapPoint = new PointF(x, y); 2177 FocusMeteringAction focusMeteringAction = createFocusMeteringAction(meteringPointFactory, 2178 tapPoint); 2179 2180 Logger.d(TAG, "Tap to focus started: " + x + ", " + y); 2181 2182 // Previous callback closed first so that TAP_TO_FOCUS_STARTED of this operation is not 2183 // overwritten by any event from previous operations. 2184 if (mFocusMeteringResultCallback != null) { 2185 mFocusMeteringResultCallback.close(); 2186 } 2187 2188 mTapToFocusInfoState.postValue(new TapToFocusInfo(TAP_TO_FOCUS_STARTED, tapPoint)); 2189 2190 FocusMeteringResultCallback focusMeteringResultCallback = new FocusMeteringResultCallback( 2191 tapPoint, mTapToFocusInfoState); 2192 mFocusMeteringResultCallback = focusMeteringResultCallback; 2193 Futures.addCallback(mCamera.getCameraControl().startFocusAndMetering(focusMeteringAction), 2194 focusMeteringResultCallback, directExecutor()); 2195 2196 long cancelDuration = TimeUnit.NANOSECONDS.toMillis(mTapToFocusAutoCancelDurationNanos); 2197 Logger.d(TAG, "Tap to focus auto cancel duration: " + cancelDuration + " ms"); 2198 2199 if (cancelDuration > 0L) { 2200 new Handler(Looper.getMainLooper()).postDelayed( 2201 focusMeteringResultCallback::resetStateAndClose, cancelDuration); 2202 } 2203 } 2204 2205 private FocusMeteringAction createFocusMeteringAction(MeteringPointFactory meteringPointFactory, 2206 PointF tapPoint) { 2207 MeteringPoint afPoint = meteringPointFactory.createPoint(tapPoint.x, tapPoint.y, AF_SIZE); 2208 MeteringPoint aePoint = meteringPointFactory.createPoint(tapPoint.x, tapPoint.y, AE_SIZE); 2209 FocusMeteringAction.Builder focusMeteringActionBuilder = new FocusMeteringAction 2210 .Builder(afPoint, FocusMeteringAction.FLAG_AF) 2211 .addPoint(aePoint, FocusMeteringAction.FLAG_AE); 2212 if (mTapToFocusAutoCancelDurationNanos > 0L) { 2213 focusMeteringActionBuilder = focusMeteringActionBuilder.setAutoCancelDuration( 2214 mTapToFocusAutoCancelDurationNanos, TimeUnit.NANOSECONDS); 2215 } else { 2216 focusMeteringActionBuilder = focusMeteringActionBuilder.disableAutoCancel(); 2217 } 2218 return focusMeteringActionBuilder.build(); 2219 } 2220 2221 /** 2222 * Returns whether tap-to-focus is enabled. 2223 * 2224 * <p>By default tap-to-focus is enabled. 2225 * 2226 * @return {@code true} if tap-to-focus is enabled. 2227 */ 2228 @MainThread 2229 public boolean isTapToFocusEnabled() { 2230 checkMainThread(); 2231 return mTapToFocusEnabled; 2232 } 2233 2234 /** 2235 * Enables/disables tap-to-focus. 2236 * 2237 * <p>Once enabled, end user can tap on the {@link PreviewView} to set focus point. 2238 * 2239 * @param enabled {@code true} to enable tap-to-focus. 2240 */ 2241 @MainThread 2242 public void setTapToFocusEnabled(boolean enabled) { 2243 checkMainThread(); 2244 mTapToFocusEnabled = enabled; 2245 } 2246 2247 /** 2248 * Returns a {@link LiveData} with the latest tap-to-focus state. 2249 * 2250 * <p>When tap-to-focus feature is enabled, the {@link LiveData} will receive updates of 2251 * focusing states. This happens when the end user taps on {@link PreviewView}, and then again 2252 * when focusing is finished either successfully or unsuccessfully. The following table 2253 * displays the states the {@link LiveData} can be in, and the possible transitions between 2254 * them. 2255 * 2256 * <table> 2257 * <tr> 2258 * <th>State</th> 2259 * <th>Transition cause</th> 2260 * <th>New State</th> 2261 * </tr> 2262 * <tr> 2263 * <td>TAP_TO_FOCUS_NOT_STARTED</td> 2264 * <td>User taps on {@link PreviewView}</td> 2265 * <td>TAP_TO_FOCUS_STARTED</td> 2266 * </tr> 2267 * <tr> 2268 * <td>TAP_TO_FOCUS_FOCUSED</td> 2269 * <td>User taps on {@link PreviewView}</td> 2270 * <td>TAP_TO_FOCUS_STARTED</td> 2271 * </tr> 2272 * <tr> 2273 * <td>TAP_TO_FOCUS_NOT_FOCUSED</td> 2274 * <td>User taps on {@link PreviewView}</td> 2275 * <td>TAP_TO_FOCUS_STARTED</td> 2276 * </tr> 2277 * <tr> 2278 * <td>TAP_TO_FOCUS_FAILED</td> 2279 * <td>User taps on {@link PreviewView}</td> 2280 * <td>TAP_TO_FOCUS_STARTED</td> 2281 * </tr> 2282 * <tr> 2283 * <td rowspan="3">TAP_TO_FOCUS_STARTED</td> 2284 * <td>Focusing succeeded</td> 2285 * <td>TAP_TO_FOCUS_FOCUSED</td> 2286 * </tr> 2287 * <tr> 2288 * <td>Focusing failed due to lighting and/or camera distance</td> 2289 * <td>TAP_TO_FOCUS_NOT_FOCUSED</td> 2290 * </tr> 2291 * <tr> 2292 * <td>Focusing failed due to device constraints</td> 2293 * <td>TAP_TO_FOCUS_FAILED</td> 2294 * </tr> 2295 * <tr> 2296 * <td>TAP_TO_FOCUS_FOCUSED</td> 2297 * <td>Auto-cancel duration elapses</td> 2298 * <td>TAP_TO_FOCUS_NOT_STARTED</td> 2299 * </tr> 2300 * <tr> 2301 * <td>TAP_TO_FOCUS_NOT_FOCUSED</td> 2302 * <td>Auto-cancel duration elapses</td> 2303 * <td>TAP_TO_FOCUS_NOT_STARTED</td> 2304 * </tr> 2305 * <tr> 2306 * <td>TAP_TO_FOCUS_FAILED</td> 2307 * <td>Auto-cancel duration elapses</td> 2308 * <td>TAP_TO_FOCUS_NOT_STARTED</td> 2309 * </tr> 2310 * </table> 2311 * 2312 * @see #setTapToFocusEnabled(boolean) 2313 * @see CameraControl#startFocusAndMetering(FocusMeteringAction) 2314 * 2315 * @deprecated Use {@link #getTapToFocusInfoState()} instead. 2316 */ 2317 @Deprecated 2318 @MainThread 2319 public @NonNull LiveData<Integer> getTapToFocusState() { 2320 checkMainThread(); 2321 return mTapToFocusState; 2322 } 2323 2324 /** 2325 * Returns a {@link LiveData} with a {@link TapToFocusInfo} containing the latest focus state 2326 * and corresponding tap position. 2327 * 2328 * <p>When tap-to-focus feature is enabled, the {@link LiveData} will receive updates of 2329 * focusing states. This usually happens when the end user taps on {@link PreviewView}, and then 2330 * again when focusing is finished either successfully or unsuccessfully. The following table 2331 * displays the states the {@link LiveData} can be in, and the possible transitions between 2332 * them. 2333 * 2334 * <table> 2335 * <tr> 2336 * <th>State</th> 2337 * <th>Transition cause</th> 2338 * <th>New State</th> 2339 * </tr> 2340 * <tr> 2341 * <td>TAP_TO_FOCUS_NOT_STARTED</td> 2342 * <td>User taps on {@link PreviewView}</td> 2343 * <td>TAP_TO_FOCUS_STARTED</td> 2344 * </tr> 2345 * <tr> 2346 * <td>TAP_TO_FOCUS_FOCUSED</td> 2347 * <td>User taps on {@link PreviewView}</td> 2348 * <td>TAP_TO_FOCUS_STARTED</td> 2349 * </tr> 2350 * <tr> 2351 * <td>TAP_TO_FOCUS_NOT_FOCUSED</td> 2352 * <td>User taps on {@link PreviewView}</td> 2353 * <td>TAP_TO_FOCUS_STARTED</td> 2354 * </tr> 2355 * <tr> 2356 * <td>TAP_TO_FOCUS_FAILED</td> 2357 * <td>User taps on {@link PreviewView}</td> 2358 * <td>TAP_TO_FOCUS_STARTED</td> 2359 * </tr> 2360 * <tr> 2361 * <td rowspan="3">TAP_TO_FOCUS_STARTED</td> 2362 * <td>Focusing succeeded</td> 2363 * <td>TAP_TO_FOCUS_FOCUSED</td> 2364 * </tr> 2365 * <tr> 2366 * <td>Focusing failed due to lighting and/or camera distance</td> 2367 * <td>TAP_TO_FOCUS_NOT_FOCUSED</td> 2368 * </tr> 2369 * <tr> 2370 * <td>Focusing failed due to device constraints</td> 2371 * <td>TAP_TO_FOCUS_FAILED</td> 2372 * </tr> 2373 * <tr> 2374 * <td>TAP_TO_FOCUS_FOCUSED</td> 2375 * <td>Auto-cancel duration elapses</td> 2376 * <td>TAP_TO_FOCUS_NOT_STARTED</td> 2377 * </tr> 2378 * <tr> 2379 * <td>TAP_TO_FOCUS_NOT_FOCUSED</td> 2380 * <td>Auto-cancel duration elapses</td> 2381 * <td>TAP_TO_FOCUS_NOT_STARTED</td> 2382 * </tr> 2383 * <tr> 2384 * <td>TAP_TO_FOCUS_FAILED</td> 2385 * <td>Auto-cancel duration elapses</td> 2386 * <td>TAP_TO_FOCUS_NOT_STARTED</td> 2387 * </tr> 2388 * </table> 2389 * 2390 * @see #setTapToFocusEnabled(boolean) 2391 * @see CameraControl#startFocusAndMetering(FocusMeteringAction) 2392 */ 2393 @MainThread 2394 public @NonNull LiveData<TapToFocusInfo> getTapToFocusInfoState() { 2395 checkMainThread(); 2396 return mTapToFocusInfoState; 2397 } 2398 2399 /** 2400 * Sets the auto-cancel duration for tap-to-focus events. 2401 * 2402 * <p> By default, CameraX uses a value of 5 seconds. 2403 * 2404 * @param duration The duration after which CameraX automatically cancels a tap-to-focus event. 2405 * A value of 0 will disable the auto-cancellation behavior. 2406 * @param timeUnit The {@link TimeUnit} for the {@code duration} parameter. 2407 * 2408 * @see #getTapToFocusInfoState() 2409 */ 2410 @MainThread 2411 public void setTapToFocusAutoCancelDuration( 2412 @IntRange(from = 0) long duration, 2413 @NonNull TimeUnit timeUnit 2414 ) { 2415 Preconditions.checkArgument( 2416 duration >= 0, 2417 "Tap-to-focus auto-cancellation duration must be at least 0" 2418 ); 2419 mTapToFocusAutoCancelDurationNanos = timeUnit.toNanos(duration); 2420 Logger.d(TAG, "setTapToFocusAutoCancelDuration: " + mTapToFocusAutoCancelDurationNanos 2421 + " ns set!"); 2422 } 2423 2424 /** 2425 * Returns a {@link LiveData} of {@link ZoomState}. 2426 * 2427 * <p>The LiveData will be updated whenever the set zoom state has been changed. This can 2428 * occur when the application updates the zoom via {@link #setZoomRatio(float)} 2429 * or {@link #setLinearZoom(float)}. The zoom state can also change anytime a 2430 * camera starts up, for example when {@link #setCameraSelector} is called. 2431 * 2432 * @see CameraInfo#getZoomState() 2433 */ 2434 @MainThread 2435 public @NonNull LiveData<ZoomState> getZoomState() { 2436 checkMainThread(); 2437 return mZoomState; 2438 } 2439 2440 /** 2441 * Gets the {@link CameraInfo} of the currently attached camera. 2442 * 2443 * <p>For info available directly through CameraController as well as {@link CameraInfo}, it's 2444 * recommended to use the ones with CameraController, e.g. {@link #getTorchState()} v.s. 2445 * {@link CameraInfo#getTorchState()}. {@link CameraInfo} is a lower-layer API and may require 2446 * more steps to achieve the same effect, and will not maintain values when switching between 2447 * cameras. 2448 * 2449 * @return The {@link CameraInfo} of the current camera. Returns {@code null} if camera is not 2450 * ready. 2451 * @see Camera#getCameraInfo() 2452 */ 2453 @MainThread 2454 public @Nullable CameraInfo getCameraInfo() { 2455 checkMainThread(); 2456 return mCamera == null ? null : mCamera.getCameraInfo(); 2457 } 2458 2459 /** 2460 * Gets the {@link CameraControl} of the currently attached camera. 2461 * 2462 * <p>For controls available directly through CameraController as well as 2463 * {@link CameraControl}, it's recommended to use the ones with CameraController, e.g. 2464 * {@link #setLinearZoom(float)} v.s. {@link CameraControl#setLinearZoom(float)}. 2465 * CameraControl is a lower-layer API and may require more steps to achieve the same effect, 2466 * and will not maintain control values when switching between cameras. 2467 * 2468 * @return The {@link CameraControl} of the current camera. Returns {@code null} if camera is 2469 * not ready. 2470 * @see Camera#getCameraControl() 2471 */ 2472 @MainThread 2473 public @Nullable CameraControl getCameraControl() { 2474 checkMainThread(); 2475 return mCamera == null ? null : mCamera.getCameraControl(); 2476 } 2477 2478 /** 2479 * Sets current zoom by ratio. 2480 * 2481 * <p>Valid zoom values range from {@link ZoomState#getMinZoomRatio()} to 2482 * {@link ZoomState#getMaxZoomRatio()}. 2483 * 2484 * <p>If the value is set before the camera is ready, {@link CameraController} waits for the 2485 * camera to be ready and then sets the zoom ratio. 2486 * 2487 * @param zoomRatio The requested zoom ratio. 2488 * @return A {@link ListenableFuture} which is finished when camera is set to the given ratio. 2489 * It fails with {@link CameraControl.OperationCanceledException} if there is newer value 2490 * being set or camera is closed. If the ratio is out of range, it fails with 2491 * {@link IllegalArgumentException}. Cancellation of this future is a no-op. 2492 * @see #getZoomState() 2493 * @see CameraControl#setZoomRatio(float) 2494 */ 2495 @MainThread 2496 public @NonNull ListenableFuture<Void> setZoomRatio(float zoomRatio) { 2497 checkMainThread(); 2498 if (!isCameraAttached()) { 2499 return mPendingZoomRatio.setValue(zoomRatio); 2500 } 2501 return mCamera.getCameraControl().setZoomRatio(zoomRatio); 2502 } 2503 2504 /** 2505 * Sets current zoom by a linear zoom value ranging from 0f to 1.0f. 2506 * 2507 * <p>LinearZoom 0f represents the minimum zoom while linearZoom 1.0f represents the maximum 2508 * zoom. The advantage of linearZoom is that it ensures the field of view (FOV) varies 2509 * linearly with the linearZoom value, for use with slider UI elements (while 2510 * {@link #setZoomRatio(float)} works well for pinch-zoom gestures). 2511 * 2512 * <p>If the value is set before the camera is ready, {@link CameraController} waits for the 2513 * camera to be ready and then sets the linear zoom. 2514 * 2515 * @return A {@link ListenableFuture} which is finished when camera is set to the given ratio. 2516 * It fails with {@link CameraControl.OperationCanceledException} if there is newer value 2517 * being set or camera is closed. If the ratio is out of range, it fails with 2518 * {@link IllegalArgumentException}. Cancellation of this future is a no-op. 2519 * @see CameraControl#setLinearZoom(float) 2520 */ 2521 @MainThread 2522 public @NonNull ListenableFuture<Void> setLinearZoom( 2523 @FloatRange(from = 0f, to = 1f) float linearZoom) { 2524 checkMainThread(); 2525 if (!isCameraAttached()) { 2526 return mPendingLinearZoom.setValue(linearZoom); 2527 } 2528 return mCamera.getCameraControl().setLinearZoom(linearZoom); 2529 } 2530 2531 /** 2532 * Returns a {@link LiveData} of current {@link TorchState}. 2533 * 2534 * <p>The torch can be turned on and off via {@link #enableTorch(boolean)} which will trigger 2535 * the change event to the returned {@link LiveData}. 2536 * 2537 * @return A {@link LiveData} containing current torch state. 2538 * @see CameraInfo#getTorchState() 2539 */ 2540 @MainThread 2541 public @NonNull LiveData<Integer> getTorchState() { 2542 checkMainThread(); 2543 return mTorchState; 2544 } 2545 2546 /** 2547 * Enable the torch or disable the torch. 2548 * 2549 * <p>If the value is set before the camera is ready, {@link CameraController} waits for the 2550 * camera to be ready and then enables the torch. 2551 * 2552 * @param torchEnabled {@code true} to turn on the torch, {@code false} to turn it off. 2553 * @return A {@link ListenableFuture} which is successful when the torch was changed to the 2554 * value specified. It fails when it is unable to change the torch state. Cancellation of 2555 * this future is a no-op. 2556 * @see CameraControl#enableTorch(boolean) 2557 */ 2558 @MainThread 2559 public @NonNull ListenableFuture<Void> enableTorch(boolean torchEnabled) { 2560 checkMainThread(); 2561 if (!isCameraAttached()) { 2562 return mPendingEnableTorch.setValue(torchEnabled); 2563 } 2564 return mCamera.getCameraControl().enableTorch(torchEnabled); 2565 } 2566 2567 // ------------------------ 2568 // Effects and extensions 2569 // ------------------------ 2570 2571 /** 2572 * Sets {@link CameraEffect}. 2573 * 2574 * <p>Call this method to set a list of active effects. There is maximum one effect per 2575 * {@link UseCase}. Adding effects with duplicate or invalid targets throws 2576 * {@link IllegalArgumentException}. Once called, CameraX will rebind the {@link UseCase} 2577 * with the effects applied. Effects not in the list are automatically removed. 2578 * 2579 * <p>The method throws {@link IllegalArgumentException} if the effects combination is not 2580 * supported by CameraX. Please see the Javadoc of {@link UseCaseGroup.Builder#addEffect} to 2581 * see the supported effects combinations. 2582 * 2583 * @param effects The effects applied to camera output. 2584 * @throws IllegalArgumentException if the combination of effects is not supported by CameraX. 2585 * @see UseCaseGroup.Builder#addEffect 2586 */ 2587 @MainThread 2588 public void setEffects(@NonNull Set<CameraEffect> effects) { 2589 checkMainThread(); 2590 if (Objects.equals(mEffects, effects)) { 2591 // Same effect. No change needed. 2592 return; 2593 } 2594 if (mCameraProvider != null) { 2595 // Unbind to make sure the pipelines will be recreated. 2596 mCameraProvider.unbindAll(); 2597 } 2598 mEffects.clear(); 2599 mEffects.addAll(effects); 2600 startCameraAndTrackStates(); 2601 } 2602 2603 /** 2604 * Removes all effects. 2605 * 2606 * <p>Once called, CameraX will remove all the effects and rebind the {@link UseCase}. 2607 */ 2608 @MainThread 2609 public void clearEffects() { 2610 checkMainThread(); 2611 if (mCameraProvider != null) { 2612 // Unbind to make sure the pipelines will be recreated. 2613 mCameraProvider.unbindAll(); 2614 } 2615 mEffects.clear(); 2616 startCameraAndTrackStates(); 2617 } 2618 2619 // ------------------------------ 2620 // Binding to lifecycle 2621 // ------------------------------ 2622 2623 /** 2624 * Binds use cases, gets a new {@link Camera} instance and tracks the state of the camera. 2625 */ 2626 void startCameraAndTrackStates() { 2627 startCameraAndTrackStates(null); 2628 } 2629 2630 /** 2631 * @param restoreStateRunnable {@link Runnable} to restore the controller to the previous good 2632 * state if the binding fails. 2633 * @throws IllegalStateException for invalid {@link UseCase} combinations. 2634 * @throws RuntimeException for invalid {@link CameraEffect} combinations. 2635 */ 2636 void startCameraAndTrackStates(@Nullable Runnable restoreStateRunnable) { 2637 try { 2638 mCamera = startCamera(); 2639 } catch (RuntimeException exception) { 2640 // Restore the previous state before re-throwing the exception. 2641 if (restoreStateRunnable != null) { 2642 restoreStateRunnable.run(); 2643 } 2644 throw exception; 2645 } 2646 if (!isCameraAttached()) { 2647 Logger.d(TAG, CAMERA_NOT_ATTACHED); 2648 return; 2649 } 2650 mZoomState.setSource(mCamera.getCameraInfo().getZoomState()); 2651 mTorchState.setSource(mCamera.getCameraInfo().getTorchState()); 2652 mPendingEnableTorch.propagateIfHasValue(this::enableTorch); 2653 mPendingLinearZoom.propagateIfHasValue(this::setLinearZoom); 2654 mPendingZoomRatio.propagateIfHasValue(this::setZoomRatio); 2655 } 2656 2657 /** 2658 * Creates {@link UseCaseGroup} from all the use cases. 2659 * 2660 * <p>Preview is required. If it is {@code null}, then controller is not ready. Return 2661 * {@code null} and ignore other use cases. 2662 */ 2663 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 2664 protected @Nullable UseCaseGroup createUseCaseGroup() { 2665 if (!isCameraInitialized()) { 2666 Logger.d(TAG, CAMERA_NOT_INITIALIZED); 2667 return null; 2668 } 2669 if (!isPreviewViewAttached()) { 2670 // Preview is required. Return early if preview Surface is not ready. 2671 Logger.d(TAG, PREVIEW_VIEW_NOT_ATTACHED); 2672 return null; 2673 } 2674 2675 UseCaseGroup.Builder builder = new UseCaseGroup.Builder().addUseCase(mPreview); 2676 2677 if (isImageCaptureEnabled()) { 2678 builder.addUseCase(mImageCapture); 2679 } else { 2680 mCameraProvider.unbind(mImageCapture); 2681 } 2682 2683 if (isImageAnalysisEnabled()) { 2684 builder.addUseCase(mImageAnalysis); 2685 } else { 2686 mCameraProvider.unbind(mImageAnalysis); 2687 } 2688 2689 if (isVideoCaptureEnabled()) { 2690 builder.addUseCase(mVideoCapture); 2691 } else { 2692 mCameraProvider.unbind(mVideoCapture); 2693 } 2694 2695 builder.setViewPort(mViewPort); 2696 for (CameraEffect effect : mEffects) { 2697 builder.addEffect(effect); 2698 } 2699 return builder.build(); 2700 } 2701 2702 /** 2703 * Represents the output size of a {@link UseCase}. 2704 * 2705 * <p>This class is a preferred output size to be used with {@link CameraController}. The 2706 * preferred output size can be based on either resolution or aspect ratio, but not both. 2707 * 2708 * @see #setImageAnalysisTargetSize(OutputSize) 2709 * @see #setPreviewTargetSize(OutputSize) 2710 * @see #setImageCaptureTargetSize(OutputSize) 2711 * @deprecated Use {@link ResolutionSelector} instead. 2712 */ 2713 @Deprecated 2714 public static final class OutputSize { 2715 2716 /** 2717 * A value that represents the aspect ratio is not assigned. 2718 */ 2719 public static final int UNASSIGNED_ASPECT_RATIO = -1; 2720 2721 /** 2722 * Possible value for {@link #getAspectRatio()} 2723 */ 2724 @RestrictTo(RestrictTo.Scope.LIBRARY) 2725 @Retention(RetentionPolicy.SOURCE) 2726 @IntDef(value = {UNASSIGNED_ASPECT_RATIO, AspectRatio.RATIO_4_3, AspectRatio.RATIO_16_9}) 2727 public @interface OutputAspectRatio { 2728 } 2729 2730 @OutputAspectRatio 2731 private final int mAspectRatio; 2732 2733 private final @Nullable Size mResolution; 2734 2735 /** 2736 * Creates a {@link OutputSize} that is based on aspect ratio. 2737 * 2738 * @see Preview.Builder#setTargetAspectRatio(int) 2739 * @see ImageAnalysis.Builder#setTargetAspectRatio(int) 2740 */ 2741 public OutputSize(@AspectRatio.Ratio int aspectRatio) { 2742 Preconditions.checkArgument(aspectRatio != UNASSIGNED_ASPECT_RATIO); 2743 mAspectRatio = aspectRatio; 2744 mResolution = null; 2745 } 2746 2747 /** 2748 * Creates a {@link OutputSize} that is based on resolution. 2749 * 2750 * @see Preview.Builder#setTargetResolution(Size) 2751 * @see ImageAnalysis.Builder#setTargetResolution(Size) 2752 */ 2753 public OutputSize(@NonNull Size resolution) { 2754 Preconditions.checkNotNull(resolution); 2755 mAspectRatio = UNASSIGNED_ASPECT_RATIO; 2756 mResolution = resolution; 2757 } 2758 2759 /** 2760 * Gets the value of aspect ratio. 2761 * 2762 * @return {@link #UNASSIGNED_ASPECT_RATIO} if the size is not based on aspect ratio. 2763 */ 2764 @OutputAspectRatio 2765 public int getAspectRatio() { 2766 return mAspectRatio; 2767 } 2768 2769 /** 2770 * Gets the value of resolution. 2771 * 2772 * @return {@code null} if the size is not based on resolution. 2773 */ 2774 public @Nullable Size getResolution() { 2775 return mResolution; 2776 } 2777 2778 @Override 2779 public @NonNull String toString() { 2780 return "aspect ratio: " + mAspectRatio + " resolution: " + mResolution; 2781 } 2782 } 2783 2784 static class FocusMeteringResultCallback implements FutureCallback<FocusMeteringResult> { 2785 private boolean mIsCanceled = false; 2786 private final PointF mTapPoint; 2787 private final MutableLiveData<TapToFocusInfo> mTapToFocusInfoState; 2788 2789 private final Object mLock = new Object(); 2790 2791 FocusMeteringResultCallback(PointF tapPoint, 2792 MutableLiveData<TapToFocusInfo> tapToFocusInfoState) { 2793 mTapPoint = tapPoint; 2794 mTapToFocusInfoState = tapToFocusInfoState; 2795 } 2796 2797 @Override 2798 public void onSuccess(@Nullable FocusMeteringResult result) { 2799 synchronized (mLock) { 2800 if (mIsCanceled) return; 2801 2802 if (result == null) { 2803 return; 2804 } 2805 2806 Logger.d(TAG, "Tap-to-focus onSuccess: " + result.isFocusSuccessful()); 2807 mTapToFocusInfoState.postValue(new TapToFocusInfo( 2808 result.isFocusSuccessful() ? TAP_TO_FOCUS_FOCUSED 2809 : TAP_TO_FOCUS_NOT_FOCUSED, mTapPoint)); 2810 } 2811 } 2812 2813 @Override 2814 public void onFailure(@NonNull Throwable t) { 2815 synchronized (mLock) { 2816 if (mIsCanceled) return; 2817 2818 if (t instanceof CameraControl.OperationCanceledException) { 2819 Logger.d(TAG, "Tap-to-focus canceled", t); 2820 2821 // Resetting focus state, also closing earlier to avoid multiple reset updates. 2822 mTapToFocusInfoState.postValue( 2823 new TapToFocusInfo(TAP_TO_FOCUS_NOT_STARTED, null)); 2824 close(); 2825 2826 return; 2827 } 2828 2829 Logger.d(TAG, "Tap-to-focus failed.", t); 2830 mTapToFocusInfoState.postValue( 2831 new TapToFocusInfo(TAP_TO_FOCUS_FAILED, mTapPoint)); 2832 } 2833 } 2834 2835 /** 2836 * Resets the tap-to-focus state and closes this callback class. 2837 * 2838 * <P> This method, like the other callback methods in this class, is no-op if the class has 2839 * already been closed once. 2840 * 2841 * @see #close 2842 */ 2843 void resetStateAndClose() { 2844 synchronized (mLock) { 2845 if (mIsCanceled) return; 2846 2847 Logger.d(TAG, "Tap-to-focus reset."); 2848 mTapToFocusInfoState.postValue(new TapToFocusInfo(TAP_TO_FOCUS_NOT_STARTED, null)); 2849 mIsCanceled = true; 2850 } 2851 } 2852 2853 /** Closes the callback class to ignore all future callback invocations. */ 2854 void close() { 2855 synchronized (mLock) { 2856 mIsCanceled = true; 2857 } 2858 } 2859 } 2860 } 2861