1 /* 2 * Copyright 2021 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.pipe.compat 18 19 import android.hardware.camera2.CameraCaptureSession.StateCallback 20 import android.hardware.camera2.CameraDevice 21 import android.hardware.camera2.CameraExtensionSession 22 import android.hardware.camera2.CaptureRequest 23 import android.hardware.camera2.TotalCaptureResult 24 import android.hardware.camera2.params.InputConfiguration 25 import android.hardware.camera2.params.OutputConfiguration 26 import android.os.Build 27 import android.view.Surface 28 import androidx.annotation.GuardedBy 29 import androidx.annotation.RequiresApi 30 import androidx.camera.camera2.pipe.AudioRestrictionMode 31 import androidx.camera.camera2.pipe.CameraId 32 import androidx.camera.camera2.pipe.CameraMetadata 33 import androidx.camera.camera2.pipe.RequestTemplate 34 import androidx.camera.camera2.pipe.UnsafeWrapper 35 import androidx.camera.camera2.pipe.core.Debug 36 import androidx.camera.camera2.pipe.core.Log 37 import androidx.camera.camera2.pipe.core.Threads 38 import androidx.camera.camera2.pipe.internal.CameraErrorListener 39 import androidx.camera.camera2.pipe.writeParameter 40 import kotlin.reflect.KClass 41 import kotlinx.atomicfu.atomic 42 43 /** 44 * Interface around a [CameraDevice] with minor modifications. 45 * 46 * This interface has been modified to correct nullness, adjust exceptions, and to return or produce 47 * wrapper interfaces instead of the native Camera2 types. 48 */ 49 internal interface CameraDeviceWrapper : UnsafeWrapper, AudioRestrictionController.Listener { 50 /** @see [CameraDevice.getId] */ 51 val cameraId: CameraId 52 53 /** @see CameraDevice.createCaptureRequest */ createCaptureRequestnull54 fun createCaptureRequest(template: RequestTemplate): CaptureRequest.Builder? 55 56 /** @see CameraDevice.createReprocessCaptureRequest */ 57 @RequiresApi(23) 58 fun createReprocessCaptureRequest(inputResult: TotalCaptureResult): CaptureRequest.Builder? 59 60 /** @see CameraDevice.createCaptureSession */ 61 fun createCaptureSession( 62 outputs: List<Surface>, 63 stateCallback: CameraCaptureSessionWrapper.StateCallback 64 ): Boolean 65 66 /** @see CameraDevice.createReprocessableCaptureSession */ 67 @RequiresApi(23) 68 fun createReprocessableCaptureSession( 69 input: InputConfiguration, 70 outputs: List<Surface>, 71 stateCallback: CameraCaptureSessionWrapper.StateCallback 72 ): Boolean 73 74 /** @see CameraDevice.createConstrainedHighSpeedCaptureSession */ 75 @RequiresApi(23) 76 fun createConstrainedHighSpeedCaptureSession( 77 outputs: List<Surface>, 78 stateCallback: CameraCaptureSessionWrapper.StateCallback 79 ): Boolean 80 81 /** @see CameraDevice.createCaptureSessionByOutputConfigurations */ 82 @RequiresApi(24) 83 fun createCaptureSessionByOutputConfigurations( 84 outputConfigurations: List<OutputConfigurationWrapper>, 85 stateCallback: CameraCaptureSessionWrapper.StateCallback 86 ): Boolean 87 88 /** @see CameraDevice.createReprocessableCaptureSessionByConfigurations */ 89 @RequiresApi(24) 90 fun createReprocessableCaptureSessionByConfigurations( 91 inputConfig: InputConfigData, 92 outputs: List<OutputConfigurationWrapper>, 93 stateCallback: CameraCaptureSessionWrapper.StateCallback 94 ): Boolean 95 96 /** @see CameraDevice.createCaptureSession */ 97 @RequiresApi(28) fun createCaptureSession(config: SessionConfigData): Boolean 98 99 /** @see CameraDevice.createExtensionSession */ 100 @RequiresApi(31) fun createExtensionSession(config: ExtensionSessionConfigData): Boolean 101 102 /** Invoked when the [CameraDevice] has been closed */ 103 fun onDeviceClosed() 104 105 /** @see CameraDevice.getCameraAudioRestriction */ 106 @RequiresApi(30) fun getCameraAudioRestriction(): AudioRestrictionMode 107 } 108 109 internal fun CameraDevice?.closeWithTrace() { 110 this?.let { 111 Log.info { "Closing Camera ${it.id}" } 112 Debug.instrument("CXCP#CameraDevice-${it.id}#close") { it.close() } 113 } 114 } 115 116 internal class AndroidCameraDevice( 117 private val cameraMetadata: CameraMetadata, 118 private val cameraDevice: CameraDevice, 119 override val cameraId: CameraId, 120 private val cameraErrorListener: CameraErrorListener, 121 private val interopSessionStateCallback: StateCallback? = null, 122 private val interopExtensionSessionStateCallback: CameraExtensionSession.StateCallback? = null, 123 private val threads: Threads 124 ) : CameraDeviceWrapper { 125 private val closed = atomic(false) 126 private val _lastStateCallback = atomic<SessionStateCallback?>(null) 127 createCaptureSessionnull128 override fun createCaptureSession( 129 outputs: List<Surface>, 130 stateCallback: CameraCaptureSessionWrapper.StateCallback 131 ): Boolean { 132 val (success, previousStateCallback) = checkAndSetStateCallback(stateCallback) 133 if (!success) return false 134 previousStateCallback?.onSessionDisconnectedWithTrace() 135 val result = 136 instrumentAndCatch("createCaptureSession") { 137 // This function was deprecated in Android Q, but is required for some 138 // configurations when running on older versions of the OS. 139 @Suppress("deprecation") 140 cameraDevice.createCaptureSession( 141 outputs, 142 AndroidCaptureSessionStateCallback( 143 this, 144 stateCallback, 145 previousStateCallback, 146 cameraErrorListener, 147 interopSessionStateCallback, 148 threads.camera2Handler 149 ), 150 threads.camera2Handler 151 ) 152 } 153 if (result == null) { 154 // CameraCaptureSession.StateCallback.onConfigureFailed isn't called in certain 155 // situations, such as when the camera is closed, or when it encounters an error. As 156 // such, we need to make sure we finalize the previous session too. 157 Log.warn { 158 "Failed to create capture session from $cameraDevice. Finalizing previous session" 159 } 160 previousStateCallback?.onSessionFinalized() 161 } 162 return result != null 163 } 164 165 @RequiresApi(31) createExtensionSessionnull166 override fun createExtensionSession(config: ExtensionSessionConfigData): Boolean { 167 val stateCallback = config.extensionStateCallback 168 checkNotNull(stateCallback) { 169 "extensionStateCallback must be set to create Extension session" 170 } 171 checkNotNull(config.extensionMode) { 172 "extensionMode must be set to create Extension session" 173 } 174 val (success, previousStateCallback) = checkAndSetStateCallback(stateCallback) 175 if (!success) return false 176 previousStateCallback?.onSessionDisconnectedWithTrace() 177 val result = 178 instrumentAndCatch("createExtensionSession") { 179 val sessionConfig = 180 Api31Compat.newExtensionSessionConfiguration( 181 config.extensionMode, 182 config.outputConfigurations.map { it.unwrapAs(OutputConfiguration::class) }, 183 config.executor, 184 AndroidExtensionSessionStateCallback( 185 this, 186 stateCallback, 187 previousStateCallback, 188 cameraErrorListener, 189 interopExtensionSessionStateCallback, 190 config.executor 191 ), 192 ) 193 194 if ( 195 config.postviewOutputConfiguration != null && 196 Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE 197 ) { 198 val postviewOutput = 199 config.postviewOutputConfiguration.unwrapAs(OutputConfiguration::class) 200 checkNotNull(postviewOutput) { "Failed to unwrap Postview OutputConfiguration" } 201 Api34Compat.setPostviewOutputConfiguration(sessionConfig, postviewOutput) 202 } 203 204 Api31Compat.createExtensionCaptureSession(cameraDevice, sessionConfig) 205 } 206 if (result == null) { 207 // CameraCaptureSession.StateCallback.onConfigureFailed isn't called in certain 208 // situations, such as when the camera is closed, or when it encounters an error. As 209 // such, we need to make sure we finalize the previous session too. 210 Log.warn { 211 "Failed to create extension session from $cameraDevice. Finalizing previous session" 212 } 213 previousStateCallback?.onSessionFinalized() 214 } 215 return result != null 216 } 217 218 @RequiresApi(23) createReprocessableCaptureSessionnull219 override fun createReprocessableCaptureSession( 220 input: InputConfiguration, 221 outputs: List<Surface>, 222 stateCallback: CameraCaptureSessionWrapper.StateCallback 223 ): Boolean { 224 val (success, previousStateCallback) = checkAndSetStateCallback(stateCallback) 225 if (!success) return false 226 previousStateCallback?.onSessionDisconnectedWithTrace() 227 val result = 228 instrumentAndCatch("createReprocessableCaptureSession") { 229 // This function was deprecated in Android Q, but is required for some 230 // configurations when running on older versions of the OS. 231 Api23Compat.createReprocessableCaptureSession( 232 cameraDevice, 233 input, 234 outputs, 235 AndroidCaptureSessionStateCallback( 236 this, 237 stateCallback, 238 previousStateCallback, 239 cameraErrorListener, 240 interopSessionStateCallback, 241 threads.camera2Handler 242 ), 243 threads.camera2Handler 244 ) 245 } 246 if (result == null) { 247 // CameraCaptureSession.StateCallback.onConfigureFailed isn't called in certain 248 // situations, such as when the camera is closed, or when it encounters an error. As 249 // such, we need to make sure we finalize the previous session too. 250 Log.warn { 251 "Failed to create reprocess session from $cameraDevice. Finalizing previous session" 252 } 253 previousStateCallback?.onSessionFinalized() 254 } 255 return result != null 256 } 257 258 @RequiresApi(23) createConstrainedHighSpeedCaptureSessionnull259 override fun createConstrainedHighSpeedCaptureSession( 260 outputs: List<Surface>, 261 stateCallback: CameraCaptureSessionWrapper.StateCallback 262 ): Boolean { 263 val (success, previousStateCallback) = checkAndSetStateCallback(stateCallback) 264 if (!success) return false 265 previousStateCallback?.onSessionDisconnectedWithTrace() 266 val result = 267 instrumentAndCatch("createConstrainedHighSpeedCaptureSession") { 268 // This function was deprecated in Android Q, but is required for some 269 // configurations 270 // when running on older versions of the OS. 271 Api23Compat.createConstrainedHighSpeedCaptureSession( 272 cameraDevice, 273 outputs, 274 AndroidCaptureSessionStateCallback( 275 this, 276 stateCallback, 277 previousStateCallback, 278 cameraErrorListener, 279 interopSessionStateCallback, 280 threads.camera2Handler 281 ), 282 threads.camera2Handler 283 ) 284 } 285 if (result == null) { 286 // CameraCaptureSession.StateCallback.onConfigureFailed isn't called in certain 287 // situations, such as when the camera is closed, or when it encounters an error. As 288 // such, we need to make sure we finalize the previous session too. 289 Log.warn { 290 "Failed to create capture session from $cameraDevice. Finalizing previous session" 291 } 292 previousStateCallback?.onSessionFinalized() 293 } 294 return result != null 295 } 296 297 @RequiresApi(24) createCaptureSessionByOutputConfigurationsnull298 override fun createCaptureSessionByOutputConfigurations( 299 outputConfigurations: List<OutputConfigurationWrapper>, 300 stateCallback: CameraCaptureSessionWrapper.StateCallback 301 ): Boolean { 302 val (success, previousStateCallback) = checkAndSetStateCallback(stateCallback) 303 if (!success) return false 304 previousStateCallback?.onSessionDisconnectedWithTrace() 305 val result = 306 instrumentAndCatch("createCaptureSessionByOutputConfigurations") { 307 // This function was deprecated in Android Q, but is required for some 308 // configurations 309 // when running on older versions of the OS. 310 Api24Compat.createCaptureSessionByOutputConfigurations( 311 cameraDevice, 312 outputConfigurations.map { it.unwrapAs(OutputConfiguration::class) }, 313 AndroidCaptureSessionStateCallback( 314 this, 315 stateCallback, 316 previousStateCallback, 317 cameraErrorListener, 318 interopSessionStateCallback, 319 threads.camera2Handler 320 ), 321 threads.camera2Handler 322 ) 323 } 324 if (result == null) { 325 // CameraCaptureSession.StateCallback.onConfigureFailed isn't called in certain 326 // situations, such as when the camera is closed, or when it encounters an error. As 327 // such, we need to make sure we finalize the previous session too. 328 Log.warn { 329 "Failed to create capture session from $cameraDevice. Finalizing previous session" 330 } 331 previousStateCallback?.onSessionFinalized() 332 } 333 return result != null 334 } 335 336 @RequiresApi(24) createReprocessableCaptureSessionByConfigurationsnull337 override fun createReprocessableCaptureSessionByConfigurations( 338 inputConfig: InputConfigData, 339 outputs: List<OutputConfigurationWrapper>, 340 stateCallback: CameraCaptureSessionWrapper.StateCallback 341 ): Boolean { 342 val (success, previousStateCallback) = checkAndSetStateCallback(stateCallback) 343 if (!success) return false 344 previousStateCallback?.onSessionDisconnectedWithTrace() 345 val result = 346 instrumentAndCatch("createReprocessableCaptureSessionByConfigurations") { 347 // This function was deprecated in Android Q, but is required for some 348 // configurations when running on older versions of the OS. 349 Api24Compat.createReprocessableCaptureSessionByConfigurations( 350 cameraDevice, 351 Api23Compat.newInputConfiguration( 352 inputConfig.width, 353 inputConfig.height, 354 inputConfig.format 355 ), 356 outputs.map { it.unwrapAs(OutputConfiguration::class) }, 357 AndroidCaptureSessionStateCallback( 358 this, 359 stateCallback, 360 previousStateCallback, 361 cameraErrorListener, 362 interopSessionStateCallback, 363 threads.camera2Handler 364 ), 365 threads.camera2Handler 366 ) 367 } 368 if (result == null) { 369 // CameraCaptureSession.StateCallback.onConfigureFailed isn't called in certain 370 // situations, such as when the camera is closed, or when it encounters an error. As 371 // such, we need to make sure we finalize the previous session too. 372 Log.warn { 373 "Failed to create reprocess session from $cameraDevice. Finalizing previous session" 374 } 375 previousStateCallback?.onSessionFinalized() 376 } 377 return result != null 378 } 379 380 @RequiresApi(28) createCaptureSessionnull381 override fun createCaptureSession(config: SessionConfigData): Boolean { 382 val (success, previousStateCallback) = checkAndSetStateCallback(config.stateCallback) 383 if (!success) return false 384 previousStateCallback?.onSessionDisconnectedWithTrace() 385 val result = 386 instrumentAndCatch("createCaptureSession") { 387 val sessionConfig = 388 Api28Compat.newSessionConfiguration( 389 config.sessionType, 390 config.outputConfigurations.map { it.unwrapAs(OutputConfiguration::class) }, 391 config.executor, 392 AndroidCaptureSessionStateCallback( 393 this, 394 config.stateCallback, 395 previousStateCallback, 396 cameraErrorListener, 397 interopSessionStateCallback, 398 threads.camera2Handler 399 ) 400 ) 401 402 if (config.inputConfiguration != null) { 403 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 404 Api28Compat.setInputConfiguration( 405 sessionConfig, 406 Api31Compat.newInputConfiguration( 407 config.inputConfiguration, 408 cameraId.value 409 ) 410 ) 411 } else { 412 Api28Compat.setInputConfiguration( 413 sessionConfig, 414 Api23Compat.newInputConfiguration( 415 config.inputConfiguration.single().width, 416 config.inputConfiguration.single().height, 417 config.inputConfiguration.single().format 418 ) 419 ) 420 } 421 } 422 423 val requestBuilder = 424 Debug.trace("createCaptureRequest") { 425 cameraDevice.createCaptureRequest(config.sessionTemplateId) 426 } 427 428 // This compares and sets ONLY the session keys for this camera. Setting parameters 429 // that are not listed in availableSessionKeys can cause an unusual amount of extra 430 // latency. 431 val sessionKeyNames = cameraMetadata.sessionKeys.map { it.name } 432 433 // Iterate template parameters and CHECK BY NAME, as there have been cases where 434 // equality checks did not pass. 435 for ((key, value) in config.sessionParameters) { 436 if (key !is CaptureRequest.Key<*>) continue 437 if (sessionKeyNames.contains(key.name)) { 438 requestBuilder.writeParameter(key, value) 439 } 440 } 441 Api28Compat.setSessionParameters(sessionConfig, requestBuilder.build()) 442 Debug.trace("Api28Compat.createCaptureSession") { 443 Api28Compat.createCaptureSession(cameraDevice, sessionConfig) 444 } 445 } 446 if (result == null) { 447 // CameraCaptureSession.StateCallback.onConfigureFailed isn't called in certain 448 // situations, such as when the camera is closed, or when it encounters an error. As 449 // such, we need to make sure we finalize the previous session too. 450 Log.warn { 451 "Failed to create capture session from $cameraDevice. Finalizing previous session" 452 } 453 previousStateCallback?.onSessionFinalized() 454 } 455 return result != null 456 } 457 createCaptureRequestnull458 override fun createCaptureRequest(template: RequestTemplate): CaptureRequest.Builder? = 459 instrumentAndCatch("createCaptureRequest") { 460 cameraDevice.createCaptureRequest(template.value) 461 } 462 463 @RequiresApi(23) createReprocessCaptureRequestnull464 override fun createReprocessCaptureRequest( 465 inputResult: TotalCaptureResult 466 ): CaptureRequest.Builder? = 467 instrumentAndCatch("createReprocessCaptureRequest") { 468 Api23Compat.createReprocessCaptureRequest(cameraDevice, inputResult) 469 } 470 471 @RequiresApi(30) getCameraAudioRestrictionnull472 override fun getCameraAudioRestriction(): AudioRestrictionMode = 473 Debug.trace("getCameraAudioRestriction") { 474 AudioRestrictionMode(Api30Compat.getCameraAudioRestriction(cameraDevice)) 475 } 476 477 @RequiresApi(30) onCameraAudioRestrictionUpdatednull478 override fun onCameraAudioRestrictionUpdated(mode: AudioRestrictionMode) { 479 Debug.trace("setCameraAudioRestriction") { 480 catchAndReportCameraExceptions(cameraId, cameraErrorListener) { 481 Api30Compat.setCameraAudioRestriction(cameraDevice, mode.value) 482 } 483 } 484 } 485 onDeviceClosednull486 override fun onDeviceClosed() { 487 if (closed.compareAndSet(expect = false, update = true)) { 488 val lastStateCallback = _lastStateCallback.getAndSet(null) 489 lastStateCallback?.onSessionFinalized() 490 } 491 } 492 493 @Suppress("UNCHECKED_CAST") unwrapAsnull494 override fun <T : Any> unwrapAs(type: KClass<T>): T? = 495 when (type) { 496 CameraDevice::class -> cameraDevice as T 497 else -> null 498 } 499 toStringnull500 override fun toString(): String = "AndroidCameraDevice(camera=$cameraId)" 501 502 /** Utility function to trace, measure, and suppress exceptions for expensive method calls. */ 503 @Throws(ObjectUnavailableException::class) 504 private inline fun <T> instrumentAndCatch(fnName: String, crossinline block: () -> T) = 505 Debug.instrument("CXCP#$fnName-${cameraId.value}") { 506 catchAndReportCameraExceptions(cameraId, cameraErrorListener, block) 507 } 508 checkAndSetStateCallbacknull509 private fun checkAndSetStateCallback( 510 stateCallback: SessionStateCallback 511 ): Pair<Boolean, SessionStateCallback?> { 512 if (closed.value) { 513 stateCallback.onSessionFinalized() 514 return Pair(false, null) 515 } 516 return Pair(true, _lastStateCallback.getAndSet(stateCallback)) 517 } 518 onSessionDisconnectedWithTracenull519 private fun SessionStateCallback.onSessionDisconnectedWithTrace() { 520 Debug.trace("${this@AndroidCameraDevice}#onSessionDisconnected") { onSessionDisconnected() } 521 } 522 } 523 524 /** 525 * VirtualAndroidCameraDevice creates a simple wrapper around a [AndroidCameraDevice], augmenting it 526 * by enabling it to reject further capture session/request calls when it is "disconnected'. 527 */ 528 internal class VirtualAndroidCameraDevice( 529 internal val androidCameraDevice: AndroidCameraDevice, 530 ) : CameraDeviceWrapper { 531 private val lock = Any() 532 533 @GuardedBy("lock") private var disconnected = false 534 535 override val cameraId: CameraId 536 get() = androidCameraDevice.cameraId 537 createCaptureSessionnull538 override fun createCaptureSession( 539 outputs: List<Surface>, 540 stateCallback: CameraCaptureSessionWrapper.StateCallback 541 ) = 542 synchronized(lock) { 543 if (disconnected) { 544 Log.warn { "createCaptureSession failed: Virtual device disconnected" } 545 stateCallback.onSessionFinalized() 546 false 547 } else { 548 androidCameraDevice.createCaptureSession(outputs, stateCallback) 549 } 550 } 551 552 @RequiresApi(23) createReprocessableCaptureSessionnull553 override fun createReprocessableCaptureSession( 554 input: InputConfiguration, 555 outputs: List<Surface>, 556 stateCallback: CameraCaptureSessionWrapper.StateCallback 557 ) = 558 synchronized(lock) { 559 if (disconnected) { 560 Log.warn { "createReprocessableCaptureSession failed: Virtual device disconnected" } 561 stateCallback.onSessionFinalized() 562 false 563 } else { 564 androidCameraDevice.createReprocessableCaptureSession(input, outputs, stateCallback) 565 } 566 } 567 568 @RequiresApi(23) createConstrainedHighSpeedCaptureSessionnull569 override fun createConstrainedHighSpeedCaptureSession( 570 outputs: List<Surface>, 571 stateCallback: CameraCaptureSessionWrapper.StateCallback 572 ) = 573 synchronized(lock) { 574 if (disconnected) { 575 Log.warn { 576 "createConstrainedHighSpeedCaptureSession failed: Virtual device disconnected" 577 } 578 stateCallback.onSessionFinalized() 579 false 580 } else { 581 androidCameraDevice.createConstrainedHighSpeedCaptureSession(outputs, stateCallback) 582 } 583 } 584 585 @RequiresApi(24) createCaptureSessionByOutputConfigurationsnull586 override fun createCaptureSessionByOutputConfigurations( 587 outputConfigurations: List<OutputConfigurationWrapper>, 588 stateCallback: CameraCaptureSessionWrapper.StateCallback 589 ) = 590 synchronized(lock) { 591 if (disconnected) { 592 Log.warn { 593 "createCaptureSessionByOutputConfigurations failed: Virtual device disconnected" 594 } 595 stateCallback.onSessionFinalized() 596 false 597 } else { 598 androidCameraDevice.createCaptureSessionByOutputConfigurations( 599 outputConfigurations, 600 stateCallback 601 ) 602 } 603 } 604 605 @RequiresApi(24) createReprocessableCaptureSessionByConfigurationsnull606 override fun createReprocessableCaptureSessionByConfigurations( 607 inputConfig: InputConfigData, 608 outputs: List<OutputConfigurationWrapper>, 609 stateCallback: CameraCaptureSessionWrapper.StateCallback 610 ) = 611 synchronized(lock) { 612 if (disconnected) { 613 Log.warn { 614 "createReprocessableCaptureSessionByConfigurations failed: " + 615 "Virtual device disconnected" 616 } 617 stateCallback.onSessionFinalized() 618 false 619 } else { 620 androidCameraDevice.createReprocessableCaptureSessionByConfigurations( 621 inputConfig, 622 outputs, 623 stateCallback 624 ) 625 } 626 } 627 628 @RequiresApi(31) createExtensionSessionnull629 override fun createExtensionSession(config: ExtensionSessionConfigData) = 630 synchronized(lock) { 631 if (disconnected) { 632 Log.warn { "createExtensionSession failed: Virtual device disconnected" } 633 config.extensionStateCallback!!.onSessionFinalized() 634 false 635 } else { 636 androidCameraDevice.createExtensionSession(config) 637 } 638 } 639 640 @RequiresApi(28) createCaptureSessionnull641 override fun createCaptureSession(config: SessionConfigData) = 642 synchronized(lock) { 643 if (disconnected) { 644 Log.warn { "createCaptureSession failed: Virtual device disconnected" } 645 config.stateCallback.onSessionFinalized() 646 false 647 } else { 648 androidCameraDevice.createCaptureSession(config) 649 } 650 } 651 createCaptureRequestnull652 override fun createCaptureRequest(template: RequestTemplate) = 653 synchronized(lock) { 654 if (disconnected) { 655 Log.warn { "createCaptureRequest failed: Virtual device disconnected" } 656 null 657 } else { 658 androidCameraDevice.createCaptureRequest(template) 659 } 660 } 661 662 @RequiresApi(23) createReprocessCaptureRequestnull663 override fun createReprocessCaptureRequest(inputResult: TotalCaptureResult) = 664 synchronized(lock) { 665 if (disconnected) { 666 Log.warn { "createReprocessCaptureRequest failed: Virtual device disconnected" } 667 null 668 } else { 669 androidCameraDevice.createReprocessCaptureRequest(inputResult) 670 } 671 } 672 onDeviceClosednull673 override fun onDeviceClosed() = androidCameraDevice.onDeviceClosed() 674 675 override fun <T : Any> unwrapAs(type: KClass<T>): T? = androidCameraDevice.unwrapAs(type) 676 677 internal fun disconnect() = synchronized(lock) { disconnected = true } 678 679 @RequiresApi(30) getCameraAudioRestrictionnull680 override fun getCameraAudioRestriction(): AudioRestrictionMode { 681 return androidCameraDevice.getCameraAudioRestriction() 682 } 683 684 @RequiresApi(30) onCameraAudioRestrictionUpdatednull685 override fun onCameraAudioRestrictionUpdated(mode: AudioRestrictionMode) { 686 androidCameraDevice.onCameraAudioRestrictionUpdated(mode) 687 } 688 } 689