1 /* <lambda>null2 * Copyright 2022 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 /* 18 * Copyright 2021 The Android Open Source Project 19 * 20 * Licensed under the Apache License, Version 2.0 (the "License"); 21 * you may not use this file except in compliance with the License. 22 * You may obtain a copy of the License at 23 * 24 * http://www.apache.org/licenses/LICENSE-2.0 25 * 26 * Unless required by applicable law or agreed to in writing, software 27 * distributed under the License is distributed on an "AS IS" BASIS, 28 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29 * See the License for the specific language governing permissions and 30 * limitations under the License. 31 */ 32 33 package androidx.camera.camera2.pipe.integration.impl 34 35 import android.hardware.camera2.CameraCharacteristics.CONTROL_AE_STATE_FLASH_REQUIRED 36 import android.hardware.camera2.CameraDevice 37 import android.hardware.camera2.CaptureRequest 38 import android.hardware.camera2.CaptureResult 39 import android.view.Surface 40 import androidx.annotation.VisibleForTesting 41 import androidx.camera.camera2.pipe.CameraGraph 42 import androidx.camera.camera2.pipe.CameraId 43 import androidx.camera.camera2.pipe.FrameInfo 44 import androidx.camera.camera2.pipe.FrameMetadata 45 import androidx.camera.camera2.pipe.FrameNumber 46 import androidx.camera.camera2.pipe.Lock3ABehavior 47 import androidx.camera.camera2.pipe.Metadata 48 import androidx.camera.camera2.pipe.Request 49 import androidx.camera.camera2.pipe.RequestFailure 50 import androidx.camera.camera2.pipe.RequestMetadata 51 import androidx.camera.camera2.pipe.RequestNumber 52 import androidx.camera.camera2.pipe.RequestTemplate 53 import androidx.camera.camera2.pipe.Result3A 54 import androidx.camera.camera2.pipe.StreamId 55 import androidx.camera.camera2.pipe.core.Log.debug 56 import androidx.camera.camera2.pipe.core.Log.info 57 import androidx.camera.camera2.pipe.integration.adapter.CaptureConfigAdapter 58 import androidx.camera.camera2.pipe.integration.adapter.CaptureResultAdapter 59 import androidx.camera.camera2.pipe.integration.adapter.future 60 import androidx.camera.camera2.pipe.integration.compat.workaround.UseTorchAsFlash 61 import androidx.camera.camera2.pipe.integration.compat.workaround.isFlashAvailable 62 import androidx.camera.camera2.pipe.integration.compat.workaround.shouldStopRepeatingBeforeCapture 63 import androidx.camera.camera2.pipe.integration.config.UseCaseCameraScope 64 import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig 65 import androidx.camera.camera2.pipe.integration.impl.CapturePipelineImpl.PipelineTask.MAIN_CAPTURE 66 import androidx.camera.camera2.pipe.integration.impl.CapturePipelineImpl.PipelineTask.POST_CAPTURE 67 import androidx.camera.camera2.pipe.integration.impl.CapturePipelineImpl.PipelineTask.PRE_CAPTURE 68 import androidx.camera.camera2.pipe.integration.impl.TorchControl.TorchMode 69 import androidx.camera.core.ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY 70 import androidx.camera.core.ImageCapture.CaptureMode 71 import androidx.camera.core.ImageCapture.ERROR_CAMERA_CLOSED 72 import androidx.camera.core.ImageCapture.ERROR_CAPTURE_FAILED 73 import androidx.camera.core.ImageCapture.FLASH_MODE_AUTO 74 import androidx.camera.core.ImageCapture.FLASH_MODE_OFF 75 import androidx.camera.core.ImageCapture.FLASH_MODE_ON 76 import androidx.camera.core.ImageCapture.FLASH_MODE_SCREEN 77 import androidx.camera.core.ImageCapture.FLASH_TYPE_USE_TORCH_AS_FLASH 78 import androidx.camera.core.ImageCapture.FlashMode 79 import androidx.camera.core.ImageCapture.FlashType 80 import androidx.camera.core.ImageCaptureException 81 import androidx.camera.core.TorchState 82 import androidx.camera.core.imagecapture.CameraCapturePipeline 83 import androidx.camera.core.impl.CameraCaptureFailure 84 import androidx.camera.core.impl.CameraCaptureResult 85 import androidx.camera.core.impl.CameraCaptureResult.EmptyCameraCaptureResult 86 import androidx.camera.core.impl.CaptureConfig 87 import androidx.camera.core.impl.Config 88 import androidx.camera.core.impl.ConvergenceUtils 89 import androidx.camera.core.impl.SessionProcessor.CaptureCallback 90 import com.google.common.util.concurrent.ListenableFuture 91 import java.util.concurrent.TimeUnit 92 import javax.inject.Inject 93 import kotlin.reflect.KClass 94 import kotlinx.coroutines.CancellationException 95 import kotlinx.coroutines.CompletableDeferred 96 import kotlinx.coroutines.Deferred 97 import kotlinx.coroutines.joinAll 98 import kotlinx.coroutines.launch 99 import kotlinx.coroutines.withTimeoutOrNull 100 101 private val CHECK_FLASH_REQUIRED_TIMEOUT_IN_NS = TimeUnit.SECONDS.toNanos(1) 102 private val CHECK_3A_TIMEOUT_IN_NS = TimeUnit.SECONDS.toNanos(1) 103 private val CHECK_3A_WITH_FLASH_TIMEOUT_IN_NS = TimeUnit.SECONDS.toNanos(5) 104 private val CHECK_3A_WITH_SCREEN_FLASH_TIMEOUT_IN_NS = TimeUnit.SECONDS.toNanos(2) 105 106 public interface CapturePipeline { 107 108 public var template: Int 109 110 public suspend fun submitStillCaptures( 111 configs: List<CaptureConfig>, 112 requestTemplate: RequestTemplate, 113 sessionConfigOptions: Config, 114 @CaptureMode captureMode: Int, 115 @FlashType flashType: Int, 116 @FlashMode flashMode: Int, 117 ): List<Deferred<Void?>> 118 119 /** Gets the [CameraCapturePipeline] instance corresponding to a [CapturePipeline] instance. */ 120 public suspend fun getCameraCapturePipeline( 121 @CaptureMode captureMode: Int, 122 @FlashMode flashMode: Int, 123 @FlashType flashType: Int 124 ): CameraCapturePipeline 125 } 126 127 /** Implementations for the single capture. */ 128 @UseCaseCameraScope 129 public class CapturePipelineImpl 130 @Inject 131 constructor( 132 private val configAdapter: CaptureConfigAdapter, 133 private val flashControl: FlashControl, 134 private val torchControl: TorchControl, 135 private val videoUsageControl: VideoUsageControl, 136 private val threads: UseCaseThreads, 137 private val requestListener: ComboRequestListener, 138 private val useTorchAsFlash: UseTorchAsFlash, 139 cameraProperties: CameraProperties, 140 private val useCaseCameraState: UseCaseCameraState, 141 useCaseGraphConfig: UseCaseGraphConfig, 142 private val sessionProcessorManager: SessionProcessorManager?, 143 ) : CapturePipeline { 144 private val graph = useCaseGraphConfig.graph 145 146 // If there is no flash unit, skip the flash related task instead of failing the pipeline. 147 private val hasFlashUnit = cameraProperties.isFlashAvailable() 148 149 override var template: Int = CameraDevice.TEMPLATE_PREVIEW 150 151 private enum class PipelineTask { 152 PRE_CAPTURE, 153 MAIN_CAPTURE, 154 POST_CAPTURE, 155 } 156 157 private data class MainCaptureParams( 158 val configs: List<CaptureConfig>, 159 val requestTemplate: RequestTemplate, 160 val sessionConfigOptions: Config, 161 ) 162 163 /** 164 * Invokes various capture pipelines (e.g. pre-capture or main capture or post-capture). 165 * 166 * @param pipelineTasks List of [PipelineTask] to invoke. 167 * @param captureMode [CaptureMode] integer for the capture. 168 * @param flashMode [FlashMode] integer for the capture. 169 * @param flashType [FlashType] integer for the capture. 170 * @param mainCaptureParams Parameters required for the main capture, must not be null if 171 * [pipelineTasks] contain [PipelineTask.MAIN_CAPTURE]. 172 */ invokeCaptureTasksnull173 private suspend fun invokeCaptureTasks( 174 pipelineTasks: List<PipelineTask>, 175 @CaptureMode captureMode: Int, 176 @FlashMode flashMode: Int, 177 @FlashType flashType: Int, 178 mainCaptureParams: MainCaptureParams?, 179 ): List<Deferred<Void?>> { 180 debug { 181 "CapturePipeline#invokeCaptureTasks: tasks = $pipelineTasks" + 182 ", captureMode = $captureMode, flashMode = $flashMode, flashType = $flashType" 183 } 184 185 if (pipelineTasks.contains(MAIN_CAPTURE)) { 186 checkNotNull(mainCaptureParams) { "Must not be null for PipelineType.MAIN_CAPTURE" } 187 } 188 189 return if (flashMode == FLASH_MODE_SCREEN) { 190 screenFlashCapture( 191 mainCaptureParams, 192 captureMode, 193 pipelineTasks, 194 ) 195 } else if (isTorchAsFlash(flashType)) { 196 torchAsFlashCapture( 197 mainCaptureParams, 198 captureMode, 199 flashMode, 200 pipelineTasks, 201 ) 202 } else { 203 defaultCapture( 204 mainCaptureParams, 205 captureMode, 206 flashMode, 207 pipelineTasks, 208 ) 209 } 210 } 211 submitStillCapturesnull212 override suspend fun submitStillCaptures( 213 configs: List<CaptureConfig>, 214 requestTemplate: RequestTemplate, 215 sessionConfigOptions: Config, 216 @CaptureMode captureMode: Int, 217 @FlashType flashType: Int, 218 @FlashMode flashMode: Int, 219 ): List<Deferred<Void?>> = 220 invokeCaptureTasks( 221 pipelineTasks = listOf(PRE_CAPTURE, MAIN_CAPTURE, POST_CAPTURE), 222 captureMode = captureMode, 223 flashMode = flashMode, 224 flashType = flashType, 225 mainCaptureParams = MainCaptureParams(configs, requestTemplate, sessionConfigOptions), 226 ) 227 228 override suspend fun getCameraCapturePipeline( 229 captureMode: Int, 230 flashMode: Int, 231 flashType: Int 232 ): CameraCapturePipeline { 233 return object : CameraCapturePipeline { 234 override fun invokePreCapture(): ListenableFuture<Void?> { 235 return threads.scope.future { 236 invokeCaptureTasks( 237 pipelineTasks = listOf(PRE_CAPTURE), 238 captureMode = captureMode, 239 flashMode = flashMode, 240 flashType = flashType, 241 mainCaptureParams = null, 242 ) 243 .joinAll() 244 null // Since the joinAll earlier returns Unit type mismatching with Void? type 245 } 246 } 247 248 override fun invokePostCapture(): ListenableFuture<Void?> { 249 return threads.scope.future { 250 invokeCaptureTasks( 251 pipelineTasks = listOf(POST_CAPTURE), 252 captureMode = captureMode, 253 flashMode = flashMode, 254 flashType = flashType, 255 mainCaptureParams = null, 256 ) 257 .joinAll() 258 null // Since the joinAll earlier returns Unit type mismatching with Void? type 259 } 260 } 261 } 262 } 263 264 /** 265 * Invokes a capture pipeline with the sequence of pre-capture -> main capture -> post-capture 266 * based on the pipeline tasks in the receiver list. 267 * 268 * @param mainCaptureParams Parameters required for the main capture, must not be null if the 269 * receiver list contains [PipelineTask.MAIN_CAPTURE]. 270 * @param preCapture A function invoked during pre-capture. 271 * @param postCapture A function invoked during post-capture. 272 * @receiver A list of [PipelineTask]. 273 */ invokenull274 private suspend inline fun List<PipelineTask>.invoke( 275 mainCaptureParams: MainCaptureParams?, 276 crossinline preCapture: suspend () -> Unit, 277 crossinline postCapture: suspend () -> Unit, 278 ): List<Deferred<Void?>> { 279 debug { "CapturePipeline#List<PipelineTask>.invoke: tasks = $this" } 280 if (contains(PRE_CAPTURE)) { 281 preCapture() 282 } 283 return if (contains(MAIN_CAPTURE)) { 284 submitRequestInternal( 285 checkNotNull(mainCaptureParams), 286 ) 287 } else { 288 listOf(CompletableDeferred(value = null)) 289 } 290 .also { captureSignal -> 291 if (contains(POST_CAPTURE)) { 292 threads.sequentialScope.launch { 293 debug { 294 "CapturePipeline#List<PipelineTask>.invoke: Waiting for capture signal" 295 } 296 captureSignal.joinAll() 297 debug { 298 "CapturePipeline#List<PipelineTask>.invoke:" + 299 " Waiting for capture signal done" 300 } 301 postCapture() 302 } 303 } 304 } 305 } 306 torchAsFlashCapturenull307 private suspend fun torchAsFlashCapture( 308 mainCaptureParams: MainCaptureParams?, 309 @CaptureMode captureMode: Int, 310 @FlashMode flashMode: Int, 311 pipelineTasks: List<PipelineTask>, 312 ): List<Deferred<Void?>> { 313 debug { "CapturePipeline#torchAsFlashCapture" } 314 return if (hasFlashUnit && isPhysicalFlashRequired(flashMode)) { 315 torchApplyCapture( 316 mainCaptureParams, 317 captureMode, 318 CHECK_3A_WITH_FLASH_TIMEOUT_IN_NS, 319 pipelineTasks, 320 // TODO: b/339846763 - Disable AE precap only for the quirks where AE precapture 321 // is problematic, instead of all TorchAsFlash quirks. 322 !useTorchAsFlash.shouldUseTorchAsFlash() && !videoUsageControl.isInVideoUsage(), 323 ) 324 } else { 325 defaultNoFlashCapture(mainCaptureParams, captureMode, pipelineTasks) 326 } 327 } 328 defaultCapturenull329 private suspend fun defaultCapture( 330 mainCaptureParams: MainCaptureParams?, 331 @CaptureMode captureMode: Int, 332 @FlashMode flashMode: Int, 333 pipelineTasks: List<PipelineTask>, 334 ): List<Deferred<Void?>> { 335 return if (hasFlashUnit) { 336 val isFlashRequired = isPhysicalFlashRequired(flashMode) 337 val timeout = 338 if (isFlashRequired) CHECK_3A_WITH_FLASH_TIMEOUT_IN_NS else CHECK_3A_TIMEOUT_IN_NS 339 340 if (isFlashRequired || captureMode == CAPTURE_MODE_MAXIMIZE_QUALITY) { 341 aePreCaptureApplyCapture( 342 mainCaptureParams, 343 timeout, 344 captureMode, 345 pipelineTasks, 346 ) 347 } else { 348 defaultNoFlashCapture( 349 mainCaptureParams, 350 captureMode, 351 pipelineTasks, 352 ) 353 } 354 } else { 355 defaultNoFlashCapture( 356 mainCaptureParams, 357 captureMode, 358 pipelineTasks, 359 ) 360 } 361 } 362 defaultNoFlashCapturenull363 private suspend fun defaultNoFlashCapture( 364 mainCaptureParams: MainCaptureParams?, 365 @CaptureMode captureMode: Int, 366 pipelineTasks: List<PipelineTask>, 367 ): List<Deferred<Void?>> { 368 debug { "CapturePipeline#defaultNoFlashCapture" } 369 val lock3ARequired = captureMode == CAPTURE_MODE_MAXIMIZE_QUALITY 370 return pipelineTasks.invoke( 371 mainCaptureParams = mainCaptureParams, 372 preCapture = { 373 if (lock3ARequired) { 374 debug { "CapturePipeline#defaultNoFlashCapture: Locking 3A" } 375 lockAf(CHECK_3A_TIMEOUT_IN_NS, isTorchAsFlash = false) 376 debug { "CapturePipeline#defaultNoFlashCapture: Locking 3A done" } 377 } 378 }, 379 postCapture = { 380 if (lock3ARequired) { 381 debug { "CapturePipeline#defaultNoFlashCapture: Unlocking 3A" } 382 unlockAf(CHECK_3A_TIMEOUT_IN_NS) 383 debug { "CapturePipeline#defaultNoFlashCapture: Unlocking 3A done" } 384 } 385 } 386 ) 387 } 388 torchApplyCapturenull389 private suspend fun torchApplyCapture( 390 mainCaptureParams: MainCaptureParams?, 391 @CaptureMode captureMode: Int, 392 timeLimitNs: Long, 393 pipelineTasks: List<PipelineTask>, 394 triggerAePreCapture: Boolean, 395 ): List<Deferred<Void?>> { 396 debug { "CapturePipeline#torchApplyCapture" } 397 val torchOnRequired = torchControl.torchStateLiveData.value == TorchState.OFF 398 val lock3ARequired = torchOnRequired || captureMode == CAPTURE_MODE_MAXIMIZE_QUALITY 399 400 return pipelineTasks.invoke( 401 mainCaptureParams = mainCaptureParams, 402 preCapture = { 403 if (torchOnRequired) { 404 debug { "CapturePipeline#torchApplyCapture: Setting torch" } 405 torchControl.setTorchAsync(TorchMode.USED_AS_FLASH).join() 406 debug { "CapturePipeline#torchApplyCapture: Setting torch done" } 407 } 408 409 if (triggerAePreCapture) { 410 debug { "CapturePipeline#torchApplyCapture: Locking 3A for capture" } 411 val result3A = 412 graph.acquireSession().use { 413 it.lock3AForCapture( 414 timeLimitNs = timeLimitNs, 415 triggerAf = captureMode == CAPTURE_MODE_MAXIMIZE_QUALITY, 416 waitForAwb = captureMode == CAPTURE_MODE_MAXIMIZE_QUALITY, 417 ) 418 .await() 419 } 420 debug { 421 "CapturePipeline#torchApplyCapture: Locking 3A for capture done" + 422 ", result3A = $result3A" 423 } 424 } else { 425 // TODO: b/339846763 - When triggerAePreCapture is false, AE pre-capture may 426 // cause issues in some devices and thus should not be used here. When capture 427 // mode is not max quality, we should only wait for 3A convergence without any 428 // additional locking. In case of max quality, only AF should be locked, not 429 // AE/AWB too. 430 if (lock3ARequired) { 431 if (captureMode == CAPTURE_MODE_MAXIMIZE_QUALITY) { 432 debug { "CapturePipeline#torchApplyCapture: Locking 3A" } 433 lockAf(timeLimitNs, isTorchAsFlash = true) 434 debug { "CapturePipeline#torchApplyCapture: Locking 3A done" } 435 } else { 436 debug { "CapturePipeline#torchApplyCapture: Awaiting 3A convergence" } 437 waitForResult(waitTimeoutNanos = timeLimitNs) { 438 ConvergenceUtils.is3AConverged( 439 it.metadata.toCameraCaptureResult(), 440 /* isTorchAsFlash = */ true 441 ) 442 } 443 debug { 444 "CapturePipeline#torchApplyCapture: 3A convergence waiting done" 445 } 446 } 447 } 448 } 449 }, 450 postCapture = { 451 if (torchOnRequired) { 452 debug { "CapturePipeline#torchApplyCapture: Unsetting torch" } 453 @Suppress("DeferredResultUnused") torchControl.setTorchAsync(TorchMode.OFF) 454 debug { "CapturePipeline#torchApplyCapture: Unsetting torch done" } 455 } 456 if (triggerAePreCapture) { 457 debug { "CapturePipeline#torchApplyCapture: Unlocking 3A for capture" } 458 @Suppress("DeferredResultUnused") 459 graph.acquireSession().use { 460 it.unlock3APostCapture( 461 cancelAf = captureMode == CAPTURE_MODE_MAXIMIZE_QUALITY, 462 ) 463 } 464 } else { 465 if (lock3ARequired && captureMode == CAPTURE_MODE_MAXIMIZE_QUALITY) { 466 debug { "CapturePipeline#torchApplyCapture: Unlocking 3A" } 467 unlockAf(CHECK_3A_TIMEOUT_IN_NS) 468 debug { "CapturePipeline#torchApplyCapture: Unlocking 3A done" } 469 } 470 } 471 } 472 ) 473 } 474 aePreCaptureApplyCapturenull475 private suspend fun aePreCaptureApplyCapture( 476 mainCaptureParams: MainCaptureParams?, 477 timeLimitNs: Long, 478 @CaptureMode captureMode: Int, 479 pipelineTasks: List<PipelineTask>, 480 ): List<Deferred<Void?>> { 481 debug { "CapturePipeline#aePreCaptureApplyCapture" } 482 483 return pipelineTasks.invoke( 484 mainCaptureParams = mainCaptureParams, 485 preCapture = { 486 debug { 487 "CapturePipeline#aePreCaptureApplyCapture: Acquiring session for locking 3A" 488 } 489 graph.acquireSession().use { 490 debug { "CapturePipeline#aePreCaptureApplyCapture: Locking 3A for capture" } 491 it.lock3AForCapture( 492 timeLimitNs = timeLimitNs, 493 triggerAf = captureMode == CAPTURE_MODE_MAXIMIZE_QUALITY, 494 waitForAwb = captureMode == CAPTURE_MODE_MAXIMIZE_QUALITY, 495 ) 496 .join() 497 debug { 498 "CapturePipeline#aePreCaptureApplyCapture: Locking 3A for capture done" 499 } 500 } 501 }, 502 postCapture = { 503 debug { 504 "CapturePipeline#aePreCaptureApplyCapture: Acquiring session for unlocking 3A" 505 } 506 graph.acquireSession().use { 507 debug { "CapturePipeline#aePreCaptureApplyCapture: Unlocking 3A" } 508 @Suppress("DeferredResultUnused") 509 it.unlock3APostCapture(cancelAf = captureMode == CAPTURE_MODE_MAXIMIZE_QUALITY) 510 debug { "CapturePipeline#aePreCaptureApplyCapture: Unlocking 3A done" } 511 } 512 } 513 ) 514 } 515 screenFlashCapturenull516 private suspend fun screenFlashCapture( 517 mainCaptureParams: MainCaptureParams?, 518 @CaptureMode captureMode: Int, 519 pipelineTasks: List<PipelineTask>, 520 ): List<Deferred<Void?>> { 521 debug { "CapturePipeline#screenFlashCapture" } 522 523 return pipelineTasks.invoke( 524 mainCaptureParams = mainCaptureParams, 525 preCapture = { invokeScreenFlashPreCaptureTasks(captureMode) }, 526 postCapture = { invokeScreenFlashPostCaptureTasks(captureMode) } 527 ) 528 } 529 530 /** 531 * Invokes the pre-capture tasks required for a screen flash capture. 532 * 533 * This method may modify the preferred AE mode in [State3AControl] to enable external flash AE 534 * mode. [invokeScreenFlashPostCaptureTasks] should be used to restore the previous AE mode in 535 * such case. 536 * 537 * @return The previous preferred AE mode in [State3AControl], null if not modified. 538 */ 539 @VisibleForTesting invokeScreenFlashPreCaptureTasksnull540 public suspend fun invokeScreenFlashPreCaptureTasks(@CaptureMode captureMode: Int) { 541 flashControl.startScreenFlashCaptureTasks() 542 543 graph.acquireSession().use { session -> 544 // Trigger AE precapture & wait for 3A converge 545 debug { "screenFlashPreCapture: Locking 3A for capture" } 546 val result3A = 547 session 548 .lock3AForCapture( 549 timeLimitNs = CHECK_3A_WITH_SCREEN_FLASH_TIMEOUT_IN_NS, 550 triggerAf = captureMode == CAPTURE_MODE_MAXIMIZE_QUALITY, 551 waitForAwb = true, 552 ) 553 .await() 554 debug { "screenFlashPreCapture: Locking 3A for capture done, result3A = $result3A" } 555 } 556 } 557 558 @VisibleForTesting invokeScreenFlashPostCaptureTasksnull559 public suspend fun invokeScreenFlashPostCaptureTasks(@CaptureMode captureMode: Int) { 560 flashControl.stopScreenFlashCaptureTasks() 561 562 // Unlock 3A 563 debug { "screenFlashPostCapture: Acquiring session for unlocking 3A" } 564 graph.acquireSession().use { session -> 565 debug { "screenFlashPostCapture: Unlocking 3A" } 566 @Suppress("DeferredResultUnused") 567 session.unlock3APostCapture(cancelAf = captureMode == CAPTURE_MODE_MAXIMIZE_QUALITY) 568 debug { "screenFlashPostCapture: Unlocking 3A done" } 569 } 570 } 571 572 /** 573 * Locks AF by triggering a new AF scan and awaits 3A convergence. 574 * 575 * This function uses [CameraGraph.Session.lock3A] with `aeLockBehavior` and `awbLockBehavior` 576 * set to null to avoid redundant AE/AWB locking. For 3A convergence condition, CameraX custom 577 * condition is used (i.e. [ConvergenceUtils.is3AConverged]). 578 */ lockAfnull579 private suspend fun lockAf(convergedTimeLimitNs: Long, isTorchAsFlash: Boolean): Result3A = 580 graph 581 .acquireSession() 582 .use { 583 it.lock3A( 584 aeLockBehavior = null, 585 afLockBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN, 586 awbLockBehavior = null, 587 convergedCondition = getConvergeCondition(isTorchAsFlash), 588 convergedTimeLimitNs = convergedTimeLimitNs, 589 lockedTimeLimitNs = CHECK_3A_TIMEOUT_IN_NS 590 ) 591 } 592 .await() 593 getConvergeConditionnull594 private fun getConvergeCondition( 595 isTorchAsFlash: Boolean 596 ): (frameMetadata: FrameMetadata) -> Boolean = convergeCondition@{ frameMetadata -> 597 ConvergenceUtils.is3AConverged(frameMetadata.toCameraCaptureResult(), isTorchAsFlash) 598 } 599 FrameMetadatanull600 private fun FrameMetadata.toCameraCaptureResult(): CameraCaptureResult { 601 val frameInfo = 602 object : FrameInfo { 603 private val frameMetadata = this@toCameraCaptureResult 604 override val metadata: FrameMetadata = frameMetadata 605 606 override fun get(camera: CameraId): FrameMetadata? = frameMetadata 607 608 override val camera: CameraId = frameMetadata.camera 609 override val frameNumber: FrameNumber = frameMetadata.frameNumber 610 override val requestMetadata: RequestMetadata = emptyRequestMetadata 611 612 @Suppress("UNCHECKED_CAST") 613 override fun <T : Any> unwrapAs(type: KClass<T>): T? = null 614 } 615 616 return CaptureResultAdapter( 617 emptyRequestMetadata, 618 /** RequestMetadata not to be used here */ 619 frameNumber, 620 frameInfo, 621 ) 622 } 623 624 private val emptyRequestMetadata = 625 object : RequestMetadata { getnull626 override fun <T> get(key: CaptureRequest.Key<T>): T? = null 627 628 override fun <T> getOrDefault(key: CaptureRequest.Key<T>, default: T): T = default 629 630 override val template: RequestTemplate = RequestTemplate(0) 631 override val streams: Map<StreamId, Surface> = mapOf() 632 override val repeating: Boolean = true 633 override val request: Request = Request(listOf()) 634 override val requestNumber: RequestNumber = RequestNumber(0) 635 636 override fun <T> get(key: Metadata.Key<T>): T? = null 637 638 override fun <T> getOrDefault(key: Metadata.Key<T>, default: T): T = default 639 640 override fun <T : Any> unwrapAs(type: KClass<T>): T? = null 641 } 642 643 /** Unlocks any active AF lock by triggering an AF cancel. */ 644 private suspend fun unlockAf(timeLimitNs: Long): Result3A = 645 graph 646 .acquireSession() 647 .use { 648 it.unlock3A( 649 af = true, 650 timeLimitNs = timeLimitNs, 651 ) 652 } 653 .await() 654 submitRequestInternalnull655 private fun submitRequestInternal( 656 params: MainCaptureParams, 657 ): List<Deferred<Void?>> { 658 if (sessionProcessorManager != null) { 659 return submitRequestInternalWithSessionProcessor(params.configs) 660 } 661 debug { 662 "CapturePipeline#submitRequestInternal; Submitting ${params.configs} with CameraPipe" 663 } 664 val deferredList = mutableListOf<CompletableDeferred<Void?>>() 665 val requests = 666 params.configs.mapNotNull { 667 val completeSignal = CompletableDeferred<Void?>().also { deferredList.add(it) } 668 try { 669 configAdapter.mapToRequest( 670 it, 671 params.requestTemplate, 672 params.sessionConfigOptions, 673 listOf( 674 object : Request.Listener { 675 override fun onAborted(request: Request) { 676 completeSignal.completeExceptionally( 677 ImageCaptureException( 678 ERROR_CAMERA_CLOSED, 679 "Capture request is cancelled because camera is closed", 680 null 681 ) 682 ) 683 } 684 685 override fun onTotalCaptureResult( 686 requestMetadata: RequestMetadata, 687 frameNumber: FrameNumber, 688 totalCaptureResult: FrameInfo, 689 ) { 690 completeSignal.complete(null) 691 } 692 693 override fun onFailed( 694 requestMetadata: RequestMetadata, 695 frameNumber: FrameNumber, 696 requestFailure: RequestFailure 697 ) { 698 completeSignal.completeExceptionally( 699 ImageCaptureException( 700 ERROR_CAPTURE_FAILED, 701 "Capture request failed with reason " + 702 requestFailure.reason, 703 null 704 ) 705 ) 706 } 707 } 708 ) 709 ) 710 } catch (e: IllegalStateException) { 711 info(e) { 712 "CapturePipeline#submitRequestInternal: configAdapter.mapToRequest failed!" 713 } 714 completeSignal.completeExceptionally( 715 ImageCaptureException( 716 ERROR_CAPTURE_FAILED, 717 "Capture request failed with reason " + e.message, 718 e 719 ) 720 ) 721 null 722 } 723 } 724 725 if (requests.isEmpty()) { 726 // requests can be empty due to configAdapter.mapToRequest throwing exception, all the 727 // deferred instances in the list should already be completed exceptionally. 728 return deferredList 729 } 730 731 threads.sequentialScope.launch { 732 debug { 733 "CapturePipeline#submitRequestInternal: Acquiring session for submitting requests" 734 } 735 // graph.acquireSession may fail if camera has entered closing stage 736 var cameraGraphSession: CameraGraph.Session? = null 737 try { 738 cameraGraphSession = graph.acquireSession() 739 } catch (_: CancellationException) { 740 info { 741 "CapturePipeline#submitRequestInternal: " + 742 "CameraGraph.Session could not be acquired, requests may need re-submission" 743 } 744 745 // completing the requests exceptionally so that they are retried with next camera 746 deferredList.forEach { 747 it.completeExceptionally( 748 ImageCaptureException( 749 ERROR_CAMERA_CLOSED, 750 "Capture request is cancelled because camera is closed", 751 null 752 ) 753 ) 754 } 755 } 756 757 cameraGraphSession?.use { 758 val requiresStopRepeating = requests.shouldStopRepeatingBeforeCapture() 759 if (requiresStopRepeating) { 760 it.stopRepeating() 761 } 762 763 debug { "CapturePipeline#submitRequestInternal: Submitting $requests" } 764 it.submit(requests) 765 766 if (requiresStopRepeating) { 767 deferredList.joinAll() 768 useCaseCameraState.tryStartRepeating() 769 } 770 } 771 } 772 773 return deferredList 774 } 775 submitRequestInternalWithSessionProcessornull776 private fun submitRequestInternalWithSessionProcessor( 777 configs: List<CaptureConfig> 778 ): List<Deferred<Void?>> { 779 debug { 780 "CapturePipeline#submitRequestInternal: Submitting $configs using SessionProcessor" 781 } 782 val deferredList = mutableListOf<CompletableDeferred<Void?>>() 783 val callbacks = 784 configs.map { 785 val completeSignal = CompletableDeferred<Void?>().also { deferredList.add(it) } 786 object : CaptureCallback { 787 private var cameraCaptureResult: CameraCaptureResult? = null 788 789 override fun onCaptureStarted(captureSequenceId: Int, timestamp: Long) { 790 for (captureCallback in it.cameraCaptureCallbacks) { 791 captureCallback.onCaptureStarted(it.id) 792 } 793 } 794 795 override fun onCaptureFailed(captureSequenceId: Int) { 796 completeSignal.completeExceptionally( 797 ImageCaptureException( 798 ERROR_CAPTURE_FAILED, 799 "Capture request failed", 800 null 801 ) 802 ) 803 for (captureCallback in it.cameraCaptureCallbacks) { 804 captureCallback.onCaptureFailed( 805 it.id, 806 CameraCaptureFailure(CameraCaptureFailure.Reason.ERROR) 807 ) 808 } 809 } 810 811 override fun onCaptureCompleted( 812 timestamp: Long, 813 captureSequenceId: Int, 814 captureResult: CameraCaptureResult 815 ) { 816 cameraCaptureResult = captureResult 817 } 818 819 override fun onCaptureSequenceCompleted(captureSequenceId: Int) { 820 completeSignal.complete(null) 821 val captureResult = cameraCaptureResult ?: EmptyCameraCaptureResult() 822 for (captureCallback in it.cameraCaptureCallbacks) { 823 captureCallback.onCaptureCompleted(it.id, captureResult) 824 } 825 } 826 827 override fun onCaptureProcessProgressed(progress: Int) { 828 for (captureCallback in it.cameraCaptureCallbacks) { 829 captureCallback.onCaptureProcessProgressed(it.id, progress) 830 } 831 } 832 833 override fun onCaptureSequenceAborted(captureSequenceId: Int) { 834 completeSignal.completeExceptionally( 835 ImageCaptureException( 836 ERROR_CAMERA_CLOSED, 837 "Capture request is cancelled because camera is closed", 838 null 839 ) 840 ) 841 } 842 } 843 } 844 sessionProcessorManager!!.submitCaptureConfigs(configs, callbacks) 845 return deferredList 846 } 847 isPhysicalFlashRequirednull848 private suspend fun isPhysicalFlashRequired(@FlashMode flashMode: Int): Boolean = 849 when (flashMode) { 850 FLASH_MODE_ON -> true 851 FLASH_MODE_AUTO -> { 852 waitForResult(CHECK_FLASH_REQUIRED_TIMEOUT_IN_NS) 853 ?.metadata 854 ?.get(CaptureResult.CONTROL_AE_STATE) == CONTROL_AE_STATE_FLASH_REQUIRED 855 } 856 FLASH_MODE_OFF -> false 857 FLASH_MODE_SCREEN -> false 858 else -> throw AssertionError(flashMode) 859 } 860 waitForResultnull861 private suspend fun waitForResult( 862 waitTimeoutNanos: Long, 863 checker: (totalCaptureResult: FrameInfo) -> Boolean = { _ -> true } 864 ): FrameInfo? { 865 val resultListener = listenernull866 ResultListener(waitTimeoutNanos, checker).also { listener -> 867 requestListener.addListener(listener, threads.sequentialExecutor) 868 threads.sequentialScope.launch { 869 listener.result.join() 870 requestListener.removeListener(listener) 871 } 872 } 873 <lambda>null874 return withTimeoutOrNull(TimeUnit.NANOSECONDS.toMillis(waitTimeoutNanos)) { 875 // ResultListener timeout is checked only when there is a captured frame, so it 876 // might get stuck indefinitely without withTimeOrNull 877 resultListener.result.await() 878 } frameInfonull879 .also { frameInfo -> 880 if (frameInfo == null) { 881 requestListener.removeListener(resultListener) 882 } 883 } 884 } 885 isTorchAsFlashnull886 private fun isTorchAsFlash(@FlashType flashType: Int): Boolean { 887 return template == CameraDevice.TEMPLATE_RECORD || 888 flashType == FLASH_TYPE_USE_TORCH_AS_FLASH || 889 useTorchAsFlash.shouldUseTorchAsFlash() 890 } 891 } 892 893 /** 894 * A listener receives the result from the repeating request, and sends it to the [checker] to 895 * determine if the [completeSignal] can be completed. 896 * 897 * @param timeLimitNs timeout threshold in Nanos, set 0 for no timeout case. 898 * @param checker the checker to define the condition to complete the [completeSignal]. Return true 899 * will complete the [completeSignal], otherwise it will continue to receive the results until the 900 * timeLimitNs is reached. 901 * @constructor 902 */ 903 public class ResultListener( 904 private val timeLimitNs: Long, 905 private val checker: (totalCaptureResult: FrameInfo) -> Boolean, 906 ) : Request.Listener { 907 908 private val completeSignal = CompletableDeferred<FrameInfo?>() 909 public val result: Deferred<FrameInfo?> 910 get() = completeSignal 911 912 @Volatile private var timestampOfFirstUpdateNs: Long? = null 913 onTotalCaptureResultnull914 override fun onTotalCaptureResult( 915 requestMetadata: RequestMetadata, 916 frameNumber: FrameNumber, 917 totalCaptureResult: FrameInfo, 918 ) { 919 // Save some compute if the task is already complete or has been canceled. 920 if (completeSignal.isCompleted || completeSignal.isCancelled) { 921 return 922 } 923 924 val currentTimestampNs: Long? = totalCaptureResult.metadata[CaptureResult.SENSOR_TIMESTAMP] 925 926 if (currentTimestampNs != null && timestampOfFirstUpdateNs == null) { 927 timestampOfFirstUpdateNs = currentTimestampNs 928 } 929 930 val timestampOfFirstUpdateNs = timestampOfFirstUpdateNs 931 if ( 932 timeLimitNs != 0L && 933 timestampOfFirstUpdateNs != null && 934 currentTimestampNs != null && 935 currentTimestampNs - timestampOfFirstUpdateNs > timeLimitNs 936 ) { 937 completeSignal.complete(null) 938 debug { 939 "Wait for capture result timeout, current: $currentTimestampNs " + 940 "first: $timestampOfFirstUpdateNs" 941 } 942 return 943 } 944 if (!checker(totalCaptureResult)) { 945 return 946 } 947 948 completeSignal.complete(totalCaptureResult) 949 } 950 } 951