1 /* <lambda>null2 * Copyright 2024 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.lifecycle 18 19 import android.content.Context 20 import android.content.pm.PackageManager.FEATURE_CAMERA_CONCURRENT 21 import android.util.Range 22 import androidx.annotation.GuardedBy 23 import androidx.annotation.MainThread 24 import androidx.annotation.VisibleForTesting 25 import androidx.camera.core.Camera 26 import androidx.camera.core.CameraEffect 27 import androidx.camera.core.CameraFilter 28 import androidx.camera.core.CameraInfo 29 import androidx.camera.core.CameraInfoUnavailableException 30 import androidx.camera.core.CameraSelector 31 import androidx.camera.core.CameraX 32 import androidx.camera.core.CameraXConfig 33 import androidx.camera.core.CompositionSettings 34 import androidx.camera.core.ConcurrentCamera 35 import androidx.camera.core.ConcurrentCamera.SingleCameraConfig 36 import androidx.camera.core.ImageAnalysis 37 import androidx.camera.core.ImageCapture 38 import androidx.camera.core.Preview 39 import androidx.camera.core.UseCase 40 import androidx.camera.core.UseCaseGroup 41 import androidx.camera.core.ViewPort 42 import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_CONCURRENT 43 import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_SINGLE 44 import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_UNSPECIFIED 45 import androidx.camera.core.concurrent.CameraCoordinator.CameraOperatingMode 46 import androidx.camera.core.impl.AdapterCameraInfo 47 import androidx.camera.core.impl.CameraConfig 48 import androidx.camera.core.impl.CameraConfigs 49 import androidx.camera.core.impl.CameraInternal 50 import androidx.camera.core.impl.ExtendedCameraConfigProviderStore 51 import androidx.camera.core.impl.StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED 52 import androidx.camera.core.impl.UseCaseConfig 53 import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType 54 import androidx.camera.core.impl.utils.ContextUtil 55 import androidx.camera.core.impl.utils.Threads 56 import androidx.camera.core.impl.utils.executor.CameraXExecutors 57 import androidx.camera.core.impl.utils.futures.FutureCallback 58 import androidx.camera.core.impl.utils.futures.FutureChain 59 import androidx.camera.core.impl.utils.futures.Futures 60 import androidx.camera.core.internal.CameraUseCaseAdapter 61 import androidx.core.util.Preconditions 62 import androidx.lifecycle.Lifecycle 63 import androidx.lifecycle.LifecycleOwner 64 import androidx.tracing.trace 65 import com.google.common.util.concurrent.ListenableFuture 66 import java.util.Objects 67 import java.util.Objects.requireNonNull 68 69 /** Implementation of the [LifecycleCameraProvider] interface. */ 70 internal class LifecycleCameraProviderImpl : LifecycleCameraProvider { 71 private val lock = Any() 72 @VisibleForTesting 73 @GuardedBy("mLock") 74 internal var cameraXConfigProvider: CameraXConfig.Provider? = null 75 @GuardedBy("mLock") private var cameraXInitializeFuture: ListenableFuture<Void>? = null 76 @GuardedBy("mLock") private var cameraXShutdownFuture = Futures.immediateFuture<Void>(null) 77 private val lifecycleCameraRepository = LifecycleCameraRepository.getInstance() 78 private var cameraX: CameraX? = null 79 @VisibleForTesting internal var context: Context? = null 80 @GuardedBy("mLock") 81 private val cameraInfoMap: MutableMap<CameraUseCaseAdapter.CameraId, AdapterCameraInfo> = 82 HashMap() 83 private val lifecycleCameraKeys = HashSet<LifecycleCameraRepository.Key>() 84 override var configImplType = CameraXConfig.CAMERAX_CONFIG_IMPL_TYPE_UNKNOWN 85 86 internal fun initAsync( 87 context: Context, 88 cameraXConfig: CameraXConfig? = null 89 ): ListenableFuture<Void> { 90 synchronized(lock) { 91 if (cameraXInitializeFuture != null) { 92 return cameraXInitializeFuture as ListenableFuture<Void> 93 } 94 cameraXConfig?.let { configure(it) } 95 val cameraX = CameraX(context, cameraXConfigProvider) 96 configImplType = cameraX.configImplType 97 98 val initFuture = 99 FutureChain.from(cameraXShutdownFuture) 100 .transformAsync({ cameraX.initializeFuture }, CameraXExecutors.directExecutor()) 101 102 cameraXInitializeFuture = initFuture 103 104 Futures.addCallback( 105 initFuture, 106 object : FutureCallback<Void?> { 107 override fun onSuccess(void: Void?) { 108 this@LifecycleCameraProviderImpl.cameraX = cameraX 109 this@LifecycleCameraProviderImpl.context = 110 ContextUtil.getApplicationContext(context) 111 } 112 113 override fun onFailure(t: Throwable) { 114 shutdownAsync(clearConfigProvider = false) 115 } 116 }, 117 CameraXExecutors.directExecutor() 118 ) 119 120 return Futures.nonCancellationPropagating(initFuture) 121 } 122 } 123 124 /** 125 * Configures the camera provider. 126 * 127 * The camera provider can only be configured once. Trying to configure it multiple times will 128 * throw an [IllegalStateException]. 129 * 130 * @param cameraXConfig The CameraX configuration. 131 */ 132 internal fun configure(cameraXConfig: CameraXConfig) = 133 trace("CX:configureInstanceInternal") { 134 synchronized(lock) { 135 Preconditions.checkNotNull(cameraXConfig) 136 Preconditions.checkState( 137 cameraXConfigProvider == null, 138 "CameraX has already been configured. To use a different configuration, " + 139 "shutdown() must be called." 140 ) 141 cameraXConfigProvider = CameraXConfig.Provider { cameraXConfig } 142 } 143 } 144 145 internal fun shutdownAsync(clearConfigProvider: Boolean = true): ListenableFuture<Void> { 146 Threads.runOnMainSync { 147 unbindAll() 148 lifecycleCameraRepository.removeLifecycleCameras(lifecycleCameraKeys) 149 } 150 151 if (cameraX != null) { 152 cameraX!!.cameraFactory.cameraCoordinator.shutdown() 153 } 154 155 val shutdownFuture = 156 if (cameraX != null) cameraX!!.shutdown() else Futures.immediateFuture<Void>(null) 157 158 synchronized(lock) { 159 if (clearConfigProvider) { 160 cameraXConfigProvider = null 161 } 162 cameraXInitializeFuture = null 163 cameraXShutdownFuture = shutdownFuture 164 cameraInfoMap.clear() 165 lifecycleCameraKeys.clear() 166 } 167 cameraX = null 168 context = null 169 return shutdownFuture 170 } 171 172 override fun isBound(useCase: UseCase): Boolean { 173 for (lifecycleCamera: LifecycleCamera in lifecycleCameraRepository.lifecycleCameras) { 174 if (lifecycleCamera.isBound(useCase)) { 175 return true 176 } 177 } 178 179 return false 180 } 181 182 @MainThread 183 override fun unbind(vararg useCases: UseCase?): Unit = 184 trace("CX:unbind") { 185 Threads.checkMainThread() 186 187 if (cameraOperatingMode == CAMERA_OPERATING_MODE_CONCURRENT) { 188 throw UnsupportedOperationException( 189 "Unbind UseCase is not supported in concurrent camera mode, call unbindAll() first." 190 ) 191 } 192 193 lifecycleCameraRepository.unbind(listOf(*useCases), lifecycleCameraKeys) 194 } 195 196 @MainThread 197 override fun unbindAll(): Unit = 198 trace("CX:unbindAll") { 199 Threads.checkMainThread() 200 cameraOperatingMode = CAMERA_OPERATING_MODE_UNSPECIFIED 201 lifecycleCameraRepository.unbindAll(lifecycleCameraKeys) 202 } 203 204 @Throws(CameraInfoUnavailableException::class) 205 override fun hasCamera(cameraSelector: CameraSelector): Boolean = 206 trace("CX:hasCamera") { 207 try { 208 cameraSelector.select(cameraX!!.cameraRepository.cameras) 209 } catch (_: IllegalArgumentException) { 210 return@trace false 211 } 212 213 return@trace true 214 } 215 216 @MainThread 217 override fun bindToLifecycle( 218 lifecycleOwner: LifecycleOwner, 219 cameraSelector: CameraSelector, 220 vararg useCases: UseCase? 221 ): Camera = 222 trace("CX:bindToLifecycle") { 223 if (cameraOperatingMode == CAMERA_OPERATING_MODE_CONCURRENT) { 224 throw UnsupportedOperationException( 225 "bindToLifecycle for single camera is not supported in concurrent camera mode, " + 226 "call unbindAll() first" 227 ) 228 } 229 cameraOperatingMode = CAMERA_OPERATING_MODE_SINGLE 230 val camera = 231 bindToLifecycle(lifecycleOwner, cameraSelector, useCases = useCases.filterNotNull()) 232 return@trace camera 233 } 234 235 @MainThread 236 override fun bindToLifecycle( 237 lifecycleOwner: LifecycleOwner, 238 cameraSelector: CameraSelector, 239 useCaseGroup: UseCaseGroup 240 ): Camera = 241 trace("CX:bindToLifecycle-UseCaseGroup") { 242 if (cameraOperatingMode == CAMERA_OPERATING_MODE_CONCURRENT) { 243 throw UnsupportedOperationException( 244 "bindToLifecycle for single camera is not supported in concurrent camera mode, " + 245 "call unbindAll() first." 246 ) 247 } 248 cameraOperatingMode = CAMERA_OPERATING_MODE_SINGLE 249 val camera = 250 bindToLifecycle( 251 lifecycleOwner, 252 cameraSelector, 253 viewPort = useCaseGroup.viewPort, 254 effects = useCaseGroup.effects, 255 targetHighSpeedFrameRate = useCaseGroup.targetHighSpeedFrameRate, 256 useCases = useCaseGroup.useCases 257 ) 258 return@trace camera 259 } 260 261 @MainThread 262 override fun bindToLifecycle(singleCameraConfigs: List<SingleCameraConfig?>): ConcurrentCamera = 263 trace("CX:bindToLifecycle-Concurrent") { 264 if (singleCameraConfigs.size < 2) { 265 throw IllegalArgumentException("Concurrent camera needs two camera configs.") 266 } 267 268 if (singleCameraConfigs.size > 2) { 269 throw IllegalArgumentException( 270 "Concurrent camera is only supporting two cameras at maximum." 271 ) 272 } 273 274 val firstCameraConfig = singleCameraConfigs[0]!! 275 val secondCameraConfig = singleCameraConfigs[1]!! 276 277 val cameras: MutableList<Camera> = ArrayList() 278 if ( 279 firstCameraConfig.cameraSelector.lensFacing == 280 secondCameraConfig.cameraSelector.lensFacing 281 ) { 282 if (cameraOperatingMode == CAMERA_OPERATING_MODE_CONCURRENT) { 283 throw UnsupportedOperationException( 284 "Camera is already running, call unbindAll() before binding more cameras." 285 ) 286 } 287 if ( 288 firstCameraConfig.lifecycleOwner != secondCameraConfig.lifecycleOwner || 289 firstCameraConfig.useCaseGroup.viewPort != 290 secondCameraConfig.useCaseGroup.viewPort || 291 firstCameraConfig.useCaseGroup.effects != 292 secondCameraConfig.useCaseGroup.effects 293 ) { 294 throw IllegalArgumentException( 295 "Two camera configs need to have the same lifecycle owner, view port and " + 296 "effects." 297 ) 298 } 299 val lifecycleOwner = firstCameraConfig.lifecycleOwner 300 val cameraSelector = firstCameraConfig.cameraSelector 301 val viewPort = firstCameraConfig.useCaseGroup.viewPort 302 val effects = firstCameraConfig.useCaseGroup.effects 303 val targetHighSpeedFrameRate = 304 firstCameraConfig.useCaseGroup.targetHighSpeedFrameRate 305 val useCases: MutableList<UseCase> = ArrayList() 306 for (config: SingleCameraConfig? in singleCameraConfigs) { 307 // Connect physical camera id with use case. 308 for (useCase: UseCase in config!!.useCaseGroup.useCases) { 309 config.cameraSelector.physicalCameraId?.let { 310 useCase.setPhysicalCameraId(it) 311 } 312 } 313 useCases.addAll(config.useCaseGroup.useCases) 314 } 315 316 cameraOperatingMode = CAMERA_OPERATING_MODE_SINGLE 317 val camera = 318 bindToLifecycle( 319 lifecycleOwner, 320 cameraSelector, 321 viewPort = viewPort, 322 effects = effects, 323 targetHighSpeedFrameRate = targetHighSpeedFrameRate, 324 useCases = useCases 325 ) 326 cameras.add(camera) 327 } else { 328 if (!context!!.packageManager.hasSystemFeature(FEATURE_CAMERA_CONCURRENT)) { 329 throw UnsupportedOperationException( 330 "Concurrent camera is not supported on the device." 331 ) 332 } 333 334 if (cameraOperatingMode == CAMERA_OPERATING_MODE_SINGLE) { 335 throw UnsupportedOperationException( 336 "Camera is already running, call unbindAll() before binding more cameras." 337 ) 338 } 339 340 val cameraInfosToBind: MutableList<CameraInfo> = ArrayList() 341 val firstCameraInfo: CameraInfo 342 val secondCameraInfo: CameraInfo 343 try { 344 firstCameraInfo = getCameraInfo(firstCameraConfig.cameraSelector) 345 secondCameraInfo = getCameraInfo(secondCameraConfig.cameraSelector) 346 } catch (_: IllegalArgumentException) { 347 throw IllegalArgumentException("Invalid camera selectors in camera configs.") 348 } 349 cameraInfosToBind.add(firstCameraInfo) 350 cameraInfosToBind.add(secondCameraInfo) 351 if ( 352 activeConcurrentCameraInfos.isNotEmpty() && 353 cameraInfosToBind != activeConcurrentCameraInfos 354 ) { 355 throw UnsupportedOperationException( 356 "Cameras are already running, call unbindAll() before binding more cameras." 357 ) 358 } 359 360 cameraOperatingMode = CAMERA_OPERATING_MODE_CONCURRENT 361 362 // For dual camera video capture, we are only supporting two use cases: 363 // Preview + VideoCapture. If ImageCapture support is added, the validation logic 364 // will be updated accordingly. 365 var isDualCameraVideoCapture = false 366 if ( 367 Objects.equals( 368 firstCameraConfig.useCaseGroup.useCases, 369 secondCameraConfig.useCaseGroup.useCases 370 ) && firstCameraConfig.useCaseGroup.useCases.size == 2 371 ) { 372 val useCase0 = firstCameraConfig.useCaseGroup.useCases[0] 373 val useCase1 = firstCameraConfig.useCaseGroup.useCases[1] 374 isDualCameraVideoCapture = 375 (isVideoCapture(useCase0) && isPreview(useCase1)) || 376 (isPreview(useCase0) && isVideoCapture(useCase1)) 377 } 378 379 if (isDualCameraVideoCapture) { 380 cameras.add( 381 bindToLifecycle( 382 firstCameraConfig.lifecycleOwner, 383 firstCameraConfig.cameraSelector, 384 secondCameraConfig.cameraSelector, 385 firstCameraConfig.compositionSettings, 386 secondCameraConfig.compositionSettings, 387 firstCameraConfig.useCaseGroup.viewPort, 388 firstCameraConfig.useCaseGroup.effects, 389 firstCameraConfig.useCaseGroup.targetHighSpeedFrameRate, 390 useCases = firstCameraConfig.useCaseGroup.useCases, 391 ) 392 ) 393 } else { 394 for (config: SingleCameraConfig? in singleCameraConfigs) { 395 val camera = 396 bindToLifecycle( 397 config!!.lifecycleOwner, 398 config.cameraSelector, 399 viewPort = config.useCaseGroup.viewPort, 400 effects = config.useCaseGroup.effects, 401 targetHighSpeedFrameRate = 402 config.useCaseGroup.targetHighSpeedFrameRate, 403 useCases = config.useCaseGroup.useCases 404 ) 405 cameras.add(camera) 406 } 407 } 408 activeConcurrentCameraInfos = cameraInfosToBind 409 } 410 return@trace ConcurrentCamera(cameras) 411 } 412 413 override val availableCameraInfos: List<CameraInfo> 414 get() = 415 trace("CX:getAvailableCameraInfos") { 416 val availableCameraInfos: MutableList<CameraInfo> = ArrayList() 417 val cameras: Set<CameraInternal> = cameraX!!.cameraRepository.cameras 418 for (camera: CameraInternal in cameras) { 419 availableCameraInfos.add(camera.cameraInfo) 420 } 421 return@trace availableCameraInfos 422 } 423 424 override val availableConcurrentCameraInfos: List<List<CameraInfo>> 425 get() = 426 trace("CX:getAvailableConcurrentCameraInfos") { 427 requireNonNull(cameraX) 428 requireNonNull(cameraX!!.cameraFactory.cameraCoordinator) 429 val concurrentCameraSelectorLists = 430 cameraX!!.cameraFactory.cameraCoordinator.concurrentCameraSelectors 431 432 val availableConcurrentCameraInfos: MutableList<List<CameraInfo>> = ArrayList() 433 for (cameraSelectors in concurrentCameraSelectorLists) { 434 val cameraInfos: MutableList<CameraInfo> = ArrayList() 435 for (cameraSelector in cameraSelectors) { 436 var cameraInfo: CameraInfo 437 try { 438 cameraInfo = getCameraInfo(cameraSelector) 439 } catch (_: IllegalArgumentException) { 440 continue 441 } 442 cameraInfos.add(cameraInfo) 443 } 444 availableConcurrentCameraInfos.add(cameraInfos) 445 } 446 return@trace availableConcurrentCameraInfos 447 } 448 449 override val isConcurrentCameraModeOn: Boolean 450 /** 451 * Returns whether there is a [ConcurrentCamera] bound. 452 * 453 * @return `true` if there is a [ConcurrentCamera] bound, otherwise `false`. 454 */ 455 @MainThread get() = cameraOperatingMode == CAMERA_OPERATING_MODE_CONCURRENT 456 457 /** 458 * Binds [ViewPort] and a collection of [UseCase] to a [LifecycleOwner]. 459 * 460 * The state of the lifecycle will determine when the cameras are open, started, stopped and 461 * closed. When started, the use cases receive camera data. 462 * 463 * Binding to a [LifecycleOwner] in state currently in [Lifecycle.State.STARTED] or greater will 464 * also initialize and start data capture. If the camera was already running this may cause a 465 * new initialization to occur temporarily stopping data from the camera before restarting it. 466 * 467 * Multiple use cases can be bound via adding them all to a single [bindToLifecycle] call, or by 468 * using multiple [bindToLifecycle] calls. Using a single call that includes all the use cases 469 * helps to set up a camera session correctly for all uses cases, such as by allowing 470 * determination of resolutions depending on all the use cases bound being bound. If the use 471 * cases are bound separately, it will find the supported resolution with the priority depending 472 * on the binding sequence. If the use cases are bound with a single call, it will find the 473 * supported resolution with the priority in sequence of [ImageCapture], [Preview] and then 474 * [ImageAnalysis]. The resolutions that can be supported depends on the camera device hardware 475 * level that there are some default guaranteed resolutions listed in 476 * [android.hardware.camera2.CameraDevice.createCaptureSession]. 477 * 478 * Currently up to 3 use cases may be bound to a [Lifecycle] at any time. Exceeding capability 479 * of target camera device will throw an IllegalArgumentException. 480 * 481 * A [UseCase] should only be bound to a single lifecycle and camera selector a time. Attempting 482 * to bind a use case to a lifecycle when it is already bound to another lifecycle is an error, 483 * and the use case binding will not change. Attempting to bind the same use case to multiple 484 * camera selectors is also an error and will not change the binding. 485 * 486 * If different use cases are bound to different camera selectors that resolve to distinct 487 * cameras, but the same lifecycle, only one of the cameras will operate at a time. The 488 * non-operating camera will not become active until it is the only camera with use cases bound. 489 * 490 * The [Camera] returned is determined by the given camera selector, plus other internal 491 * requirements, possibly from use case configurations. The camera returned from 492 * [bindToLifecycle] may differ from the camera determined solely by a camera selector. If the 493 * camera selector can't resolve a camera under the requirements, an [IllegalArgumentException] 494 * will be thrown. 495 * 496 * Only [UseCase] bound to latest active [Lifecycle] can keep alive. [UseCase] bound to other 497 * [Lifecycle] will be stopped. 498 * 499 * @param lifecycleOwner The [LifecycleOwner] which controls the lifecycle transitions of the 500 * use cases. 501 * @param primaryCameraSelector The primary camera selector which determines the camera to use 502 * for set of use cases. 503 * @param secondaryCameraSelector The secondary camera selector in dual camera case. 504 * @param primaryCompositionSettings The composition settings for the primary camera. 505 * @param secondaryCompositionSettings The composition settings for the secondary camera. 506 * @param viewPort The viewPort which represents the visible camera sensor rect. 507 * @param effects The effects applied to the camera outputs. 508 * @param targetHighSpeedFrameRate The high speed frame rate applied to the camera output. 509 * @param useCases The use cases to bind to a lifecycle. 510 * @return The [Camera] instance which is determined by the camera selector and internal 511 * requirements. 512 * @throws IllegalStateException If the use case has already been bound to another lifecycle or 513 * method is not called on main thread. 514 * @throws IllegalArgumentException If the provided camera selector is unable to resolve a 515 * camera to be used for the given use cases. 516 */ 517 @Suppress("unused") 518 private fun bindToLifecycle( 519 lifecycleOwner: LifecycleOwner, 520 primaryCameraSelector: CameraSelector, 521 secondaryCameraSelector: CameraSelector? = null, 522 primaryCompositionSettings: CompositionSettings = CompositionSettings.DEFAULT, 523 secondaryCompositionSettings: CompositionSettings = CompositionSettings.DEFAULT, 524 viewPort: ViewPort? = null, 525 effects: List<CameraEffect?> = emptyList(), 526 targetHighSpeedFrameRate: Range<Int> = FRAME_RATE_RANGE_UNSPECIFIED, 527 useCases: List<UseCase> = emptyList() 528 ): Camera = 529 trace("CX:bindToLifecycle-internal") { 530 Threads.checkMainThread() 531 // TODO(b/153096869): override UseCase's target rotation. 532 533 // Get the LifecycleCamera if existed. 534 val primaryCameraInternal = 535 primaryCameraSelector.select(cameraX!!.cameraRepository.cameras) 536 primaryCameraInternal.setPrimary(true) 537 val primaryAdapterCameraInfo = getCameraInfo(primaryCameraSelector) as AdapterCameraInfo 538 539 var secondaryCameraInternal: CameraInternal? = null 540 var secondaryAdapterCameraInfo: AdapterCameraInfo? = null 541 if (secondaryCameraSelector != null) { 542 secondaryCameraInternal = 543 secondaryCameraSelector.select(cameraX!!.cameraRepository.cameras) 544 secondaryCameraInternal.setPrimary(false) 545 secondaryAdapterCameraInfo = 546 getCameraInfo(secondaryCameraSelector) as AdapterCameraInfo 547 } 548 549 val cameraId = 550 CameraUseCaseAdapter.generateCameraId( 551 primaryAdapterCameraInfo, 552 secondaryAdapterCameraInfo 553 ) 554 var lifecycleCameraToBind = 555 lifecycleCameraRepository.getLifecycleCamera(lifecycleOwner, cameraId) 556 557 // Check if there's another camera that has already been bound. 558 val lifecycleCameras = lifecycleCameraRepository.lifecycleCameras 559 useCases.forEach { useCase -> 560 for (lifecycleCamera: LifecycleCamera in lifecycleCameras) { 561 if ( 562 lifecycleCamera.isBound(useCase) && lifecycleCamera != lifecycleCameraToBind 563 ) { 564 throw IllegalStateException( 565 String.format( 566 "Use case %s already bound to a different lifecycle.", 567 useCase 568 ) 569 ) 570 } 571 } 572 } 573 574 // Create the LifecycleCamera if there's no existing one that can be used. 575 if (lifecycleCameraToBind == null) { 576 lifecycleCameraToBind = 577 lifecycleCameraRepository.createLifecycleCamera( 578 lifecycleOwner, 579 CameraUseCaseAdapter( 580 primaryCameraInternal, 581 secondaryCameraInternal, 582 primaryAdapterCameraInfo, 583 secondaryAdapterCameraInfo, 584 primaryCompositionSettings, 585 secondaryCompositionSettings, 586 cameraX!!.cameraFactory.cameraCoordinator, 587 cameraX!!.cameraDeviceSurfaceManager, 588 cameraX!!.defaultConfigFactory 589 ) 590 ) 591 } 592 593 if (useCases.isEmpty()) { 594 return@trace lifecycleCameraToBind!! 595 } 596 597 lifecycleCameraRepository.bindToLifecycleCamera( 598 lifecycleCameraToBind!!, 599 viewPort, 600 effects, 601 targetHighSpeedFrameRate, 602 useCases, 603 cameraX!!.cameraFactory.cameraCoordinator 604 ) 605 606 lifecycleCameraKeys.add(LifecycleCameraRepository.Key.create(lifecycleOwner, cameraId)) 607 608 return@trace lifecycleCameraToBind 609 } 610 611 override fun getCameraInfo(cameraSelector: CameraSelector): CameraInfo = 612 trace("CX:getCameraInfo") { 613 val cameraInfoInternal = 614 cameraSelector.select(cameraX!!.cameraRepository.cameras).cameraInfoInternal 615 val cameraConfig = getCameraConfig(cameraSelector, cameraInfoInternal) 616 617 val key = 618 CameraUseCaseAdapter.CameraId.create( 619 cameraInfoInternal.cameraId, 620 cameraConfig.compatibilityId 621 ) 622 var adapterCameraInfo: AdapterCameraInfo? 623 synchronized(lock) { 624 adapterCameraInfo = cameraInfoMap[key] 625 if (adapterCameraInfo == null) { 626 adapterCameraInfo = AdapterCameraInfo(cameraInfoInternal, cameraConfig) 627 cameraInfoMap[key] = adapterCameraInfo!! 628 } 629 } 630 631 return@trace adapterCameraInfo!! 632 } 633 634 private fun isVideoCapture(useCase: UseCase): Boolean { 635 return useCase.currentConfig.containsOption(UseCaseConfig.OPTION_CAPTURE_TYPE) && 636 useCase.currentConfig.captureType == CaptureType.VIDEO_CAPTURE 637 } 638 639 private fun isPreview(useCase: UseCase): Boolean { 640 return useCase is Preview 641 } 642 643 private fun getCameraConfig( 644 cameraSelector: CameraSelector, 645 cameraInfo: CameraInfo 646 ): CameraConfig { 647 var cameraConfig: CameraConfig? = null 648 for (cameraFilter: CameraFilter in cameraSelector.cameraFilterSet) { 649 if (cameraFilter.identifier != CameraFilter.DEFAULT_ID) { 650 val extendedCameraConfig = 651 ExtendedCameraConfigProviderStore.getConfigProvider(cameraFilter.identifier) 652 .getConfig(cameraInfo, (context)!!) 653 if (extendedCameraConfig == null) { // ignore IDs unrelated to camera configs. 654 continue 655 } 656 657 // Only allows one camera config now. 658 if (cameraConfig != null) { 659 throw IllegalArgumentException( 660 "Cannot apply multiple extended camera configs at the same time." 661 ) 662 } 663 cameraConfig = extendedCameraConfig 664 } 665 } 666 667 if (cameraConfig == null) { 668 cameraConfig = CameraConfigs.defaultConfig() 669 } 670 return cameraConfig 671 } 672 673 @get:CameraOperatingMode 674 private var cameraOperatingMode: Int 675 get() { 676 if (cameraX == null) { 677 return CAMERA_OPERATING_MODE_UNSPECIFIED 678 } 679 return cameraX!!.cameraFactory.cameraCoordinator.cameraOperatingMode 680 } 681 set(cameraOperatingMode) { 682 if (cameraX == null) { 683 return 684 } 685 cameraX!!.cameraFactory.cameraCoordinator.cameraOperatingMode = cameraOperatingMode 686 } 687 688 private var activeConcurrentCameraInfos: List<CameraInfo> 689 get() { 690 if (cameraX == null) { 691 return java.util.ArrayList() 692 } 693 return cameraX!!.cameraFactory.cameraCoordinator.activeConcurrentCameraInfos 694 } 695 set(cameraInfos) { 696 if (cameraX == null) { 697 return 698 } 699 cameraX!!.cameraFactory.cameraCoordinator.activeConcurrentCameraInfos = cameraInfos 700 } 701 } 702