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 package androidx.camera.integration.core.camera2 17 18 import android.content.Context 19 import android.graphics.ImageFormat 20 import android.graphics.Point 21 import android.graphics.SurfaceTexture 22 import android.hardware.camera2.CameraCaptureSession 23 import android.hardware.camera2.CameraCaptureSession.CaptureCallback 24 import android.hardware.camera2.CameraCharacteristics 25 import android.hardware.camera2.CaptureRequest 26 import android.hardware.camera2.CaptureResult 27 import android.hardware.camera2.TotalCaptureResult 28 import android.os.Build 29 import android.util.Range 30 import android.util.Rational 31 import android.util.Size 32 import android.view.Surface 33 import androidx.camera.camera2.Camera2Config 34 import androidx.camera.camera2.internal.DisplayInfoManager 35 import androidx.camera.camera2.interop.Camera2Interop 36 import androidx.camera.camera2.pipe.integration.CameraPipeConfig 37 import androidx.camera.camera2.pipe.integration.compat.quirk.DeviceQuirks 38 import androidx.camera.camera2.pipe.integration.compat.quirk.ExtraCroppingQuirk 39 import androidx.camera.core.AspectRatio 40 import androidx.camera.core.CameraSelector 41 import androidx.camera.core.CameraXConfig 42 import androidx.camera.core.DynamicRange 43 import androidx.camera.core.MirrorMode 44 import androidx.camera.core.Preview 45 import androidx.camera.core.SurfaceRequest 46 import androidx.camera.core.SurfaceRequest.TransformationInfo 47 import androidx.camera.core.UseCaseGroup 48 import androidx.camera.core.ViewPort 49 import androidx.camera.core.impl.CameraInfoInternal 50 import androidx.camera.core.impl.ImageOutputConfig 51 import androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR 52 import androidx.camera.core.impl.SessionConfig 53 import androidx.camera.core.impl.utils.Threads.runOnMainSync 54 import androidx.camera.core.impl.utils.executor.CameraXExecutors 55 import androidx.camera.core.internal.utils.SizeUtil 56 import androidx.camera.core.resolutionselector.AspectRatioStrategy 57 import androidx.camera.core.resolutionselector.ResolutionFilter 58 import androidx.camera.core.resolutionselector.ResolutionSelector 59 import androidx.camera.core.resolutionselector.ResolutionSelector.PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE 60 import androidx.camera.core.resolutionselector.ResolutionStrategy 61 import androidx.camera.integration.core.util.CameraInfoUtil 62 import androidx.camera.lifecycle.ProcessCameraProvider 63 import androidx.camera.testing.impl.CameraPipeConfigTestRule 64 import androidx.camera.testing.impl.CameraUtil 65 import androidx.camera.testing.impl.CameraUtil.PreTestCameraIdList 66 import androidx.camera.testing.impl.ExtensionsUtil 67 import androidx.camera.testing.impl.SurfaceTextureProvider 68 import androidx.camera.testing.impl.WakelockEmptyActivityRule 69 import androidx.camera.testing.impl.fakes.FakeLifecycleOwner 70 import androidx.camera.testing.impl.fakes.FakeSessionProcessor 71 import androidx.camera.video.Recorder 72 import androidx.camera.video.VideoCapture 73 import androidx.concurrent.futures.await 74 import androidx.core.util.Consumer 75 import androidx.test.core.app.ApplicationProvider 76 import androidx.test.filters.LargeTest 77 import androidx.test.filters.SdkSuppress 78 import androidx.test.platform.app.InstrumentationRegistry 79 import androidx.testutils.fail 80 import com.google.common.truth.Truth.assertThat 81 import java.util.concurrent.ExecutionException 82 import java.util.concurrent.Executor 83 import java.util.concurrent.Executors 84 import java.util.concurrent.Semaphore 85 import java.util.concurrent.ThreadFactory 86 import java.util.concurrent.TimeUnit 87 import java.util.concurrent.TimeoutException 88 import java.util.concurrent.atomic.AtomicReference 89 import kotlin.math.abs 90 import kotlinx.coroutines.CompletableDeferred 91 import kotlinx.coroutines.Dispatchers 92 import kotlinx.coroutines.delay 93 import kotlinx.coroutines.runBlocking 94 import kotlinx.coroutines.withContext 95 import kotlinx.coroutines.withTimeout 96 import kotlinx.coroutines.withTimeoutOrNull 97 import org.junit.After 98 import org.junit.Assume.assumeFalse 99 import org.junit.Assume.assumeTrue 100 import org.junit.Before 101 import org.junit.Rule 102 import org.junit.Test 103 import org.junit.runner.RunWith 104 import org.junit.runners.Parameterized 105 106 @LargeTest 107 @RunWith(Parameterized::class) 108 @SdkSuppress(minSdkVersion = 21) 109 class PreviewTest(private val implName: String, private val cameraConfig: CameraXConfig) { 110 @get:Rule 111 val cameraPipeConfigTestRule = 112 CameraPipeConfigTestRule( 113 active = implName == CameraPipeConfig::class.simpleName, 114 ) 115 116 @get:Rule 117 val cameraRule = 118 CameraUtil.grantCameraPermissionAndPreTestAndPostTest(PreTestCameraIdList(cameraConfig)) 119 120 // Launch activity when testing in Vivo devices to prevent testing process from being killed. 121 @get:Rule 122 val wakelockEmptyActivityRule = WakelockEmptyActivityRule(brandsToEnable = listOf("vivo")) 123 124 companion object { 125 private const val ANY_THREAD_NAME = "any-thread-name" 126 private val DEFAULT_RESOLUTION: Size by lazy { Size(640, 480) } 127 private val DEFAULT_RESOLUTION_PORTRAIT: Size by lazy { Size(480, 640) } 128 private const val FRAMES_TO_VERIFY = 10 129 private const val RESULT_TIMEOUT = 5000L 130 private const val TOLERANCE = 0.1f 131 132 @JvmStatic 133 @Parameterized.Parameters(name = "{0}") 134 fun data() = 135 listOf( 136 arrayOf(Camera2Config::class.simpleName, Camera2Config.defaultConfig()), 137 arrayOf(CameraPipeConfig::class.simpleName, CameraPipeConfig.defaultConfig()) 138 ) 139 } 140 141 private val instrumentation = InstrumentationRegistry.getInstrumentation() 142 private lateinit var cameraProvider: ProcessCameraProvider 143 private val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA 144 private var previewResolution: Size? = null 145 private var frameSemaphore: Semaphore? = null 146 private val context: Context = ApplicationProvider.getApplicationContext() 147 private val lifecycleOwner = FakeLifecycleOwner() 148 149 @Before 150 @Throws(ExecutionException::class, InterruptedException::class) 151 fun setUp() = runBlocking { 152 assumeTrue(CameraUtil.hasCameraWithLensFacing(cameraSelector.lensFacing!!)) 153 154 ProcessCameraProvider.configureInstance(cameraConfig) 155 cameraProvider = ProcessCameraProvider.getInstance(context).await() 156 frameSemaphore = Semaphore(/* permits= */ 0) 157 lifecycleOwner.startAndResume() 158 } 159 160 @After 161 @Throws(ExecutionException::class, InterruptedException::class, TimeoutException::class) 162 fun tearDown() { 163 cameraProvider.shutdownAsync()[10000, TimeUnit.MILLISECONDS] 164 } 165 166 // ====================================================== 167 // Section 1: SurfaceProvider behavior testing 168 // ====================================================== 169 @Test 170 fun surfaceProvider_isUsedAfterSetting() = runBlocking { 171 val preview = Preview.Builder().build() 172 val completableDeferred = CompletableDeferred<Unit>() 173 174 instrumentation.runOnMainSync { 175 preview.setSurfaceProvider { request -> 176 val surfaceTexture = SurfaceTexture(0) 177 surfaceTexture.setDefaultBufferSize( 178 request.resolution.width, 179 request.resolution.height 180 ) 181 surfaceTexture.detachFromGLContext() 182 val surface = Surface(surfaceTexture) 183 request.provideSurface(surface, CameraXExecutors.directExecutor()) { 184 surface.release() 185 surfaceTexture.release() 186 } 187 completableDeferred.complete(Unit) 188 } 189 190 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 191 } 192 withTimeout(3_000) { completableDeferred.await() } 193 } 194 195 private fun <T> CompletableDeferred<T>.completeOnceOnly(value: T) { 196 if (!this.complete(value)) { 197 throw IllegalStateException("Result Listener being invoked twice") 198 } 199 } 200 201 @Test 202 @Throws(InterruptedException::class) 203 fun previewUnbound_RESULT_SURFACE_USED_SUCCESSFULLY_isCalled() = runBlocking { 204 // Arrange. 205 val preview = Preview.Builder().build() 206 val resultDeferred = CompletableDeferred<Int>() 207 // Act. 208 instrumentation.runOnMainSync { 209 preview.setSurfaceProvider( 210 CameraXExecutors.mainThreadExecutor(), 211 getSurfaceProvider( 212 frameAvailableListener = { frameSemaphore!!.release() }, 213 resultListener = { result -> 214 resultDeferred.completeOnceOnly(result.resultCode) 215 } 216 ) 217 ) 218 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 219 } 220 221 // Wait until preview gets frame. 222 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 5) 223 224 // Ensure resultListener is not invoked before unbind. 225 assertThat(withTimeoutOrNull(500) { resultDeferred.await() }).isNull() 226 227 // Remove the UseCase from the camera 228 instrumentation.runOnMainSync { cameraProvider.unbind(preview) } 229 230 // Assert. 231 assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred.await() }) 232 .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY) 233 } 234 235 @Test 236 @Throws(InterruptedException::class) 237 fun setSurfaceProviderBeforeBind_getsFrame() { 238 // Arrange. 239 val preview = Preview.Builder().build() 240 instrumentation.runOnMainSync { 241 preview.setSurfaceProvider( 242 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() }) 243 ) 244 // Act. 245 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 246 } 247 248 // Assert. 249 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 250 } 251 252 @Test 253 @Throws(InterruptedException::class) 254 fun setSurfaceProviderBeforeBind_providesSurfaceOnWorkerExecutorThread() { 255 val threadName = AtomicReference<String>() 256 257 // Arrange. 258 val preview = Preview.Builder().build() 259 instrumentation.runOnMainSync { 260 preview.setSurfaceProvider( 261 workExecutorWithNamedThread, 262 getSurfaceProvider( 263 threadNameConsumer = { newValue: String -> threadName.set(newValue) }, 264 frameAvailableListener = { frameSemaphore!!.release() } 265 ) 266 ) 267 268 // Act. 269 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 270 } 271 272 // Assert. 273 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 274 assertThat(threadName.get()).isEqualTo(ANY_THREAD_NAME) 275 } 276 277 @Test 278 @Throws(InterruptedException::class) 279 fun setSurfaceProviderAfterBind_getsFrame() { 280 // Arrange. 281 val preview = Preview.Builder().build() 282 283 instrumentation.runOnMainSync { 284 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 285 286 // Act. 287 preview.setSurfaceProvider( 288 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() }) 289 ) 290 } 291 292 // Assert. 293 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 294 } 295 296 @Test 297 @Throws(InterruptedException::class) 298 fun setSurfaceProviderAfterBind_providesSurfaceOnWorkerExecutorThread() { 299 val threadName = AtomicReference<String>() 300 301 // Arrange. 302 val preview = Preview.Builder().build() 303 304 instrumentation.runOnMainSync { 305 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 306 // Act. 307 preview.setSurfaceProvider( 308 workExecutorWithNamedThread, 309 getSurfaceProvider( 310 threadNameConsumer = { newValue: String -> threadName.set(newValue) }, 311 frameAvailableListener = { frameSemaphore!!.release() } 312 ) 313 ) 314 } 315 316 // Assert. 317 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 318 assertThat(threadName.get()).isEqualTo(ANY_THREAD_NAME) 319 } 320 321 @Test 322 @Throws(InterruptedException::class) 323 fun setMultipleNonNullSurfaceProviders_getsFrame() { 324 val preview = Preview.Builder().build() 325 326 instrumentation.runOnMainSync { 327 // Set a different SurfaceProvider which will provide a different surface to be used 328 // for preview. 329 preview.setSurfaceProvider( 330 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() }) 331 ) 332 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 333 } 334 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 335 336 // Use another sSemaphore to monitor the frames from the 2nd surfaceProvider. 337 val frameSemaphore2 = Semaphore(/* permits= */ 0) 338 339 instrumentation.runOnMainSync { 340 // Set a different SurfaceProvider which will provide a different surface to be used 341 // for preview. 342 preview.setSurfaceProvider( 343 getSurfaceProvider(frameAvailableListener = { frameSemaphore2.release() }) 344 ) 345 } 346 frameSemaphore2.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 347 } 348 349 @Test 350 @Throws(InterruptedException::class) 351 fun setMultipleNullableSurfaceProviders_getsFrame() { 352 val preview = Preview.Builder().build() 353 354 instrumentation.runOnMainSync { 355 // Set a different SurfaceProvider which will provide a different surface to be used 356 // for preview. 357 preview.setSurfaceProvider( 358 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() }) 359 ) 360 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 361 } 362 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 363 364 // Recreate the semaphore to monitor the frame available callback. 365 val frameSemaphore2 = Semaphore(/* permits= */ 0) 366 367 instrumentation.runOnMainSync { 368 // Set the SurfaceProvider to null in order to force the Preview into an inactive 369 // state before setting a different SurfaceProvider for preview. 370 preview.setSurfaceProvider(null) 371 preview.setSurfaceProvider( 372 getSurfaceProvider(frameAvailableListener = { frameSemaphore2.release() }) 373 ) 374 } 375 frameSemaphore2.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 376 } 377 378 @Test 379 fun willNotProvideSurface_resultCode_WILL_NOT_PROVIDE_SURFACE(): Unit = runBlocking { 380 val preview = Preview.Builder().build() 381 382 val result: Int = 383 withContext(Dispatchers.Main) { 384 val surfaceRequestDeferred = CompletableDeferred<SurfaceRequest>() 385 preview.setSurfaceProvider { request -> 386 if (!surfaceRequestDeferred.complete(request)) { 387 // Ignore any new results. Could also call preview.setSurfaceProvider(null) 388 // on successful completion to ensure no further requests are sent. 389 request.willNotProvideSurface() 390 } 391 } 392 393 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 394 395 withTimeoutOrNull(RESULT_TIMEOUT) { surfaceRequestDeferred.await() } 396 ?.let { request -> 397 val resultDeferred = CompletableDeferred<Int>() 398 request.willNotProvideSurface() 399 val surface = Surface(SurfaceTexture(0)) 400 // can't provideSurface successfully after willNotProvideSurface. 401 // RESULT_WILL_NOT_PROVIDE_SURFACE will be notified. 402 request.provideSurface(surface, CameraXExecutors.directExecutor()) { result 403 -> 404 resultDeferred.completeOnceOnly(result.resultCode) 405 } 406 407 withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred.await() } 408 ?: fail("Timed out while waiting for surface result.") 409 } ?: fail("Timed out while waiting for surface request.") 410 } 411 412 assertThat(result).isEqualTo(SurfaceRequest.Result.RESULT_WILL_NOT_PROVIDE_SURFACE) 413 } 414 415 @Test 416 fun provideSurfaceTwice_resultCode_SURFACE_ALREADY_PROVIDED(): Unit = runBlocking { 417 val preview = Preview.Builder().build() 418 419 val resultDeferred1 = CompletableDeferred<Int>() 420 val resultDeferred2 = CompletableDeferred<Int>() 421 instrumentation.runOnMainSync { 422 preview.setSurfaceProvider { surfaceRequest -> 423 runBlocking { 424 val surfaceTextureHolder = 425 SurfaceTextureProvider.createAutoDrainingSurfaceTextureAsync( 426 surfaceRequest.resolution.width, 427 surfaceRequest.resolution.height, 428 { frameSemaphore!!.release() } 429 ) 430 .await() 431 val surface = Surface(surfaceTextureHolder!!.surfaceTexture) 432 surfaceRequest.provideSurface( 433 surface, 434 CameraXExecutors.directExecutor(), 435 { result -> 436 surfaceTextureHolder.close() 437 surface.release() 438 resultDeferred1.completeOnceOnly(result.resultCode) 439 } 440 ) 441 442 // Invoking provideSurface twice is a no-op and the result will be 443 // RESULT_SURFACE_ALREADY_PROVIDED 444 surfaceRequest.provideSurface( 445 Surface(SurfaceTexture(1)), 446 CameraXExecutors.directExecutor() 447 ) { result -> 448 resultDeferred2.completeOnceOnly(result.resultCode) 449 } 450 } 451 } 452 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 453 } 454 455 // Wait until preview gets frame. 456 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 457 458 instrumentation.runOnMainSync { cameraProvider.unbind(preview) } 459 460 assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred2.await() }) 461 .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED) 462 463 assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred1.await() }) 464 .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY) 465 } 466 467 @Test 468 fun surfaceRequestCancelled_resultCode_REQUEST_CANCELLED() = runBlocking { 469 val preview = Preview.Builder().build() 470 471 val resultDeferred = CompletableDeferred<Int>() 472 val surfaceRequestDeferred = CompletableDeferred<SurfaceRequest>() 473 instrumentation.runOnMainSync { 474 preview.setSurfaceProvider { surfaceRequest -> 475 surfaceRequestDeferred.complete(surfaceRequest) 476 } 477 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 478 // unbind the use case to cancel the surface request. 479 cameraProvider.unbind(preview) 480 } 481 482 // Small delay to allow the SurfaceRequest to be cancelled. 483 delay(500) 484 485 val surfaceRequest = surfaceRequestDeferred.await() 486 instrumentation.runOnMainSync { 487 surfaceRequest.provideSurface( 488 Surface(SurfaceTexture(0)), 489 CameraXExecutors.directExecutor() 490 ) { result -> 491 resultDeferred.completeOnceOnly(result.resultCode) 492 } 493 } 494 495 assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred.await() }) 496 .isEqualTo(SurfaceRequest.Result.RESULT_REQUEST_CANCELLED) 497 } 498 499 @Test 500 fun newSurfaceProviderAfterSurfaceProvided_resultCode_SURFACE_USED_SUCCESSFULLY() = 501 runBlocking { 502 val preview = Preview.Builder().build() 503 504 val resultDeferred1 = CompletableDeferred<Int>() 505 val resultDeferred2 = CompletableDeferred<Int>() 506 instrumentation.runOnMainSync { 507 preview.setSurfaceProvider(CameraXExecutors.mainThreadExecutor()) { surfaceRequest 508 -> 509 val surface = Surface(SurfaceTexture(0)) 510 surfaceRequest.provideSurface(surface, CameraXExecutors.directExecutor()) { 511 result -> 512 resultDeferred1.completeOnceOnly(result.resultCode) 513 } 514 515 // After the surface is provided, if there is a new request (here we trigger by 516 // setting another surfaceProvider), the previous surfaceRequest will receive 517 // RESULT_SURFACE_USED_SUCCESSFULLY. 518 preview.setSurfaceProvider( 519 getSurfaceProvider( 520 frameAvailableListener = { frameSemaphore!!.release() }, 521 resultListener = { resultDeferred2.completeOnceOnly(it.resultCode) } 522 ) 523 ) 524 } 525 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 526 } 527 528 assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred1.await() }) 529 .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY) 530 531 // Wait until preview gets frame. 532 frameSemaphore!!.verifyFramesReceived( 533 frameCount = FRAMES_TO_VERIFY, 534 timeoutInSeconds = 5 535 ) 536 537 instrumentation.runOnMainSync { cameraProvider.unbindAll() } 538 539 assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred2.await() }) 540 .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY) 541 } 542 543 @Test 544 fun newSurfaceRequestAfterSurfaceProvided_resultCode_SURFACE_USED_SUCCESSFULLY() = runBlocking { 545 val preview = Preview.Builder().build() 546 547 val resultDeferred1 = CompletableDeferred<Int>() 548 val resultDeferred2 = CompletableDeferred<Int>() 549 550 var surfaceRequestCount = 0 551 instrumentation.runOnMainSync { 552 preview.setSurfaceProvider(CameraXExecutors.mainThreadExecutor()) { surfaceRequest -> 553 // the surface will be requested twice on the same SurfaceProvider instance. 554 if (surfaceRequestCount == 0) { 555 val surfaceTexture = SurfaceTexture(0) 556 val surface = Surface(surfaceTexture) 557 surfaceRequest.provideSurface(surface, CameraXExecutors.directExecutor()) { 558 result -> 559 surfaceTexture.release() 560 surface.release() 561 resultDeferred1.completeOnceOnly(result.resultCode) 562 } 563 564 // After the surface is provided, if there is a new request (here we trigger by 565 // unbind and rebind), the previous surfaceRequest will receive 566 // RESULT_SURFACE_USED_SUCCESSFULLY. 567 cameraProvider.unbind(preview) 568 surfaceRequestCount++ 569 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 570 } else { 571 runBlocking { 572 val surfaceTextureHolder = 573 SurfaceTextureProvider.createAutoDrainingSurfaceTextureAsync( 574 surfaceRequest.resolution.width, 575 surfaceRequest.resolution.height, 576 { frameSemaphore!!.release() } 577 ) 578 .await() 579 val surface = Surface(surfaceTextureHolder.surfaceTexture) 580 surfaceRequest.provideSurface(surface, CameraXExecutors.directExecutor()) { 581 result -> 582 surfaceTextureHolder.close() 583 surface.release() 584 resultDeferred2.completeOnceOnly(result.resultCode) 585 } 586 } 587 } 588 } 589 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 590 } 591 592 assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred1.await() }) 593 .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY) 594 595 // Wait until preview gets frame. 596 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 5) 597 598 instrumentation.runOnMainSync { cameraProvider.unbindAll() } 599 600 assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred2.await() }) 601 .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY) 602 } 603 604 @Test 605 @Throws(InterruptedException::class) 606 fun setNullSurfaceProvider_shouldStopPreview() { 607 // Arrange. 608 val preview = Preview.Builder().build() 609 610 // Act. 611 instrumentation.runOnMainSync { 612 preview.setSurfaceProvider( 613 CameraXExecutors.mainThreadExecutor(), 614 SurfaceTextureProvider.createAutoDrainingSurfaceTextureProvider { 615 frameSemaphore!!.release() 616 } 617 ) 618 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 619 } 620 621 // Assert. 622 // Wait until preview gets frame. 623 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 624 625 // Act. 626 instrumentation.runOnMainSync { 627 preview.setSurfaceProvider(CameraXExecutors.mainThreadExecutor(), null) 628 } 629 630 // Assert. 631 // No frame coming for 3 seconds in 10 seconds timeout. 632 assertThat(noFrameCome(3000L, 10000L)).isTrue() 633 } 634 635 @Test 636 fun surfaceClosed_resultCode_INVALID_SURFACE() = runBlocking { 637 // Arrange. 638 val preview = Preview.Builder().build() 639 val resultDeferred1 = CompletableDeferred<Int>() 640 641 // Act. 642 instrumentation.runOnMainSync { 643 preview.setSurfaceProvider( 644 CameraXExecutors.mainThreadExecutor(), 645 { request -> 646 request.provideSurface( 647 Surface(SurfaceTexture(0)).also { it.release() }, // invalid surface 648 CameraXExecutors.directExecutor() 649 ) { result -> 650 resultDeferred1.completeOnceOnly(result.resultCode) 651 } 652 } 653 ) 654 655 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 656 } 657 658 assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred1.await() }) 659 .isEqualTo(SurfaceRequest.Result.RESULT_INVALID_SURFACE) 660 } 661 662 // ====================================================== 663 // Section 2: targetResolution / targetRotation / targetAspectRatio 664 // ====================================================== 665 666 @Test 667 fun defaultAspectRatioWillBeSet_whenTargetResolutionIsNotSet() = 668 runBlocking(Dispatchers.Main) { 669 val useCase = Preview.Builder().build() 670 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCase) 671 val config = useCase.currentConfig as ImageOutputConfig 672 assertThat(config.targetAspectRatio).isEqualTo(AspectRatio.RATIO_4_3) 673 } 674 675 @Suppress("DEPRECATION") // test for legacy resolution API 676 @Test 677 fun defaultAspectRatioWillBeSet_whenRatioDefaultIsSet() = 678 runBlocking(Dispatchers.Main) { 679 assumeTrue( 680 !hasExtraCroppingQuirk() && 681 isAspectRatioResolutionSupported(4.0f / 3.0f, isLegacyApi = true) 682 ) 683 val useCase = Preview.Builder().setTargetAspectRatio(AspectRatio.RATIO_DEFAULT).build() 684 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCase) 685 val config = useCase.currentConfig as ImageOutputConfig 686 assertThat(config.targetAspectRatio).isEqualTo(AspectRatio.RATIO_4_3) 687 val resolution = useCase.resolutionInfo!!.resolution 688 assertThat(resolution.width.toFloat() / resolution.height) 689 .isWithin(TOLERANCE) 690 .of(4.0f / 3.0f) 691 } 692 693 @Suppress("DEPRECATION") // test for legacy resolution API 694 @Test 695 fun aspectRatio4_3_resolutionIsSet() = 696 runBlocking(Dispatchers.Main) { 697 assumeTrue( 698 !hasExtraCroppingQuirk() && 699 isAspectRatioResolutionSupported(4.0f / 3.0f, isLegacyApi = true) 700 ) 701 702 val useCase = Preview.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3).build() 703 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCase) 704 val config = useCase.currentConfig as ImageOutputConfig 705 assertThat(config.targetAspectRatio).isEqualTo(AspectRatio.RATIO_4_3) 706 val resolution = useCase.resolutionInfo!!.resolution 707 assertThat(resolution.width.toFloat() / resolution.height) 708 .isWithin(TOLERANCE) 709 .of(4.0f / 3.0f) 710 } 711 712 @Suppress("DEPRECATION") // test for legacy resolution API 713 @Test 714 fun aspectRatio16_9_resolutionIsSet() = 715 runBlocking(Dispatchers.Main) { 716 assumeTrue( 717 !hasAspectRatioLegacyApi21Quirk() && 718 isAspectRatioResolutionSupported(16.0f / 9.0f, isLegacyApi = true) 719 ) 720 val useCase = Preview.Builder().setTargetAspectRatio(AspectRatio.RATIO_16_9).build() 721 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCase) 722 val config = useCase.currentConfig as ImageOutputConfig 723 assertThat(config.targetAspectRatio).isEqualTo(AspectRatio.RATIO_16_9) 724 val resolution = useCase.resolutionInfo!!.resolution 725 assertThat(resolution.width.toFloat() / resolution.height) 726 .isWithin(TOLERANCE) 727 .of(16.0f / 9.0f) 728 } 729 730 private fun isAspectRatioResolutionSupported( 731 targetAspectRatioValue: Float, 732 isLegacyApi: Boolean = false 733 ): Boolean { 734 val cameraCharacteristics = 735 (cameraProvider.getCameraInfo(cameraSelector) as CameraInfoInternal) 736 .cameraCharacteristics as CameraCharacteristics 737 val map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) 738 val previewSizes = map!!.getOutputSizes(SurfaceTexture::class.java) 739 return previewSizes.find { size -> 740 val aspectRatioVal = size.width.toFloat() / size.height.toFloat() 741 abs(targetAspectRatioValue - aspectRatioVal) <= TOLERANCE && 742 isValidPreviewSize(size, isLegacyApi) 743 } != null 744 } 745 746 private fun isValidPreviewSize(size: Size, isLegacyApi: Boolean): Boolean { 747 val previewDefinitionSize = getPreviewDefinitionSize() 748 val sizeArea = SizeUtil.getArea(size) 749 val previewSizeArea = SizeUtil.getArea(previewDefinitionSize) 750 751 // When using ResolutionSelector API, all sizes equal to or smaller than PREVIEW size can 752 // be selected. 753 if (!isLegacyApi) { 754 return sizeArea <= previewSizeArea 755 } 756 757 // When using Legacy API, if the PREVIEW size is smaller than 640x480, all output sizes 758 // smaller than it can be selected. The reason is, for some devices, there might be 16:9 759 // output sizes smaller than 480P. But it might make the preview have bad image quality 760 // when the display size is larger than 480P. Therefore, those sizes can be selected to use 761 // only when the device display size is smaller than 480P or apps explicitly set a target 762 // resolution smaller than 480P. 763 if (SizeUtil.isSmallerByArea(previewDefinitionSize, SizeUtil.RESOLUTION_480P)) { 764 return SizeUtil.isSmallerByArea(size, previewDefinitionSize) 765 } 766 767 // Otherwise, only sizes between PREVIEW size and 640x480 can be selected 768 val vgaSizeArea = SizeUtil.getArea(SizeUtil.RESOLUTION_VGA) 769 770 return sizeArea in vgaSizeArea..previewSizeArea 771 } 772 773 @Suppress("DEPRECATION") // getRealSize 774 private fun getPreviewDefinitionSize(): Size { 775 val point = Point() 776 DisplayInfoManager.getInstance(context).getMaxSizeDisplay(false).also { 777 it.getRealSize(point) 778 } 779 val previewSize = 780 if (point.x > point.y) { 781 Size(point.x, point.y) 782 } else { 783 Size(point.y, point.x) 784 } 785 return if (SizeUtil.isSmallerByArea(previewSize, SizeUtil.RESOLUTION_1080P)) { 786 previewSize 787 } else { 788 SizeUtil.RESOLUTION_1080P 789 } 790 } 791 792 private fun hasExtraCroppingQuirk(): Boolean { 793 return (implName.contains(CameraPipeConfig::class.simpleName!!) && 794 DeviceQuirks[ExtraCroppingQuirk::class.java] != null) || 795 androidx.camera.camera2.internal.compat.quirk.DeviceQuirks.get( 796 androidx.camera.camera2.internal.compat.quirk.ExtraCroppingQuirk::class.java 797 ) != null 798 } 799 800 // Checks whether it is the device for AspectRatioLegacyApi21Quirk 801 private fun hasAspectRatioLegacyApi21Quirk(): Boolean { 802 val quirks = 803 (cameraProvider.getCameraInfo(cameraSelector) as CameraInfoInternal).cameraQuirks 804 return if (implName == CameraPipeConfig::class.simpleName) { 805 quirks.contains( 806 androidx.camera.camera2.pipe.integration.compat.quirk 807 .AspectRatioLegacyApi21Quirk::class 808 .java 809 ) 810 } else { 811 quirks.contains( 812 androidx.camera.camera2.internal.compat.quirk.AspectRatioLegacyApi21Quirk::class 813 .java 814 ) 815 } 816 } 817 818 @Suppress("DEPRECATION") // legacy resolution API 819 @Test 820 fun defaultAspectRatioWontBeSet_andResolutionIsSet_whenTargetResolutionIsSet() = 821 runBlocking(Dispatchers.Main) { 822 assumeTrue( 823 CameraUtil.isCameraSensorPortraitInNativeOrientation(cameraSelector.lensFacing!!) && 824 !hasExtraCroppingQuirk() 825 ) 826 val useCase = 827 Preview.Builder() 828 .setTargetResolution(DEFAULT_RESOLUTION_PORTRAIT) 829 .setTargetRotation(Surface.ROTATION_0) // enforce native orientation. 830 .build() 831 assertThat( 832 useCase.currentConfig.containsOption( 833 ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO 834 ) 835 ) 836 .isFalse() 837 838 cameraProvider.bindToLifecycle( 839 lifecycleOwner, 840 CameraSelector.DEFAULT_BACK_CAMERA, 841 useCase 842 ) 843 844 assertThat( 845 useCase.currentConfig.containsOption( 846 ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO 847 ) 848 ) 849 .isFalse() 850 assertThat(useCase.resolutionInfo!!.resolution).isEqualTo(DEFAULT_RESOLUTION) 851 } 852 853 @Test 854 fun targetRotationIsRetained_whenUseCaseIsReused() = 855 runBlocking(Dispatchers.Main) { 856 val useCase = Preview.Builder().build() 857 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCase) 858 859 // Generally, the device can't be rotated to Surface.ROTATION_180. Therefore, 860 // use it to do the test. 861 useCase.targetRotation = Surface.ROTATION_180 862 cameraProvider.unbind(useCase) 863 864 // Check the target rotation is kept when the use case is unbound. 865 assertThat(useCase.targetRotation).isEqualTo(Surface.ROTATION_180) 866 867 // Check the target rotation is kept when the use case is rebound to the 868 // lifecycle. 869 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCase) 870 assertThat(useCase.targetRotation).isEqualTo(Surface.ROTATION_180) 871 } 872 873 @Test 874 fun targetRotationReturnsDisplayRotationIfNotSet() = 875 runBlocking(Dispatchers.Main) { 876 val displayRotation = 877 DisplayInfoManager.getInstance(context).getMaxSizeDisplay(true).rotation 878 val useCase = Preview.Builder().build() 879 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCase) 880 881 assertThat(useCase.targetRotation).isEqualTo(displayRotation) 882 } 883 884 @Test 885 fun returnValidTargetRotation_afterUseCaseIsCreated() { 886 val preview = Preview.Builder().build() 887 assertThat(preview.targetRotation).isNotEqualTo(ImageOutputConfig.INVALID_ROTATION) 888 } 889 890 @Test 891 fun returnCorrectTargetRotation_afterUseCaseIsBound() = 892 runBlocking(Dispatchers.Main) { 893 val preview = Preview.Builder().setTargetRotation(Surface.ROTATION_180).build() 894 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 895 assertThat(preview.targetRotation).isEqualTo(Surface.ROTATION_180) 896 } 897 898 @Suppress("DEPRECATION") // legacy resolution API 899 @Test 900 fun setTargetRotationOnBuilder_ResolutionIsSetCorrectly() = 901 runBlocking(Dispatchers.Main) { 902 assumeTrue( 903 CameraUtil.isCameraSensorPortraitInNativeOrientation(cameraSelector.lensFacing!!) && 904 !hasExtraCroppingQuirk() 905 ) 906 val preview = 907 Preview.Builder() 908 .setTargetRotation(Surface.ROTATION_90) 909 .setTargetResolution(DEFAULT_RESOLUTION) 910 .build() 911 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 912 assertThat(preview.resolutionInfo!!.resolution).isEqualTo(DEFAULT_RESOLUTION) 913 assertThat(preview.resolutionInfo!!.rotationDegrees).isEqualTo(0) 914 } 915 916 @Test 917 fun setTargetRotationAfterBind_transformationInfoIsUpdated() = 918 runBlocking(Dispatchers.Main) { 919 assumeTrue( 920 CameraUtil.isCameraSensorPortraitInNativeOrientation(cameraSelector.lensFacing!!) 921 ) 922 923 var transformationInfoDeferred = CompletableDeferred<TransformationInfo>() 924 val surfaceProvidedDeferred = CompletableDeferred<SurfaceRequest>() 925 926 val preview = Preview.Builder().setTargetRotation(Surface.ROTATION_0).build() 927 preview.surfaceProvider = 928 Preview.SurfaceProvider { request -> 929 request.setTransformationInfoListener(CameraXExecutors.directExecutor()) { 930 transformationInfoDeferred.complete(it) 931 } 932 request.provideSurface( 933 Surface(SurfaceTexture(0)), 934 CameraXExecutors.directExecutor() 935 ) {} 936 surfaceProvidedDeferred.complete(request) 937 } 938 939 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 940 surfaceProvidedDeferred.await() 941 var transformationInfo = withTimeoutOrNull(5000) { transformationInfoDeferred.await() } 942 assertThat(transformationInfo).isNotNull() 943 assertThat(transformationInfo!!.rotationDegrees).isEqualTo(90) 944 945 transformationInfoDeferred = CompletableDeferred() 946 preview.targetRotation = Surface.ROTATION_90 947 948 transformationInfo = withTimeoutOrNull(5000) { transformationInfoDeferred.await() } 949 assertThat(transformationInfo).isNotNull() 950 assertThat(transformationInfo!!.rotationDegrees).isEqualTo(0) 951 } 952 953 @Test 954 fun viewPort_OverwriteTransformation() = runBlocking { 955 // Arrange. 956 val rotation = 957 if (CameraUtil.getSensorOrientation(CameraSelector.LENS_FACING_BACK)!! % 180 != 0) 958 Surface.ROTATION_90 959 else Surface.ROTATION_0 960 val transformationInfoDeferred = CompletableDeferred<TransformationInfo>() 961 val preview = Preview.Builder().setTargetRotation(rotation).build() 962 val viewPort = ViewPort.Builder(Rational(2, 1), preview.targetRotation).build() 963 964 // Act. 965 withContext(Dispatchers.Main) { 966 preview.setSurfaceProvider { request -> 967 request.setTransformationInfoListener(CameraXExecutors.directExecutor()) { 968 transformationInfoDeferred.complete(it) 969 } 970 } 971 val useCaseGroup = 972 UseCaseGroup.Builder().setViewPort(viewPort).addUseCase(preview).build() 973 cameraProvider.bindToLifecycle( 974 lifecycleOwner, 975 CameraSelector.DEFAULT_BACK_CAMERA, 976 useCaseGroup 977 ) 978 } 979 val transformationInfo = withTimeoutOrNull(5000) { transformationInfoDeferred.await() } 980 981 // Assert. 982 assertThat( 983 Rational( 984 transformationInfo!!.cropRect.width(), 985 transformationInfo.cropRect.height() 986 ) 987 .toFloat() 988 ) 989 .isWithin(TOLERANCE) 990 .of(viewPort.aspectRatio.toFloat()) 991 } 992 993 // ====================================================== 994 // Section 3: UseCase Reusability Test 995 // ====================================================== 996 997 @Test 998 fun useCaseConfigCanBeReset_afterUnbind() = 999 runBlocking(Dispatchers.Main) { 1000 val preview = Preview.Builder().build() 1001 val initialConfig = preview.currentConfig 1002 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 1003 cameraProvider.unbind(preview) 1004 val configAfterUnbinding = preview.currentConfig 1005 assertThat(initialConfig == configAfterUnbinding).isTrue() 1006 } 1007 1008 @Test 1009 @Throws(InterruptedException::class) 1010 fun useCaseCanBeReusedInSameCamera() = runBlocking { 1011 val preview = Preview.Builder().build() 1012 var resultDeferred = CompletableDeferred<Int>() 1013 1014 withContext(Dispatchers.Main) { 1015 preview.setSurfaceProvider( 1016 getSurfaceProvider( 1017 frameAvailableListener = { frameSemaphore!!.release() }, 1018 resultListener = { result -> 1019 resultDeferred.completeOnceOnly(result.resultCode) 1020 } 1021 ) 1022 ) 1023 // This is the first time the use case bound to the lifecycle. 1024 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 1025 } 1026 1027 // Check the frame available callback is called. 1028 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 1029 withContext(Dispatchers.Main) { cameraProvider.unbind(preview) } 1030 1031 assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred.await() }) 1032 .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY) 1033 1034 // Recreate the semaphore / deferred to monitor the frame available callback. 1035 frameSemaphore = Semaphore(/* permits= */ 0) 1036 // Recreate the resultDeferred to monitor the result listener again. 1037 resultDeferred = CompletableDeferred() 1038 1039 withContext(Dispatchers.Main) { 1040 // Rebind the use case to the same camera. 1041 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 1042 } 1043 1044 // Check the frame available callback can be called after reusing the use case. 1045 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 1046 withContext(Dispatchers.Main) { cameraProvider.unbind(preview) } 1047 assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred.await() }) 1048 .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY) 1049 } 1050 1051 @Test 1052 @Throws(InterruptedException::class) 1053 fun useCaseCanBeReusedInDifferentCamera() = runBlocking { 1054 assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_FRONT)) 1055 1056 val preview = Preview.Builder().build() 1057 var resultDeferred = CompletableDeferred<Int>() 1058 instrumentation.runOnMainSync { 1059 preview.setSurfaceProvider( 1060 getSurfaceProvider( 1061 frameAvailableListener = { frameSemaphore!!.release() }, 1062 resultListener = { result -> 1063 resultDeferred.completeOnceOnly(result.resultCode) 1064 } 1065 ) 1066 ) 1067 // This is the first time the use case bound to the lifecycle. 1068 cameraProvider.bindToLifecycle( 1069 lifecycleOwner, 1070 CameraSelector.DEFAULT_BACK_CAMERA, 1071 preview 1072 ) 1073 } 1074 1075 // Check the frame available callback is called. 1076 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 1077 // Unbind and rebind the use case to the same lifecycle. 1078 instrumentation.runOnMainSync { cameraProvider.unbind(preview) } 1079 1080 assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred.await() }) 1081 .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY) 1082 1083 // Recreate the semaphore to monitor the frame available callback. 1084 frameSemaphore = Semaphore(/* permits= */ 0) 1085 // Recreate the resultDeferred to monitor the result listener again. 1086 resultDeferred = CompletableDeferred() 1087 1088 instrumentation.runOnMainSync { 1089 // Rebind the use case to different camera. 1090 cameraProvider.bindToLifecycle( 1091 lifecycleOwner, 1092 CameraSelector.DEFAULT_FRONT_CAMERA, 1093 preview 1094 ) 1095 } 1096 1097 // Check the frame available callback can be called after reusing the use case. 1098 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 1099 withContext(Dispatchers.Main) { cameraProvider.unbind(preview) } 1100 assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred.await() }) 1101 .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY) 1102 } 1103 1104 // ====================================================== 1105 // Section 4: ResolutionSelector 1106 // ====================================================== 1107 1108 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M) 1109 @Test 1110 fun verifyHighResolutionIsDisabledForPreview() = runBlocking { 1111 val highResolutionOutputSizes = 1112 CameraInfoUtil.getHighResolutionOutputSizes( 1113 cameraProvider.getCameraInfo(CameraSelector.DEFAULT_BACK_CAMERA), 1114 ImageFormat.PRIVATE 1115 ) 1116 // Only runs the test when the device has high resolution output sizes 1117 assumeTrue(highResolutionOutputSizes.isNotEmpty()) 1118 1119 // Arrange. 1120 // Sets the mode to allow high resolution support and uses a ResolutionFilter to verify the 1121 // high resolution output sizes are not included in the provided sizes list 1122 val resolutionSelector = 1123 ResolutionSelector.Builder() 1124 .setAllowedResolutionMode(PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE) 1125 .setResolutionStrategy(ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY) 1126 .setResolutionFilter { outputSizes, _ -> 1127 assertThat(outputSizes).containsNoneIn(highResolutionOutputSizes) 1128 outputSizes 1129 } 1130 .build() 1131 val preview = Preview.Builder().setResolutionSelector(resolutionSelector).build() 1132 1133 withContext(Dispatchers.Main) { 1134 preview.setSurfaceProvider( 1135 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() }) 1136 ) 1137 // Act. 1138 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 1139 } 1140 1141 // Assert. 1142 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 1143 } 1144 1145 @Test 1146 fun getsFrame_withHighestAvailableResolutionStrategy() = runBlocking { 1147 // Arrange. 1148 val resolutionSelector = 1149 ResolutionSelector.Builder() 1150 .setResolutionStrategy(ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY) 1151 .build() 1152 val preview = Preview.Builder().setResolutionSelector(resolutionSelector).build() 1153 1154 withContext(Dispatchers.Main) { 1155 preview.setSurfaceProvider( 1156 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() }) 1157 ) 1158 // Act. 1159 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 1160 } 1161 1162 // Assert. 1163 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 1164 } 1165 1166 @Test 1167 fun getsFrame_withAspectRatio_4_3_strategy() = runBlocking { 1168 assumeTrue(!hasExtraCroppingQuirk() && isAspectRatioResolutionSupported(4.0f / 3.0f)) 1169 // Arrange. 1170 val resolutionSelector = 1171 ResolutionSelector.Builder() 1172 .setAspectRatioStrategy(AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY) 1173 .build() 1174 val preview = Preview.Builder().setResolutionSelector(resolutionSelector).build() 1175 1176 withContext(Dispatchers.Main) { 1177 preview.setSurfaceProvider( 1178 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() }) 1179 ) 1180 // Act. 1181 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 1182 } 1183 1184 // Assert. 1185 val resolution = preview.resolutionInfo!!.resolution 1186 assertThat(resolution.width.toFloat() / resolution.height) 1187 .isWithin(TOLERANCE) 1188 .of(4.0f / 3.0f) 1189 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 1190 } 1191 1192 @Test 1193 fun getsFrame_withAspectRatio_16_9_strategy() = runBlocking { 1194 assumeTrue( 1195 !hasAspectRatioLegacyApi21Quirk() && isAspectRatioResolutionSupported(16.0f / 9.0f) 1196 ) 1197 1198 // Arrange. 1199 val resolutionSelector = 1200 ResolutionSelector.Builder() 1201 .setAspectRatioStrategy(AspectRatioStrategy.RATIO_16_9_FALLBACK_AUTO_STRATEGY) 1202 .build() 1203 val preview = Preview.Builder().setResolutionSelector(resolutionSelector).build() 1204 1205 withContext(Dispatchers.Main) { 1206 preview.setSurfaceProvider( 1207 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() }) 1208 ) 1209 // Act. 1210 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 1211 } 1212 1213 // Assert. 1214 val resolution = preview.resolutionInfo!!.resolution 1215 assertThat(resolution.width.toFloat() / resolution.height) 1216 .isWithin(TOLERANCE) 1217 .of(16.0f / 9.0f) 1218 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 1219 } 1220 1221 @Test 1222 fun defaultMaxResolutionCanBeKept_whenResolutionStrategyIsNotSet() = 1223 runBlocking(Dispatchers.Main) { 1224 assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK)) 1225 val useCase = Preview.Builder().build() 1226 cameraProvider.bindToLifecycle( 1227 lifecycleOwner, 1228 CameraSelector.DEFAULT_BACK_CAMERA, 1229 useCase 1230 ) 1231 1232 assertThat( 1233 useCase.currentConfig.containsOption(ImageOutputConfig.OPTION_MAX_RESOLUTION) 1234 ) 1235 .isTrue() 1236 } 1237 1238 @Test 1239 fun defaultMaxResolutionCanBeRemoved_whenResolutionStrategyIsSet() = 1240 runBlocking(Dispatchers.Main) { 1241 assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK)) 1242 val useCase = 1243 Preview.Builder() 1244 .setResolutionSelector( 1245 ResolutionSelector.Builder() 1246 .setResolutionStrategy(ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY) 1247 .build() 1248 ) 1249 .build() 1250 1251 cameraProvider.bindToLifecycle( 1252 lifecycleOwner, 1253 CameraSelector.DEFAULT_BACK_CAMERA, 1254 useCase 1255 ) 1256 assertThat( 1257 useCase.currentConfig.containsOption(ImageOutputConfig.OPTION_MAX_RESOLUTION) 1258 ) 1259 .isFalse() 1260 } 1261 1262 @Test 1263 fun resolutionSelectorConfigCorrectlyMerged_afterBindToLifecycle() = 1264 runBlocking(Dispatchers.Main) { 1265 val resolutionFilter = ResolutionFilter { supportedSizes, _ -> supportedSizes } 1266 val useCase = 1267 Preview.Builder() 1268 .setResolutionSelector( 1269 ResolutionSelector.Builder() 1270 .setResolutionFilter(resolutionFilter) 1271 .setAllowedResolutionMode(PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE) 1272 .build() 1273 ) 1274 .build() 1275 cameraProvider.bindToLifecycle( 1276 lifecycleOwner, 1277 CameraSelector.DEFAULT_BACK_CAMERA, 1278 useCase 1279 ) 1280 1281 val resolutionSelector = 1282 useCase.currentConfig.retrieveOption(OPTION_RESOLUTION_SELECTOR) 1283 // The default 4:3 AspectRatioStrategy is kept 1284 assertThat(resolutionSelector!!.aspectRatioStrategy) 1285 .isEqualTo(AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY) 1286 // The default highest available ResolutionStrategy is kept 1287 assertThat(resolutionSelector.resolutionStrategy) 1288 .isEqualTo(ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY) 1289 // The set resolutionFilter is kept 1290 assertThat(resolutionSelector.resolutionFilter).isEqualTo(resolutionFilter) 1291 // The set allowedResolutionMode is kept 1292 assertThat(resolutionSelector.allowedResolutionMode) 1293 .isEqualTo(PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE) 1294 } 1295 1296 // ====================================================== 1297 // Section 5: Session error handling 1298 // ====================================================== 1299 1300 @Test 1301 fun sessionErrorListenerReceivesError_getsFrame(): Unit = runBlocking { 1302 // Arrange. 1303 val preview = Preview.Builder().build() 1304 withContext(Dispatchers.Main) { 1305 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 1306 // Act. 1307 preview.surfaceProvider = 1308 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() }) 1309 } 1310 1311 // Retrieves the initial session config 1312 val initialSessionConfig = preview.sessionConfig 1313 1314 // Checks that image can be received successfully when onError is received. 1315 triggerOnErrorAndVerifyNewImageReceived(initialSessionConfig) 1316 1317 // Rebinds to different camera 1318 if (CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_FRONT)) { 1319 withContext(Dispatchers.Main) { 1320 cameraProvider.unbind(preview) 1321 cameraProvider.bindToLifecycle( 1322 lifecycleOwner, 1323 CameraSelector.DEFAULT_FRONT_CAMERA, 1324 preview 1325 ) 1326 } 1327 1328 // Checks that image can be received successfully when onError is received by the old 1329 // error listener. 1330 triggerOnErrorAndVerifyNewImageReceived(initialSessionConfig) 1331 } 1332 1333 val sessionConfigBeforeValidErrorNotification = preview.sessionConfig 1334 // Checks that image can be received successfully when onError is received by the new 1335 // error listener. 1336 triggerOnErrorAndVerifyNewImageReceived(preview.sessionConfig) 1337 // Checks that triggering onError to valid listener has recreated the pipeline 1338 assertThat(preview.sessionConfig).isNotEqualTo(sessionConfigBeforeValidErrorNotification) 1339 } 1340 1341 private fun triggerOnErrorAndVerifyNewImageReceived(sessionConfig: SessionConfig) { 1342 frameSemaphore = Semaphore(0) 1343 // Forces invoke the onError callback 1344 runOnMainSync { 1345 sessionConfig.errorListener!!.onError( 1346 sessionConfig, 1347 SessionConfig.SessionError.SESSION_ERROR_UNKNOWN 1348 ) 1349 } 1350 // Assert. 1351 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 1352 } 1353 1354 // ====================================================== 1355 // Section 6: Preview Stabilization, Dynamic Range, Frame rate 1356 // ====================================================== 1357 @Test 1358 fun previewStabilizationIsSetCorrectly(): Unit = runBlocking { 1359 val preview = Preview.Builder().setPreviewStabilizationEnabled(true).build() 1360 assertThat(preview.isPreviewStabilizationEnabled).isTrue() 1361 } 1362 1363 private fun isPreviewStabilizationModeSupported(cameraSelector: CameraSelector): Boolean { 1364 val cameraInfoInternal = cameraProvider.getCameraInfo(cameraSelector) as CameraInfoInternal 1365 val cameraCharacteristics = 1366 cameraInfoInternal.cameraCharacteristics as CameraCharacteristics 1367 val stabilizationModes = 1368 cameraCharacteristics.get( 1369 CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES 1370 ) as IntArray 1371 return stabilizationModes.contains( 1372 CameraCharacteristics.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION 1373 ) 1374 } 1375 1376 @Test 1377 fun getPreviewCapabilitiesStabilizationSupportIsCorrect() { 1378 val capabilities = 1379 Preview.getPreviewCapabilities(cameraProvider.getCameraInfo(cameraSelector)) 1380 1381 assertThat(capabilities.isStabilizationSupported()) 1382 .isEqualTo(isPreviewStabilizationModeSupported(cameraSelector)) 1383 } 1384 1385 @Test 1386 fun previewStabilizationOn_videoStabilizationModeIsPreviewStabilization(): Unit = runBlocking { 1387 val previewCapabilities = 1388 Preview.getPreviewCapabilities(cameraProvider.getCameraInfo(cameraSelector)) 1389 assumeTrue(previewCapabilities.isStabilizationSupported) 1390 1391 val previewBuilder = Preview.Builder().setPreviewStabilizationEnabled(true) 1392 verifyVideoStabilizationModeInResultAndFramesAvailable( 1393 cameraSelector = cameraSelector, 1394 previewBuilder = previewBuilder, 1395 expectedMode = CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION 1396 ) 1397 } 1398 1399 @Test 1400 fun previewStabilizationOnAndVideoOff_videoStabilizationModeIsOff(): Unit = runBlocking { 1401 val previewCapabilities = 1402 Preview.getPreviewCapabilities(cameraProvider.getCameraInfo(cameraSelector)) 1403 val videoCaptureCapabilities = 1404 Recorder.getVideoCapabilities(cameraProvider.getCameraInfo(cameraSelector)) 1405 assumeTrue( 1406 previewCapabilities.isStabilizationSupported && 1407 videoCaptureCapabilities.isStabilizationSupported 1408 ) 1409 1410 val previewBuilder = Preview.Builder().setPreviewStabilizationEnabled(true) 1411 val videoCapture = 1412 VideoCapture.Builder(Recorder.Builder().build()) 1413 .setVideoStabilizationEnabled(false) 1414 .build() 1415 1416 verifyVideoStabilizationModeInResultAndFramesAvailable( 1417 cameraSelector = cameraSelector, 1418 previewBuilder = previewBuilder, 1419 videoCapture = videoCapture, 1420 expectedMode = CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE_OFF 1421 ) 1422 } 1423 1424 @Test 1425 fun previewStabilizationOnAndVideoOn_videoStabilizationModeIsPreviewStabilization(): Unit = 1426 runBlocking { 1427 val previewCapabilities = 1428 Preview.getPreviewCapabilities(cameraProvider.getCameraInfo(cameraSelector)) 1429 val videoCaptureCapabilities = 1430 Recorder.getVideoCapabilities(cameraProvider.getCameraInfo(cameraSelector)) 1431 assumeTrue( 1432 previewCapabilities.isStabilizationSupported && 1433 videoCaptureCapabilities.isStabilizationSupported 1434 ) 1435 1436 val previewBuilder = Preview.Builder().setPreviewStabilizationEnabled(true) 1437 val videoCapture = 1438 VideoCapture.Builder(Recorder.Builder().build()) 1439 .setVideoStabilizationEnabled(true) 1440 .build() 1441 1442 verifyVideoStabilizationModeInResultAndFramesAvailable( 1443 cameraSelector = cameraSelector, 1444 previewBuilder = previewBuilder, 1445 videoCapture = videoCapture, 1446 expectedMode = CaptureResult.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION 1447 ) 1448 } 1449 1450 @Test 1451 @SdkSuppress(minSdkVersion = 23) 1452 fun getPreviewCapabilitiesStabilizationSupportIsCorrect_whenNotSupportedInExtensions() { 1453 assumeTrue(isPreviewStabilizationModeSupported(CameraSelector.DEFAULT_BACK_CAMERA)) 1454 val sessionProcessor = 1455 FakeSessionProcessor( 1456 extensionSpecificChars = 1457 listOf( 1458 android.util.Pair( 1459 CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES, 1460 intArrayOf(CameraCharacteristics.CONTROL_VIDEO_STABILIZATION_MODE_OFF) 1461 ) 1462 ) 1463 ) 1464 val cameraSelector = 1465 ExtensionsUtil.getCameraSelectorWithSessionProcessor( 1466 cameraProvider, 1467 CameraSelector.DEFAULT_BACK_CAMERA, 1468 sessionProcessor 1469 ) 1470 val capabilities = 1471 Preview.getPreviewCapabilities(cameraProvider.getCameraInfo(cameraSelector)) 1472 1473 assertThat(capabilities.isStabilizationSupported()).isFalse() 1474 } 1475 1476 @Test 1477 @SdkSuppress(minSdkVersion = 23) 1478 fun getPreviewCapabilitiesStabilizationSupportIsCorrect_whenSupportedInExtensions() { 1479 assumeFalse(isPreviewStabilizationModeSupported(CameraSelector.DEFAULT_BACK_CAMERA)) 1480 val sessionProcessor = 1481 FakeSessionProcessor( 1482 extensionSpecificChars = 1483 listOf( 1484 android.util.Pair( 1485 CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES, 1486 intArrayOf( 1487 CameraCharacteristics 1488 .CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION 1489 ) 1490 ) 1491 ) 1492 ) 1493 val cameraSelector = 1494 ExtensionsUtil.getCameraSelectorWithSessionProcessor( 1495 cameraProvider, 1496 CameraSelector.DEFAULT_BACK_CAMERA, 1497 sessionProcessor 1498 ) 1499 val capabilities = 1500 Preview.getPreviewCapabilities(cameraProvider.getCameraInfo(cameraSelector)) 1501 1502 assertThat(capabilities.isStabilizationSupported()).isTrue() 1503 } 1504 1505 @Test 1506 @SdkSuppress(minSdkVersion = 23) 1507 fun previewStabilizationCanBeSet_whenSupportedInExtensions() = runBlocking { 1508 assumeTrue(isPreviewStabilizationModeSupported(CameraSelector.DEFAULT_BACK_CAMERA)) 1509 val sessionProcessor = 1510 FakeSessionProcessor( 1511 extensionSpecificChars = 1512 listOf( 1513 android.util.Pair( 1514 CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES, 1515 intArrayOf( 1516 CameraCharacteristics 1517 .CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION 1518 ) 1519 ) 1520 ) 1521 ) 1522 val cameraSelectorWithExtensions = 1523 ExtensionsUtil.getCameraSelectorWithSessionProcessor( 1524 cameraProvider, 1525 CameraSelector.DEFAULT_BACK_CAMERA, 1526 sessionProcessor 1527 ) 1528 1529 val previewBuilder = Preview.Builder().setPreviewStabilizationEnabled(true) 1530 verifyVideoStabilizationModeInResultAndFramesAvailable( 1531 cameraSelector = cameraSelectorWithExtensions, 1532 previewBuilder = previewBuilder, 1533 expectedMode = CaptureResult.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION 1534 ) 1535 } 1536 1537 @Test 1538 @SdkSuppress(minSdkVersion = 21, maxSdkVersion = 32) 1539 fun setMirrorModeIsNoOp_priorToAPI33() = runBlocking { 1540 // Skip for b/404348154 1541 assumeFalse("Skip test for API 26.", Build.VERSION.SDK_INT == 26) 1542 verifyMirrorMode( 1543 CameraSelector.DEFAULT_BACK_CAMERA, 1544 mirrorMode = MirrorMode.MIRROR_MODE_ON, 1545 expectedMirrorMode = MirrorMode.MIRROR_MODE_ON_FRONT_ONLY, 1546 expectedIsMirroringInTransformationInfo = false 1547 ) 1548 1549 verifyMirrorMode( 1550 CameraSelector.DEFAULT_FRONT_CAMERA, 1551 mirrorMode = MirrorMode.MIRROR_MODE_ON, 1552 expectedMirrorMode = MirrorMode.MIRROR_MODE_ON_FRONT_ONLY, 1553 expectedIsMirroringInTransformationInfo = true 1554 ) 1555 } 1556 1557 @Test 1558 @SdkSuppress(minSdkVersion = 33) 1559 fun defaultMirrorMode() = runBlocking { 1560 verifyMirrorMode( 1561 CameraSelector.DEFAULT_BACK_CAMERA, 1562 mirrorMode = null, // don't set the mirror mode 1563 expectedMirrorMode = MirrorMode.MIRROR_MODE_ON_FRONT_ONLY, 1564 expectedIsMirroringInTransformationInfo = false 1565 ) 1566 1567 verifyMirrorMode( 1568 CameraSelector.DEFAULT_FRONT_CAMERA, 1569 mirrorMode = null, // don't set the mirror mode 1570 expectedMirrorMode = MirrorMode.MIRROR_MODE_ON_FRONT_ONLY, 1571 expectedIsMirroringInTransformationInfo = true 1572 ) 1573 } 1574 1575 @Test 1576 @SdkSuppress(minSdkVersion = 33) 1577 fun mirrorModeOn() = runBlocking { 1578 verifyMirrorMode( 1579 CameraSelector.DEFAULT_BACK_CAMERA, 1580 mirrorMode = MirrorMode.MIRROR_MODE_ON, 1581 expectedMirrorMode = MirrorMode.MIRROR_MODE_ON, 1582 expectedIsMirroringInTransformationInfo = true 1583 ) 1584 1585 verifyMirrorMode( 1586 CameraSelector.DEFAULT_FRONT_CAMERA, 1587 mirrorMode = MirrorMode.MIRROR_MODE_ON, 1588 expectedMirrorMode = MirrorMode.MIRROR_MODE_ON, 1589 expectedIsMirroringInTransformationInfo = true 1590 ) 1591 } 1592 1593 @Test 1594 @SdkSuppress(minSdkVersion = 33) 1595 fun mirrorModeOff() = runBlocking { 1596 verifyMirrorMode( 1597 CameraSelector.DEFAULT_BACK_CAMERA, 1598 mirrorMode = MirrorMode.MIRROR_MODE_OFF, 1599 expectedMirrorMode = MirrorMode.MIRROR_MODE_OFF, 1600 expectedIsMirroringInTransformationInfo = false 1601 ) 1602 1603 verifyMirrorMode( 1604 CameraSelector.DEFAULT_FRONT_CAMERA, 1605 mirrorMode = MirrorMode.MIRROR_MODE_OFF, 1606 expectedMirrorMode = MirrorMode.MIRROR_MODE_OFF, 1607 expectedIsMirroringInTransformationInfo = false 1608 ) 1609 } 1610 1611 @Test 1612 @SdkSuppress(minSdkVersion = 33) 1613 fun mirrorModeOnFrontOnly() = runBlocking { 1614 verifyMirrorMode( 1615 CameraSelector.DEFAULT_BACK_CAMERA, 1616 mirrorMode = MirrorMode.MIRROR_MODE_ON_FRONT_ONLY, 1617 expectedMirrorMode = MirrorMode.MIRROR_MODE_ON_FRONT_ONLY, 1618 expectedIsMirroringInTransformationInfo = false 1619 ) 1620 1621 verifyMirrorMode( 1622 CameraSelector.DEFAULT_FRONT_CAMERA, 1623 MirrorMode.MIRROR_MODE_ON_FRONT_ONLY, 1624 expectedMirrorMode = MirrorMode.MIRROR_MODE_ON_FRONT_ONLY, 1625 expectedIsMirroringInTransformationInfo = true 1626 ) 1627 } 1628 1629 private suspend fun verifyMirrorMode( 1630 cameraSelector: CameraSelector, 1631 mirrorMode: Int? = null, 1632 expectedMirrorMode: Int, 1633 expectedIsMirroringInTransformationInfo: Boolean 1634 ) { 1635 val preview = 1636 Preview.Builder() 1637 .also { builder -> mirrorMode?.let { builder.setMirrorMode(it) } } 1638 .build() 1639 1640 val transformationInfoDeferred = CompletableDeferred<TransformationInfo>() 1641 withContext(Dispatchers.Main) { 1642 cameraProvider.unbindAll() 1643 preview.surfaceProvider = 1644 SurfaceTextureProvider.createAutoDrainingSurfaceTextureProvider( 1645 null, 1646 { surfaceRequest -> 1647 surfaceRequest.setTransformationInfoListener( 1648 CameraXExecutors.directExecutor(), 1649 { transformationInfo -> 1650 transformationInfoDeferred.complete(transformationInfo) 1651 } 1652 ) 1653 }, 1654 {} 1655 ) 1656 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 1657 } 1658 1659 // TODO: better check the Camera2 OutputConfiguration but currently there is no way do that. 1660 assertThat(preview.sessionConfig.outputConfigs.get(0).mirrorMode) 1661 .isEqualTo(expectedMirrorMode) 1662 assertThat(withTimeoutOrNull(1000) { transformationInfoDeferred.await() }!!.isMirroring) 1663 .isEqualTo(expectedIsMirroringInTransformationInfo) 1664 } 1665 1666 private suspend fun verifyVideoStabilizationModeInResultAndFramesAvailable( 1667 cameraSelector: CameraSelector, 1668 previewBuilder: Preview.Builder, 1669 videoCapture: VideoCapture<Recorder>? = null, 1670 expectedMode: Int 1671 ) { 1672 val captureResultDeferred = CompletableDeferred<TotalCaptureResult>() 1673 Camera2Interop.Extender(previewBuilder) 1674 .setSessionCaptureCallback( 1675 object : CaptureCallback() { 1676 override fun onCaptureCompleted( 1677 session: CameraCaptureSession, 1678 request: CaptureRequest, 1679 result: TotalCaptureResult 1680 ) { 1681 captureResultDeferred.complete(result) 1682 } 1683 } 1684 ) 1685 val useCaseGroupBuilder = UseCaseGroup.Builder() 1686 val preview = previewBuilder.build() 1687 useCaseGroupBuilder.addUseCase(preview) 1688 videoCapture?.let { useCaseGroupBuilder.addUseCase(it) } 1689 1690 withContext(Dispatchers.Main) { 1691 preview.surfaceProvider = 1692 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() }) 1693 cameraProvider.bindToLifecycle( 1694 lifecycleOwner, 1695 cameraSelector, 1696 useCaseGroupBuilder.build() 1697 ) 1698 } 1699 1700 assertThat( 1701 withTimeoutOrNull(5000) { captureResultDeferred.await() }!! 1702 .request 1703 .get(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE) 1704 ) 1705 .isEqualTo(expectedMode) 1706 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 1707 } 1708 1709 @Test 1710 fun dynamicRange_HLG10BIT(): Unit = runBlocking { 1711 assumeTrue( 1712 cameraProvider 1713 .getCameraInfo(cameraSelector) 1714 .querySupportedDynamicRanges(setOf(DynamicRange.HLG_10_BIT)) 1715 .contains(DynamicRange.HLG_10_BIT) 1716 ) 1717 1718 val preview = Preview.Builder().setDynamicRange(DynamicRange.HLG_10_BIT).build() 1719 withContext(Dispatchers.Main) { 1720 preview.surfaceProvider = 1721 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() }) 1722 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 1723 } 1724 1725 assertThat(preview.dynamicRange).isEqualTo(DynamicRange.HLG_10_BIT) 1726 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 5) 1727 } 1728 1729 @Test 1730 fun dynamicRangeIsSetInSurfaceRequest(): Unit = runBlocking { 1731 assumeTrue( 1732 cameraProvider 1733 .getCameraInfo(cameraSelector) 1734 .querySupportedDynamicRanges(setOf(DynamicRange.HLG_10_BIT)) 1735 .contains(DynamicRange.HLG_10_BIT) 1736 ) 1737 1738 val surfaceRequestDeferred = CompletableDeferred<SurfaceRequest>() 1739 val preview = Preview.Builder().setDynamicRange(DynamicRange.HLG_10_BIT).build() 1740 withContext(Dispatchers.Main) { 1741 preview.surfaceProvider = 1742 Preview.SurfaceProvider { surfaceRequest -> 1743 surfaceRequestDeferred.complete(surfaceRequest) 1744 } 1745 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 1746 } 1747 1748 assertThat(withTimeoutOrNull(3000) { surfaceRequestDeferred.await() }!!.dynamicRange) 1749 .isEqualTo(DynamicRange.HLG_10_BIT) 1750 } 1751 1752 @Test 1753 fun dynamicRangeIsNotSet_SDRInSurfaceRequest(): Unit = runBlocking { 1754 val surfaceRequestDeferred = CompletableDeferred<SurfaceRequest>() 1755 val preview = Preview.Builder().build() 1756 withContext(Dispatchers.Main) { 1757 preview.surfaceProvider = 1758 Preview.SurfaceProvider { surfaceRequest -> 1759 surfaceRequestDeferred.complete(surfaceRequest) 1760 } 1761 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 1762 } 1763 1764 assertThat(withTimeoutOrNull(3000) { surfaceRequestDeferred.await() }!!.dynamicRange) 1765 .isEqualTo(DynamicRange.SDR) 1766 } 1767 1768 @Test 1769 fun canSetFrameRate30_30(): Unit = runBlocking { 1770 val fpsToVerify = Range(30, 30) 1771 assumeTrue( 1772 cameraProvider 1773 .getCameraInfo(cameraSelector) 1774 .supportedFrameRateRanges 1775 .contains(fpsToVerify) 1776 ) 1777 val previewBuilder = Preview.Builder().setTargetFrameRate(fpsToVerify) 1778 verifyFrameRateRangeInResultAndFramesAvailable( 1779 previewBuilder = previewBuilder, 1780 expectedFpsRange = fpsToVerify 1781 ) 1782 } 1783 1784 @Test 1785 fun frameRateIsSetInSurfaceRequest(): Unit = runBlocking { 1786 val fpsToVerify = Range(30, 30) 1787 assumeTrue( 1788 cameraProvider 1789 .getCameraInfo(cameraSelector) 1790 .supportedFrameRateRanges 1791 .contains(fpsToVerify) 1792 ) 1793 val previewBuilder = Preview.Builder().setTargetFrameRate(fpsToVerify) 1794 1795 val preview = previewBuilder.build() 1796 val surfaceRequestDeferred = CompletableDeferred<SurfaceRequest>() 1797 1798 withContext(Dispatchers.Main) { 1799 preview.surfaceProvider = 1800 Preview.SurfaceProvider { surfaceRequest -> 1801 surfaceRequestDeferred.complete(surfaceRequest) 1802 } 1803 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 1804 } 1805 assertThat(withTimeoutOrNull(3000) { surfaceRequestDeferred.await() }!!.expectedFrameRate) 1806 .isEqualTo(fpsToVerify) 1807 } 1808 1809 @Test 1810 fun canSetFrameRate60_60(): Unit = runBlocking { 1811 val fpsToVerify = Range(60, 60) 1812 assumeTrue( 1813 cameraProvider 1814 .getCameraInfo(cameraSelector) 1815 .supportedFrameRateRanges 1816 .contains(fpsToVerify) 1817 ) 1818 val previewBuilder = Preview.Builder().setTargetFrameRate(fpsToVerify) 1819 1820 verifyFrameRateRangeInResultAndFramesAvailable( 1821 previewBuilder = previewBuilder, 1822 expectedFpsRange = fpsToVerify 1823 ) 1824 } 1825 1826 private suspend fun verifyFrameRateRangeInResultAndFramesAvailable( 1827 previewBuilder: Preview.Builder, 1828 expectedFpsRange: Range<Int> 1829 ) { 1830 val captureResultDeferred = CompletableDeferred<TotalCaptureResult>() 1831 Camera2Interop.Extender(previewBuilder) 1832 .setSessionCaptureCallback( 1833 object : CaptureCallback() { 1834 override fun onCaptureCompleted( 1835 session: CameraCaptureSession, 1836 request: CaptureRequest, 1837 result: TotalCaptureResult 1838 ) { 1839 captureResultDeferred.complete(result) 1840 } 1841 } 1842 ) 1843 val preview = previewBuilder.build() 1844 assertThat(preview.targetFrameRate).isEqualTo(expectedFpsRange) 1845 1846 withContext(Dispatchers.Main) { 1847 preview.surfaceProvider = 1848 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() }) 1849 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) 1850 } 1851 1852 val captureResult = withTimeoutOrNull(5000) { captureResultDeferred.await() } 1853 assertThat(captureResult).isNotNull() 1854 val fpsRangeInResult = 1855 captureResult!!.request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE) 1856 // ignore the case CONTROL_AE_TARGET_FPS_RANGE is null 1857 assumeTrue(fpsRangeInResult != null) 1858 assertThat(fpsRangeInResult).isEqualTo(expectedFpsRange) 1859 frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10) 1860 } 1861 1862 private val workExecutorWithNamedThread: Executor 1863 get() { 1864 val threadFactory = ThreadFactory { runnable: Runnable? -> 1865 Thread(runnable, ANY_THREAD_NAME) 1866 } 1867 return Executors.newSingleThreadExecutor(threadFactory) 1868 } 1869 1870 private fun getSurfaceProvider( 1871 threadNameConsumer: Consumer<String>? = null, 1872 resultListener: Consumer<SurfaceRequest.Result>? = null, 1873 frameAvailableListener: SurfaceTexture.OnFrameAvailableListener? = null 1874 ): Preview.SurfaceProvider { 1875 return SurfaceTextureProvider.createAutoDrainingSurfaceTextureProvider( 1876 frameAvailableListener, 1877 { surfaceRequest -> 1878 previewResolution = surfaceRequest.resolution 1879 threadNameConsumer?.accept(Thread.currentThread().name) 1880 }, 1881 resultListener 1882 ) 1883 } 1884 1885 /* 1886 * Check if there is no frame callback for `noFrameIntervalMs` milliseconds, then it will 1887 * return true; If the total check time is over `timeoutMs` milliseconds, then it will return 1888 * false. 1889 */ 1890 @Throws(InterruptedException::class) 1891 private fun noFrameCome(noFrameIntervalMs: Long, timeoutMs: Long): Boolean { 1892 require(!(noFrameIntervalMs <= 0 || timeoutMs <= 0)) { "Time can't be negative value." } 1893 require(timeoutMs >= noFrameIntervalMs) { 1894 "timeoutMs should be larger than noFrameIntervalMs." 1895 } 1896 val checkFrequency = 200L 1897 var totalCheckTime = 0L 1898 var zeroFrameTimer = 0L 1899 do { 1900 Thread.sleep(checkFrequency) 1901 if (frameSemaphore!!.availablePermits() > 0) { 1902 // Has frame, reset timer and frame count. 1903 zeroFrameTimer = 0 1904 frameSemaphore!!.drainPermits() 1905 } else { 1906 zeroFrameTimer += checkFrequency 1907 } 1908 if (zeroFrameTimer > noFrameIntervalMs) { 1909 return true 1910 } 1911 totalCheckTime += checkFrequency 1912 } while (totalCheckTime < timeoutMs) 1913 return false 1914 } 1915 1916 private fun Semaphore.verifyFramesReceived(frameCount: Int, timeoutInSeconds: Long = 10) { 1917 assertThat(this.tryAcquire(frameCount, timeoutInSeconds, TimeUnit.SECONDS)).isTrue() 1918 } 1919 } 1920