[go: nahoru, domu]

blob: ed4755d1043efb51c55b80579e6c5fb7ff528766 [file] [log] [blame]
/*
* 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.internal
import android.content.Context
import android.content.pm.PackageManager.FEATURE_CAMERA_CONCURRENT
import android.graphics.ImageFormat
import android.graphics.SurfaceTexture
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.hardware.camera2.CameraMetadata
import android.hardware.camera2.params.StreamConfigurationMap
import android.media.CamcorderProfile
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.Camera2Config
import androidx.camera.camera2.internal.compat.CameraManagerCompat
import androidx.camera.core.CameraSelector.LensFacing
import androidx.camera.core.CameraX
import androidx.camera.core.CameraXConfig
import androidx.camera.core.UseCase
import androidx.camera.core.impl.AttachedSurfaceInfo
import androidx.camera.core.impl.CameraDeviceSurfaceManager
import androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
import androidx.camera.core.impl.SurfaceCombination
import androidx.camera.core.impl.SurfaceConfig
import androidx.camera.core.impl.SurfaceConfig.ConfigSize
import androidx.camera.core.impl.SurfaceConfig.ConfigType
import androidx.camera.core.impl.UseCaseConfig
import androidx.camera.core.impl.UseCaseConfigFactory
import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType
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.CameraUtil
import androidx.camera.testing.CameraXUtil
import androidx.camera.testing.EncoderProfilesUtil
import androidx.camera.testing.fakes.FakeCamera
import androidx.camera.testing.fakes.FakeCameraFactory
import androidx.camera.testing.fakes.FakeCameraInfoInternal
import androidx.camera.testing.fakes.FakeEncoderProfilesProvider
import androidx.camera.testing.fakes.FakeUseCaseConfig
import androidx.test.core.app.ApplicationProvider
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit
import org.codehaus.plexus.util.ReflectionUtils
import org.junit.After
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers
import org.mockito.Mockito
import org.robolectric.RobolectricTestRunner
import org.robolectric.Shadows.shadowOf
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
private const val DEFAULT_CAMERA_ID = "0"
private const val EXTERNAL_CAMERA_ID = "0-external"
private const val SENSOR_ORIENTATION_90 = 90
private val LANDSCAPE_PIXEL_ARRAY_SIZE = Size(4032, 3024)
private val DISPLAY_SIZE = Size(720, 1280)
private val PREVIEW_SIZE = Size(1280, 720)
private val RECORD_SIZE = Size(3840, 2160)
private val MAXIMUM_SIZE = Size(4032, 3024)
private val LEGACY_VIDEO_MAXIMUM_SIZE = Size(1920, 1080)
private val DEFAULT_SUPPORTED_SIZES = 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(960, 544), // a mod16 version of resolution with 16:9 aspect ratio.
Size(800, 450), // 16:9
Size(640, 480), // 4:3
)
/** Robolectric test for [SupportedSurfaceCombination] class */
@RunWith(RobolectricTestRunner::class)
@DoNotInstrument
@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
class SupportedSurfaceCombinationTest {
private val mockCamcorderProfileHelper = Mockito.mock(
CamcorderProfileHelper::class.java
)
private val mockCamcorderProfile = Mockito.mock(
CamcorderProfile::class.java
)
private var cameraManagerCompat: CameraManagerCompat? = null
private val profileUhd = EncoderProfilesUtil.createFakeEncoderProfilesProxy(
RECORD_SIZE.width, RECORD_SIZE.height
)
private val profileFhd = EncoderProfilesUtil.createFakeEncoderProfilesProxy(
1920, 1080
)
private val profileHd = EncoderProfilesUtil.createFakeEncoderProfilesProxy(
PREVIEW_SIZE.width, PREVIEW_SIZE.height
)
private val profileSd = EncoderProfilesUtil.createFakeEncoderProfilesProxy(
RESOLUTION_VGA.width, RESOLUTION_VGA.height
)
private val context = ApplicationProvider.getApplicationContext<Context>()
private var cameraFactory: FakeCameraFactory? = null
private var useCaseConfigFactory: UseCaseConfigFactory? = null
private lateinit var cameraDeviceSurfaceManager: CameraDeviceSurfaceManager
@Suppress("DEPRECATION") // defaultDisplay
@Before
fun setUp() {
DisplayInfoManager.releaseInstance()
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
shadowOf(windowManager.defaultDisplay).setRealWidth(DISPLAY_SIZE.width)
shadowOf(windowManager.defaultDisplay).setRealHeight(DISPLAY_SIZE.height)
Mockito.`when`(
mockCamcorderProfileHelper.hasProfile(
ArgumentMatchers.anyInt(),
ArgumentMatchers.anyInt()
)
).thenReturn(true)
ReflectionUtils.setVariableValueInObject(mockCamcorderProfile, "videoFrameWidth", 3840)
ReflectionUtils.setVariableValueInObject(mockCamcorderProfile, "videoFrameHeight", 2160)
Mockito.`when`(
mockCamcorderProfileHelper[ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt()]
).thenReturn(mockCamcorderProfile)
}
@After
fun tearDown() {
CameraXUtil.shutdown()[10000, TimeUnit.MILLISECONDS]
}
// //////////////////////////////////////////////////////////////////////////////////////////
//
// Surface combination support tests for guaranteed configurations
//
// //////////////////////////////////////////////////////////////////////////////////////////
@Test
fun checkLegacySurfaceCombinationSupportedInLegacyDevice() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getLegacySupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(
false, it.surfaceConfigList)).isTrue()
}
}
@Test
fun checkLegacySurfaceCombinationSubListSupportedInLegacyDevice() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getLegacySupportedCombinationList().also {
assertThat(isAllSubConfigListSupported(false, supportedSurfaceCombination, it)).isTrue()
}
}
@Test
fun checkLimitedSurfaceCombinationNotSupportedInLegacyDevice() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getLimitedSupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(
false, it.surfaceConfigList)).isFalse()
}
}
@Test
fun checkFullSurfaceCombinationNotSupportedInLegacyDevice() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getFullSupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(
false, it.surfaceConfigList)).isFalse()
}
}
@Test
fun checkLevel3SurfaceCombinationNotSupportedInLegacyDevice() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getLevel3SupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(
false, it.surfaceConfigList)).isFalse()
}
}
@Test
fun checkLimitedSurfaceCombinationSupportedInLimitedDevice() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getLimitedSupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(
false, it.surfaceConfigList)).isTrue()
}
}
@Test
fun checkLimitedSurfaceCombinationSubListSupportedInLimited3Device() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getLimitedSupportedCombinationList().also {
assertThat(isAllSubConfigListSupported(false, supportedSurfaceCombination, it)).isTrue()
}
}
@Test
fun checkFullSurfaceCombinationNotSupportedInLimitedDevice() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getFullSupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(
false, it.surfaceConfigList)).isFalse()
}
}
@Test
fun checkLevel3SurfaceCombinationNotSupportedInLimitedDevice() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getLevel3SupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(
false, it.surfaceConfigList)).isFalse()
}
}
@Test
fun checkFullSurfaceCombinationSupportedInFullDevice() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getFullSupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(
false, it.surfaceConfigList)).isTrue()
}
}
@Test
fun checkFullSurfaceCombinationSubListSupportedInFullDevice() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getFullSupportedCombinationList().also {
assertThat(isAllSubConfigListSupported(false, supportedSurfaceCombination, it)).isTrue()
}
}
@Test
fun checkLevel3SurfaceCombinationNotSupportedInFullDevice() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getLevel3SupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(
false, it.surfaceConfigList)).isFalse()
}
}
@Test
fun checkLimitedSurfaceCombinationSupportedInRawDevice() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
capabilities = intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_RAW)
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getLimitedSupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(
false, it.surfaceConfigList)).isTrue()
}
}
@Test
fun checkLegacySurfaceCombinationSupportedInRawDevice() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
capabilities = intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_RAW)
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getLegacySupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(
false, it.surfaceConfigList)).isTrue()
}
}
@Test
fun checkFullSurfaceCombinationSupportedInRawDevice() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
capabilities = intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_RAW)
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getFullSupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(
false, it.surfaceConfigList)).isTrue()
}
}
@Test
fun checkRawSurfaceCombinationSupportedInRawDevice() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
capabilities = intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_RAW)
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getRAWSupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(
false, it.surfaceConfigList)).isTrue()
}
}
@Test
fun checkLevel3SurfaceCombinationSupportedInLevel3Device() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getLevel3SupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(
false, it.surfaceConfigList)).isTrue()
}
}
@Test
fun checkLevel3SurfaceCombinationSubListSupportedInLevel3Device() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getLevel3SupportedCombinationList().also {
assertThat(isAllSubConfigListSupported(false, supportedSurfaceCombination, it)).isTrue()
}
}
@Test
fun checkConcurrentSurfaceCombinationSupportedInConcurrentCameraMode() {
shadowOf(context.packageManager).setSystemFeature(
FEATURE_CAMERA_CONCURRENT, true)
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getConcurrentSupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(
true, it.surfaceConfigList)).isTrue()
}
}
@Test
fun checkConcurrentSurfaceCombinationSubListSupportedInConcurrentCameraMode() {
shadowOf(context.packageManager).setSystemFeature(
FEATURE_CAMERA_CONCURRENT, true)
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getConcurrentSupportedCombinationList().also {
assertThat(isAllSubConfigListSupported(
true, supportedSurfaceCombination, it)).isTrue()
}
}
// //////////////////////////////////////////////////////////////////////////////////////////
//
// Surface config transformation tests
//
// //////////////////////////////////////////////////////////////////////////////////////////
@Test
fun transformSurfaceConfigWithYUVAnalysisSize() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
false,
ImageFormat.YUV_420_888, RESOLUTION_VGA
)
val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.YUV, ConfigSize.VGA)
assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
}
@Test
fun transformSurfaceConfigWithYUVPreviewSize() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
false,
ImageFormat.YUV_420_888, PREVIEW_SIZE
)
val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW)
assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
}
@Test
fun transformSurfaceConfigWithYUVRecordSize() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
false,
ImageFormat.YUV_420_888, RECORD_SIZE
)
val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.YUV, ConfigSize.RECORD)
assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
}
@Test
fun transformSurfaceConfigWithYUVMaximumSize() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
false,
ImageFormat.YUV_420_888, MAXIMUM_SIZE
)
val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.YUV, ConfigSize.MAXIMUM)
assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
}
@Test
fun transformSurfaceConfigWithJPEGAnalysisSize() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
false,
ImageFormat.JPEG, RESOLUTION_VGA
)
val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.JPEG, ConfigSize.VGA)
assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
}
@Test
fun transformSurfaceConfigWithJPEGPreviewSize() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
false,
ImageFormat.JPEG, PREVIEW_SIZE
)
val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.JPEG, ConfigSize.PREVIEW)
assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
}
@Test
fun transformSurfaceConfigWithJPEGRecordSize() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
false,
ImageFormat.JPEG, RECORD_SIZE
)
val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.JPEG, ConfigSize.RECORD)
assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
}
@Test
fun transformSurfaceConfigWithJPEGMaximumSize() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
false,
ImageFormat.JPEG, MAXIMUM_SIZE
)
val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM)
assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
}
@Test
fun transformSurfaceConfigWithPRIVS720PSizeInConcurrentMode() {
shadowOf(context.packageManager).setSystemFeature(FEATURE_CAMERA_CONCURRENT, true)
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
true,
ImageFormat.PRIVATE, RESOLUTION_720P
)
val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.PRIV, ConfigSize.s720p)
assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
}
@Test
fun transformSurfaceConfigWithYUVS720PSizeInConcurrentMode() {
shadowOf(context.packageManager).setSystemFeature(FEATURE_CAMERA_CONCURRENT, true)
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
true,
ImageFormat.YUV_420_888, RESOLUTION_720P
)
val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.YUV, ConfigSize.s720p)
assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
}
@Test
fun transformSurfaceConfigWithJPEGS720PSizeInConcurrentMode() {
shadowOf(context.packageManager).setSystemFeature(FEATURE_CAMERA_CONCURRENT, true)
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
true,
ImageFormat.JPEG, RESOLUTION_720P
)
val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.JPEG, ConfigSize.s720p)
assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
}
@Test
fun transformSurfaceConfigWithPRIVS1440PSizeInConcurrentMode() {
shadowOf(context.packageManager).setSystemFeature(FEATURE_CAMERA_CONCURRENT, true)
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
true,
ImageFormat.PRIVATE, RESOLUTION_1440P
)
val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.PRIV, ConfigSize.s1440p)
assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
}
@Test
fun transformSurfaceConfigWithYUVS1440PSizeInConcurrentMode() {
shadowOf(context.packageManager).setSystemFeature(FEATURE_CAMERA_CONCURRENT, true)
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
true,
ImageFormat.YUV_420_888, RESOLUTION_1440P
)
val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.YUV, ConfigSize.s1440p)
assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
}
@Test
fun transformSurfaceConfigWithJPEGS1440PSizeInConcurrentMode() {
shadowOf(context.packageManager).setSystemFeature(FEATURE_CAMERA_CONCURRENT, true)
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
true,
ImageFormat.JPEG, RESOLUTION_1440P
)
val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.JPEG, ConfigSize.s1440p)
assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
}
// //////////////////////////////////////////////////////////////////////////////////////////
//
// Resolution selection tests for LEGACY-level guaranteed configurations
//
// //////////////////////////////////////////////////////////////////////////////////////////
/**
* PRIV/MAXIMUM
*/
@Test
fun canSelectCorrectSize_singlePrivStream_inLegacyDevice() {
val privUseCase = createUseCase(CaptureType.VIDEO_CAPTURE)
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase, MAXIMUM_SIZE)
}
getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
}
/**
* JPEG/MAXIMUM
*/
@Test
fun canSelectCorrectSize_singleJpegStream_inLegacyDevice() {
val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE)
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(jpegUseCase, MAXIMUM_SIZE)
}
getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
}
/**
* YUV/MAXIMUM
*/
@Test
fun canSelectCorrectSize_singleYuvStream_inLegacyDevice() {
val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS)
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(yuvUseCase, MAXIMUM_SIZE)
}
getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
}
/**
* PRIV/PREVIEW + JPEG/MAXIMUM
*/
@Test
fun canSelectCorrectSizes_privPlusJpeg_inLegacyDevice() {
val privUseCase = createUseCase(CaptureType.PREVIEW)
val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE)
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase, PREVIEW_SIZE)
put(jpegUseCase, MAXIMUM_SIZE)
}
getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
}
/**
* YUV/PREVIEW + JPEG/MAXIMUM
*/
@Test
fun canSelectCorrectSizes_yuvPlusJpeg_inLegacyDevice() {
val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(yuvUseCase, PREVIEW_SIZE)
put(jpegUseCase, MAXIMUM_SIZE)
}
getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
}
/**
* PRIV/PREVIEW + PRIV/PREVIEW
*/
@Test
fun canSelectCorrectSizes_privPlusPriv_inLegacyDevice() {
val privUseCase1 = createUseCase(CaptureType.PREVIEW) // PRIV
val privUseCase2 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase1, PREVIEW_SIZE)
put(privUseCase2, PREVIEW_SIZE)
}
getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
}
/**
* PRIV/PREVIEW + YUV/PREVIEW
*/
@Test
fun canSelectCorrectSizes_privPlusYuv_inLegacyDevice() {
val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase, PREVIEW_SIZE)
put(yuvUseCase, PREVIEW_SIZE)
}
getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
}
/**
* PRIV/PREVIEW + YUV/PREVIEW + JPEG/MAXIMUM
*/
@Test
fun canSelectCorrectSizes_privPlusYuvPlusJpeg_inLegacyDevice() {
val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase, PREVIEW_SIZE)
put(yuvUseCase, PREVIEW_SIZE)
put(jpegUseCase, MAXIMUM_SIZE)
}
getSuggestedSpecsAndVerify(useCaseExpectedResultMap)
}
/**
* Unsupported PRIV + JPEG + PRIV for legacy level devices
*/
@Test
fun throwsException_unsupportedConfiguration_inLegacyDevice() {
val privUseCase1 = createUseCase(CaptureType.PREVIEW) // PRIV
val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
val privUseCas2 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase1, RESOLUTION_VGA)
put(jpegUseCase, RESOLUTION_VGA)
put(privUseCas2, RESOLUTION_VGA)
}
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(CaptureType.VIDEO_CAPTURE) // PRIV
val privUseCas2 = createUseCase(CaptureType.PREVIEW) // PRIV
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase1, RECORD_SIZE)
put(privUseCas2, PREVIEW_SIZE)
}
getSuggestedSpecsAndVerify(
useCaseExpectedResultMap,
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
}
/**
* PRIV/PREVIEW + YUV/RECORD
*/
@Test
fun canSelectCorrectSizes_privPlusYuv_inLimitedDevice() {
val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase, PREVIEW_SIZE)
put(yuvUseCase, RECORD_SIZE)
}
getSuggestedSpecsAndVerify(
useCaseExpectedResultMap,
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
}
/**
* YUV/PREVIEW + YUV/RECORD
*/
@Test
fun canSelectCorrectSizes_yuvPlusYuv_inLimitedDevice() {
val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(yuvUseCase1, RECORD_SIZE)
put(yuvUseCase2, PREVIEW_SIZE)
}
getSuggestedSpecsAndVerify(
useCaseExpectedResultMap,
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
}
/**
* PRIV/PREVIEW + PRIV/RECORD + JPEG/RECORD
*/
@Test
fun canSelectCorrectSizes_privPlusPrivPlusJpeg_inLimitedDevice() {
val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase1, RECORD_SIZE)
put(privUseCase2, PREVIEW_SIZE)
put(jpegUseCase, RECORD_SIZE)
}
getSuggestedSpecsAndVerify(
useCaseExpectedResultMap,
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
}
/**
* PRIV/PREVIEW + YUV/RECORD + JPEG/RECORD
*/
@Test
fun canSelectCorrectSizes_privPlusYuvPlusJpeg_inLimitedDevice() {
val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase, PREVIEW_SIZE)
put(yuvUseCase, RECORD_SIZE)
put(jpegUseCase, RECORD_SIZE)
}
getSuggestedSpecsAndVerify(
useCaseExpectedResultMap,
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
}
/**
* YUV/PREVIEW + YUV/PREVIEW + JPEG/MAXIMUM
*/
@Test
fun canSelectCorrectSizes_yuvPlusYuvPlusJpeg_inLimitedDevice() {
val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(yuvUseCase1, PREVIEW_SIZE)
put(yuvUseCase2, PREVIEW_SIZE)
put(jpegUseCase, MAXIMUM_SIZE)
}
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(CaptureType.IMAGE_ANALYSIS) // YUV
val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(yuvUseCase1, RESOLUTION_VGA)
put(privUseCase, RESOLUTION_VGA)
put(yuvUseCase2, RESOLUTION_VGA)
}
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(CaptureType.VIDEO_CAPTURE) // PRIV
val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase1, MAXIMUM_SIZE)
put(privUseCase2, PREVIEW_SIZE)
}
getSuggestedSpecsAndVerify(
useCaseExpectedResultMap,
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
)
}
/**
* PRIV/PREVIEW + YUV/MAXIMUM
*/
@Test
fun canSelectCorrectSizes_privPlusYuv_inFullDevice() {
val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase, PREVIEW_SIZE)
put(yuvUseCase, MAXIMUM_SIZE)
}
getSuggestedSpecsAndVerify(
useCaseExpectedResultMap,
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
)
}
/**
* YUV/PREVIEW + YUV/MAXIMUM
*/
@Test
fun canSelectCorrectSizes_yuvPlusYuv_inFullDevice() {
val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(yuvUseCase1, MAXIMUM_SIZE)
put(yuvUseCase2, PREVIEW_SIZE)
}
getSuggestedSpecsAndVerify(
useCaseExpectedResultMap,
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
)
}
/**
* PRIV/PREVIEW + PRIV/PREVIEW + JPEG/MAXIMUM
*/
@Test
fun canSelectCorrectSizes_privPlusPrivPlusJpeg_inFullDevice() {
val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(jpegUseCase, MAXIMUM_SIZE)
put(privUseCase1, PREVIEW_SIZE)
put(privUseCase2, PREVIEW_SIZE)
}
getSuggestedSpecsAndVerify(
useCaseExpectedResultMap,
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
)
}
/**
* YUV/VGA + PRIV/PREVIEW + YUV/MAXIMUM
*/
@Test
fun canSelectCorrectSizes_yuvPlusPrivPlusYuv_inFullDevice() {
val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase, PREVIEW_SIZE)
put(yuvUseCase1, MAXIMUM_SIZE)
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(CaptureType.IMAGE_ANALYSIS) // YUV
val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val yuvUseCase3 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(yuvUseCase1, MAXIMUM_SIZE)
put(yuvUseCase2, PREVIEW_SIZE)
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(CaptureType.VIDEO_CAPTURE) // PRIV
val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
val yuvUseCase = createUseCase(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(CaptureType.VIDEO_CAPTURE) // PRIV
val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val rawUseCase = createRawUseCase() // RAW
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase1, PREVIEW_SIZE)
put(privUseCase2, RESOLUTION_VGA)
put(yuvUseCase, MAXIMUM_SIZE)
put(rawUseCase, MAXIMUM_SIZE)
}
getSuggestedSpecsAndVerify(
useCaseExpectedResultMap,
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
)
}
/**
* PRIV/PREVIEW + PRIV/VGA + JPEG/MAXIMUM + RAW/MAXIMUM
*/
@Test
fun canSelectCorrectSizes_privPlusPrivPlusJpegPlusRaw_inLevel3Device() {
val privUseCase1 = createUseCase(CaptureType.VIDEO_CAPTURE) // PRIV
val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
val rawUseCase = createRawUseCase() // RAW
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase1, PREVIEW_SIZE)
put(privUseCase2, RESOLUTION_VGA)
put(jpegUseCase, MAXIMUM_SIZE)
put(rawUseCase, MAXIMUM_SIZE)
}
getSuggestedSpecsAndVerify(
useCaseExpectedResultMap,
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
)
}
/**
* Unsupported PRIV + YUV + YUV + RAW for level-3 devices
*/
@Test
fun throwsException_unsupportedConfiguration_inLevel3Device() {
val privUseCase = createUseCase(CaptureType.PREVIEW) // PRIV
val yuvUseCase1 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val yuvUseCase2 = createUseCase(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)
}
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(CaptureType.VIDEO_CAPTURE) // PRIV
val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase1, MAXIMUM_SIZE)
put(privUseCase2, PREVIEW_SIZE)
}
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(CaptureType.PREVIEW) // PRIV
val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase, PREVIEW_SIZE)
put(yuvUseCase, MAXIMUM_SIZE)
}
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(CaptureType.IMAGE_ANALYSIS) // YUV
val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(yuvUseCase1, MAXIMUM_SIZE)
put(yuvUseCase2, PREVIEW_SIZE)
}
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, MAXIMUM_SIZE)
}
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(CaptureType.PREVIEW) // PRIV
val rawUseCase = createRawUseCase() // RAW
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase, PREVIEW_SIZE)
put(rawUseCase, MAXIMUM_SIZE)
}
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(CaptureType.VIDEO_CAPTURE) // PRIV
val privUseCase2 = createUseCase(CaptureType.PREVIEW) // PRIV
val rawUseCase = createRawUseCase() // RAW
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase1, PREVIEW_SIZE)
put(privUseCase2, PREVIEW_SIZE)
put(rawUseCase, MAXIMUM_SIZE)
}
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(CaptureType.PREVIEW) // PRIV
val yuvUseCase = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val rawUseCase = createRawUseCase() // RAW
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase, PREVIEW_SIZE)
put(yuvUseCase, PREVIEW_SIZE)
put(rawUseCase, MAXIMUM_SIZE)
}
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(CaptureType.IMAGE_ANALYSIS) // YUV
val yuvUseCase2 = createUseCase(CaptureType.IMAGE_ANALYSIS) // YUV
val rawUseCase = createRawUseCase() // RAW
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(yuvUseCase1, PREVIEW_SIZE)
put(yuvUseCase2, PREVIEW_SIZE)
put(rawUseCase, MAXIMUM_SIZE)
}
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(CaptureType.PREVIEW) // PRIV
val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
val rawUseCase = createRawUseCase() // RAW
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(privUseCase, PREVIEW_SIZE)
put(jpegUseCase, MAXIMUM_SIZE)
put(rawUseCase, MAXIMUM_SIZE)
}
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(CaptureType.IMAGE_ANALYSIS) // YUV
val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE) // JPEG
val rawUseCase = createRawUseCase() // RAW
val useCaseExpectedResultMap = mutableMapOf<UseCase, Size>().apply {
put(yuvUseCase, PREVIEW_SIZE)
put(jpegUseCase, MAXIMUM_SIZE)
put(rawUseCase, MAXIMUM_SIZE)
}
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
) {
setupCameraAndInitCameraX(
hardwareLevel = hardwareLevel,
capabilities = capabilities
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCaseConfigMap = getUseCaseToConfigMap(useCasesExpectedResultMap.keys.toList())
val useCaseConfigToOutputSizesMap =
getUseCaseConfigToOutputSizesMap(useCaseConfigMap.values.toList())
val suggestedStreamSpecs = supportedSurfaceCombination.getSuggestedStreamSpecifications(
false,
attachedSurfaceInfoList,
useCaseConfigToOutputSizesMap
)
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()
}
}
}
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, DEFAULT_SUPPORTED_SIZES.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)
}
// //////////////////////////////////////////////////////////////////////////////////////////
//
// 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(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 getSupportedOutputSizes_single_invalid_targetFPS() {
// an invalid target means the device would neve be able to reach that fps
val useCase = createUseCase(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 getSupportedOutputSizes_multiple_targetFPS_first_is_larger() {
// a valid target means the device is capable of that fps
val useCase1 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(30, 35))
val useCase2 = createUseCase(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 getSupportedOutputSizes_multiple_targetFPS_first_is_smaller() {
// a valid target means the device is capable of that fps
val useCase1 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(30, 35))
val useCase2 = createUseCase(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 getSupportedOutputSizes_multiple_targetFPS_intersect() {
// first and second new use cases have target fps that intersect each other
val useCase1 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(30, 40))
val useCase2 = createUseCase(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 getSupportedOutputSizes_multiple_cases_first_has_targetFPS() {
// first new use case has a target fps, second new use case does not
val useCase1 = createUseCase(CaptureType.PREVIEW, targetFrameRate = Range<Int>(30, 35))
val useCase2 = createUseCase(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 getSupportedOutputSizes_multiple_cases_second_has_targetFPS() {
// second new use case does not have a target fps, first new use case does not
val useCase1 = createUseCase(CaptureType.PREVIEW)
val useCase2 = createUseCase(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 getSupportedOutputSizes_attached_with_targetFPS_no_new_targetFPS() {
// existing surface with target fps + new use case without a target fps
val useCase = createUseCase(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(
ConfigType.JPEG,
ConfigSize.PREVIEW
), ImageFormat.JPEG,
Size(1280, 720), Range(40, 50)
)
getSuggestedSpecsAndVerify(
useCaseExpectedResultMap,
attachedSurfaceInfoList = listOf(attachedSurfaceInfo),
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
compareWithAtMost = true
)
}
@Test
fun getSupportedOutputSizes_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(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(
ConfigType.JPEG,
ConfigSize.PREVIEW
), ImageFormat.JPEG,
Size(1280, 720), Range(40, 50)
)
getSuggestedSpecsAndVerify(
useCaseExpectedResultMap,
attachedSurfaceInfoList = listOf(attachedSurfaceInfo),
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
compareWithAtMost = true
)
}
@Test
fun getSupportedOutputSizes_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(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(
ConfigType.JPEG,
ConfigSize.PREVIEW
), ImageFormat.JPEG,
Size(1280, 720), Range(40, 50)
)
getSuggestedSpecsAndVerify(
useCaseExpectedResultMap,
attachedSurfaceInfoList = listOf(attachedSurfaceInfo),
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
compareWithAtMost = true
)
}
// //////////////////////////////////////////////////////////////////////////////////////////
//
// Other tests
//
// //////////////////////////////////////////////////////////////////////////////////////////
@Test
fun getMaximumSizeForImageFormat() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val maximumYUVSize =
supportedSurfaceCombination.getMaxOutputSizeByFormat(ImageFormat.YUV_420_888)
assertThat(maximumYUVSize).isEqualTo(MAXIMUM_SIZE)
val maximumJPEGSize = supportedSurfaceCombination.getMaxOutputSizeByFormat(ImageFormat.JPEG)
assertThat(maximumJPEGSize).isEqualTo(MAXIMUM_SIZE)
}
@Test
fun determineRecordSizeFromStreamConfigurationMap() {
// Setup camera with non-integer camera Id
setupCameraAndInitCameraX(cameraId = EXTERNAL_CAMERA_ID)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, EXTERNAL_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
// Checks the determined RECORD size
assertThat(
supportedSurfaceCombination.mSurfaceSizeDefinition.recordSize
).isEqualTo(
LEGACY_VIDEO_MAXIMUM_SIZE
)
}
/**
* Sets up camera according to the specified settings and initialize [CameraX].
*
* @param cameraId the camera id to be set up. Default value is [DEFAULT_CAMERA_ID].
* @param hardwareLevel the hardware level of the camera. Default value is
* [CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY].
* @param sensorOrientation the sensor orientation of the camera. Default value is
* [SENSOR_ORIENTATION_90].
* @param pixelArraySize the active pixel array size of the camera. Default value is
* [LANDSCAPE_PIXEL_ARRAY_SIZE].
* @param supportedSizes the supported sizes of the camera. Default value is
* [DEFAULT_SUPPORTED_SIZES].
* @param capabilities the capabilities of the camera. Default value is null.
*/
private fun setupCameraAndInitCameraX(
cameraId: String = DEFAULT_CAMERA_ID,
hardwareLevel: Int = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
sensorOrientation: Int = SENSOR_ORIENTATION_90,
pixelArraySize: Size = LANDSCAPE_PIXEL_ARRAY_SIZE,
supportedSizes: Array<Size> = DEFAULT_SUPPORTED_SIZES,
supportedHighResolutionSizes: Array<Size>? = null,
capabilities: IntArray? = null
) {
setupCamera(
cameraId,
hardwareLevel,
sensorOrientation,
pixelArraySize,
supportedSizes,
supportedHighResolutionSizes,
capabilities
)
@LensFacing val lensFacingEnum = CameraUtil.getLensFacingEnumFromInt(
CameraCharacteristics.LENS_FACING_BACK
)
cameraManagerCompat = CameraManagerCompat.from(context)
val cameraInfo = FakeCameraInfoInternal(
cameraId,
sensorOrientation,
CameraCharacteristics.LENS_FACING_BACK
).apply {
encoderProfilesProvider = FakeEncoderProfilesProvider.Builder()
.add(QUALITY_2160P, profileUhd)
.add(QUALITY_1080P, profileFhd)
.add(QUALITY_720P, profileHd)
.add(QUALITY_480P, profileSd)
.build()
}
cameraFactory = FakeCameraFactory().apply {
insertCamera(lensFacingEnum, cameraId) {
FakeCamera(cameraId, null, cameraInfo)
}
}
initCameraX()
}
/**
* Sets up camera according to the specified settings.
*
* @param cameraId the camera id to be set up. Default value is [DEFAULT_CAMERA_ID].
* @param hardwareLevel the hardware level of the camera. Default value is
* [CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY].
* @param sensorOrientation the sensor orientation of the camera. Default value is
* [SENSOR_ORIENTATION_90].
* @param pixelArraySize the active pixel array size of the camera. Default value is
* [LANDSCAPE_PIXEL_ARRAY_SIZE].
* @param supportedSizes the supported sizes of the camera. Default value is
* [DEFAULT_SUPPORTED_SIZES].
* @param capabilities the capabilities of the camera. Default value is null.
*/
fun setupCamera(
cameraId: String = DEFAULT_CAMERA_ID,
hardwareLevel: Int = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
sensorOrientation: Int = SENSOR_ORIENTATION_90,
pixelArraySize: Size = LANDSCAPE_PIXEL_ARRAY_SIZE,
supportedSizes: Array<Size> = DEFAULT_SUPPORTED_SIZES,
supportedHighResolutionSizes: Array<Size>? = null,
capabilities: IntArray? = null
) {
val mockMap = Mockito.mock(StreamConfigurationMap::class.java).also {
// Sets up the supported sizes
Mockito.`when`(it.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.
Mockito.`when`(it.getOutputSizes(SurfaceTexture::class.java))
.thenReturn(supportedSizes)
// This is setup for the test to determine RECORD size from StreamConfigurationMap
Mockito.`when`(it.getOutputSizes(MediaRecorder::class.java))
.thenReturn(supportedSizes)
// setup to return different minimum frame durations depending on resolution
// minimum frame durations were designated only for the purpose of testing
Mockito.`when`(it.getOutputMinFrameDuration(
ArgumentMatchers.anyInt(),
ArgumentMatchers.eq(Size(4032, 3024))
))
.thenReturn(50000000L) // 20 fps, size maximum
Mockito.`when`(it.getOutputMinFrameDuration(
ArgumentMatchers.anyInt(),
ArgumentMatchers.eq(Size(3840, 2160))
))
.thenReturn(40000000L) // 25, size record
Mockito.`when`(it.getOutputMinFrameDuration(
ArgumentMatchers.anyInt(),
ArgumentMatchers.eq(Size(1920, 1440))
))
.thenReturn(30000000L) // 30
Mockito.`when`(it.getOutputMinFrameDuration(
ArgumentMatchers.anyInt(),
ArgumentMatchers.eq(Size(1920, 1080))
))
.thenReturn(28000000L) // 35
Mockito.`when`(it.getOutputMinFrameDuration(
ArgumentMatchers.anyInt(),
ArgumentMatchers.eq(Size(1280, 960))
))
.thenReturn(25000000L) // 40
Mockito.`when`(it.getOutputMinFrameDuration(
ArgumentMatchers.anyInt(),
ArgumentMatchers.eq(Size(1280, 720))
))
.thenReturn(22000000L) // 45, size preview/display
Mockito.`when`(it.getOutputMinFrameDuration(
ArgumentMatchers.anyInt(),
ArgumentMatchers.eq(Size(960, 544))
))
.thenReturn(20000000L) // 50
Mockito.`when`(it.getOutputMinFrameDuration(
ArgumentMatchers.anyInt(),
ArgumentMatchers.eq(Size(800, 450))
))
.thenReturn(16666000L) // 60fps
Mockito.`when`(it.getOutputMinFrameDuration(
ArgumentMatchers.anyInt(),
ArgumentMatchers.eq(Size(640, 480))
))
.thenReturn(16666000L) // 60fps
// Sets up the supported high resolution sizes
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Mockito.`when`(it.getHighResolutionOutputSizes(ArgumentMatchers.anyInt()))
.thenReturn(supportedHighResolutionSizes)
}
}
val characteristics = ShadowCameraCharacteristics.newCameraCharacteristics()
Shadow.extract<ShadowCameraCharacteristics>(characteristics).apply {
set(CameraCharacteristics.LENS_FACING, CameraCharacteristics.LENS_FACING_BACK)
set(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL, hardwareLevel)
set(CameraCharacteristics.SENSOR_ORIENTATION, sensorOrientation)
set(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE, pixelArraySize)
set(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP, mockMap)
capabilities?.let {
set(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES, it)
}
}
val cameraManager = ApplicationProvider.getApplicationContext<Context>()
.getSystemService(Context.CAMERA_SERVICE) as CameraManager
(Shadow.extract<Any>(cameraManager) as ShadowCameraManager)
.addCamera(cameraId, characteristics)
}
/**
* Initializes the [CameraX].
*/
private fun initCameraX() {
val surfaceManagerProvider =
CameraDeviceSurfaceManager.Provider { context, _, availableCameraIds ->
cameraDeviceSurfaceManager = Camera2DeviceSurfaceManager(
context,
mockCamcorderProfileHelper,
CameraManagerCompat.from(this@SupportedSurfaceCombinationTest.context),
availableCameraIds
)
cameraDeviceSurfaceManager
}
val cameraXConfig = CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
.setDeviceSurfaceManagerProvider(surfaceManagerProvider)
.setCameraFactoryProvider { _, _, _ -> 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 isAllSubConfigListSupported(
isConcurrentCameraModeOn: Boolean,
supportedSurfaceCombination: SupportedSurfaceCombination,
combinationList: List<SurfaceCombination>
): Boolean {
combinationList.forEach { combination ->
val configList = combination.surfaceConfigList
val length = configList.size
if (length <= 1) {
return@forEach
}
for (index in 0 until length) {
val subConfigurationList = arrayListOf<SurfaceConfig>().apply {
addAll(configList)
removeAt(index)
}
if (!supportedSurfaceCombination.checkSupported(
isConcurrentCameraModeOn, subConfigurationList
)
) {
return false
}
}
}
return true
}
private fun createUseCase(
captureType: CaptureType,
targetFrameRate: Range<Int>? = null
): UseCase {
val builder = FakeUseCaseConfig.Builder(captureType, when (captureType) {
CaptureType.IMAGE_CAPTURE -> ImageFormat.JPEG
CaptureType.IMAGE_ANALYSIS -> ImageFormat.YUV_420_888
else -> INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
})
targetFrameRate?.let {
builder.mutableConfig.insertOption(UseCaseConfig.OPTION_TARGET_FRAME_RATE, it)
}
return builder.build()
}
private fun createRawUseCase(): UseCase {
val builder = FakeUseCaseConfig.Builder()
builder.mutableConfig.insertOption(
UseCaseConfig.OPTION_INPUT_FORMAT,
ImageFormat.RAW_SENSOR
)
return builder.build()
}
}