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 package androidx.camera.integration.extensions 18 19 import android.content.Context 20 import android.graphics.ImageFormat 21 import android.graphics.SurfaceTexture 22 import android.hardware.camera2.CameraCaptureSession 23 import android.hardware.camera2.CameraCharacteristics 24 import android.hardware.camera2.CameraDevice 25 import android.hardware.camera2.CameraManager 26 import android.hardware.camera2.CaptureRequest 27 import android.hardware.camera2.params.OutputConfiguration 28 import android.hardware.camera2.params.SessionConfiguration 29 import android.media.ImageReader 30 import android.util.Rational 31 import android.util.Size 32 import android.view.Surface 33 import androidx.annotation.RequiresApi 34 import androidx.camera.camera2.internal.compat.params.SessionConfigurationCompat 35 import androidx.camera.core.CameraXConfig 36 import androidx.camera.core.impl.CameraInfoInternal 37 import androidx.camera.core.impl.utils.AspectRatioUtil 38 import androidx.camera.core.impl.utils.executor.CameraXExecutors 39 import androidx.camera.core.internal.utils.SizeUtil 40 import androidx.camera.extensions.ExtensionsManager 41 import androidx.camera.extensions.impl.advanced.AdvancedExtenderImpl 42 import androidx.camera.extensions.impl.advanced.Camera2OutputConfigImpl 43 import androidx.camera.extensions.impl.advanced.Camera2SessionConfigImpl 44 import androidx.camera.extensions.impl.advanced.ImageReaderOutputConfigImpl 45 import androidx.camera.extensions.impl.advanced.MultiResolutionImageReaderOutputConfigImpl 46 import androidx.camera.extensions.impl.advanced.OutputSurfaceConfigurationImpl 47 import androidx.camera.extensions.impl.advanced.OutputSurfaceImpl 48 import androidx.camera.extensions.impl.advanced.SurfaceOutputConfigImpl 49 import androidx.camera.extensions.internal.ExtensionVersion 50 import androidx.camera.extensions.internal.ExtensionsUtils 51 import androidx.camera.extensions.internal.Version 52 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil 53 import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil.getImageCaptureSupportedResolutions 54 import androidx.camera.integration.extensions.utils.CameraSelectorUtil 55 import androidx.camera.lifecycle.ProcessCameraProvider 56 import androidx.camera.testing.impl.fakes.FakeLifecycleOwner 57 import androidx.test.core.app.ApplicationProvider 58 import androidx.test.filters.SdkSuppress 59 import com.google.common.truth.Truth.assertThat 60 import java.util.concurrent.TimeUnit 61 import kotlinx.coroutines.CompletableDeferred 62 import kotlinx.coroutines.Dispatchers 63 import kotlinx.coroutines.runBlocking 64 import kotlinx.coroutines.withContext 65 import org.junit.Assume 66 import org.junit.Assume.assumeTrue 67 68 @RequiresApi(28) 69 class AdvancedExtenderValidation( 70 private val cameraXConfig: CameraXConfig, 71 private val cameraId: String, 72 private val extensionMode: Int 73 ) { 74 private val context = ApplicationProvider.getApplicationContext<Context>() 75 private lateinit var cameraProvider: ProcessCameraProvider 76 private lateinit var extensionsManager: ExtensionsManager 77 private lateinit var cameraCharacteristicsMap: Map<String, CameraCharacteristics> 78 private lateinit var advancedImpl: AdvancedExtenderImpl 79 80 fun setUp(): Unit = runBlocking { 81 ProcessCameraProvider.configureInstance(cameraXConfig) 82 cameraProvider = ProcessCameraProvider.getInstance(context)[10000, TimeUnit.MILLISECONDS] 83 extensionsManager = 84 ExtensionsManager.getInstanceAsync(context, cameraProvider)[ 85 10000, TimeUnit.MILLISECONDS] 86 assumeTrue(CameraXExtensionsTestUtil.isAdvancedExtenderImplemented()) 87 val baseCameraSelector = CameraSelectorUtil.createCameraSelectorById(cameraId) 88 assumeTrue(extensionsManager.isExtensionAvailable(baseCameraSelector, extensionMode)) 89 val extensionCameraSelector = 90 extensionsManager.getExtensionEnabledCameraSelector(baseCameraSelector, extensionMode) 91 val cameraInfo = 92 withContext(Dispatchers.Main) { 93 cameraProvider 94 .bindToLifecycle(FakeLifecycleOwner(), extensionCameraSelector) 95 .cameraInfo 96 } 97 cameraCharacteristicsMap = 98 ExtensionsUtils.getCameraCharacteristicsMap(cameraInfo as CameraInfoInternal) 99 advancedImpl = 100 CameraXExtensionsTestUtil.createAdvancedExtenderImpl( 101 extensionMode, 102 cameraId, 103 cameraInfo 104 ) 105 } 106 107 private val teardownFunctions = mutableListOf<() -> Unit>() 108 109 // Adding block to be invoked when tearing down. The last added will be invoked the first. 110 private fun addTearDown(teardown: () -> Unit) { 111 synchronized(teardownFunctions) { 112 teardownFunctions.add(0, teardown) // added to the head 113 } 114 } 115 116 fun tearDown(): Unit = runBlocking { 117 synchronized(teardownFunctions) { 118 for (teardownFunction in teardownFunctions) { 119 teardownFunction() 120 } 121 teardownFunctions.clear() 122 } 123 withContext(Dispatchers.Main) { 124 extensionsManager.shutdown()[10000, TimeUnit.MILLISECONDS] 125 cameraProvider.shutdownAsync()[10000, TimeUnit.MILLISECONDS] 126 } 127 } 128 129 // Test 130 fun getSupportedPreviewOutputResolutions_returnValidData() { 131 val map = advancedImpl.getSupportedPreviewOutputResolutions(cameraId) 132 133 assertThat(map[ImageFormat.PRIVATE]).isNotEmpty() 134 } 135 136 // Test 137 fun getSupportedCaptureOutputResolutions_returnValidData() { 138 val map = advancedImpl.getSupportedCaptureOutputResolutions(cameraId) 139 140 assertThat(map[ImageFormat.JPEG]).isNotEmpty() 141 assertThat(map[ImageFormat.YUV_420_888]).isNotEmpty() 142 } 143 144 // Test 145 fun getAvailableCaptureRequestKeys_existAfter1_3() { 146 assumeTrue(ExtensionVersion.getRuntimeVersion()!! >= Version.VERSION_1_3) 147 advancedImpl.getAvailableCaptureRequestKeys() 148 } 149 150 // Test 151 fun getAvailableCaptureResultKeys_existAfter1_3() { 152 assumeTrue(ExtensionVersion.getRuntimeVersion()!! >= Version.VERSION_1_3) 153 advancedImpl.getAvailableCaptureResultKeys() 154 } 155 156 /** 157 * The following 1.4 interface methods are validated by this test. 158 * <ol> 159 * <li>AdvancedExtenderImpl#isPostviewAvailable() 160 * <li>AdvancedExtenderImpl#getSupportedPostviewResolutions() 161 * </ol> 162 */ 163 // Test 164 fun validatePostviewSupport_sinceVersion_1_4() { 165 // Runs the test only when the vendor library implementation is 1.4 or above 166 assumeTrue(ExtensionVersion.getRuntimeVersion()!! >= Version.VERSION_1_4) 167 168 // Runs the test only when postview is available 169 assumeTrue(advancedImpl.isPostviewAvailable) 170 171 var anyPostViewSupported = false 172 173 getImageCaptureSupportedResolutions( 174 advancedImpl, 175 cameraId, 176 cameraCharacteristicsMap[cameraId]!! 177 ) 178 .forEach { captureSize -> 179 anyPostViewSupported = true 180 var captureSizeSupported = false 181 var yuvFormatSupported = false 182 advancedImpl.getSupportedPostviewResolutions(captureSize).forEach { entry -> 183 captureSizeSupported = true 184 if (entry.key == ImageFormat.YUV_420_888) { 185 yuvFormatSupported = true 186 } 187 188 entry.value.forEach { postviewSize -> 189 // The postview size be smaller than or equal to the provided capture size. 190 assertThat(SizeUtil.getArea(postviewSize)) 191 .isAtMost(SizeUtil.getArea(captureSize)) 192 // The postview size must have the same aspect ratio as the given capture 193 // size. 194 assertThat( 195 AspectRatioUtil.hasMatchingAspectRatio( 196 postviewSize, 197 Rational(captureSize.width, captureSize.height) 198 ) 199 ) 200 .isTrue() 201 } 202 } 203 // When postview is supported for the capture size, as the javadoc description, 204 // YUV_420_888 format must be supported. 205 if (captureSizeSupported) { 206 assertThat(yuvFormatSupported).isTrue() 207 } 208 } 209 210 // At least one postview size must be supported when isPostviewAvailable returns true. 211 assertThat(anyPostViewSupported).isTrue() 212 } 213 214 // Test 215 fun validateProcessProgressSupport_sinceVersion_1_4() { 216 // Runs the test only when the vendor library implementation is 1.4 or above 217 assumeTrue(ExtensionVersion.getRuntimeVersion()!! >= Version.VERSION_1_4) 218 // Makes sure isCaptureProcessProgressAvailable API can be called without any exception 219 // occurring when the vendor library implementation is version 1.4 or above 220 advancedImpl.isCaptureProcessProgressAvailable 221 } 222 223 // Test 224 @SdkSuppress(minSdkVersion = 30) 225 fun validateAvailableCharacteristicsKeyValuesSupport_sinceVersion_1_5() { 226 // Runs the test only when the vendor library implementation is 1.5 or above 227 assumeTrue(ExtensionVersion.getRuntimeVersion()!! >= Version.VERSION_1_5) 228 var zoomRatioRangeFound = false 229 var afAvailableModesFound = false 230 advancedImpl.availableCharacteristicsKeyValues.forEach { 231 when (it.first) { 232 CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE -> zoomRatioRangeFound = true 233 CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES -> afAvailableModesFound = true 234 } 235 } 236 // Also checks that CONTROL_ZOOM_RATIO_RANGE and CONTROL_AF_AVAILABLE_MODES should be 237 // contained at least. 238 assertThat(zoomRatioRangeFound).isTrue() 239 assertThat(afAvailableModesFound).isTrue() 240 } 241 242 enum class SizeCategory { 243 MAXIMUM, 244 MEDIAN, 245 MINIMUM 246 } 247 248 private fun createPreviewOutput( 249 impl: AdvancedExtenderImpl, 250 sizeCategory: SizeCategory 251 ): OutputSurfaceImpl { 252 val previewSizeMap = impl.getSupportedPreviewOutputResolutions(cameraId) 253 assertThat(previewSizeMap[ImageFormat.PRIVATE]).isNotEmpty() 254 255 val previewSizes = previewSizeMap[ImageFormat.PRIVATE]!! 256 val previewSize = getSizeByClass(previewSizes, sizeCategory) 257 val surfaceTexture = SurfaceTexture(0) 258 surfaceTexture.setDefaultBufferSize(previewSize.width, previewSize.height) 259 val previewSurface = Surface(surfaceTexture) 260 addTearDown { surfaceTexture.release() } 261 return OutputSurface(previewSurface, previewSize, ImageFormat.PRIVATE) 262 } 263 264 private fun createCaptureOutput( 265 impl: AdvancedExtenderImpl, 266 sizeCategory: SizeCategory 267 ): OutputSurfaceImpl { 268 val captureSizeMap = impl.getSupportedCaptureOutputResolutions(cameraId) 269 assertThat(captureSizeMap[ImageFormat.JPEG]).isNotEmpty() 270 271 val captureSizes = captureSizeMap[ImageFormat.JPEG]!! 272 var captureSize = getSizeByClass(captureSizes, sizeCategory) 273 val imageReader = 274 ImageReader.newInstance(captureSize.width, captureSize.height, ImageFormat.JPEG, 1) 275 addTearDown { imageReader.close() } 276 return OutputSurface(imageReader.surface, captureSize, ImageFormat.JPEG) 277 } 278 279 private fun getSizeByClass(sizes: List<Size>, sizeCategory: SizeCategory): Size { 280 val sortedList = sizes.sortedByDescending { it.width * it.height } 281 var size = 282 when (sizeCategory) { 283 SizeCategory.MAXIMUM -> { 284 sortedList[0] 285 } 286 SizeCategory.MEDIAN -> { 287 sortedList[sortedList.size / 2] 288 } 289 SizeCategory.MINIMUM -> { 290 sortedList[sortedList.size - 1] 291 } 292 } 293 return size 294 } 295 296 private fun createAnalysisOutput( 297 impl: AdvancedExtenderImpl, 298 sizeCategory: SizeCategory 299 ): OutputSurfaceImpl? { 300 val analysisSizes = impl.getSupportedYuvAnalysisResolutions(cameraId) ?: return null 301 assertThat(analysisSizes).isNotEmpty() 302 303 var analysisSize = getSizeByClass(analysisSizes, sizeCategory) 304 val imageReader = 305 ImageReader.newInstance( 306 analysisSize.width, 307 analysisSize.height, 308 ImageFormat.YUV_420_888, 309 1 310 ) 311 addTearDown { imageReader.close() } 312 return OutputSurface(imageReader.surface, analysisSize, ImageFormat.YUV_420_888) 313 } 314 315 private fun createPostviewOutput( 316 impl: AdvancedExtenderImpl, 317 captureSize: Size 318 ): OutputSurfaceImpl { 319 val postviewSize = 320 impl.getSupportedPostviewResolutions(captureSize)[ImageFormat.YUV_420_888]!![0] 321 322 val postviewImageReader = 323 ImageReader.newInstance( 324 postviewSize.width, 325 postviewSize.height, 326 ImageFormat.YUV_420_888, 327 1 328 ) 329 addTearDown { postviewImageReader.close() } 330 return OutputSurface(postviewImageReader.surface, captureSize, ImageFormat.YUV_420_888) 331 } 332 333 // Test 334 fun initSession_maxSize_canConfigureSession() = 335 initSessionTest( 336 previewOutputSizeCategory = SizeCategory.MAXIMUM, 337 captureOutputSizeCategory = SizeCategory.MAXIMUM 338 ) 339 340 // Test 341 fun initSession_minSize_canConfigureSession() = 342 initSessionTest( 343 previewOutputSizeCategory = SizeCategory.MINIMUM, 344 captureOutputSizeCategory = SizeCategory.MINIMUM 345 ) 346 347 // Test 348 fun initSession_medianSize_canConfigureSession() = 349 initSessionTest( 350 previewOutputSizeCategory = SizeCategory.MEDIAN, 351 captureOutputSizeCategory = SizeCategory.MEDIAN 352 ) 353 354 // Test 355 fun initSessionWithAnalysis_maxSize_canConfigureSession() = 356 initSessionTest( 357 previewOutputSizeCategory = SizeCategory.MAXIMUM, 358 captureOutputSizeCategory = SizeCategory.MAXIMUM, 359 analysisOutputSizeCategory = SizeCategory.MAXIMUM 360 ) 361 362 // Test 363 fun initSessionWithAnalysis_minSize_canConfigureSession() = 364 initSessionTest( 365 previewOutputSizeCategory = SizeCategory.MINIMUM, 366 captureOutputSizeCategory = SizeCategory.MINIMUM, 367 analysisOutputSizeCategory = SizeCategory.MINIMUM 368 ) 369 370 // Test 371 fun initSessionWithAnalysis_medianSize_canConfigureSession() = 372 initSessionTest( 373 previewOutputSizeCategory = SizeCategory.MEDIAN, 374 captureOutputSizeCategory = SizeCategory.MEDIAN, 375 analysisOutputSizeCategory = SizeCategory.MEDIAN 376 ) 377 378 // Test 379 fun initSessionWithOutputSurfaceConfigurationImpl_maxSize_canConfigureSession() { 380 // Runs the test only when the vendor library implementation is 1.4 or above 381 assumeTrue(ExtensionVersion.getRuntimeVersion()!! >= Version.VERSION_1_4) 382 initSessionTest( 383 previewOutputSizeCategory = SizeCategory.MAXIMUM, 384 captureOutputSizeCategory = SizeCategory.MAXIMUM, 385 enablePostview = advancedImpl.isPostviewAvailable, 386 useOutputSurfaceConfigurationImpl = true 387 ) 388 } 389 390 // Test 391 fun validateSessionTypeSupport_sinceVersion_1_4() { 392 // Runs the test only when the vendor library implementation is 1.4 or above 393 assumeTrue(ExtensionVersion.getRuntimeVersion()!! >= Version.VERSION_1_4) 394 val camera2SessionConfigImpl = 395 initSession( 396 previewOutputSizeCategory = SizeCategory.MAXIMUM, 397 captureOutputSizeCategory = SizeCategory.MAXIMUM 398 ) 399 // getSessionType is allowed to return any OEM customized session type, therefore, we can 400 // only try to invoke this method to make sure that this method correctly exists in the 401 // vendor library implementation. 402 camera2SessionConfigImpl.sessionType 403 } 404 405 // Test 406 fun validateSessionTypeSupportWithOutputSurfaceConfigurationImpl_sinceVersion_1_4() { 407 // Runs the test only when the vendor library implementation is 1.4 or above 408 assumeTrue(ExtensionVersion.getRuntimeVersion()!! >= Version.VERSION_1_4) 409 val camera2SessionConfigImpl = 410 initSession( 411 previewOutputSizeCategory = SizeCategory.MAXIMUM, 412 captureOutputSizeCategory = SizeCategory.MAXIMUM, 413 enablePostview = advancedImpl.isPostviewAvailable, 414 useOutputSurfaceConfigurationImpl = true 415 ) 416 // getSessionType is allowed to return any OEM customized session type, therefore, we can 417 // only try to invoke this method to make sure that this method correctly exists in the 418 // vendor library implementation. 419 camera2SessionConfigImpl.sessionType 420 } 421 422 fun initSessionTest( 423 previewOutputSizeCategory: SizeCategory, 424 captureOutputSizeCategory: SizeCategory, 425 analysisOutputSizeCategory: SizeCategory? = null, 426 enablePostview: Boolean = false, 427 useOutputSurfaceConfigurationImpl: Boolean = false 428 ): Unit = runBlocking { 429 val camera2SessionConfigImpl = 430 initSession( 431 previewOutputSizeCategory, 432 captureOutputSizeCategory, 433 analysisOutputSizeCategory, 434 enablePostview, 435 useOutputSurfaceConfigurationImpl 436 ) 437 438 verifyCamera2SessionConfig(camera2SessionConfigImpl) 439 } 440 441 private fun initSession( 442 previewOutputSizeCategory: SizeCategory, 443 captureOutputSizeCategory: SizeCategory, 444 analysisOutputSizeCategory: SizeCategory? = null, 445 enablePostview: Boolean = false, 446 useOutputSurfaceConfigurationImpl: Boolean = false 447 ): Camera2SessionConfigImpl { 448 if (analysisOutputSizeCategory != null) { 449 Assume.assumeFalse( 450 advancedImpl.getSupportedYuvAnalysisResolutions(cameraId).isNullOrEmpty() 451 ) 452 } 453 454 val sessionProcessor = advancedImpl.createSessionProcessor() 455 val previewOutput = createPreviewOutput(advancedImpl, previewOutputSizeCategory) 456 val captureOutput = createCaptureOutput(advancedImpl, captureOutputSizeCategory) 457 val analysisOutput = 458 analysisOutputSizeCategory?.let { 459 createAnalysisOutput(advancedImpl, analysisOutputSizeCategory) 460 } 461 462 addTearDown { sessionProcessor.deInitSession() } 463 464 return if (!useOutputSurfaceConfigurationImpl) { 465 sessionProcessor.initSession( 466 cameraId, 467 cameraCharacteristicsMap, 468 context, 469 previewOutput, 470 captureOutput, 471 analysisOutput 472 ) 473 } else { 474 val postviewOutput = 475 if (enablePostview) { 476 createPostviewOutput(advancedImpl, captureOutput.size) 477 } else { 478 null 479 } 480 val outputSurfaceConfigurationImpl = 481 OutputSurfaceConfigurationImplAdapter( 482 previewOutput, 483 captureOutput, 484 analysisOutput, 485 postviewOutput 486 ) 487 sessionProcessor.initSession( 488 cameraId, 489 cameraCharacteristicsMap, 490 context, 491 outputSurfaceConfigurationImpl 492 ) 493 } 494 } 495 496 private class OutputSurface( 497 private val surface: Surface, 498 private val size: Size, 499 private val imageFormat: Int 500 ) : OutputSurfaceImpl { 501 override fun getSurface() = surface 502 503 override fun getSize() = size 504 505 override fun getImageFormat() = imageFormat 506 } 507 508 private fun getOutputConfiguration( 509 outputConfigImpl: Camera2OutputConfigImpl 510 ): OutputConfiguration { 511 var outputConfiguration: OutputConfiguration 512 when (outputConfigImpl) { 513 is SurfaceOutputConfigImpl -> { 514 val surface = outputConfigImpl.surface 515 outputConfiguration = OutputConfiguration(outputConfigImpl.surfaceGroupId, surface) 516 } 517 is ImageReaderOutputConfigImpl -> { 518 val imageReader = 519 ImageReader.newInstance( 520 outputConfigImpl.size.width, 521 outputConfigImpl.size.height, 522 outputConfigImpl.imageFormat, 523 outputConfigImpl.maxImages 524 ) 525 val surface = imageReader.surface 526 addTearDown { imageReader.close() } 527 outputConfiguration = OutputConfiguration(outputConfigImpl.surfaceGroupId, surface) 528 } 529 is MultiResolutionImageReaderOutputConfigImpl -> 530 throw java.lang.UnsupportedOperationException( 531 "MultiResolutionImageReaderOutputConfigImpl not supported" 532 ) 533 else -> 534 throw java.lang.UnsupportedOperationException( 535 "Output configuration type not supported" 536 ) 537 } 538 539 if (outputConfigImpl.physicalCameraId != null) { 540 outputConfiguration.setPhysicalCameraId(outputConfigImpl.physicalCameraId) 541 } 542 543 outputConfigImpl.surfaceSharingOutputConfigs?.let { 544 for (surfaceSharingOutputConfig in it) { 545 val sharingOutputConfiguration = getOutputConfiguration(surfaceSharingOutputConfig) 546 outputConfiguration.addSurface(sharingOutputConfiguration.surface!!) 547 outputConfiguration.enableSurfaceSharing() 548 } 549 } 550 551 return outputConfiguration 552 } 553 554 private suspend fun openCameraDevice(cameraId: String): CameraDevice { 555 val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager 556 val deferred = CompletableDeferred<CameraDevice>() 557 cameraManager.openCamera( 558 cameraId, 559 CameraXExecutors.ioExecutor(), 560 object : CameraDevice.StateCallback() { 561 override fun onOpened(cameraDevice: CameraDevice) { 562 deferred.complete(cameraDevice) 563 } 564 565 override fun onDisconnected(cameraDevice: CameraDevice) { 566 deferred.completeExceptionally(RuntimeException("Camera Disconnected")) 567 } 568 569 override fun onError(cameraDevice: CameraDevice, error: Int) { 570 deferred.completeExceptionally( 571 RuntimeException("Camera onError(error=$cameraDevice)") 572 ) 573 } 574 } 575 ) 576 return deferred.await() 577 } 578 579 private suspend fun openCaptureSession( 580 cameraDevice: CameraDevice, 581 camera2SessionConfig: Camera2SessionConfigImpl 582 ): CameraCaptureSession { 583 584 val outputConfigurationList = mutableListOf<OutputConfiguration>() 585 for (outputConfig in camera2SessionConfig.outputConfigs) { 586 val outputConfiguration = getOutputConfiguration(outputConfig) 587 outputConfigurationList.add(outputConfiguration) 588 } 589 590 val sessionDeferred = CompletableDeferred<CameraCaptureSession>() 591 val sessionConfiguration = 592 SessionConfiguration( 593 SessionConfigurationCompat.SESSION_REGULAR, 594 outputConfigurationList, 595 CameraXExecutors.ioExecutor(), 596 object : CameraCaptureSession.StateCallback() { 597 override fun onConfigured(session: CameraCaptureSession) { 598 sessionDeferred.complete(session) 599 } 600 601 override fun onConfigureFailed(session: CameraCaptureSession) { 602 sessionDeferred.completeExceptionally(RuntimeException("onConfigureFailed")) 603 } 604 605 override fun onReady(session: CameraCaptureSession) {} 606 607 override fun onActive(session: CameraCaptureSession) {} 608 609 override fun onCaptureQueueEmpty(session: CameraCaptureSession) {} 610 } 611 ) 612 613 val requestBuilder = 614 cameraDevice.createCaptureRequest(camera2SessionConfig.sessionTemplateId) 615 616 camera2SessionConfig.sessionParameters.forEach { (key, value) -> 617 @Suppress("UNCHECKED_CAST") requestBuilder.set(key as CaptureRequest.Key<Any>, value) 618 } 619 sessionConfiguration.sessionParameters = requestBuilder.build() 620 621 cameraDevice.createCaptureSession(sessionConfiguration) 622 623 return sessionDeferred.await() 624 } 625 626 private suspend fun verifyCamera2SessionConfig(camera2SessionConfig: Camera2SessionConfigImpl) { 627 val cameraDevice = openCameraDevice(cameraId) 628 assertThat(cameraDevice).isNotNull() 629 addTearDown { cameraDevice.close() } 630 val captureSession = openCaptureSession(cameraDevice, camera2SessionConfig) 631 assertThat(captureSession).isNotNull() 632 addTearDown { captureSession.close() } 633 } 634 635 private class OutputSurfaceConfigurationImplAdapter( 636 private val previewOutputSurface: OutputSurfaceImpl, 637 private val captureOutputSurface: OutputSurfaceImpl, 638 private val analysisOutputSurface: OutputSurfaceImpl?, 639 private val postviewOutputSurface: OutputSurfaceImpl? 640 ) : OutputSurfaceConfigurationImpl { 641 override fun getPreviewOutputSurface(): OutputSurfaceImpl { 642 return previewOutputSurface 643 } 644 645 override fun getImageCaptureOutputSurface(): OutputSurfaceImpl { 646 return captureOutputSurface 647 } 648 649 override fun getImageAnalysisOutputSurface(): OutputSurfaceImpl? { 650 return analysisOutputSurface 651 } 652 653 override fun getPostviewOutputSurface(): OutputSurfaceImpl? { 654 return postviewOutputSurface 655 } 656 } 657 } 658