1 /* 2 * Copyright 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.camera.camera2.internal; 18 19 import android.graphics.PointF; 20 import android.graphics.Rect; 21 import android.hardware.camera2.CameraCharacteristics; 22 import android.hardware.camera2.CameraDevice; 23 import android.hardware.camera2.CaptureRequest; 24 import android.hardware.camera2.CaptureResult; 25 import android.hardware.camera2.params.MeteringRectangle; 26 import android.os.Build; 27 import android.util.Log; 28 import android.util.Rational; 29 30 import androidx.annotation.OptIn; 31 import androidx.annotation.RequiresApi; 32 import androidx.annotation.VisibleForTesting; 33 import androidx.camera.camera2.impl.Camera2ImplConfig; 34 import androidx.camera.camera2.internal.annotation.CameraExecutor; 35 import androidx.camera.camera2.internal.compat.workaround.MeteringRegionCorrection; 36 import androidx.camera.camera2.interop.ExperimentalCamera2Interop; 37 import androidx.camera.core.CameraControl; 38 import androidx.camera.core.FocusMeteringAction; 39 import androidx.camera.core.FocusMeteringResult; 40 import androidx.camera.core.ImageCapture; 41 import androidx.camera.core.Logger; 42 import androidx.camera.core.MeteringPoint; 43 import androidx.camera.core.impl.CameraCaptureCallback; 44 import androidx.camera.core.impl.CameraCaptureFailure; 45 import androidx.camera.core.impl.CameraCaptureResult; 46 import androidx.camera.core.impl.CameraControlInternal; 47 import androidx.camera.core.impl.CaptureConfig; 48 import androidx.camera.core.impl.Config; 49 import androidx.camera.core.impl.Quirks; 50 import androidx.camera.core.impl.annotation.ExecutedBy; 51 import androidx.camera.core.impl.utils.futures.Futures; 52 import androidx.concurrent.futures.CallbackToFutureAdapter; 53 import androidx.concurrent.futures.CallbackToFutureAdapter.Completer; 54 55 import com.google.common.util.concurrent.ListenableFuture; 56 57 import org.jspecify.annotations.NonNull; 58 import org.jspecify.annotations.Nullable; 59 60 import java.util.ArrayList; 61 import java.util.Collections; 62 import java.util.List; 63 import java.util.concurrent.Executor; 64 import java.util.concurrent.ScheduledExecutorService; 65 import java.util.concurrent.ScheduledFuture; 66 import java.util.concurrent.TimeUnit; 67 68 /** 69 * Implementation of focus and metering. 70 * 71 * <p>It is intended to be used within {@link Camera2CameraControlImpl} to implement the 72 * functionality of {@link Camera2CameraControlImpl#startFocusAndMetering(FocusMeteringAction)} and 73 * {@link Camera2CameraControlImpl#cancelFocusAndMetering()}. This class depends on 74 * {@link Camera2CameraControlImpl} to provide some low-level methods such as updateSessionConfig, 75 * triggerAfInternal and cancelAfAeTriggerInternal to achieve the focus and metering functions. 76 * 77 * <p>To wait for the auto-focus lock, it calls 78 * {@link Camera2CameraControlImpl 79 * #addCaptureResultListener(Camera2CameraControlImpl.CaptureResultListener)} 80 * to monitor the capture result. It also requires {@link ScheduledExecutorService} to schedule the 81 * auto-cancel event and {@link Executor} to ensure all the methods within this class are called 82 * in the same thread as the Camera2CameraControlImpl. 83 * 84 * <p>The {@link Camera2CameraControlImpl} calls 85 * {@link FocusMeteringControl#addFocusMeteringOptions} to construct the 3A regions and append 86 * them to all repeating requests and single requests. 87 */ 88 @OptIn(markerClass = ExperimentalCamera2Interop.class) 89 class FocusMeteringControl { 90 private static final String TAG = "FocusMeteringControl"; 91 92 static final long AUTO_FOCUS_TIMEOUT_DURATION = 5000; 93 private final Camera2CameraControlImpl mCameraControl; 94 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ 95 @CameraExecutor 96 final Executor mExecutor; 97 private final ScheduledExecutorService mScheduler; 98 private volatile boolean mIsActive = false; 99 private volatile Rational mPreviewAspectRatio = null; 100 private static final MeteringRectangle[] EMPTY_RECTANGLES = new MeteringRectangle[0]; 101 private final @NonNull MeteringRegionCorrection mMeteringRegionCorrection; 102 103 //******************** Should only be accessed by executor (WorkThread) ****************// 104 private boolean mIsInAfAutoMode = false; 105 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ 106 @NonNull Integer mCurrentAfState = CaptureResult.CONTROL_AF_STATE_INACTIVE; 107 private ScheduledFuture<?> mAutoCancelHandle; 108 private ScheduledFuture<?> mAutoFocusTimeoutHandle; 109 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ 110 long mFocusTimeoutCounter = 0; 111 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ 112 boolean mIsAutoFocusCompleted = false; 113 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ 114 boolean mIsFocusSuccessful = false; 115 private int mTemplate = CameraDevice.TEMPLATE_PREVIEW; 116 117 private Camera2CameraControlImpl.CaptureResultListener mSessionListenerForFocus = null; 118 private Camera2CameraControlImpl.CaptureResultListener mSessionListenerForCancel = null; 119 private MeteringRectangle[] mAfRects = EMPTY_RECTANGLES; 120 private MeteringRectangle[] mAeRects = EMPTY_RECTANGLES; 121 private MeteringRectangle[] mAwbRects = EMPTY_RECTANGLES; 122 CallbackToFutureAdapter.Completer<FocusMeteringResult> mRunningActionCompleter = null; 123 CallbackToFutureAdapter.Completer<Void> mRunningCancelCompleter = null; 124 125 private boolean mIsExternalFlashAeModeEnabled = false; 126 private Camera2CameraControlImpl.CaptureResultListener mSessionListenerForAeMode = null; 127 //**************************************************************************************// 128 129 130 /** 131 * Constructs a FocusMeteringControl. 132 * 133 * <p>All tasks scheduled by {@code scheduler} will be immediately executed by {@code executor}. 134 * 135 * @param cameraControl Camera control to which this FocusMeteringControl belongs. 136 * @param scheduler Scheduler used for scheduling tasks in the future. 137 * @param executor Camera executor used to run all tasks scheduled on {@code scheduler}. 138 */ FocusMeteringControl(@onNull Camera2CameraControlImpl cameraControl, @NonNull ScheduledExecutorService scheduler, @CameraExecutor @NonNull Executor executor, @NonNull Quirks cameraQuirks)139 FocusMeteringControl(@NonNull Camera2CameraControlImpl cameraControl, 140 @NonNull ScheduledExecutorService scheduler, 141 @CameraExecutor @NonNull Executor executor, 142 @NonNull Quirks cameraQuirks) { 143 mCameraControl = cameraControl; 144 mExecutor = executor; 145 mScheduler = scheduler; 146 mMeteringRegionCorrection = new MeteringRegionCorrection(cameraQuirks); 147 } 148 149 /** 150 * Set current active state. Set active if it is ready to accept focus/metering operations. 151 * 152 * <p> In inactive state, startFocusAndMetering does nothing while cancelFocusAndMetering 153 * still works to cancel current operation. cancelFocusAndMetering is performed automatically 154 * when active state is changed to false. 155 */ 156 @ExecutedBy("mExecutor") setActive(boolean isActive)157 void setActive(boolean isActive) { 158 if (isActive == mIsActive) { 159 return; 160 } 161 162 mIsActive = isActive; 163 164 if (!mIsActive) { 165 cancelFocusAndMeteringWithoutAsyncResult(); 166 } 167 } 168 setPreviewAspectRatio(@ullable Rational previewAspectRatio)169 public void setPreviewAspectRatio(@Nullable Rational previewAspectRatio) { 170 mPreviewAspectRatio = previewAspectRatio; 171 } 172 173 // Preview aspect ratio is used if SurfaceAspectRatio is not specified in MeteringPoint. getDefaultAspectRatio()174 private Rational getDefaultAspectRatio() { 175 if (mPreviewAspectRatio != null) { 176 return mPreviewAspectRatio; 177 } 178 179 Rect cropSensorRegion = mCameraControl.getCropSensorRegion(); 180 return new Rational(cropSensorRegion.width(), cropSensorRegion.height()); 181 } 182 183 @ExecutedBy("mExecutor") setTemplate(int template)184 void setTemplate(int template) { 185 mTemplate = template; 186 } 187 188 /** 189 * Called by {@link Camera2CameraControlImpl} to append the 3A regions to the shared options. It 190 * applies to all repeating requests and single requests. 191 */ 192 @ExecutedBy("mExecutor") addFocusMeteringOptions(Camera2ImplConfig.@onNull Builder configBuilder)193 void addFocusMeteringOptions(Camera2ImplConfig.@NonNull Builder configBuilder) { 194 195 int afMode = mIsInAfAutoMode 196 ? CaptureRequest.CONTROL_AF_MODE_AUTO 197 : getDefaultAfMode(); 198 199 configBuilder.setCaptureRequestOptionWithPriority(CaptureRequest.CONTROL_AF_MODE, 200 mCameraControl.getSupportedAfMode(afMode), Config.OptionPriority.REQUIRED); 201 202 if (mAfRects.length != 0) { 203 configBuilder.setCaptureRequestOptionWithPriority(CaptureRequest.CONTROL_AF_REGIONS, 204 mAfRects, Config.OptionPriority.REQUIRED); 205 } 206 if (mAeRects.length != 0) { 207 configBuilder.setCaptureRequestOptionWithPriority(CaptureRequest.CONTROL_AE_REGIONS, 208 mAeRects, Config.OptionPriority.REQUIRED); 209 } 210 if (mAwbRects.length != 0) { 211 configBuilder.setCaptureRequestOptionWithPriority(CaptureRequest.CONTROL_AWB_REGIONS, 212 mAwbRects, Config.OptionPriority.REQUIRED); 213 } 214 } 215 isValid(final @NonNull MeteringPoint pt)216 private static boolean isValid(final @NonNull MeteringPoint pt) { 217 return pt.getX() >= 0f && pt.getX() <= 1f && pt.getY() >= 0f && pt.getY() <= 1f; 218 } 219 getFovAdjustedPoint(@onNull MeteringPoint meteringPoint, @NonNull Rational cropRegionAspectRatio, @NonNull Rational defaultAspectRatio, @FocusMeteringAction.MeteringMode int meteringMode, MeteringRegionCorrection correction)220 private static PointF getFovAdjustedPoint(@NonNull MeteringPoint meteringPoint, 221 @NonNull Rational cropRegionAspectRatio, 222 @NonNull Rational defaultAspectRatio, 223 @FocusMeteringAction.MeteringMode int meteringMode, 224 MeteringRegionCorrection correction) { 225 // Use default aspect ratio unless there is a custom aspect ratio in MeteringPoint. 226 Rational fovAspectRatio = defaultAspectRatio; 227 if (meteringPoint.getSurfaceAspectRatio() != null) { 228 fovAspectRatio = meteringPoint.getSurfaceAspectRatio(); 229 } 230 231 PointF adjustedPoint = correction.getCorrectedPoint(meteringPoint, meteringMode); 232 if (!fovAspectRatio.equals(cropRegionAspectRatio)) { 233 if (fovAspectRatio.compareTo(cropRegionAspectRatio) > 0) { 234 // FOV is more narrow than crop region, top and down side of FOV is cropped. 235 float heightOfCropRegion = 236 (float) (fovAspectRatio.doubleValue() 237 / cropRegionAspectRatio.doubleValue()); 238 float top_padding = (float) ((heightOfCropRegion - 1.0) / 2); 239 adjustedPoint.y = (top_padding + adjustedPoint.y) * (1 / heightOfCropRegion); 240 241 } else { 242 // FOV is wider than crop region, left and right side of FOV is cropped. 243 float widthOfCropRegion = 244 (float) (cropRegionAspectRatio.doubleValue() 245 / fovAspectRatio.doubleValue()); 246 float left_padding = (float) ((widthOfCropRegion - 1.0) / 2); 247 adjustedPoint.x = (left_padding + adjustedPoint.x) * (1f / widthOfCropRegion); 248 } 249 } 250 251 return adjustedPoint; 252 } 253 getMeteringRect(MeteringPoint meteringPoint, PointF adjustedPoint, Rect cropRegion)254 private static MeteringRectangle getMeteringRect(MeteringPoint meteringPoint, 255 PointF adjustedPoint, Rect cropRegion) { 256 int centerX = (int) (cropRegion.left + adjustedPoint.x * cropRegion.width()); 257 int centerY = (int) (cropRegion.top + adjustedPoint.y * cropRegion.height()); 258 259 int width = (int) (meteringPoint.getSize() * cropRegion.width()); 260 int height = (int) (meteringPoint.getSize() * cropRegion.height()); 261 262 Rect focusRect = new Rect(centerX - width / 2, centerY - height / 2, centerX + width / 2, 263 centerY + height / 2); 264 265 focusRect.left = rangeLimit(focusRect.left, cropRegion.right, cropRegion.left); 266 focusRect.right = rangeLimit(focusRect.right, cropRegion.right, cropRegion.left); 267 focusRect.top = rangeLimit(focusRect.top, cropRegion.bottom, cropRegion.top); 268 focusRect.bottom = rangeLimit(focusRect.bottom, cropRegion.bottom, cropRegion.top); 269 270 return new MeteringRectangle(focusRect, MeteringRectangle.METERING_WEIGHT_MAX); 271 } 272 rangeLimit(int val, int max, int min)273 private static int rangeLimit(int val, int max, int min) { 274 return Math.min(Math.max(val, min), max); 275 } 276 startFocusAndMetering( @onNull FocusMeteringAction action)277 @NonNull ListenableFuture<FocusMeteringResult> startFocusAndMetering( 278 @NonNull FocusMeteringAction action) { 279 return startFocusAndMetering(action, AUTO_FOCUS_TIMEOUT_DURATION); 280 } 281 282 @VisibleForTesting startFocusAndMetering( @onNull FocusMeteringAction action, long timeoutDurationMs)283 @NonNull ListenableFuture<FocusMeteringResult> startFocusAndMetering( 284 @NonNull FocusMeteringAction action, long timeoutDurationMs) { 285 return CallbackToFutureAdapter.getFuture(completer -> { 286 mExecutor.execute( 287 () -> startFocusAndMeteringInternal(completer, action, timeoutDurationMs)); 288 return "startFocusAndMetering"; 289 }); 290 } 291 getMeteringRectangles( @onNull List<MeteringPoint> meteringPoints, int maxRegionCount, @NonNull Rational defaultAspectRatio, @NonNull Rect cropSensorRegion, @FocusMeteringAction.MeteringMode int meteringMode)292 private @NonNull List<MeteringRectangle> getMeteringRectangles( 293 @NonNull List<MeteringPoint> meteringPoints, 294 int maxRegionCount, 295 @NonNull Rational defaultAspectRatio, 296 @NonNull Rect cropSensorRegion, 297 @FocusMeteringAction.MeteringMode int meteringMode) { 298 if (meteringPoints.isEmpty() || maxRegionCount == 0) { 299 return Collections.emptyList(); 300 } 301 302 List<MeteringRectangle> meteringRectanglesList = new ArrayList<>(); 303 Rational cropRegionAspectRatio = new Rational(cropSensorRegion.width(), 304 cropSensorRegion.height()); 305 for (MeteringPoint meteringPoint : meteringPoints) { 306 // Only enable at most maxRegionCount. 307 if (meteringRectanglesList.size() == maxRegionCount) { 308 break; 309 } 310 if (!isValid(meteringPoint)) { 311 continue; 312 } 313 314 PointF adjustedPoint = getFovAdjustedPoint(meteringPoint, cropRegionAspectRatio, 315 defaultAspectRatio, meteringMode, mMeteringRegionCorrection); 316 MeteringRectangle meteringRectangle = getMeteringRect(meteringPoint, adjustedPoint, 317 cropSensorRegion); 318 if (meteringRectangle.getWidth() == 0 || meteringRectangle.getHeight() == 0) { 319 continue; 320 } 321 meteringRectanglesList.add(meteringRectangle); 322 } 323 324 return Collections.unmodifiableList(meteringRectanglesList); 325 } 326 327 @ExecutedBy("mExecutor") startFocusAndMeteringInternal(@onNull Completer<FocusMeteringResult> completer, @NonNull FocusMeteringAction action, long timeoutDurationMs)328 void startFocusAndMeteringInternal(@NonNull Completer<FocusMeteringResult> completer, 329 @NonNull FocusMeteringAction action, long timeoutDurationMs) { 330 if (!mIsActive) { 331 completer.setException( 332 new CameraControl.OperationCanceledException("Camera is not active.")); 333 return; 334 } 335 336 Rect cropSensorRegion = mCameraControl.getCropSensorRegion(); 337 Rational defaultAspectRatio = getDefaultAspectRatio(); 338 List<MeteringRectangle> rectanglesAf = 339 getMeteringRectangles(action.getMeteringPointsAf(), 340 mCameraControl.getMaxAfRegionCount(), 341 defaultAspectRatio, cropSensorRegion, FocusMeteringAction.FLAG_AF); 342 List<MeteringRectangle> rectanglesAe = 343 getMeteringRectangles(action.getMeteringPointsAe(), 344 mCameraControl.getMaxAeRegionCount(), 345 defaultAspectRatio, cropSensorRegion, FocusMeteringAction.FLAG_AE); 346 List<MeteringRectangle> rectanglesAwb = 347 getMeteringRectangles(action.getMeteringPointsAwb(), 348 mCameraControl.getMaxAwbRegionCount(), 349 defaultAspectRatio, cropSensorRegion, FocusMeteringAction.FLAG_AWB); 350 351 if (rectanglesAf.isEmpty() && rectanglesAe.isEmpty() && rectanglesAwb.isEmpty()) { 352 completer.setException( 353 new IllegalArgumentException("None of the specified AF/AE/AWB MeteringPoints " 354 + "is supported on this camera.")); 355 return; 356 } 357 358 failActionFuture("Cancelled by another startFocusAndMetering()"); 359 failCancelFuture("Cancelled by another startFocusAndMetering()"); 360 disableAutoCancel(); 361 mRunningActionCompleter = completer; 362 363 executeMeteringAction( 364 rectanglesAf.toArray(EMPTY_RECTANGLES), 365 rectanglesAe.toArray(EMPTY_RECTANGLES), 366 rectanglesAwb.toArray(EMPTY_RECTANGLES), 367 action, 368 timeoutDurationMs 369 ); 370 } 371 372 /** 373 * Trigger an AF scan. 374 * 375 * @param completer used to complete the associated {@link ListenableFuture} when the 376 * operation succeeds or fails. Passing null to simply ignore the result. 377 * @param overrideAeMode true for overriding AE_MODE to CONTROL_AE_MODE_ON 378 * 379 */ 380 @ExecutedBy("mExecutor") triggerAf(@ullable Completer<CameraCaptureResult> completer, boolean overrideAeMode)381 void triggerAf(@Nullable Completer<CameraCaptureResult> completer, boolean overrideAeMode) { 382 if (!mIsActive) { 383 if (completer != null) { 384 completer.setException( 385 new CameraControl.OperationCanceledException("Camera is not active.")); 386 } 387 return; 388 } 389 390 CaptureConfig.Builder builder = new CaptureConfig.Builder(); 391 builder.setTemplateType(mTemplate); 392 builder.setUseRepeatingSurface(true); 393 Camera2ImplConfig.Builder configBuilder = new Camera2ImplConfig.Builder(); 394 configBuilder.setCaptureRequestOption(CaptureRequest.CONTROL_AF_TRIGGER, 395 CaptureRequest.CONTROL_AF_TRIGGER_START); 396 397 if (overrideAeMode) { 398 // This option will override the AE_MODE option in repeating request. 399 // On many devices, triggering Af with CONTROL_AE_MODE_ON_ALWAYS_FLASH or 400 // CONTROL_AE_MODE_ON_AUTO_FLASH will fire the flash when it's low light. 401 // Override it to AE_MODE_ON to prevent from this issue. 402 configBuilder.setCaptureRequestOptionWithPriority(CaptureRequest.CONTROL_AE_MODE, 403 mCameraControl.getSupportedAeMode(CaptureRequest.CONTROL_AE_MODE_ON), 404 Config.OptionPriority.HIGH_PRIORITY_REQUIRED); 405 } 406 builder.addImplementationOptions(configBuilder.build()); 407 builder.addCameraCaptureCallback(new CameraCaptureCallback() { 408 @Override 409 public void onCaptureCompleted(int captureConfigId, 410 @NonNull CameraCaptureResult cameraCaptureResult) { 411 if (completer != null) { 412 completer.set(cameraCaptureResult); 413 } 414 } 415 416 @Override 417 public void onCaptureFailed(int captureConfigId, 418 @NonNull CameraCaptureFailure failure) { 419 if (completer != null) { 420 completer.setException( 421 new CameraControlInternal.CameraControlException(failure)); 422 } 423 } 424 425 @Override 426 public void onCaptureCancelled(int captureConfigId) { 427 if (completer != null) { 428 completer.setException( 429 new CameraControl.OperationCanceledException("Camera is closed")); 430 } 431 } 432 }); 433 434 mCameraControl.submitCaptureRequestsInternal(Collections.singletonList(builder.build())); 435 } 436 437 /** 438 * Returns a {@link ListenableFuture} as result after triggering AE precapture. 439 */ triggerAePrecapture()440 ListenableFuture<Void> triggerAePrecapture() { 441 return CallbackToFutureAdapter.getFuture(completer -> { 442 mExecutor.execute(() -> { 443 triggerAePrecapture(completer); 444 }); 445 return "triggerAePrecapture"; 446 }); 447 } 448 449 /** 450 * Trigger an AE precapture sequence. 451 * 452 * @param completer used to complete the associated {@link ListenableFuture} when the 453 * operation succeeds or fails. Passing null to simply ignore the result. 454 */ 455 @ExecutedBy("mExecutor") 456 void triggerAePrecapture(@Nullable Completer<Void> completer) { 457 Logger.d(TAG, "triggerAePrecapture"); 458 459 if (!mIsActive) { 460 if (completer != null) { 461 completer.setException( 462 new CameraControl.OperationCanceledException("Camera is not active.")); 463 } 464 return; 465 } 466 467 CaptureConfig.Builder builder = new CaptureConfig.Builder(); 468 builder.setTemplateType(mTemplate); 469 builder.setUseRepeatingSurface(true); 470 Camera2ImplConfig.Builder configBuilder = new Camera2ImplConfig.Builder(); 471 configBuilder.setCaptureRequestOption(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 472 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 473 builder.addImplementationOptions(configBuilder.build()); 474 builder.addCameraCaptureCallback(new CameraCaptureCallback() { 475 @Override 476 public void onCaptureCompleted(int captureConfigId, 477 @NonNull CameraCaptureResult cameraCaptureResult) { 478 if (completer != null) { 479 Logger.d(TAG, "triggerAePrecapture: triggering capture request completed"); 480 completer.set(null); 481 } 482 } 483 484 @Override 485 public void onCaptureFailed(int captureConfigId, 486 @NonNull CameraCaptureFailure failure) { 487 if (completer != null) { 488 completer.setException( 489 new CameraControlInternal.CameraControlException(failure)); 490 } 491 } 492 493 @Override 494 public void onCaptureCancelled(int captureConfigId) { 495 if (completer != null) { 496 completer.setException( 497 new CameraControl.OperationCanceledException("Camera is closed")); 498 } 499 } 500 }); 501 mCameraControl.submitCaptureRequestsInternal(Collections.singletonList(builder.build())); 502 } 503 504 @ExecutedBy("mExecutor") 505 void cancelAfAeTrigger(final boolean cancelAfTrigger, 506 final boolean cancelAePrecaptureTrigger) { 507 if (!mIsActive) { 508 return; 509 } 510 511 CaptureConfig.Builder builder = new CaptureConfig.Builder(); 512 builder.setUseRepeatingSurface(true); 513 builder.setTemplateType(mTemplate); 514 515 Camera2ImplConfig.Builder configBuilder = new Camera2ImplConfig.Builder(); 516 if (cancelAfTrigger) { 517 configBuilder.setCaptureRequestOption(CaptureRequest.CONTROL_AF_TRIGGER, 518 CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); 519 } 520 if (Build.VERSION.SDK_INT >= 23 && cancelAePrecaptureTrigger) { 521 configBuilder.setCaptureRequestOption(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 522 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL); 523 } 524 builder.addImplementationOptions(configBuilder.build()); 525 mCameraControl.submitCaptureRequestsInternal(Collections.singletonList(builder.build())); 526 } 527 528 /** 529 * Returns whether external flash AE mode is enabled. 530 * 531 * @see #enableExternalFlashAeMode 532 */ 533 boolean isExternalFlashAeModeEnabled() { 534 return mIsExternalFlashAeModeEnabled; 535 } 536 537 /** 538 * Enables or disables AE_MODE_ON_EXTERNAL_FLASH. 539 * 540 * <p> It will be enabled only if the AE mode is supported i.e. API >= 28 and available in 541 * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_MODES}, and the flash mode is actually 542 * external (i.e. not the usual physical flash unit attached near camera) which is only 543 * {@link ImageCapture#FLASH_MODE_SCREEN} as of now. In case of other flash modes, the AE mode 544 * may get overwritten in {@link Camera2CameraControlImpl#getSessionOptions} and the future 545 * will never complete. 546 * 547 * @param enable Whether to enable or disable the AE mode. 548 * @return A {@link ListenableFuture} that is completed when the capture request to set the 549 * AE mode has been processed in framework side. 550 */ 551 ListenableFuture<Void> enableExternalFlashAeMode(boolean enable) { 552 if (Build.VERSION.SDK_INT < 28) { 553 Log.d(TAG, "CONTROL_AE_MODE_ON_EXTERNAL_FLASH is not supported in API " 554 + Build.VERSION.SDK_INT); 555 return Futures.immediateFuture(null); 556 } 557 558 if (mCameraControl.getSupportedAeMode(CaptureRequest.CONTROL_AE_MODE_ON_EXTERNAL_FLASH) 559 != CaptureRequest.CONTROL_AE_MODE_ON_EXTERNAL_FLASH) { 560 Log.d(TAG, "CONTROL_AE_MODE_ON_EXTERNAL_FLASH is not supported in this device"); 561 return Futures.immediateFuture(null); 562 } 563 564 Log.d(TAG, "enableExternalFlashAeMode: CONTROL_AE_MODE_ON_EXTERNAL_FLASH supported"); 565 566 return CallbackToFutureAdapter.getFuture(completer -> { 567 mExecutor.execute(() -> { 568 mCameraControl.removeCaptureResultListener(mSessionListenerForAeMode); 569 mIsExternalFlashAeModeEnabled = enable; 570 enableExternalFlashAeMode(completer); 571 }); 572 return "enableExternalFlashAeMode"; 573 }); 574 } 575 576 /** 577 * Enables or disables AE_MODE_ON_EXTERNAL_FLASH. 578 * 579 * @param completer used to complete the associated {@link ListenableFuture} when the 580 * operation succeeds or fails. Passing null to simply ignore the result. 581 * 582 * @see #enableExternalFlashAeMode 583 */ 584 @RequiresApi(28) 585 @ExecutedBy("mExecutor") 586 private void enableExternalFlashAeMode(@Nullable Completer<Void> completer) { 587 if (!mIsActive) { 588 if (completer != null) { 589 completer.setException( 590 new CameraControl.OperationCanceledException("Camera is not active.")); 591 } 592 return; 593 } 594 595 long sessionUpdateId = mCameraControl.updateSessionConfigSynchronous(); 596 597 // Will be called on mExecutor since mSessionCallback was created with mExecutor 598 mSessionListenerForAeMode = 599 result -> { 600 boolean isAeModeExternalFlash = result.get(CaptureResult.CONTROL_AE_MODE) 601 == CaptureRequest.CONTROL_AE_MODE_ON_EXTERNAL_FLASH; 602 Logger.d(TAG, "enableExternalFlashAeMode: " 603 + "isAeModeExternalFlash = " + isAeModeExternalFlash); 604 605 // Check if the AE mode is as desired 606 // TODO: Currently this check will never pass if AE mode request is overwritten 607 // due to other flash mode in Camera2CameraControlImpl#getSessionOptions. To 608 // handle this gracefully, we should have a central code controlling the AE 609 // mode value to set to capture requests and we can compare with that instead. 610 if (isAeModeExternalFlash == mIsExternalFlashAeModeEnabled) { 611 // Ensure the session is actually updated 612 if (Camera2CameraControlImpl.isSessionUpdated(result, sessionUpdateId)) { 613 Logger.d(TAG, "enableExternalFlashAeMode: session updated with " 614 + "isAeModeExternalFlash = " + isAeModeExternalFlash); 615 if (completer != null) { 616 completer.set(null); 617 } 618 return true; // remove this listener 619 } 620 } 621 622 return false; // continue checking 623 }; 624 625 mCameraControl.addCaptureResultListener(mSessionListenerForAeMode); 626 } 627 628 @ExecutedBy("mExecutor") 629 private void disableAutoCancel() { 630 if (mAutoCancelHandle != null) { 631 mAutoCancelHandle.cancel(/*mayInterruptIfRunning=*/true); 632 mAutoCancelHandle = null; 633 } 634 } 635 636 @ExecutedBy("mExecutor") 637 private void clearAutoFocusTimeoutHandle() { 638 if (mAutoFocusTimeoutHandle != null) { 639 mAutoFocusTimeoutHandle.cancel(/*mayInterruptIfRunning=*/true); 640 mAutoFocusTimeoutHandle = null; 641 } 642 } 643 644 @VisibleForTesting 645 @ExecutedBy("mExecutor") 646 int getDefaultAfMode() { 647 switch (mTemplate) { 648 case CameraDevice.TEMPLATE_RECORD: 649 return CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO; 650 case CameraDevice.TEMPLATE_PREVIEW: 651 default: 652 return CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE; 653 } 654 } 655 656 private boolean isAfModeSupported() { 657 return mCameraControl.getSupportedAfMode(CaptureRequest.CONTROL_AF_MODE_AUTO) 658 == CaptureRequest.CONTROL_AF_MODE_AUTO; 659 } 660 661 @ExecutedBy("mExecutor") 662 void completeActionFuture(boolean isFocusSuccessful) { 663 clearAutoFocusTimeoutHandle(); 664 if (mRunningActionCompleter != null) { 665 mRunningActionCompleter.set(FocusMeteringResult.create(isFocusSuccessful)); 666 mRunningActionCompleter = null; 667 } 668 } 669 670 @ExecutedBy("mExecutor") 671 private void failActionFuture(String message) { 672 mCameraControl.removeCaptureResultListener(mSessionListenerForFocus); 673 if (mRunningActionCompleter != null) { 674 mRunningActionCompleter.setException(new CameraControl.OperationCanceledException( 675 message)); 676 mRunningActionCompleter = null; 677 } 678 } 679 680 @ExecutedBy("mExecutor") 681 private void failCancelFuture(String message) { 682 mCameraControl.removeCaptureResultListener(mSessionListenerForCancel); 683 if (mRunningCancelCompleter != null) { 684 mRunningCancelCompleter.setException( 685 new CameraControl.OperationCanceledException(message)); 686 mRunningCancelCompleter = null; 687 } 688 } 689 690 @ExecutedBy("mExecutor") 691 private void completeCancelFuture() { 692 if (mRunningCancelCompleter != null) { 693 mRunningCancelCompleter.set(null); 694 mRunningCancelCompleter = null; 695 } 696 } 697 698 @ExecutedBy("mExecutor") 699 private void executeMeteringAction( 700 MeteringRectangle @NonNull [] afRects, 701 MeteringRectangle @NonNull [] aeRects, 702 MeteringRectangle @NonNull [] awbRects, 703 FocusMeteringAction focusMeteringAction, 704 long timeoutDurationMs) { 705 mCameraControl.removeCaptureResultListener(mSessionListenerForFocus); 706 707 disableAutoCancel(); 708 clearAutoFocusTimeoutHandle(); 709 710 mAfRects = afRects; 711 mAeRects = aeRects; 712 mAwbRects = awbRects; 713 714 long sessionUpdateId; 715 // Trigger AF scan if any AF points are added. 716 if (shouldTriggerAF()) { 717 mIsInAfAutoMode = true; 718 mIsAutoFocusCompleted = false; 719 mIsFocusSuccessful = false; 720 sessionUpdateId = mCameraControl.updateSessionConfigSynchronous(); 721 triggerAf(null, /* overrideAeMode */ true); 722 } else { 723 mIsInAfAutoMode = false; 724 mIsAutoFocusCompleted = true; // Don't need to wait for auto-focus 725 mIsFocusSuccessful = false; // False because AF is not triggered. 726 sessionUpdateId = mCameraControl.updateSessionConfigSynchronous(); 727 } 728 729 mCurrentAfState = CaptureResult.CONTROL_AF_STATE_INACTIVE; 730 final boolean isAfModeSupported = isAfModeSupported(); 731 732 // Will be called on mExecutor since mSessionCallback was created with mExecutor 733 mSessionListenerForFocus = 734 result -> { 735 Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); 736 if (shouldTriggerAF()) { 737 if (!isAfModeSupported || afState == null) { 738 // set isFocusSuccessful to true when camera does not support AF_AUTO. 739 mIsFocusSuccessful = true; 740 mIsAutoFocusCompleted = true; 741 } else if (mCurrentAfState == CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN) { 742 if (afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED) { 743 mIsFocusSuccessful = true; 744 mIsAutoFocusCompleted = true; 745 } else if (afState 746 == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) { 747 mIsFocusSuccessful = false; 748 mIsAutoFocusCompleted = true; 749 } 750 } 751 } 752 753 // Check 3A regions 754 if (mIsAutoFocusCompleted) { 755 if (Camera2CameraControlImpl.isSessionUpdated(result, sessionUpdateId)) { 756 completeActionFuture(mIsFocusSuccessful); 757 return true; // remove this listener 758 } 759 } 760 761 if (!mCurrentAfState.equals(afState) && afState != null) { 762 mCurrentAfState = afState; 763 } 764 return false; // continue checking 765 }; 766 767 mCameraControl.addCaptureResultListener(mSessionListenerForFocus); 768 769 final long timeoutId = ++mFocusTimeoutCounter; 770 771 // Sets auto focus timeout runnable first so that action will be completed with 772 // mIsFocusSuccessful is false when auto cancel is enabled with the same default 5000ms 773 // duration. 774 final Runnable autoFocusTimeoutRunnable = () -> mExecutor.execute(() -> { 775 if (timeoutId == mFocusTimeoutCounter) { 776 mIsFocusSuccessful = false; 777 completeActionFuture(mIsFocusSuccessful); 778 } 779 }); 780 781 mAutoFocusTimeoutHandle = mScheduler.schedule(autoFocusTimeoutRunnable, 782 timeoutDurationMs, 783 TimeUnit.MILLISECONDS); 784 785 if (focusMeteringAction.isAutoCancelEnabled()) { 786 final Runnable autoCancelRunnable = () -> mExecutor.execute(() -> { 787 if (timeoutId == mFocusTimeoutCounter) { 788 cancelFocusAndMeteringWithoutAsyncResult(); 789 } 790 }); 791 792 mAutoCancelHandle = mScheduler.schedule(autoCancelRunnable, 793 focusMeteringAction.getAutoCancelDurationInMillis(), 794 TimeUnit.MILLISECONDS); 795 } 796 } 797 798 @ExecutedBy("mExecutor") 799 private boolean shouldTriggerAF() { 800 return mAfRects.length > 0; 801 } 802 803 ListenableFuture<Void> cancelFocusAndMetering() { 804 return CallbackToFutureAdapter.getFuture( 805 completer -> { 806 mExecutor.execute(() -> cancelFocusAndMeteringInternal(completer)); 807 return "cancelFocusAndMetering"; 808 }); 809 } 810 811 @ExecutedBy("mExecutor") 812 void cancelFocusAndMeteringWithoutAsyncResult() { 813 cancelFocusAndMeteringInternal(null); 814 } 815 816 @ExecutedBy("mExecutor") 817 void cancelFocusAndMeteringInternal( 818 CallbackToFutureAdapter.@Nullable Completer<Void> completer) { 819 failCancelFuture("Cancelled by another cancelFocusAndMetering()"); 820 failActionFuture("Cancelled by cancelFocusAndMetering()"); 821 mRunningCancelCompleter = completer; 822 disableAutoCancel(); 823 clearAutoFocusTimeoutHandle(); 824 825 if (shouldTriggerAF()) { 826 cancelAfAeTrigger(true, false); 827 } 828 mAfRects = EMPTY_RECTANGLES; 829 mAeRects = EMPTY_RECTANGLES; 830 mAwbRects = EMPTY_RECTANGLES; 831 832 mIsInAfAutoMode = false; 833 long sessionUpdateId = mCameraControl.updateSessionConfigSynchronous(); 834 835 if (mRunningCancelCompleter != null) { 836 int targetAfMode = mCameraControl.getSupportedAfMode(getDefaultAfMode()); 837 mSessionListenerForCancel = 838 captureResult -> { 839 Integer afMode = captureResult.get(CaptureResult.CONTROL_AF_MODE); 840 if (afMode == targetAfMode 841 && Camera2CameraControlImpl.isSessionUpdated(captureResult, 842 sessionUpdateId)) { 843 completeCancelFuture(); 844 return true; // remove this listener 845 } 846 return false; 847 }; 848 849 mCameraControl.addCaptureResultListener(mSessionListenerForCancel); 850 } 851 } 852 853 boolean isFocusMeteringSupported(@NonNull FocusMeteringAction action) { 854 Rect cropSensorRegion = mCameraControl.getCropSensorRegion(); 855 Rational defaultAspectRatio = getDefaultAspectRatio(); 856 List<MeteringRectangle> rectanglesAf = 857 getMeteringRectangles(action.getMeteringPointsAf(), 858 mCameraControl.getMaxAfRegionCount(), 859 defaultAspectRatio, cropSensorRegion, FocusMeteringAction.FLAG_AF); 860 List<MeteringRectangle> rectanglesAe = 861 getMeteringRectangles(action.getMeteringPointsAe(), 862 mCameraControl.getMaxAeRegionCount(), 863 defaultAspectRatio, cropSensorRegion, FocusMeteringAction.FLAG_AE); 864 List<MeteringRectangle> rectanglesAwb = 865 getMeteringRectangles(action.getMeteringPointsAwb(), 866 mCameraControl.getMaxAwbRegionCount(), 867 defaultAspectRatio, cropSensorRegion, FocusMeteringAction.FLAG_AWB); 868 return !rectanglesAf.isEmpty() || !rectanglesAe.isEmpty() || !rectanglesAwb.isEmpty(); 869 } 870 } 871