| /* |
| * Copyright 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package androidx.camera.camera2.pipe.integration.adapter |
| |
| import android.content.Context |
| import android.content.pm.PackageManager |
| import android.graphics.ImageFormat |
| import android.graphics.SurfaceTexture |
| import android.hardware.camera2.CameraCharacteristics |
| import android.hardware.camera2.CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES |
| import android.hardware.camera2.CameraCharacteristics.REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE |
| import android.hardware.camera2.CameraManager |
| import android.hardware.camera2.CameraMetadata |
| import android.hardware.camera2.params.DynamicRangeProfiles |
| import android.hardware.camera2.params.StreamConfigurationMap |
| import android.media.CamcorderProfile.QUALITY_1080P |
| import android.media.CamcorderProfile.QUALITY_2160P |
| import android.media.CamcorderProfile.QUALITY_480P |
| import android.media.CamcorderProfile.QUALITY_720P |
| import android.media.MediaRecorder |
| import android.os.Build |
| import android.util.Range |
| import android.util.Size |
| import android.view.WindowManager |
| import androidx.camera.camera2.pipe.CameraBackendId |
| import androidx.camera.camera2.pipe.CameraId |
| import androidx.camera.camera2.pipe.integration.CameraPipeConfig |
| import androidx.camera.camera2.pipe.integration.adapter.GuaranteedConfigurationsUtil.getConcurrentSupportedCombinationList |
| import androidx.camera.camera2.pipe.integration.adapter.GuaranteedConfigurationsUtil.getFullSupportedCombinationList |
| import androidx.camera.camera2.pipe.integration.adapter.GuaranteedConfigurationsUtil.getLegacySupportedCombinationList |
| import androidx.camera.camera2.pipe.integration.adapter.GuaranteedConfigurationsUtil.getLevel3SupportedCombinationList |
| import androidx.camera.camera2.pipe.integration.adapter.GuaranteedConfigurationsUtil.getLimitedSupportedCombinationList |
| import androidx.camera.camera2.pipe.integration.adapter.GuaranteedConfigurationsUtil.getRAWSupportedCombinationList |
| import androidx.camera.camera2.pipe.integration.config.CameraAppComponent |
| import androidx.camera.camera2.pipe.integration.internal.DOLBY_VISION_10B_UNCONSTRAINED |
| import androidx.camera.camera2.pipe.integration.internal.DOLBY_VISION_8B_SDR_UNCONSTRAINED |
| import androidx.camera.camera2.pipe.integration.internal.DOLBY_VISION_8B_UNCONSTRAINED |
| import androidx.camera.camera2.pipe.integration.internal.DOLBY_VISION_8B_UNCONSTRAINED_HLG10_UNCONSTRAINED |
| import androidx.camera.camera2.pipe.integration.internal.DOLBY_VISION_CONSTRAINED |
| import androidx.camera.camera2.pipe.integration.internal.HDR10_HDR10_PLUS_UNCONSTRAINED |
| import androidx.camera.camera2.pipe.integration.internal.HDR10_UNCONSTRAINED |
| import androidx.camera.camera2.pipe.integration.internal.HLG10_CONSTRAINED |
| import androidx.camera.camera2.pipe.integration.internal.HLG10_SDR_CONSTRAINED |
| import androidx.camera.camera2.pipe.integration.internal.HLG10_UNCONSTRAINED |
| import androidx.camera.camera2.pipe.integration.internal.LATENCY_NONE |
| import androidx.camera.camera2.pipe.testing.FakeCameraBackend |
| import androidx.camera.camera2.pipe.testing.FakeCameraDevices |
| import androidx.camera.camera2.pipe.testing.FakeCameraMetadata |
| import androidx.camera.core.CameraSelector |
| import androidx.camera.core.CameraSelector.LensFacing |
| import androidx.camera.core.CameraX |
| import androidx.camera.core.CameraXConfig |
| import androidx.camera.core.DynamicRange |
| import androidx.camera.core.UseCase |
| import androidx.camera.core.concurrent.CameraCoordinator |
| import androidx.camera.core.impl.AttachedSurfaceInfo |
| import androidx.camera.core.impl.CameraMode |
| import androidx.camera.core.impl.CameraThreadConfig |
| import androidx.camera.core.impl.EncoderProfilesProxy |
| import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy |
| import androidx.camera.core.impl.ImageFormatConstants |
| import androidx.camera.core.impl.ImageInputConfig |
| import androidx.camera.core.impl.StreamSpec |
| import androidx.camera.core.impl.SurfaceConfig |
| import androidx.camera.core.impl.UseCaseConfig |
| import androidx.camera.core.impl.UseCaseConfigFactory |
| import androidx.camera.core.internal.utils.SizeUtil |
| import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_1440P |
| import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_720P |
| import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_VGA |
| import androidx.camera.testing.fakes.FakeCamera |
| import androidx.camera.testing.fakes.FakeCameraInfoInternal |
| import androidx.camera.testing.impl.CameraUtil |
| import androidx.camera.testing.impl.CameraXUtil |
| import androidx.camera.testing.impl.EncoderProfilesUtil |
| import androidx.camera.testing.impl.fakes.FakeCameraCoordinator |
| import androidx.camera.testing.impl.fakes.FakeCameraFactory |
| import androidx.camera.testing.impl.fakes.FakeEncoderProfilesProvider |
| import androidx.camera.testing.impl.fakes.FakeUseCaseConfig |
| import androidx.test.core.app.ApplicationProvider |
| import androidx.test.platform.app.InstrumentationRegistry |
| import androidx.testutils.assertThrows |
| import com.google.common.truth.Truth.assertThat |
| import java.util.concurrent.ExecutionException |
| import java.util.concurrent.TimeUnit |
| import org.junit.After |
| import org.junit.Assert |
| import org.junit.Before |
| import org.junit.Test |
| import org.junit.runner.RunWith |
| import org.mockito.ArgumentMatchers |
| import org.mockito.Mockito |
| import org.mockito.kotlin.mock |
| import org.mockito.kotlin.whenever |
| import org.robolectric.RobolectricTestRunner |
| import org.robolectric.Shadows |
| import org.robolectric.annotation.Config |
| import org.robolectric.annotation.internal.DoNotInstrument |
| import org.robolectric.shadow.api.Shadow |
| import org.robolectric.shadows.ShadowCameraCharacteristics |
| import org.robolectric.shadows.ShadowCameraManager |
| import org.robolectric.util.ReflectionHelpers |
| |
| @Suppress("DEPRECATION") |
| @RunWith(RobolectricTestRunner::class) |
| @DoNotInstrument |
| @Config(minSdk = Build.VERSION_CODES.LOLLIPOP) |
| class SupportedSurfaceCombinationTest { |
| private val sensorOrientation90 = 90 |
| private val landscapePixelArraySize = Size(4032, 3024) |
| private val displaySize = Size(720, 1280) |
| private val vgaSize = Size(640, 480) |
| private val previewSize = Size(1280, 720) |
| private val recordSize = Size(3840, 2160) |
| private val maximumSize = Size(4032, 3024) |
| private val legacyVideoMaximumVideoSize = Size(1920, 1080) |
| private val mod16Size = Size(960, 544) |
| private val profileUhd = EncoderProfilesUtil.createFakeEncoderProfilesProxy( |
| recordSize.width, recordSize.height |
| ) |
| private val profileFhd = EncoderProfilesUtil.createFakeEncoderProfilesProxy( |
| 1920, 1080 |
| ) |
| private val profileHd = EncoderProfilesUtil.createFakeEncoderProfilesProxy( |
| previewSize.width, previewSize.height |
| ) |
| private val profileSd = EncoderProfilesUtil.createFakeEncoderProfilesProxy( |
| vgaSize.width, vgaSize.height |
| ) |
| private val supportedSizes = arrayOf( |
| Size(4032, 3024), // 4:3 |
| Size(3840, 2160), // 16:9 |
| Size(1920, 1440), // 4:3 |
| Size(1920, 1080), // 16:9 |
| Size(1280, 960), // 4:3 |
| Size(1280, 720), // 16:9 |
| Size(1280, 720), // duplicate the size since Nexus 5X emulator has the |
| Size(960, 544), // a mod16 version of resolution with 16:9 aspect ratio. |
| Size(800, 450), // 16:9 |
| Size(640, 480), // 4:3 |
| ) |
| private val highResolutionMaximumSize = Size(6000, 4500) |
| private val highResolutionSupportedSizes = arrayOf( |
| Size(6000, 4500), // 4:3 |
| Size(6000, 3375), // 16:9 |
| ) |
| private val ultraHighMaximumSize = Size(8000, 6000) |
| private val maximumResolutionSupportedSizes = arrayOf( |
| Size(7200, 5400), // 4:3 |
| Size(7200, 4050), // 16:9 |
| ) |
| private val maximumResolutionHighResolutionSupportedSizes = arrayOf( |
| Size(8000, 6000) |
| ) |
| private val context = InstrumentationRegistry.getInstrumentation().context |
| private var cameraFactory: FakeCameraFactory? = null |
| private var useCaseConfigFactory: UseCaseConfigFactory = mock() |
| private lateinit var fakeCameraMetadata: FakeCameraMetadata |
| private lateinit var cameraCoordinator: CameraCoordinator |
| |
| private val mockCameraAppComponent: CameraAppComponent = mock() |
| private val mockEncoderProfilesAdapter: EncoderProfilesProviderAdapter = mock() |
| private val mockEncoderProfilesProxy: EncoderProfilesProxy = mock() |
| private val mockVideoProfileProxy: VideoProfileProxy = mock() |
| |
| @Before |
| fun setUp() { |
| val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager |
| Shadows.shadowOf(windowManager.defaultDisplay).setRealWidth(displaySize.width) |
| Shadows.shadowOf(windowManager.defaultDisplay).setRealHeight( |
| displaySize |
| .height |
| ) |
| whenever(mockEncoderProfilesAdapter.hasProfile(ArgumentMatchers.anyInt())).thenReturn(true) |
| whenever(mockVideoProfileProxy.width).thenReturn(3840) |
| whenever(mockVideoProfileProxy.height).thenReturn(2160) |
| whenever(mockEncoderProfilesProxy.videoProfiles).thenReturn(listOf(mockVideoProfileProxy)) |
| whenever(mockEncoderProfilesAdapter.getAll(ArgumentMatchers.anyInt())) |
| .thenReturn(mockEncoderProfilesProxy) |
| cameraCoordinator = FakeCameraCoordinator() |
| } |
| |
| @After |
| fun tearDown() { |
| CameraXUtil.shutdown()[10000, TimeUnit.MILLISECONDS] |
| } |
| |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // Surface combination support tests for guaranteed configurations |
| // |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| @Test |
| fun checkLegacySurfaceCombinationSupportedInLegacyDevice() { |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val combinationList = getLegacySupportedCombinationList() |
| for (combination in combinationList) { |
| val isSupported = |
| supportedSurfaceCombination.checkSupported( |
| SupportedSurfaceCombination.FeatureSettings( |
| CameraMode.DEFAULT, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ), combination.surfaceConfigList |
| ) |
| assertThat(isSupported).isTrue() |
| } |
| } |
| |
| @Test |
| fun checkLimitedSurfaceCombinationNotSupportedInLegacyDevice() { |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val combinationList = getLimitedSupportedCombinationList() |
| for (combination in combinationList) { |
| val isSupported = |
| supportedSurfaceCombination.checkSupported( |
| SupportedSurfaceCombination.FeatureSettings( |
| CameraMode.DEFAULT, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ), combination.surfaceConfigList |
| ) |
| assertThat(isSupported).isFalse() |
| } |
| } |
| |
| @Test |
| fun checkFullSurfaceCombinationNotSupportedInLegacyDevice() { |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val combinationList = getFullSupportedCombinationList() |
| for (combination in combinationList) { |
| val isSupported = |
| supportedSurfaceCombination.checkSupported( |
| SupportedSurfaceCombination.FeatureSettings( |
| CameraMode.DEFAULT, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ), combination.surfaceConfigList |
| ) |
| assertThat(isSupported).isFalse() |
| } |
| } |
| |
| @Test |
| fun checkLevel3SurfaceCombinationNotSupportedInLegacyDevice() { |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val combinationList = getLevel3SupportedCombinationList() |
| for (combination in combinationList) { |
| val isSupported = |
| supportedSurfaceCombination.checkSupported( |
| SupportedSurfaceCombination.FeatureSettings( |
| CameraMode.DEFAULT, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ), combination.surfaceConfigList |
| ) |
| assertThat(isSupported).isFalse() |
| } |
| } |
| |
| @Test |
| fun checkLimitedSurfaceCombinationSupportedInLimitedDevice() { |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val combinationList = getLimitedSupportedCombinationList() |
| for (combination in combinationList) { |
| val isSupported = |
| supportedSurfaceCombination.checkSupported( |
| SupportedSurfaceCombination.FeatureSettings( |
| CameraMode.DEFAULT, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ), combination.surfaceConfigList |
| ) |
| assertThat(isSupported).isTrue() |
| } |
| } |
| |
| @Test |
| fun checkFullSurfaceCombinationNotSupportedInLimitedDevice() { |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val combinationList = getFullSupportedCombinationList() |
| for (combination in combinationList) { |
| val isSupported = |
| supportedSurfaceCombination.checkSupported( |
| SupportedSurfaceCombination.FeatureSettings( |
| CameraMode.DEFAULT, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ), combination.surfaceConfigList |
| ) |
| assertThat(isSupported).isFalse() |
| } |
| } |
| |
| @Test |
| fun checkLevel3SurfaceCombinationNotSupportedInLimitedDevice() { |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val combinationList = getLevel3SupportedCombinationList() |
| for (combination in combinationList) { |
| val isSupported = |
| supportedSurfaceCombination.checkSupported( |
| SupportedSurfaceCombination.FeatureSettings( |
| CameraMode.DEFAULT, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ), combination.surfaceConfigList |
| ) |
| assertThat(isSupported).isFalse() |
| } |
| } |
| |
| @Test |
| fun checkFullSurfaceCombinationSupportedInFullDevice() { |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val combinationList = getFullSupportedCombinationList() |
| for (combination in combinationList) { |
| val isSupported = |
| supportedSurfaceCombination.checkSupported( |
| SupportedSurfaceCombination.FeatureSettings( |
| CameraMode.DEFAULT, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ), combination.surfaceConfigList |
| ) |
| assertThat(isSupported).isTrue() |
| } |
| } |
| |
| @Test |
| fun checkLevel3SurfaceCombinationNotSupportedInFullDevice() { |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val combinationList = getLevel3SupportedCombinationList() |
| for (combination in combinationList) { |
| val isSupported = |
| supportedSurfaceCombination.checkSupported( |
| SupportedSurfaceCombination.FeatureSettings( |
| CameraMode.DEFAULT, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ), combination.surfaceConfigList |
| ) |
| assertThat(isSupported).isFalse() |
| } |
| } |
| |
| @Test |
| fun checkLimitedSurfaceCombinationSupportedInRawDevice() { |
| setupCamera( |
| CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW) |
| ) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val combinationList = getLimitedSupportedCombinationList() |
| for (combination in combinationList) { |
| val isSupported = |
| supportedSurfaceCombination.checkSupported( |
| SupportedSurfaceCombination.FeatureSettings( |
| CameraMode.DEFAULT, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ), combination.surfaceConfigList |
| ) |
| assertThat(isSupported).isTrue() |
| } |
| } |
| |
| @Test |
| fun checkLegacySurfaceCombinationSupportedInRawDevice() { |
| setupCamera( |
| CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW) |
| ) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val combinationList = getLegacySupportedCombinationList() |
| for (combination in combinationList) { |
| val isSupported = |
| supportedSurfaceCombination.checkSupported( |
| SupportedSurfaceCombination.FeatureSettings( |
| CameraMode.DEFAULT, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ), combination.surfaceConfigList |
| ) |
| assertThat(isSupported).isTrue() |
| } |
| } |
| |
| @Test |
| fun checkFullSurfaceCombinationSupportedInRawDevice() { |
| setupCamera( |
| CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW) |
| ) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val combinationList = getFullSupportedCombinationList() |
| for (combination in combinationList) { |
| val isSupported = |
| supportedSurfaceCombination.checkSupported( |
| SupportedSurfaceCombination.FeatureSettings( |
| CameraMode.DEFAULT, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ), combination.surfaceConfigList |
| ) |
| assertThat(isSupported).isTrue() |
| } |
| } |
| |
| @Test |
| fun checkRawSurfaceCombinationSupportedInRawDevice() { |
| setupCamera( |
| CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW) |
| ) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val combinationList = getRAWSupportedCombinationList() |
| for (combination in combinationList) { |
| val isSupported = |
| supportedSurfaceCombination.checkSupported( |
| SupportedSurfaceCombination.FeatureSettings( |
| CameraMode.DEFAULT, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ), combination.surfaceConfigList |
| ) |
| assertThat(isSupported).isTrue() |
| } |
| } |
| |
| @Test |
| fun checkLevel3SurfaceCombinationSupportedInLevel3Device() { |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val combinationList = getLevel3SupportedCombinationList() |
| for (combination in combinationList) { |
| val isSupported = |
| supportedSurfaceCombination.checkSupported( |
| SupportedSurfaceCombination.FeatureSettings( |
| CameraMode.DEFAULT, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ), combination.surfaceConfigList |
| ) |
| assertThat(isSupported).isTrue() |
| } |
| } |
| |
| @Test |
| fun checkConcurrentSurfaceCombinationSupportedInConcurrentCameraMode() { |
| Shadows.shadowOf(context.packageManager).setSystemFeature( |
| PackageManager.FEATURE_CAMERA_CONCURRENT, true |
| ) |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val combinationList = getConcurrentSupportedCombinationList() |
| for (combination in combinationList) { |
| val isSupported = |
| supportedSurfaceCombination.checkSupported( |
| SupportedSurfaceCombination.FeatureSettings( |
| CameraMode.CONCURRENT_CAMERA, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ), combination.surfaceConfigList |
| ) |
| assertThat(isSupported).isTrue() |
| } |
| } |
| |
| @Test |
| @Config(minSdk = Build.VERSION_CODES.S) |
| fun checkUltraHighResolutionSurfaceCombinationSupportedInUltraHighCameraMode() { |
| setupCamera( |
| maximumResolutionSupportedSizes = maximumResolutionSupportedSizes, |
| maximumResolutionHighResolutionSupportedSizes = |
| maximumResolutionHighResolutionSupportedSizes, |
| capabilities = intArrayOf( |
| CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR |
| ) |
| ) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| GuaranteedConfigurationsUtil.getUltraHighResolutionSupportedCombinationList().forEach { |
| assertThat( |
| supportedSurfaceCombination.checkSupported( |
| SupportedSurfaceCombination.FeatureSettings( |
| CameraMode.ULTRA_HIGH_RESOLUTION_CAMERA, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ), it.surfaceConfigList |
| ) |
| ).isTrue() |
| } |
| } |
| |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // Surface config transformation tests |
| // |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| @Test |
| fun transformSurfaceConfigWithYUVAnalysisSize() { |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig( |
| CameraMode.DEFAULT, |
| ImageFormat.YUV_420_888, vgaSize |
| ) |
| val expectedSurfaceConfig = |
| SurfaceConfig.create(SurfaceConfig.ConfigType.YUV, SurfaceConfig.ConfigSize.VGA) |
| assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig) |
| } |
| |
| @Test |
| fun transformSurfaceConfigWithYUVPreviewSize() { |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig( |
| CameraMode.DEFAULT, |
| ImageFormat.YUV_420_888, previewSize |
| ) |
| val expectedSurfaceConfig = |
| SurfaceConfig.create(SurfaceConfig.ConfigType.YUV, SurfaceConfig.ConfigSize.PREVIEW) |
| assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig) |
| } |
| |
| @Test |
| fun transformSurfaceConfigWithYUVRecordSize() { |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig( |
| CameraMode.DEFAULT, |
| ImageFormat.YUV_420_888, recordSize |
| ) |
| val expectedSurfaceConfig = |
| SurfaceConfig.create(SurfaceConfig.ConfigType.YUV, SurfaceConfig.ConfigSize.RECORD) |
| assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig) |
| } |
| |
| @Test |
| fun transformSurfaceConfigWithYUVMaximumSize() { |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig( |
| CameraMode.DEFAULT, |
| ImageFormat.YUV_420_888, maximumSize |
| ) |
| val expectedSurfaceConfig = |
| SurfaceConfig.create(SurfaceConfig.ConfigType.YUV, SurfaceConfig.ConfigSize.MAXIMUM) |
| assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig) |
| } |
| |
| @Test |
| fun transformSurfaceConfigWithJPEGAnalysisSize() { |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig( |
| CameraMode.DEFAULT, |
| ImageFormat.JPEG, vgaSize |
| ) |
| val expectedSurfaceConfig = |
| SurfaceConfig.create(SurfaceConfig.ConfigType.JPEG, SurfaceConfig.ConfigSize.VGA) |
| assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig) |
| } |
| |
| @Test |
| fun transformSurfaceConfigWithJPEGPreviewSize() { |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig( |
| CameraMode.DEFAULT, |
| ImageFormat.JPEG, previewSize |
| ) |
| val expectedSurfaceConfig = |
| SurfaceConfig.create(SurfaceConfig.ConfigType.JPEG, SurfaceConfig.ConfigSize.PREVIEW) |
| assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig) |
| } |
| |
| @Test |
| fun transformSurfaceConfigWithJPEGRecordSize() { |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig( |
| CameraMode.DEFAULT, |
| ImageFormat.JPEG, recordSize |
| ) |
| val expectedSurfaceConfig = |
| SurfaceConfig.create(SurfaceConfig.ConfigType.JPEG, SurfaceConfig.ConfigSize.RECORD) |
| assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig) |
| } |
| |
| @Test |
| fun transformSurfaceConfigWithJPEGMaximumSize() { |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig( |
| CameraMode.DEFAULT, |
| ImageFormat.JPEG, maximumSize |
| ) |
| val expectedSurfaceConfig = |
| SurfaceConfig.create(SurfaceConfig.ConfigType.JPEG, SurfaceConfig.ConfigSize.MAXIMUM) |
| assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig) |
| } |
| |
| @Test |
| fun transformSurfaceConfigWithPRIVS720PSizeInConcurrentMode() { |
| Shadows.shadowOf(context.packageManager) |
| .setSystemFeature(PackageManager.FEATURE_CAMERA_CONCURRENT, true) |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig( |
| CameraMode.CONCURRENT_CAMERA, |
| ImageFormat.PRIVATE, SizeUtil.RESOLUTION_720P |
| ) |
| val expectedSurfaceConfig = |
| SurfaceConfig.create(SurfaceConfig.ConfigType.PRIV, SurfaceConfig.ConfigSize.s720p) |
| assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig) |
| } |
| |
| @Test |
| fun transformSurfaceConfigWithYUVS720PSizeInConcurrentMode() { |
| Shadows.shadowOf(context.packageManager) |
| .setSystemFeature(PackageManager.FEATURE_CAMERA_CONCURRENT, true) |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig( |
| CameraMode.CONCURRENT_CAMERA, |
| ImageFormat.YUV_420_888, SizeUtil.RESOLUTION_720P |
| ) |
| val expectedSurfaceConfig = |
| SurfaceConfig.create(SurfaceConfig.ConfigType.YUV, SurfaceConfig.ConfigSize.s720p) |
| assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig) |
| } |
| |
| @Test |
| fun transformSurfaceConfigWithJPEGS720PSizeInConcurrentMode() { |
| Shadows.shadowOf(context.packageManager) |
| .setSystemFeature(PackageManager.FEATURE_CAMERA_CONCURRENT, true) |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig( |
| CameraMode.CONCURRENT_CAMERA, |
| ImageFormat.JPEG, SizeUtil.RESOLUTION_720P |
| ) |
| val expectedSurfaceConfig = |
| SurfaceConfig.create(SurfaceConfig.ConfigType.JPEG, SurfaceConfig.ConfigSize.s720p) |
| assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig) |
| } |
| |
| @Test |
| fun transformSurfaceConfigWithPRIVS1440PSizeInConcurrentMode() { |
| Shadows.shadowOf(context.packageManager) |
| .setSystemFeature(PackageManager.FEATURE_CAMERA_CONCURRENT, true) |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig( |
| CameraMode.CONCURRENT_CAMERA, |
| ImageFormat.PRIVATE, RESOLUTION_1440P |
| ) |
| val expectedSurfaceConfig = |
| SurfaceConfig.create(SurfaceConfig.ConfigType.PRIV, SurfaceConfig.ConfigSize.s1440p) |
| assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig) |
| } |
| |
| @Test |
| fun transformSurfaceConfigWithYUVS1440PSizeInConcurrentMode() { |
| Shadows.shadowOf(context.packageManager) |
| .setSystemFeature(PackageManager.FEATURE_CAMERA_CONCURRENT, true) |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig( |
| CameraMode.CONCURRENT_CAMERA, |
| ImageFormat.YUV_420_888, RESOLUTION_1440P |
| ) |
| val expectedSurfaceConfig = |
| SurfaceConfig.create(SurfaceConfig.ConfigType.YUV, SurfaceConfig.ConfigSize.s1440p) |
| assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig) |
| } |
| |
| @Test |
| fun transformSurfaceConfigWithJPEGS1440PSizeInConcurrentMode() { |
| Shadows.shadowOf(context.packageManager) |
| .setSystemFeature(PackageManager.FEATURE_CAMERA_CONCURRENT, true) |
| setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig( |
| CameraMode.CONCURRENT_CAMERA, |
| ImageFormat.JPEG, RESOLUTION_1440P |
| ) |
| val expectedSurfaceConfig = |
| SurfaceConfig.create(SurfaceConfig.ConfigType.JPEG, SurfaceConfig.ConfigSize.s1440p) |
| assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig) |
| } |
| |
| @Test |
| @Config(minSdk = 31) |
| fun transformSurfaceConfigWithUltraHighResolution() { |
| setupCamera( |
| maximumResolutionSupportedSizes = maximumResolutionSupportedSizes, |
| maximumResolutionHighResolutionSupportedSizes = |
| maximumResolutionHighResolutionSupportedSizes, |
| capabilities = intArrayOf( |
| CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR |
| ) |
| ) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| assertThat( |
| supportedSurfaceCombination.transformSurfaceConfig( |
| CameraMode.DEFAULT, |
| ImageFormat.PRIVATE, ultraHighMaximumSize |
| ) |
| ).isEqualTo( |
| SurfaceConfig.create( |
| SurfaceConfig.ConfigType.PRIV, |
| SurfaceConfig.ConfigSize.ULTRA_MAXIMUM |
| ) |
| ) |
| assertThat( |
| supportedSurfaceCombination.transformSurfaceConfig( |
| CameraMode.DEFAULT, |
| ImageFormat.YUV_420_888, ultraHighMaximumSize |
| ) |
| ).isEqualTo( |
| SurfaceConfig.create( |
| SurfaceConfig.ConfigType.YUV, |
| SurfaceConfig.ConfigSize.ULTRA_MAXIMUM |
| ) |
| ) |
| assertThat( |
| supportedSurfaceCombination.transformSurfaceConfig( |
| CameraMode.DEFAULT, |
| ImageFormat.JPEG, ultraHighMaximumSize |
| ) |
| ).isEqualTo( |
| SurfaceConfig.create( |
| SurfaceConfig.ConfigType.JPEG, |
| SurfaceConfig.ConfigSize.ULTRA_MAXIMUM |
| ) |
| ) |
| } |
| |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // Resolution selection tests for LEGACY-level guaranteed configurations |
| // |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * PRIV/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSize_singlePrivStream_inLegacyDevice() { |
| val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase, maximumSize) |
| } |
| getSuggestedSpecsAndVerify(useCaseExpectedResultMap) |
| } |
| |
| /** |
| * JPEG/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSize_singleJpegStream_inLegacyDevice() { |
| val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(jpegUseCase, maximumSize) |
| } |
| getSuggestedSpecsAndVerify(useCaseExpectedResultMap) |
| } |
| |
| /** |
| * YUV/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSize_singleYuvStream_inLegacyDevice() { |
| val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(yuvUseCase, maximumSize) |
| } |
| getSuggestedSpecsAndVerify(useCaseExpectedResultMap) |
| } |
| |
| /** |
| * PRIV/PREVIEW + JPEG/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusJpeg_inLegacyDevice() { |
| val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) |
| val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase, if (Build.VERSION.SDK_INT == 21) RESOLUTION_VGA else previewSize) |
| put(jpegUseCase, maximumSize) |
| } |
| getSuggestedSpecsAndVerify(useCaseExpectedResultMap) |
| } |
| |
| /** |
| * YUV/PREVIEW + JPEG/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_yuvPlusJpeg_inLegacyDevice() { |
| val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(yuvUseCase, if (Build.VERSION.SDK_INT == 21) RESOLUTION_VGA else previewSize) |
| put(jpegUseCase, maximumSize) |
| } |
| getSuggestedSpecsAndVerify(useCaseExpectedResultMap) |
| } |
| |
| /** |
| * PRIV/PREVIEW + PRIV/PREVIEW |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusPriv_inLegacyDevice() { |
| val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase1, if (Build.VERSION.SDK_INT == 21) RESOLUTION_VGA else previewSize) |
| put(privUseCase2, if (Build.VERSION.SDK_INT == 21) RESOLUTION_VGA else previewSize) |
| } |
| getSuggestedSpecsAndVerify(useCaseExpectedResultMap) |
| } |
| |
| /** |
| * PRIV/PREVIEW + YUV/PREVIEW |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusYuv_inLegacyDevice() { |
| val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase, if (Build.VERSION.SDK_INT == 21) RESOLUTION_VGA else previewSize) |
| put(yuvUseCase, if (Build.VERSION.SDK_INT == 21) RESOLUTION_VGA else previewSize) |
| } |
| getSuggestedSpecsAndVerify(useCaseExpectedResultMap) |
| } |
| |
| /** |
| * PRIV/PREVIEW + YUV/PREVIEW + JPEG/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusYuvPlusJpeg_inLegacyDevice() { |
| val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase, if (Build.VERSION.SDK_INT == 21) RESOLUTION_VGA else previewSize) |
| put(yuvUseCase, if (Build.VERSION.SDK_INT == 21) RESOLUTION_VGA else previewSize) |
| put(jpegUseCase, maximumSize) |
| } |
| getSuggestedSpecsAndVerify(useCaseExpectedResultMap) |
| } |
| |
| /** |
| * Unsupported PRIV + JPEG + PRIV for legacy level devices |
| */ |
| @Test |
| fun throwsException_unsupportedConfiguration_inLegacyDevice() { |
| val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG |
| val privUseCas2 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase1, RESOLUTION_VGA) |
| put(jpegUseCase, RESOLUTION_VGA) |
| put(privUseCas2, RESOLUTION_VGA) |
| } |
| Assert.assertThrows(IllegalArgumentException::class.java) { |
| getSuggestedSpecsAndVerify(useCaseExpectedResultMap) |
| } |
| } |
| |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // Resolution selection tests for LIMITED-level guaranteed configurations |
| // |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * PRIV/PREVIEW + PRIV/RECORD |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusPriv_inLimitedDevice() { |
| val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV |
| val privUseCas2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase1, recordSize) |
| put(privUseCas2, previewSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED |
| ) |
| } |
| |
| /** |
| * PRIV/PREVIEW + YUV/RECORD |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusYuv_inLimitedDevice() { |
| val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase, previewSize) |
| put(yuvUseCase, recordSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED |
| ) |
| } |
| |
| /** |
| * YUV/PREVIEW + YUV/RECORD |
| */ |
| @Test |
| fun canSelectCorrectSizes_yuvPlusYuv_inLimitedDevice() { |
| val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(yuvUseCase1, recordSize) |
| put(yuvUseCase2, previewSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED |
| ) |
| } |
| |
| /** |
| * PRIV/PREVIEW + PRIV/RECORD + JPEG/RECORD |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusPrivPlusJpeg_inLimitedDevice() { |
| val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV |
| val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase1, recordSize) |
| put(privUseCase2, previewSize) |
| put(jpegUseCase, recordSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED |
| ) |
| } |
| |
| /** |
| * PRIV/PREVIEW + YUV/RECORD + JPEG/RECORD |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusYuvPlusJpeg_inLimitedDevice() { |
| val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase, previewSize) |
| put(yuvUseCase, recordSize) |
| put(jpegUseCase, recordSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED |
| ) |
| } |
| |
| /** |
| * YUV/PREVIEW + YUV/PREVIEW + JPEG/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_yuvPlusYuvPlusJpeg_inLimitedDevice() { |
| val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(yuvUseCase1, previewSize) |
| put(yuvUseCase2, previewSize) |
| put(jpegUseCase, maximumSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED |
| ) |
| } |
| |
| /** |
| * Unsupported YUV + PRIV + YUV for limited level devices |
| */ |
| @Test |
| fun throwsException_unsupportedConfiguration_inLimitedDevice() { |
| val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(yuvUseCase1, RESOLUTION_VGA) |
| put(privUseCase, RESOLUTION_VGA) |
| put(yuvUseCase2, RESOLUTION_VGA) |
| } |
| Assert.assertThrows(IllegalArgumentException::class.java) { |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED |
| ) |
| } |
| } |
| |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // Resolution selection tests for FULL-level guaranteed configurations |
| // |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * PRIV/PREVIEW + PRIV/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusPriv_inFullDevice() { |
| val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV |
| val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase1, maximumSize) |
| put(privUseCase2, previewSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL |
| ) |
| } |
| |
| /** |
| * PRIV/PREVIEW + YUV/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusYuv_inFullDevice() { |
| val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase, previewSize) |
| put(yuvUseCase, maximumSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL |
| ) |
| } |
| |
| /** |
| * YUV/PREVIEW + YUV/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_yuvPlusYuv_inFullDevice() { |
| val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(yuvUseCase1, maximumSize) |
| put(yuvUseCase2, previewSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL |
| ) |
| } |
| |
| /** |
| * PRIV/PREVIEW + PRIV/PREVIEW + JPEG/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusPrivPlusJpeg_inFullDevice() { |
| val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG |
| val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV |
| val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(jpegUseCase, maximumSize) |
| put(privUseCase1, previewSize) |
| put(privUseCase2, previewSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL |
| ) |
| } |
| |
| /** |
| * YUV/VGA + PRIV/PREVIEW + YUV/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_yuvPlusPrivPlusYuv_inFullDevice() { |
| val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase, previewSize) |
| put(yuvUseCase1, maximumSize) |
| put(yuvUseCase2, RESOLUTION_VGA) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL |
| ) |
| } |
| |
| /** |
| * YUV/VGA + YUV/PREVIEW + YUV/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_yuvPlusYuvPlusYuv_inFullDevice() { |
| val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val yuvUseCase3 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(yuvUseCase1, maximumSize) |
| put(yuvUseCase2, previewSize) |
| put(yuvUseCase3, RESOLUTION_VGA) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL |
| ) |
| } |
| |
| /** |
| * Unsupported PRIV + PRIV + YUV + RAW for full level devices |
| */ |
| @Test |
| fun throwsException_unsupportedConfiguration_inFullDevice() { |
| val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV |
| val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val rawUseCase = createRawUseCase() // RAW |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase1, RESOLUTION_VGA) |
| put(privUseCase2, RESOLUTION_VGA) |
| put(yuvUseCase, RESOLUTION_VGA) |
| put(rawUseCase, RESOLUTION_VGA) |
| } |
| assertThrows(IllegalArgumentException::class.java) { |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED |
| ) |
| } |
| } |
| |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // Resolution selection tests for Level-3 guaranteed configurations |
| // |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * PRIV/PREVIEW + PRIV/VGA + YUV/MAXIMUM + RAW/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusPrivPlusYuvPlusRaw_inLevel3Device() { |
| val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV |
| val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val rawUseCase = createRawUseCase() // RAW |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase1, previewSize) |
| put(privUseCase2, RESOLUTION_VGA) |
| put(yuvUseCase, maximumSize) |
| put(rawUseCase, maximumSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3, |
| capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW) |
| ) |
| } |
| |
| /** |
| * PRIV/PREVIEW + PRIV/VGA + JPEG/MAXIMUM + RAW/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusPrivPlusJpegPlusRaw_inLevel3Device() { |
| val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV |
| val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG |
| val rawUseCase = createRawUseCase() // RAW |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase1, previewSize) |
| put(privUseCase2, RESOLUTION_VGA) |
| put(jpegUseCase, maximumSize) |
| put(rawUseCase, maximumSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3, |
| capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW) |
| ) |
| } |
| |
| /** |
| * Unsupported PRIV + YUV + YUV + RAW for level-3 devices |
| */ |
| @Test |
| fun throwsException_unsupportedConfiguration_inLevel3Device() { |
| val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val rawUseCase = createRawUseCase() // RAW |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase, RESOLUTION_VGA) |
| put(yuvUseCase1, RESOLUTION_VGA) |
| put(yuvUseCase2, RESOLUTION_VGA) |
| put(rawUseCase, RESOLUTION_VGA) |
| } |
| Assert.assertThrows(IllegalArgumentException::class.java) { |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3 |
| ) |
| } |
| } |
| |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // Resolution selection tests for Burst-capability guaranteed configurations |
| // |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * PRIV/PREVIEW + PRIV/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusPriv_inLimitedDevice_withBurstCapability() { |
| val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV |
| val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase1, maximumSize) |
| put(privUseCase2, previewSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| capabilities = intArrayOf( |
| CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE |
| ) |
| ) |
| } |
| |
| /** |
| * PRIV/PREVIEW + YUV/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusYuv_inLimitedDevice_withBurstCapability() { |
| val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase, previewSize) |
| put(yuvUseCase, maximumSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| capabilities = intArrayOf( |
| CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE |
| ) |
| ) |
| } |
| |
| /** |
| * YUV/PREVIEW + YUV/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_yuvPlusYuv_inLimitedDevice_withBurstCapability() { |
| val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(yuvUseCase1, maximumSize) |
| put(yuvUseCase2, previewSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| capabilities = intArrayOf( |
| CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE |
| ) |
| ) |
| } |
| |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // Resolution selection tests for RAW-capability guaranteed configurations |
| // |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * RAW/MAX |
| */ |
| @Test |
| fun canSelectCorrectSizes_singleRawStream_inLimitedDevice_withRawCapability() { |
| val rawUseCase = createRawUseCase() // RAW |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(rawUseCase, maximumSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW) |
| ) |
| } |
| |
| /** |
| * PRIV/PREVIEW + RAW/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusRAW_inLimitedDevice_withRawCapability() { |
| val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val rawUseCase = createRawUseCase() // RAW |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase, previewSize) |
| put(rawUseCase, maximumSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW) |
| ) |
| } |
| |
| /** |
| * PRIV/PREVIEW + PRIV/PREVIEW + RAW/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusPrivPlusRAW_inLimitedDevice_withRawCapability() { |
| val privUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE) // PRIV |
| val privUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val rawUseCase = createRawUseCase() // RAW |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase1, previewSize) |
| put(privUseCase2, previewSize) |
| put(rawUseCase, maximumSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW) |
| ) |
| } |
| |
| /** |
| * PRIV/PREVIEW + YUV/PREVIEW + RAW/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusYuvPlusRAW_inLimitedDevice_withRawCapability() { |
| val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val rawUseCase = createRawUseCase() // RAW |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase, previewSize) |
| put(yuvUseCase, previewSize) |
| put(rawUseCase, maximumSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW) |
| ) |
| } |
| |
| /** |
| * YUV/PREVIEW + YUV/PREVIEW + RAW/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_yuvPlusYuvPlusRAW_inLimitedDevice_withRawCapability() { |
| val yuvUseCase1 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val yuvUseCase2 = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val rawUseCase = createRawUseCase() // RAW |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(yuvUseCase1, previewSize) |
| put(yuvUseCase2, previewSize) |
| put(rawUseCase, maximumSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW) |
| ) |
| } |
| |
| /** |
| * PRIV/PREVIEW + JPEG/MAXIMUM + RAW/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_privPlusJpegPlusRAW_inLimitedDevice_withRawCapability() { |
| val privUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) // PRIV |
| val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG |
| val rawUseCase = createRawUseCase() // RAW |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(privUseCase, previewSize) |
| put(jpegUseCase, maximumSize) |
| put(rawUseCase, maximumSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW) |
| ) |
| } |
| |
| /** |
| * YUV/PREVIEW + JPEG/MAXIMUM + RAW/MAXIMUM |
| */ |
| @Test |
| fun canSelectCorrectSizes_yuvPlusJpegPlusRAW_inLimitedDevice_withRawCapability() { |
| val yuvUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS) // YUV |
| val jpegUseCase = createUseCase(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE) // JPEG |
| val rawUseCase = createRawUseCase() // RAW |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(yuvUseCase, previewSize) |
| put(jpegUseCase, maximumSize) |
| put(rawUseCase, maximumSize) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW) |
| ) |
| } |
| |
| private fun getSuggestedSpecsAndVerify( |
| useCasesExpectedResultMap: Map<UseCase, Size>, |
| attachedSurfaceInfoList: List<AttachedSurfaceInfo> = emptyList(), |
| hardwareLevel: Int = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY, |
| capabilities: IntArray? = null, |
| compareWithAtMost: Boolean = false, |
| compareExpectedFps: Range<Int>? = null, |
| cameraMode: Int = CameraMode.DEFAULT, |
| useCasesExpectedDynamicRangeMap: Map<UseCase, DynamicRange> = emptyMap(), |
| dynamicRangeProfiles: DynamicRangeProfiles? = null, |
| default10BitProfile: Long? = null, |
| ) { |
| setupCamera( |
| hardwareLevel = hardwareLevel, |
| capabilities = capabilities, |
| dynamicRangeProfiles = dynamicRangeProfiles, |
| default10BitProfile = default10BitProfile |
| ) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| |
| val useCaseConfigMap = getUseCaseToConfigMap(useCasesExpectedResultMap.keys.toList()) |
| val useCaseConfigToOutputSizesMap = |
| getUseCaseConfigToOutputSizesMap(useCaseConfigMap.values.toList()) |
| val suggestedStreamSpecs = supportedSurfaceCombination.getSuggestedStreamSpecifications( |
| cameraMode, |
| attachedSurfaceInfoList, |
| useCaseConfigToOutputSizesMap |
| ).first |
| |
| useCasesExpectedResultMap.keys.forEach { |
| val resultSize = suggestedStreamSpecs[useCaseConfigMap[it]]!!.resolution |
| val expectedSize = useCasesExpectedResultMap[it]!! |
| if (!compareWithAtMost) { |
| assertThat(resultSize).isEqualTo(expectedSize) |
| } else { |
| assertThat(sizeIsAtMost(resultSize, expectedSize)).isTrue() |
| } |
| |
| compareExpectedFps?.let { _ -> |
| assertThat( |
| suggestedStreamSpecs[useCaseConfigMap[it]]!!.expectedFrameRateRange |
| ).isEqualTo(compareExpectedFps) |
| } |
| } |
| |
| useCasesExpectedDynamicRangeMap.keys.forEach { |
| val resultDynamicRange = suggestedStreamSpecs[useCaseConfigMap[it]]!!.dynamicRange |
| val expectedDynamicRange = useCasesExpectedDynamicRangeMap[it] |
| |
| assertThat(resultDynamicRange).isEqualTo(expectedDynamicRange) |
| } |
| } |
| |
| private fun getUseCaseToConfigMap(useCases: List<UseCase>): Map<UseCase, UseCaseConfig<*>> { |
| val useCaseConfigMap = mutableMapOf<UseCase, UseCaseConfig<*>>().apply { |
| useCases.forEach { |
| put(it, it.currentConfig) |
| } |
| } |
| return useCaseConfigMap |
| } |
| |
| private fun getUseCaseConfigToOutputSizesMap( |
| useCaseConfigs: List<UseCaseConfig<*>> |
| ): Map<UseCaseConfig<*>, List<Size>> { |
| val resultMap = mutableMapOf<UseCaseConfig<*>, List<Size>>().apply { |
| useCaseConfigs.forEach { |
| put(it, supportedSizes.toList()) |
| } |
| } |
| |
| return resultMap |
| } |
| |
| /** |
| * Helper function that returns whether size is <= maxSize |
| * |
| */ |
| private fun sizeIsAtMost(size: Size, maxSize: Size): Boolean { |
| return (size.height * size.width) <= (maxSize.height * maxSize.width) |
| } |
| |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // StreamSpec selection tests for DynamicRange |
| // |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun check10BitDynamicRangeCombinationsSupported() { |
| setupCamera( |
| capabilities = intArrayOf( |
| CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT |
| ) |
| ) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| |
| GuaranteedConfigurationsUtil.get10BitSupportedCombinationList().forEach { |
| assertThat( |
| supportedSurfaceCombination.checkSupported( |
| SupportedSurfaceCombination.FeatureSettings( |
| CameraMode.DEFAULT, |
| DynamicRange.BIT_DEPTH_10_BIT |
| ), |
| it.surfaceConfigList |
| ) |
| ).isTrue() |
| } |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun getSupportedStreamSpecThrows_whenUsingUnsupportedDynamicRange() { |
| val useCase = |
| createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange.HDR_UNSPECIFIED_10_BIT |
| ) |
| val useCaseExpectedResultMap = mapOf( |
| useCase to Size(0, 0) // Should throw before verifying size |
| ) |
| |
| Assert.assertThrows(IllegalArgumentException::class.java) { |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| capabilities = |
| intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT) |
| ) |
| } |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun getSupportedStreamSpecThrows_whenUsingConcurrentCameraAndSupported10BitRange() { |
| Shadows.shadowOf(context.packageManager).setSystemFeature( |
| PackageManager.FEATURE_CAMERA_CONCURRENT, true |
| ) |
| val useCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange.HDR_UNSPECIFIED_10_BIT |
| ) |
| val useCaseExpectedSizeMap = mapOf( |
| useCase to Size(0, 0) // Should throw before verifying size |
| ) |
| |
| Assert.assertThrows(IllegalArgumentException::class.java) { |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| cameraMode = CameraMode.CONCURRENT_CAMERA, |
| dynamicRangeProfiles = HLG10_CONSTRAINED |
| ) |
| } |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun getSupportedStreamSpecThrows_whenUsingUltraHighResolutionAndSupported10BitRange() { |
| Shadows.shadowOf(context.packageManager).setSystemFeature( |
| PackageManager.FEATURE_CAMERA_CONCURRENT, true |
| ) |
| val useCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange.HDR_UNSPECIFIED_10_BIT |
| ) |
| val useCaseExpectedSizeMap = mapOf( |
| useCase to Size(0, 0) // Should throw before verifying size |
| ) |
| |
| Assert.assertThrows(IllegalArgumentException::class.java) { |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| cameraMode = CameraMode.ULTRA_HIGH_RESOLUTION_CAMERA, |
| dynamicRangeProfiles = HLG10_CONSTRAINED |
| ) |
| } |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun dynamicRangeResolver_returnsHlg_dueToMandatory10Bit() { |
| val useCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange.HDR_UNSPECIFIED_10_BIT |
| ) |
| val useCaseExpectedSizeMap = mapOf( |
| useCase to maximumSize |
| ) |
| val useCaseExpectedDynamicRangeMap = mapOf( |
| useCase to DynamicRange.HLG_10_BIT |
| ) |
| |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| dynamicRangeProfiles = HLG10_CONSTRAINED, |
| capabilities = |
| intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT), |
| useCasesExpectedDynamicRangeMap = useCaseExpectedDynamicRangeMap |
| ) |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun dynamicRangeResolver_returnsHdr10_dueToRecommended10BitDynamicRange() { |
| val useCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange.HDR_UNSPECIFIED_10_BIT |
| ) |
| val useCaseExpectedSizeMap = mapOf( |
| useCase to maximumSize |
| ) |
| val useCaseExpectedDynamicRangeMap = mapOf( |
| useCase to DynamicRange.HDR10_10_BIT |
| ) |
| |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| dynamicRangeProfiles = HDR10_UNCONSTRAINED, |
| capabilities = |
| intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT), |
| useCasesExpectedDynamicRangeMap = useCaseExpectedDynamicRangeMap, |
| default10BitProfile = DynamicRangeProfiles.HDR10 |
| ) |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun dynamicRangeResolver_returnsDolbyVision8_dueToSupportedDynamicRanges() { |
| val useCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange( |
| DynamicRange.ENCODING_HDR_UNSPECIFIED, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ) |
| ) |
| val useCaseExpectedSizeMap = mapOf( |
| useCase to maximumSize |
| ) |
| val useCaseExpectedDynamicRangeMap = mapOf( |
| useCase to DynamicRange.DOLBY_VISION_8_BIT |
| ) |
| |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| dynamicRangeProfiles = DOLBY_VISION_8B_UNCONSTRAINED, |
| useCasesExpectedDynamicRangeMap = useCaseExpectedDynamicRangeMap |
| ) |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun dynamicRangeResolver_returnsDolbyVision8_fromUnspecifiedBitDepth() { |
| val useCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange( |
| DynamicRange.ENCODING_DOLBY_VISION, |
| DynamicRange.BIT_DEPTH_UNSPECIFIED |
| ) |
| ) |
| val useCaseExpectedSizeMap = mapOf( |
| useCase to maximumSize |
| ) |
| val useCaseExpectedDynamicRangeMap = mapOf( |
| useCase to DynamicRange.DOLBY_VISION_8_BIT |
| ) |
| |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| dynamicRangeProfiles = DOLBY_VISION_8B_UNCONSTRAINED, |
| useCasesExpectedDynamicRangeMap = useCaseExpectedDynamicRangeMap |
| ) |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun dynamicRangeResolver_returnsDolbyVision10_fromUnspecifiedBitDepth() { |
| val useCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange( |
| DynamicRange.ENCODING_DOLBY_VISION, |
| DynamicRange.BIT_DEPTH_UNSPECIFIED |
| ) |
| ) |
| val useCaseExpectedSizeMap = mapOf( |
| useCase to maximumSize |
| ) |
| val useCaseExpectedDynamicRangeMap = mapOf( |
| useCase to DynamicRange.DOLBY_VISION_10_BIT |
| ) |
| |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| dynamicRangeProfiles = DOLBY_VISION_10B_UNCONSTRAINED, |
| capabilities = |
| intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT), |
| useCasesExpectedDynamicRangeMap = useCaseExpectedDynamicRangeMap |
| ) |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun dynamicRangeResolver_returnsDolbyVision8_fromUnspecifiedHdrWithUnspecifiedBitDepth() { |
| val useCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange( |
| DynamicRange.ENCODING_HDR_UNSPECIFIED, |
| DynamicRange.BIT_DEPTH_UNSPECIFIED |
| ) |
| ) |
| val useCaseExpectedSizeMap = mapOf( |
| useCase to maximumSize |
| ) |
| val useCaseExpectedDynamicRangeMap = mapOf( |
| useCase to DynamicRange.DOLBY_VISION_8_BIT |
| ) |
| |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| dynamicRangeProfiles = DOLBY_VISION_8B_UNCONSTRAINED, |
| useCasesExpectedDynamicRangeMap = useCaseExpectedDynamicRangeMap |
| ) |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun dynamicRangeResolver_returnsDolbyVision10_fromUnspecifiedHdrWithUnspecifiedBitDepth() { |
| val useCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange( |
| DynamicRange.ENCODING_HDR_UNSPECIFIED, |
| DynamicRange.BIT_DEPTH_UNSPECIFIED |
| ) |
| ) |
| val useCaseExpectedSizeMap = mapOf( |
| useCase to maximumSize |
| ) |
| val useCaseExpectedDynamicRangeMap = mapOf( |
| useCase to DynamicRange.DOLBY_VISION_10_BIT |
| ) |
| |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| dynamicRangeProfiles = DOLBY_VISION_CONSTRAINED, |
| capabilities = |
| intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT), |
| useCasesExpectedDynamicRangeMap = useCaseExpectedDynamicRangeMap, |
| default10BitProfile = DynamicRangeProfiles.DOLBY_VISION_10B_HDR_OEM |
| ) |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun dynamicRangeResolver_returnsDolbyVision8_withUndefinedBitDepth_andFullyDefinedHlg10() { |
| val videoUseCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE, |
| dynamicRange = DynamicRange.HLG_10_BIT |
| ) |
| val previewUseCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange( |
| DynamicRange.ENCODING_DOLBY_VISION, |
| DynamicRange.BIT_DEPTH_UNSPECIFIED |
| ) |
| ) |
| val useCaseExpectedSizeMap = mutableMapOf( |
| videoUseCase to recordSize, |
| previewUseCase to previewSize |
| ) |
| val useCaseExpectedDynamicRangeMap = mapOf( |
| videoUseCase to DynamicRange.HLG_10_BIT, |
| previewUseCase to DynamicRange.DOLBY_VISION_8_BIT |
| ) |
| |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| dynamicRangeProfiles = DOLBY_VISION_8B_UNCONSTRAINED_HLG10_UNCONSTRAINED, |
| capabilities = |
| intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT), |
| useCasesExpectedDynamicRangeMap = useCaseExpectedDynamicRangeMap |
| ) |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun dynamicRangeResolver_returnsDolbyVision10_dueToDynamicRangeConstraints() { |
| // VideoCapture partially defined dynamic range |
| val videoUseCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE, |
| dynamicRange = DynamicRange.HDR_UNSPECIFIED_10_BIT |
| ) |
| // Preview fully defined dynamic range |
| val previewUseCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange.DOLBY_VISION_8_BIT, |
| |
| ) |
| val useCaseExpectedSizeMap = mutableMapOf( |
| videoUseCase to recordSize, |
| previewUseCase to previewSize |
| ) |
| val useCaseExpectedDynamicRangeMap = mapOf( |
| videoUseCase to DynamicRange.DOLBY_VISION_10_BIT, |
| previewUseCase to DynamicRange.DOLBY_VISION_8_BIT |
| ) |
| |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| dynamicRangeProfiles = DOLBY_VISION_CONSTRAINED, |
| capabilities = |
| intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT), |
| useCasesExpectedDynamicRangeMap = useCaseExpectedDynamicRangeMap |
| ) |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun dynamicRangeResolver_resolvesUnspecifiedDynamicRange_afterPartiallySpecifiedDynamicRange() { |
| // VideoCapture partially defined dynamic range |
| val videoUseCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE, |
| dynamicRange = DynamicRange.HDR_UNSPECIFIED_10_BIT |
| ) |
| // Preview unspecified dynamic range |
| val previewUseCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) |
| |
| val useCaseExpectedSizeMap = mutableMapOf( |
| videoUseCase to recordSize, |
| previewUseCase to previewSize |
| ) |
| val useCaseExpectedDynamicRangeMap = mapOf( |
| previewUseCase to DynamicRange.HLG_10_BIT, |
| videoUseCase to DynamicRange.HLG_10_BIT |
| ) |
| |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| dynamicRangeProfiles = HLG10_UNCONSTRAINED, |
| capabilities = |
| intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT), |
| useCasesExpectedDynamicRangeMap = useCaseExpectedDynamicRangeMap |
| ) |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun dynamicRangeResolver_resolvesUnspecifiedDynamicRangeToSdr() { |
| // Preview unspecified dynamic range |
| val useCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) |
| |
| val useCaseExpectedSizeMap = mutableMapOf( |
| useCase to maximumSize |
| ) |
| val useCaseExpectedDynamicRangeMap = mapOf( |
| useCase to DynamicRange.SDR |
| ) |
| |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| dynamicRangeProfiles = HLG10_CONSTRAINED, |
| capabilities = |
| intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT), |
| useCasesExpectedDynamicRangeMap = useCaseExpectedDynamicRangeMap |
| ) |
| } |
| |
| @Test |
| fun dynamicRangeResolver_resolvesToSdr_when10BitNotSupported() { |
| // Preview unspecified dynamic range |
| val useCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) |
| |
| val useCaseExpectedSizeMap = mutableMapOf( |
| useCase to maximumSize |
| ) |
| val useCaseExpectedDynamicRangeMap = mapOf( |
| useCase to DynamicRange.SDR |
| ) |
| |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| useCasesExpectedDynamicRangeMap = useCaseExpectedDynamicRangeMap |
| ) |
| } |
| |
| @Test |
| fun dynamicRangeResolver_resolvesToSdr8Bit_whenSdrWithUnspecifiedBitDepthProvided() { |
| // Preview unspecified dynamic range |
| val useCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange( |
| DynamicRange.ENCODING_SDR, |
| DynamicRange.BIT_DEPTH_UNSPECIFIED |
| ) |
| ) |
| |
| val useCaseExpectedSizeMap = mutableMapOf( |
| useCase to maximumSize |
| ) |
| val useCaseExpectedDynamicRangeMap = mapOf( |
| useCase to DynamicRange.SDR |
| ) |
| |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| useCasesExpectedDynamicRangeMap = useCaseExpectedDynamicRangeMap |
| ) |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun dynamicRangeResolver_resolvesUnspecified8Bit_usingConstraintsFrom10BitDynamicRange() { |
| // VideoCapture has 10-bit HDR range with constraint for 8-bit non-SDR range |
| val videoUseCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE, |
| dynamicRange = DynamicRange.DOLBY_VISION_10_BIT |
| ) |
| // Preview unspecified encoding but 8-bit bit depth |
| val previewUseCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange( |
| DynamicRange.ENCODING_UNSPECIFIED, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ) |
| ) |
| |
| val useCaseExpectedSizeMap = mutableMapOf( |
| videoUseCase to recordSize, |
| previewUseCase to previewSize |
| ) |
| |
| val useCaseExpectedDynamicRangeMap = mapOf( |
| videoUseCase to DynamicRange.DOLBY_VISION_10_BIT, |
| previewUseCase to DynamicRange.DOLBY_VISION_8_BIT |
| ) |
| |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| useCasesExpectedDynamicRangeMap = useCaseExpectedDynamicRangeMap, |
| capabilities = |
| intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT), |
| dynamicRangeProfiles = DOLBY_VISION_CONSTRAINED |
| ) |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun dynamicRangeResolver_resolvesToSdr_forUnspecified8Bit_whenNoOtherDynamicRangesPresent() { |
| val useCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange( |
| DynamicRange.ENCODING_UNSPECIFIED, |
| DynamicRange.BIT_DEPTH_8_BIT |
| ) |
| ) |
| |
| val useCaseExpectedSizeMap = mutableMapOf( |
| useCase to maximumSize |
| ) |
| |
| val useCaseExpectedDynamicRangeMap = mapOf( |
| useCase to DynamicRange.SDR |
| ) |
| |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| useCasesExpectedDynamicRangeMap = useCaseExpectedDynamicRangeMap, |
| dynamicRangeProfiles = DOLBY_VISION_8B_SDR_UNCONSTRAINED |
| ) |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun dynamicRangeResolver_resolvesUnspecified8BitToDolbyVision8Bit_whenAlreadyPresent() { |
| // VideoCapture fully resolved Dolby Vision 8-bit |
| val videoUseCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE, |
| dynamicRange = DynamicRange.DOLBY_VISION_8_BIT |
| ) |
| // Preview unspecified encoding / 8-bit |
| val previewUseCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange.UNSPECIFIED |
| ) |
| |
| // Since there are no 10-bit dynamic ranges, the 10-bit resolution table isn't used. |
| // Instead, this will use the camera default LIMITED table which is limited to preview |
| // size for 2 PRIV use cases. |
| val useCaseExpectedSizeMap = mutableMapOf( |
| videoUseCase to previewSize, |
| previewUseCase to previewSize |
| ) |
| |
| val useCaseExpectedDynamicRangeMap = mapOf( |
| videoUseCase to DynamicRange.DOLBY_VISION_8_BIT, |
| previewUseCase to DynamicRange.DOLBY_VISION_8_BIT |
| ) |
| |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| useCasesExpectedDynamicRangeMap = useCaseExpectedDynamicRangeMap, |
| dynamicRangeProfiles = DOLBY_VISION_8B_SDR_UNCONSTRAINED |
| ) |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun tenBitTable_isUsed_whenAttaching10BitUseCaseToAlreadyAttachedSdrUseCases() { |
| // JPEG use case can't be attached with an existing PRIV + YUV in the 10-bit tables |
| val useCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE, |
| dynamicRange = DynamicRange.HLG_10_BIT |
| ) |
| val useCaseExpectedSizeMap = mapOf( |
| // Size would be valid for LIMITED table |
| useCase to recordSize |
| ) |
| // existing surfaces (Preview + ImageAnalysis) |
| val attachedPreview = AttachedSurfaceInfo.create( |
| SurfaceConfig.create( |
| SurfaceConfig.ConfigType.PRIV, |
| SurfaceConfig.ConfigSize.PREVIEW |
| ), |
| ImageFormat.PRIVATE, |
| previewSize, |
| DynamicRange.SDR, |
| listOf(UseCaseConfigFactory.CaptureType.PREVIEW), |
| useCase.currentConfig, |
| /*targetFrameRate=*/null |
| ) |
| val attachedAnalysis = AttachedSurfaceInfo.create( |
| SurfaceConfig.create( |
| SurfaceConfig.ConfigType.YUV, |
| SurfaceConfig.ConfigSize.RECORD |
| ), |
| ImageFormat.YUV_420_888, |
| recordSize, |
| DynamicRange.SDR, |
| listOf(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS), |
| useCase.currentConfig, |
| /*targetFrameRate=*/null |
| ) |
| |
| Assert.assertThrows(IllegalArgumentException::class.java) { |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| attachedSurfaceInfoList = listOf(attachedPreview, attachedAnalysis), |
| // LIMITED allows this combination, but 10-bit table does not |
| hardwareLevel = CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| dynamicRangeProfiles = HLG10_SDR_CONSTRAINED, |
| capabilities = |
| intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT) |
| ) |
| } |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun dynamicRangeConstraints_causeAutoResolutionToThrow() { |
| val useCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE, |
| dynamicRange = DynamicRange.HLG_10_BIT |
| ) |
| val useCaseExpectedSizeMap = mapOf( |
| // Size would be valid for 10-bit table within constraints |
| useCase to recordSize |
| ) |
| // existing surfaces (PRIV + PRIV) |
| val attachedPriv1 = AttachedSurfaceInfo.create( |
| SurfaceConfig.create( |
| SurfaceConfig.ConfigType.PRIV, |
| SurfaceConfig.ConfigSize.PREVIEW |
| ), |
| ImageFormat.PRIVATE, |
| previewSize, |
| DynamicRange.HDR10_10_BIT, |
| listOf(UseCaseConfigFactory.CaptureType.PREVIEW), |
| useCase.currentConfig, |
| /*targetFrameRate=*/null |
| ) |
| val attachedPriv2 = AttachedSurfaceInfo.create( |
| SurfaceConfig.create( |
| SurfaceConfig.ConfigType.PRIV, |
| SurfaceConfig.ConfigSize.RECORD |
| ), |
| ImageFormat.YUV_420_888, |
| recordSize, |
| DynamicRange.HDR10_PLUS_10_BIT, |
| listOf(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE), |
| useCase.currentConfig, |
| /*targetFrameRate=*/null |
| ) |
| |
| // These constraints say HDR10 and HDR10_PLUS can be combined, but not HLG |
| val constraintsTable = |
| DynamicRangeProfiles( |
| longArrayOf( |
| DynamicRangeProfiles.HLG10, |
| DynamicRangeProfiles.HLG10, |
| LATENCY_NONE, |
| DynamicRangeProfiles.HDR10, |
| DynamicRangeProfiles.HDR10 or DynamicRangeProfiles.HDR10_PLUS, |
| LATENCY_NONE, |
| DynamicRangeProfiles.HDR10_PLUS, |
| DynamicRangeProfiles.HDR10_PLUS or DynamicRangeProfiles.HDR10, |
| LATENCY_NONE |
| ) |
| ) |
| |
| Assert.assertThrows(IllegalArgumentException::class.java) { |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| attachedSurfaceInfoList = listOf(attachedPriv1, attachedPriv2), |
| dynamicRangeProfiles = constraintsTable, |
| capabilities = |
| intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT) |
| ) |
| } |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun canAttachHlgDynamicRange_toExistingSdrStreams() { |
| // JPEG use case can be attached with an existing PRIV + PRIV in the 10-bit tables |
| val useCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE, |
| dynamicRange = DynamicRange.HLG_10_BIT |
| ) |
| val useCaseExpectedSizeMap = mapOf( |
| // Size is valid for 10-bit table within constraints |
| useCase to recordSize |
| ) |
| // existing surfaces (PRIV + PRIV) |
| val attachedPriv1 = AttachedSurfaceInfo.create( |
| SurfaceConfig.create( |
| SurfaceConfig.ConfigType.PRIV, |
| SurfaceConfig.ConfigSize.PREVIEW |
| ), |
| ImageFormat.PRIVATE, |
| previewSize, |
| DynamicRange.SDR, |
| listOf(UseCaseConfigFactory.CaptureType.PREVIEW), |
| useCase.currentConfig, |
| /*targetFrameRate=*/null |
| ) |
| val attachedPriv2 = AttachedSurfaceInfo.create( |
| SurfaceConfig.create( |
| SurfaceConfig.ConfigType.PRIV, |
| SurfaceConfig.ConfigSize.RECORD |
| ), |
| ImageFormat.YUV_420_888, |
| recordSize, |
| DynamicRange.SDR, |
| listOf(UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS), |
| useCase.currentConfig, |
| /*targetFrameRate=*/null |
| ) |
| |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| attachedSurfaceInfoList = listOf(attachedPriv1, attachedPriv2), |
| dynamicRangeProfiles = HLG10_SDR_CONSTRAINED, |
| capabilities = |
| intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT) |
| ) |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun requiredSdrDynamicRangeThrows_whenCombinedWithConstrainedHlg() { |
| // VideoCapture HLG dynamic range |
| val videoUseCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE, |
| dynamicRange = DynamicRange.HLG_10_BIT |
| ) |
| // Preview SDR dynamic range |
| val previewUseCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange.SDR |
| ) |
| |
| val useCaseExpectedSizeMap = mutableMapOf( |
| videoUseCase to recordSize, |
| previewUseCase to previewSize |
| ) |
| |
| // Fails because HLG10 is constrained to only HLG10 |
| Assert.assertThrows(IllegalArgumentException::class.java) { |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| dynamicRangeProfiles = HLG10_CONSTRAINED, |
| capabilities = |
| intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT), |
| ) |
| } |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun requiredSdrDynamicRange_canBeCombinedWithUnconstrainedHlg() { |
| // VideoCapture HLG dynamic range |
| val videoUseCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE, |
| dynamicRange = DynamicRange.HLG_10_BIT |
| ) |
| // Preview SDR dynamic range |
| val previewUseCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange.SDR |
| ) |
| |
| val useCaseExpectedSizeMap = mutableMapOf( |
| videoUseCase to recordSize, |
| previewUseCase to previewSize |
| ) |
| |
| // Should succeed due to HLG10 being unconstrained |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| dynamicRangeProfiles = HLG10_UNCONSTRAINED, |
| capabilities = |
| intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT), |
| ) |
| } |
| |
| @Config(minSdk = Build.VERSION_CODES.TIRAMISU) |
| @Test |
| fun multiple10BitUnconstrainedDynamicRanges_canBeCombined() { |
| // VideoCapture HDR10 dynamic range |
| val videoUseCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE, |
| dynamicRange = DynamicRange.HDR10_10_BIT |
| ) |
| // Preview HDR10_PLUS dynamic range |
| val previewUseCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| dynamicRange = DynamicRange.HDR10_PLUS_10_BIT |
| ) |
| |
| val useCaseExpectedSizeMap = mutableMapOf( |
| videoUseCase to recordSize, |
| previewUseCase to previewSize |
| ) |
| |
| // Succeeds because both HDR10 and HDR10_PLUS are unconstrained |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedSizeMap, |
| dynamicRangeProfiles = HDR10_HDR10_PLUS_UNCONSTRAINED, |
| capabilities = |
| intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT), |
| ) |
| } |
| |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // Resolution selection tests for FPS settings |
| // |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| @Test |
| fun getSupportedOutputSizes_single_valid_targetFPS() { |
| // a valid target means the device is capable of that fps |
| val useCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| targetFrameRate = Range<Int>(25, 30) |
| ) |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(useCase, Size(3840, 2160)) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED |
| ) |
| } |
| |
| @Test |
| fun getSuggestedStreamSpec_single_invalid_targetFPS() { |
| // an invalid target means the device would neve be able to reach that fps |
| val useCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| targetFrameRate = Range<Int>(65, 70) |
| ) |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(useCase, Size(800, 450)) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED |
| ) |
| } |
| |
| @Test |
| fun getSuggestedStreamSpec_multiple_targetFPS_first_is_larger() { |
| // a valid target means the device is capable of that fps |
| val useCase1 = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| targetFrameRate = Range<Int>(30, 35) |
| ) |
| val useCase2 = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| targetFrameRate = Range<Int>(15, 25) |
| ) |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| // both selected size should be no larger than 1920 x 1445 |
| put(useCase1, Size(1920, 1445)) |
| put(useCase2, Size(1920, 1445)) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| compareWithAtMost = true |
| ) |
| } |
| |
| @Test |
| fun getSuggestedStreamSpec_multiple_targetFPS_first_is_smaller() { |
| // a valid target means the device is capable of that fps |
| val useCase1 = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| targetFrameRate = Range<Int>(30, 35) |
| ) |
| val useCase2 = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| targetFrameRate = Range<Int>(45, 50) |
| ) |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| // both selected size should be no larger than 1920 x 1440 |
| put(useCase1, Size(1920, 1440)) |
| put(useCase2, Size(1920, 1440)) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| compareWithAtMost = true |
| ) |
| } |
| |
| @Test |
| fun getSuggestedStreamSpec_multiple_targetFPS_intersect() { |
| // first and second new use cases have target fps that intersect each other |
| val useCase1 = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| targetFrameRate = Range<Int>(30, 40) |
| ) |
| val useCase2 = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| targetFrameRate = Range<Int>(35, 45) |
| ) |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| // effective target fps becomes 35-40 |
| // both selected size should be no larger than 1920 x 1080 |
| put(useCase1, Size(1920, 1080)) |
| put(useCase2, Size(1920, 1080)) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| compareWithAtMost = true |
| ) |
| } |
| |
| @Test |
| fun getSuggestedStreamSpec_multiple_cases_first_has_targetFPS() { |
| // first new use case has a target fps, second new use case does not |
| val useCase1 = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| targetFrameRate = Range<Int>(30, 35) |
| ) |
| val useCase2 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| // both selected size should be no larger than 1920 x 1440 |
| put(useCase1, Size(1920, 1440)) |
| put(useCase2, Size(1920, 1440)) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| compareWithAtMost = true |
| ) |
| } |
| |
| @Test |
| fun getSuggestedStreamSpec_multiple_cases_second_has_targetFPS() { |
| // second new use case does not have a target fps, first new use case does not |
| val useCase1 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) |
| val useCase2 = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| targetFrameRate = Range<Int>(30, 35) |
| ) |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| // both selected size should be no larger than 1920 x 1440 |
| put(useCase1, Size(1920, 1440)) |
| put(useCase2, Size(1920, 1440)) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| compareWithAtMost = true |
| ) |
| } |
| |
| @Test |
| fun getSuggestedStreamSpec_attached_with_targetFPS_no_new_targetFPS() { |
| // existing surface with target fps + new use case without a target fps |
| val useCase = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| // size should be no larger than 1280 x 960 |
| put(useCase, Size(1280, 960)) |
| } |
| // existing surface w/ target fps |
| val attachedSurfaceInfo = AttachedSurfaceInfo.create( |
| SurfaceConfig.create( |
| SurfaceConfig.ConfigType.JPEG, |
| SurfaceConfig.ConfigSize.PREVIEW |
| ), |
| ImageFormat.JPEG, |
| Size(1280, 720), |
| DynamicRange.SDR, |
| listOf(UseCaseConfigFactory.CaptureType.PREVIEW), |
| useCase.currentConfig, |
| Range(40, 50) |
| ) |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| attachedSurfaceInfoList = listOf(attachedSurfaceInfo), |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| compareWithAtMost = true |
| ) |
| } |
| |
| @Test |
| fun getSuggestedStreamSpec_attached_with_targetFPS_and_new_targetFPS_no_intersect() { |
| // existing surface with target fps + new use case with target fps that does not intersect |
| val useCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| targetFrameRate = Range<Int>(30, 35) |
| ) |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| // size of new surface should be no larger than 1280 x 960 |
| put(useCase, Size(1280, 960)) |
| } |
| // existing surface w/ target fps |
| val attachedSurfaceInfo = AttachedSurfaceInfo.create( |
| SurfaceConfig.create( |
| SurfaceConfig.ConfigType.JPEG, |
| SurfaceConfig.ConfigSize.PREVIEW |
| ), |
| ImageFormat.JPEG, |
| Size(1280, 720), |
| DynamicRange.SDR, |
| listOf(UseCaseConfigFactory.CaptureType.PREVIEW), |
| useCase.currentConfig, |
| Range(40, 50) |
| ) |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| attachedSurfaceInfoList = listOf(attachedSurfaceInfo), |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| compareWithAtMost = true |
| ) |
| } |
| |
| @Test |
| fun getSuggestedStreamSpec_attached_with_targetFPS_and_new_targetFPS_with_intersect() { |
| // existing surface with target fps + new use case with target fps that intersect each other |
| val useCase = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| targetFrameRate = Range<Int>(45, 50) |
| ) |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| // size of new surface should be no larger than 1280 x 720 |
| put(useCase, Size(1280, 720)) |
| } |
| // existing surface w/ target fps |
| val attachedSurfaceInfo = AttachedSurfaceInfo.create( |
| SurfaceConfig.create( |
| SurfaceConfig.ConfigType.JPEG, |
| SurfaceConfig.ConfigSize.PREVIEW |
| ), |
| ImageFormat.JPEG, |
| Size(1280, 720), |
| DynamicRange.SDR, |
| listOf(UseCaseConfigFactory.CaptureType.PREVIEW), |
| useCase.currentConfig, |
| Range(40, 50) |
| ) |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| attachedSurfaceInfoList = listOf(attachedSurfaceInfo), |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, |
| compareWithAtMost = true |
| ) |
| } |
| |
| @Test |
| fun getSuggestedStreamSpec_has_device_supported_expectedFrameRateRange() { |
| // use case with target fps |
| val useCase1 = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| targetFrameRate = Range<Int>(15, 25) |
| ) |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(useCase1, Size(4032, 3024)) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| compareWithAtMost = true, |
| compareExpectedFps = Range(10, 22) |
| ) |
| // expected fps 10,22 because it has the largest intersection |
| } |
| |
| @Test |
| fun getSuggestedStreamSpec_has_exact_device_supported_expectedFrameRateRange() { |
| // use case with target fps |
| val useCase1 = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| targetFrameRate = Range<Int>(30, 40) |
| ) |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(useCase1, Size(1920, 1440)) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| compareWithAtMost = true, |
| compareExpectedFps = Range(30, 30) |
| ) |
| // expected fps 30,30 because the fps ceiling is 30 |
| } |
| |
| @Test |
| fun getSuggestedStreamSpec_has_no_device_supported_expectedFrameRateRange() { |
| // use case with target fps |
| val useCase1 = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| targetFrameRate = Range<Int>(65, 65) |
| ) |
| |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(useCase1, Size(800, 450)) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| compareWithAtMost = true, |
| compareExpectedFps = Range(60, 60) |
| ) |
| // expected fps 60,60 because it is the closest range available |
| } |
| |
| @Test |
| fun getSuggestedStreamSpec_has_multiple_device_supported_expectedFrameRateRange() { |
| |
| // use case with target fps |
| val useCase1 = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| targetFrameRate = Range<Int>(36, 45) |
| ) |
| |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(useCase1, Size(1280, 960)) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| compareWithAtMost = true, |
| compareExpectedFps = Range(30, 40) |
| ) |
| // expected size will give a maximum of 40 fps |
| // expected range 30,40. another range with the same intersection size was 30,50, but 30,40 |
| // was selected instead because its range has a larger ratio of intersecting value vs |
| // non-intersecting |
| } |
| |
| @Test |
| fun getSuggestedStreamSpec_has_no_device_intersection_expectedFrameRateRange() { |
| // target fps is between ranges, but within device capability (for some reason lol) |
| |
| // use case with target fps |
| val useCase1 = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| targetFrameRate = Range<Int>(26, 27) |
| ) |
| |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(useCase1, Size(1920, 1440)) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| compareWithAtMost = true, |
| compareExpectedFps = Range(30, 30) |
| ) |
| // 30,30 was expected because it is the closest and shortest range to our target fps |
| } |
| |
| @Test |
| fun getSuggestedStreamSpec_has_no_device_intersection_equidistant_expectedFrameRateRange() { |
| |
| // use case with target fps |
| val useCase1 = createUseCase( |
| UseCaseConfigFactory.CaptureType.PREVIEW, |
| targetFrameRate = Range<Int>(26, 26) |
| ) |
| |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(useCase1, Size(1920, 1440)) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| compareWithAtMost = true, |
| compareExpectedFps = Range(30, 30) |
| ) |
| // 30,30 selected because although there are other ranges that have the same distance to |
| // the target, 30,30 is the shortest range that also happens to be on the upper side of the |
| // target range |
| } |
| |
| @Test |
| fun getSuggestedStreamSpec_has_no_expectedFrameRateRange() { |
| // a valid target means the device is capable of that fps |
| |
| // use case with no target fps |
| val useCase1 = createUseCase(UseCaseConfigFactory.CaptureType.PREVIEW) |
| |
| val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply { |
| put(useCase1, Size(4032, 3024)) |
| } |
| getSuggestedSpecsAndVerify( |
| useCaseExpectedResultMap, |
| hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, |
| compareExpectedFps = StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED |
| ) |
| // since no target fps present, no specific device fps will be selected, and is set to |
| // unspecified: (0,0) |
| } |
| |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // Other tests |
| // |
| // ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| @Test |
| fun generateCorrectSurfaceDefinition() { |
| Shadows.shadowOf(context.packageManager).setSystemFeature( |
| PackageManager.FEATURE_CAMERA_CONCURRENT, true |
| ) |
| setupCamera() |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val imageFormat = ImageFormat.JPEG |
| val surfaceSizeDefinition = |
| supportedSurfaceCombination.getUpdatedSurfaceSizeDefinitionByFormat(imageFormat) |
| assertThat(surfaceSizeDefinition.s720pSizeMap[imageFormat]).isEqualTo(RESOLUTION_720P) |
| assertThat(surfaceSizeDefinition.previewSize).isEqualTo(previewSize) |
| assertThat(surfaceSizeDefinition.s1440pSizeMap[imageFormat]).isEqualTo(RESOLUTION_1440P) |
| assertThat(surfaceSizeDefinition.recordSize).isEqualTo(recordSize) |
| assertThat(surfaceSizeDefinition.maximumSizeMap[imageFormat]).isEqualTo(maximumSize) |
| assertThat(surfaceSizeDefinition.ultraMaximumSizeMap).isEmpty() |
| } |
| |
| @Test |
| fun correctS720pSize_withSmallerOutputSizes() { |
| Shadows.shadowOf(context.packageManager).setSystemFeature( |
| PackageManager.FEATURE_CAMERA_CONCURRENT, true |
| ) |
| setupCamera(supportedSizes = arrayOf(RESOLUTION_VGA)) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val imageFormat = ImageFormat.JPEG |
| val surfaceSizeDefinition = |
| supportedSurfaceCombination.getUpdatedSurfaceSizeDefinitionByFormat(imageFormat) |
| assertThat(surfaceSizeDefinition.s720pSizeMap[imageFormat]) |
| .isEqualTo(RESOLUTION_VGA) |
| } |
| |
| @Test |
| fun correctS1440pSize_withSmallerOutputSizes() { |
| Shadows.shadowOf(context.packageManager).setSystemFeature( |
| PackageManager.FEATURE_CAMERA_CONCURRENT, true |
| ) |
| setupCamera(supportedSizes = arrayOf(RESOLUTION_VGA)) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val imageFormat = ImageFormat.JPEG |
| val surfaceSizeDefinition = |
| supportedSurfaceCombination.getUpdatedSurfaceSizeDefinitionByFormat(imageFormat) |
| assertThat(surfaceSizeDefinition.s1440pSizeMap[imageFormat]).isEqualTo(RESOLUTION_VGA) |
| } |
| |
| @Test |
| @Config(minSdk = 23) |
| fun correctMaximumSize_withHighResolutionOutputSizes() { |
| setupCamera(highResolutionSupportedSizes = highResolutionSupportedSizes) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val imageFormat = ImageFormat.JPEG |
| val surfaceSizeDefinition = |
| supportedSurfaceCombination.getUpdatedSurfaceSizeDefinitionByFormat(imageFormat) |
| assertThat(surfaceSizeDefinition.maximumSizeMap[imageFormat]).isEqualTo( |
| highResolutionMaximumSize |
| ) |
| } |
| |
| @Test |
| @Config(minSdk = 32) |
| fun correctUltraMaximumSize_withMaximumResolutionMap() { |
| setupCamera( |
| maximumResolutionSupportedSizes = maximumResolutionSupportedSizes, |
| maximumResolutionHighResolutionSupportedSizes = |
| maximumResolutionHighResolutionSupportedSizes, |
| capabilities = intArrayOf( |
| CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR |
| ) |
| ) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val imageFormat = ImageFormat.JPEG |
| val surfaceSizeDefinition = |
| supportedSurfaceCombination.getUpdatedSurfaceSizeDefinitionByFormat(imageFormat) |
| assertThat(surfaceSizeDefinition.ultraMaximumSizeMap[imageFormat]).isEqualTo( |
| ultraHighMaximumSize |
| ) |
| } |
| |
| @Test |
| fun determineRecordSizeFromStreamConfigurationMap() { |
| // Setup camera with non-integer camera Id |
| setupCamera( |
| CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY, |
| cameraId = CameraId("externalCameraId") |
| ) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| |
| // Checks the determined RECORD size |
| assertThat( |
| supportedSurfaceCombination.surfaceSizeDefinition.recordSize |
| ).isEqualTo( |
| legacyVideoMaximumVideoSize |
| ) |
| } |
| |
| @Test |
| fun applyLegacyApi21QuirkCorrectly() { |
| setupCamera() |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val sortedSizeList = listOf( |
| // 16:9 sizes are put in the front of the list |
| Size(3840, 2160), // 16:9 |
| Size(1920, 1080), // 16:9 |
| Size(1280, 720), // 16:9 |
| Size(960, 544), // a mod16 version of resolution with 16:9 aspect ratio. |
| Size(800, 450), // 16:9 |
| |
| // 4:3 sizes are put in the end of the list |
| Size(4032, 3024), // 4:3 |
| Size(1920, 1440), // 4:3 |
| Size(1280, 960), // 4:3 |
| Size(640, 480), // 4:3 |
| ) |
| val resultList = |
| supportedSurfaceCombination.applyResolutionSelectionOrderRelatedWorkarounds( |
| sortedSizeList, |
| ImageFormat.YUV_420_888 |
| ) |
| val expectedResultList = if (Build.VERSION.SDK_INT == 21) { |
| listOf( |
| // 4:3 sizes are pulled to the front of the list |
| Size(4032, 3024), // 4:3 |
| Size(1920, 1440), // 4:3 |
| Size(1280, 960), // 4:3 |
| Size(640, 480), // 4:3 |
| |
| // 16:9 sizes are put in the end of the list |
| Size(3840, 2160), // 16:9 |
| Size(1920, 1080), // 16:9 |
| Size(1280, 720), // 16:9 |
| Size(960, 544), // a mod16 version of resolution with 16:9 aspect ratio. |
| Size(800, 450), // 16:9 |
| ) |
| } else { |
| sortedSizeList |
| } |
| assertThat(resultList).containsExactlyElementsIn(expectedResultList).inOrder() |
| } |
| |
| @Test |
| fun applyResolutionCorrectorWorkaroundCorrectly() { |
| ReflectionHelpers.setStaticField(Build::class.java, "BRAND", "Samsung") |
| ReflectionHelpers.setStaticField(Build::class.java, "MODEL", "SM-J710MN") |
| setupCamera(hardwareLevel = CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED) |
| val supportedSurfaceCombination = SupportedSurfaceCombination( |
| context, fakeCameraMetadata, |
| mockEncoderProfilesAdapter |
| ) |
| val resultList = |
| supportedSurfaceCombination.applyResolutionSelectionOrderRelatedWorkarounds( |
| supportedSizes.toList(), |
| ImageFormat.YUV_420_888 |
| ) |
| val expectedResultList = if (Build.VERSION.SDK_INT in 21..26) { |
| listOf( |
| // 1280x720 is pulled to the first position for YUV format. |
| Size(1280, 720), |
| |
| // The remaining sizes keep the original order |
| Size(4032, 3024), |
| Size(3840, 2160), |
| Size(1920, 1440), |
| Size(1920, 1080), |
| Size(1280, 960), |
| Size(960, 544), |
| Size(800, 450), |
| Size(640, 480), |
| ) |
| } else { |
| supportedSizes.toList() |
| } |
| assertThat(resultList).containsExactlyElementsIn(expectedResultList).inOrder() |
| } |
| |
| private fun setupCamera( |
| hardwareLevel: Int = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY, |
| sensorOrientation: Int = sensorOrientation90, |
| pixelArraySize: Size = landscapePixelArraySize, |
| supportedSizes: Array<Size> = this.supportedSizes, |
| highResolutionSupportedSizes: Array<Size>? = null, |
| maximumResolutionSupportedSizes: Array<Size>? = null, |
| maximumResolutionHighResolutionSupportedSizes: Array<Size>? = null, |
| dynamicRangeProfiles: DynamicRangeProfiles? = null, |
| default10BitProfile: Long? = null, |
| capabilities: IntArray? = null, |
| cameraId: CameraId = CameraId.fromCamera1Id(0) |
| ) { |
| cameraFactory = FakeCameraFactory() |
| val characteristics = ShadowCameraCharacteristics.newCameraCharacteristics() |
| val shadowCharacteristics = Shadow.extract<ShadowCameraCharacteristics>(characteristics) |
| shadowCharacteristics.set( |
| CameraCharacteristics.LENS_FACING, CameraCharacteristics.LENS_FACING_BACK |
| ) |
| shadowCharacteristics.set( |
| CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL, hardwareLevel |
| ) |
| shadowCharacteristics.set(CameraCharacteristics.SENSOR_ORIENTATION, sensorOrientation) |
| shadowCharacteristics.set( |
| CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE, |
| pixelArraySize |
| ) |
| if (capabilities != null) { |
| shadowCharacteristics.set( |
| CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES, capabilities |
| ) |
| } |
| |
| val mockMap: StreamConfigurationMap = mock() |
| val mockMaximumResolutionMap: StreamConfigurationMap? = |
| if (maximumResolutionSupportedSizes != null || |
| maximumResolutionHighResolutionSupportedSizes != null |
| ) { |
| mock() |
| } else { |
| null |
| } |
| |
| val deviceFPSRanges: Array<Range<Int>?> = arrayOf( |
| Range(10, 22), |
| Range(22, 22), |
| Range(30, 30), |
| Range(30, 50), |
| Range(30, 40), |
| Range(30, 60), |
| Range(50, 60), |
| Range(60, 60) |
| ) |
| |
| val characteristicsMap = mutableMapOf( |
| CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL to hardwareLevel, |
| CameraCharacteristics.SENSOR_ORIENTATION to sensorOrientation, |
| CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE to pixelArraySize, |
| CameraCharacteristics.LENS_FACING to CameraCharacteristics.LENS_FACING_BACK, |
| CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES to capabilities, |
| CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP to mockMap, |
| CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES to deviceFPSRanges |
| ).also { characteristicsMap -> |
| mockMaximumResolutionMap?.let { |
| if (Build.VERSION.SDK_INT >= 31) { |
| characteristicsMap[ |
| CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION |
| ] = |
| mockMaximumResolutionMap |
| } |
| } |
| } |
| |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { |
| dynamicRangeProfiles?.let { |
| characteristicsMap[REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES] = it |
| } |
| default10BitProfile?.let { |
| characteristicsMap[REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE] = it |
| } |
| } |
| |
| // set up FakeCafakeCameraMetadatameraMetadata |
| fakeCameraMetadata = FakeCameraMetadata( |
| cameraId = cameraId, |
| characteristics = characteristicsMap |
| ) |
| |
| val cameraManager = ApplicationProvider.getApplicationContext<Context>() |
| .getSystemService(Context.CAMERA_SERVICE) as CameraManager |
| (Shadow.extract<Any>(cameraManager) as ShadowCameraManager) |
| .addCamera(fakeCameraMetadata.camera.value, characteristics) |
| |
| whenever(mockMap.getOutputSizes(ArgumentMatchers.anyInt())).thenReturn(supportedSizes) |
| // ImageFormat.PRIVATE was supported since API level 23. Before that, the supported |
| // output sizes need to be retrieved via SurfaceTexture.class. |
| whenever( |
| mockMap.getOutputSizes( |
| SurfaceTexture::class.java |
| ) |
| ).thenReturn(supportedSizes) |
| // This is setup for the test to determine RECORD size from StreamConfigurationMap |
| whenever( |
| mockMap.getOutputSizes( |
| MediaRecorder::class.java |
| ) |
| ).thenReturn(supportedSizes) |
| // This is setup for high resolution output sizes |
| highResolutionSupportedSizes?.let { |
| if (Build.VERSION.SDK_INT >= 23) { |
| whenever(mockMap.getHighResolutionOutputSizes(ArgumentMatchers.anyInt())) |
| .thenReturn(it) |
| } |
| } |
| |
| // setup to return different minimum frame durations depending on resolution |
| // minimum frame durations were designated only for the purpose of testing |
| Mockito.`when`( |
| mockMap.getOutputMinFrameDuration( |
| ArgumentMatchers.anyInt(), |
| ArgumentMatchers.eq(Size(4032, 3024)) |
| ) |
| ) |
| .thenReturn(50000000L) // 20 fps, size maximum |
| |
| Mockito.`when`( |
| mockMap.getOutputMinFrameDuration( |
| ArgumentMatchers.anyInt(), |
| ArgumentMatchers.eq(Size(3840, 2160)) |
| ) |
| ) |
| .thenReturn(40000000L) // 25, size record |
| |
| Mockito.`when`( |
| mockMap.getOutputMinFrameDuration( |
| ArgumentMatchers.anyInt(), |
| ArgumentMatchers.eq(Size(1920, 1440)) |
| ) |
| ) |
| .thenReturn(33333333L) // 30 |
| |
| Mockito.`when`( |
| mockMap.getOutputMinFrameDuration( |
| ArgumentMatchers.anyInt(), |
| ArgumentMatchers.eq(Size(1920, 1080)) |
| ) |
| ) |
| .thenReturn(28571428L) // 35 |
| |
| Mockito.`when`( |
| mockMap.getOutputMinFrameDuration( |
| ArgumentMatchers.anyInt(), |
| ArgumentMatchers.eq(Size(1280, 960)) |
| ) |
| ) |
| .thenReturn(25000000L) // 40 |
| |
| Mockito.`when`( |
| mockMap.getOutputMinFrameDuration( |
| ArgumentMatchers.anyInt(), |
| ArgumentMatchers.eq(Size(1280, 720)) |
| ) |
| ) |
| .thenReturn(22222222L) // 45, size preview/display |
| |
| Mockito.`when`( |
| mockMap.getOutputMinFrameDuration( |
| ArgumentMatchers.anyInt(), |
| ArgumentMatchers.eq(Size(960, 544)) |
| ) |
| ) |
| .thenReturn(20000000L) // 50 |
| |
| Mockito.`when`( |
| mockMap.getOutputMinFrameDuration( |
| ArgumentMatchers.anyInt(), |
| ArgumentMatchers.eq(Size(800, 450)) |
| ) |
| ) |
| .thenReturn(16666666L) // 60fps |
| |
| Mockito.`when`( |
| mockMap.getOutputMinFrameDuration( |
| ArgumentMatchers.anyInt(), |
| ArgumentMatchers.eq(Size(640, 480)) |
| ) |
| ) |
| .thenReturn(16666666L) // 60fps |
| |
| shadowCharacteristics.set(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP, mockMap) |
| mockMaximumResolutionMap?.let { |
| whenever(mockMaximumResolutionMap.getOutputSizes(ArgumentMatchers.anyInt())) |
| .thenReturn(maximumResolutionSupportedSizes) |
| whenever(mockMaximumResolutionMap.getOutputSizes(SurfaceTexture::class.java)) |
| .thenReturn(maximumResolutionSupportedSizes) |
| if (Build.VERSION.SDK_INT >= 23) { |
| whenever( |
| mockMaximumResolutionMap.getHighResolutionOutputSizes( |
| ArgumentMatchers.anyInt() |
| ) |
| ).thenReturn( |
| maximumResolutionHighResolutionSupportedSizes |
| ) |
| } |
| if (Build.VERSION.SDK_INT >= 31) { |
| shadowCharacteristics.set( |
| CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION, |
| mockMaximumResolutionMap |
| ) |
| } |
| } |
| @LensFacing val lensFacingEnum = CameraUtil.getLensFacingEnumFromInt( |
| CameraCharacteristics.LENS_FACING_BACK |
| ) |
| val cameraInfo = FakeCameraInfoInternal(fakeCameraMetadata.camera.value) |
| cameraInfo.encoderProfilesProvider = FakeEncoderProfilesProvider.Builder() |
| .add(QUALITY_2160P, profileUhd) |
| .add(QUALITY_1080P, profileFhd) |
| .add(QUALITY_720P, profileHd) |
| .add(QUALITY_480P, profileSd) |
| .build() |
| cameraFactory!!.insertCamera( |
| lensFacingEnum, fakeCameraMetadata.camera.value |
| ) { FakeCamera(fakeCameraMetadata.camera.value, null, cameraInfo) } |
| |
| initCameraX(fakeCameraMetadata.camera.value) |
| } |
| |
| private fun initCameraX(cameraId: String) { |
| val fakeCameraDevices = |
| FakeCameraDevices( |
| defaultCameraBackendId = FakeCameraBackend.FAKE_CAMERA_BACKEND_ID, |
| concurrentCameraBackendIds = setOf( |
| setOf(CameraBackendId("0"), CameraBackendId("1")), |
| setOf(CameraBackendId("0"), CameraBackendId("2")) |
| ), |
| cameraMetadataMap = |
| mapOf(FakeCameraBackend.FAKE_CAMERA_BACKEND_ID to listOf(fakeCameraMetadata)) |
| ) |
| whenever(mockCameraAppComponent.getCameraDevices()) |
| .thenReturn(fakeCameraDevices) |
| cameraFactory!!.cameraManager = mockCameraAppComponent |
| val cameraXConfig = CameraXConfig.Builder.fromConfig( |
| CameraPipeConfig.defaultConfig() |
| ) |
| .setDeviceSurfaceManagerProvider { context: Context?, _: Any?, _: Set<String?>? -> |
| CameraSurfaceAdapter( |
| context!!, |
| mockCameraAppComponent, setOf(cameraId) |
| ) |
| } |
| .setCameraFactoryProvider { _: Context?, |
| _: CameraThreadConfig?, |
| _: CameraSelector?, |
| _: Long |
| -> |
| cameraFactory!! |
| } |
| .build() |
| val cameraX: CameraX = try { |
| CameraXUtil.getOrCreateInstance(context) { cameraXConfig }.get() |
| } catch (e: ExecutionException) { |
| throw IllegalStateException("Unable to initialize CameraX for test.") |
| } catch (e: InterruptedException) { |
| throw IllegalStateException("Unable to initialize CameraX for test.") |
| } |
| useCaseConfigFactory = cameraX.defaultConfigFactory |
| } |
| |
| private fun createUseCase( |
| captureType: UseCaseConfigFactory.CaptureType, |
| targetFrameRate: Range<Int>? = null, |
| dynamicRange: DynamicRange? = DynamicRange.UNSPECIFIED |
| ): UseCase { |
| val builder = FakeUseCaseConfig.Builder( |
| captureType, when (captureType) { |
| UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE -> ImageFormat.JPEG |
| UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS -> ImageFormat.YUV_420_888 |
| else -> ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE |
| } |
| ) |
| targetFrameRate?.let { |
| builder.mutableConfig.insertOption(UseCaseConfig.OPTION_TARGET_FRAME_RATE, it) |
| } |
| builder.mutableConfig.insertOption( |
| ImageInputConfig.OPTION_INPUT_DYNAMIC_RANGE, |
| dynamicRange |
| ) |
| return builder.build() |
| } |
| |
| private fun createRawUseCase(): UseCase { |
| val builder = FakeUseCaseConfig.Builder() |
| builder.mutableConfig.insertOption( |
| UseCaseConfig.OPTION_INPUT_FORMAT, |
| ImageFormat.RAW_SENSOR |
| ) |
| return builder.build() |
| } |
| } |