blob: 1d8366d5dfa850e3c4c385e9489f6153ba820d5c [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.graphics.ImageFormat
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraMetadata
import android.media.CamcorderProfile
import android.os.Build
import android.util.Pair
import android.util.Rational
import android.util.Size
import android.view.Surface
import android.view.WindowManager
import androidx.annotation.NonNull
import androidx.camera.camera2.Camera2Config
import androidx.camera.camera2.internal.SupportedOutputSizesCollectorTest.Companion.createUseCaseByResolutionSelector
import androidx.camera.camera2.internal.SupportedOutputSizesCollectorTest.Companion.setupCamera
import androidx.camera.camera2.internal.compat.CameraManagerCompat
import androidx.camera.core.AspectRatio
import androidx.camera.core.CameraSelector.LensFacing
import androidx.camera.core.CameraUnavailableException
import androidx.camera.core.CameraX
import androidx.camera.core.CameraXConfig
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageCapture
import androidx.camera.core.Preview
import androidx.camera.core.SurfaceRequest
import androidx.camera.core.UseCase
import androidx.camera.core.impl.CameraDeviceSurfaceManager
import androidx.camera.core.impl.CameraFactory
import androidx.camera.core.impl.MutableStateObservable
import androidx.camera.core.impl.SizeCoordinate
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.utils.AspectRatioUtil.ASPECT_RATIO_4_3
import androidx.camera.core.impl.utils.AspectRatioUtil.hasMatchingAspectRatio
import androidx.camera.core.impl.utils.CompareSizesByArea
import androidx.camera.core.impl.utils.executor.CameraXExecutors
import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_VGA
import androidx.camera.testing.CamcorderProfileUtil
import androidx.camera.testing.CameraUtil
import androidx.camera.testing.CameraXUtil
import androidx.camera.testing.Configs
import androidx.camera.testing.SurfaceTextureProvider
import androidx.camera.testing.SurfaceTextureProvider.SurfaceTextureCallback
import androidx.camera.testing.fakes.FakeCamcorderProfileProvider
import androidx.camera.testing.fakes.FakeCamera
import androidx.camera.testing.fakes.FakeCameraFactory
import androidx.camera.testing.fakes.FakeCameraInfoInternal
import androidx.camera.testing.fakes.FakeUseCaseConfig
import androidx.camera.video.FallbackStrategy
import androidx.camera.video.MediaSpec
import androidx.camera.video.Quality
import androidx.camera.video.QualitySelector
import androidx.camera.video.VideoCapture
import androidx.camera.video.VideoOutput
import androidx.camera.video.VideoOutput.SourceState
import androidx.camera.video.VideoSpec
import androidx.test.core.app.ApplicationProvider
import com.google.common.truth.Truth.assertThat
import java.util.Arrays
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
import org.robolectric.annotation.Config
import org.robolectric.annotation.internal.DoNotInstrument
private const val FAKE_USE_CASE = 0
private const val PREVIEW_USE_CASE = 1
private const val IMAGE_CAPTURE_USE_CASE = 2
private const val IMAGE_ANALYSIS_USE_CASE = 3
private const val UNKNOWN_ROTATION = -1
private const val UNKNOWN_ASPECT_RATIO = -1
private const val DEFAULT_CAMERA_ID = "0"
private const val EXTERNAL_CAMERA_ID = "0-external"
private const val SENSOR_ORIENTATION_0 = 0
private const val SENSOR_ORIENTATION_90 = 90
private val ASPECT_RATIO_16_9 = Rational(16, 9)
private val LANDSCAPE_PIXEL_ARRAY_SIZE = Size(4032, 3024)
private val PORTRAIT_PIXEL_ARRAY_SIZE = Size(3024, 4032)
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 MOD16_SIZE = Size(960, 544)
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
Size(320, 240), // 4:3
Size(320, 180), // 16:9
Size(256, 144) // 16:9 For checkSmallSizesAreFilteredOut test.
)
/** 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 = CamcorderProfileUtil.createCamcorderProfileProxy(
CamcorderProfile.QUALITY_2160P, RECORD_SIZE.width, RECORD_SIZE.height
)
private val profileFhd = CamcorderProfileUtil.createCamcorderProfileProxy(
CamcorderProfile.QUALITY_1080P, 1920, 1080
)
private val profileHd = CamcorderProfileUtil.createCamcorderProfileProxy(
CamcorderProfile.QUALITY_720P, PREVIEW_SIZE.width, PREVIEW_SIZE.height
)
private val profileSd = CamcorderProfileUtil.createCamcorderProfileProxy(
CamcorderProfile.QUALITY_480P, RESOLUTION_VGA.width,
RESOLUTION_VGA.height
)
private val context = ApplicationProvider.getApplicationContext<Context>()
private var cameraFactory: FakeCameraFactory? = null
private var useCaseConfigFactory: UseCaseConfigFactory? = null
private val legacyUseCaseCreator = object : UseCaseCreator {
override fun createUseCase(
useCaseType: Int,
targetRotation: Int,
preferredAspectRatio: Int,
preferredResolution: Size?,
maxResolution: Size?,
highResolutionEnabled: Boolean,
defaultResolution: Size?,
supportedResolutions: List<Pair<Int, Array<Size>>>?
): UseCase {
return createUseCaseByLegacyApi(
useCaseType,
targetRotation,
preferredAspectRatio,
preferredResolution,
maxResolution,
defaultResolution,
supportedResolutions
)
}
}
private val resolutionSelectorUseCaseCreator = object : UseCaseCreator {
override fun createUseCase(
useCaseType: Int,
targetRotation: Int,
preferredAspectRatio: Int,
preferredResolution: Size?,
maxResolution: Size?,
highResolutionEnabled: Boolean,
defaultResolution: Size?,
supportedResolutions: List<Pair<Int, Array<Size>>>?
): UseCase {
return createUseCaseByResolutionSelector(
useCaseType,
preferredAspectRatio,
preferredResolution,
sizeCoordinate = SizeCoordinate.CAMERA_SENSOR,
maxResolution,
highResolutionEnabled,
highResolutionForceDisabled = false,
defaultResolution,
supportedResolutions
)
}
}
private val viewSizeResolutionSelectorUseCaseCreator = object : UseCaseCreator {
override fun createUseCase(
useCaseType: Int,
targetRotation: Int,
preferredAspectRatio: Int,
preferredResolution: Size?,
maxResolution: Size?,
highResolutionEnabled: Boolean,
defaultResolution: Size?,
supportedResolutions: List<Pair<Int, Array<Size>>>?
): UseCase {
return createUseCaseByResolutionSelector(
useCaseType,
preferredAspectRatio,
preferredResolution,
sizeCoordinate = SizeCoordinate.ANDROID_VIEW,
maxResolution,
highResolutionEnabled,
highResolutionForceDisabled = false,
defaultResolution,
supportedResolutions
)
}
}
@Suppress("DEPRECATION") // defaultDisplay
@Before
fun setUp() {
DisplayInfoManager.releaseInstance()
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
Shadows.shadowOf(windowManager.defaultDisplay).setRealWidth(DISPLAY_SIZE.width)
Shadows.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]
}
@Test
fun checkLegacySurfaceCombinationSupportedInLegacyDevice() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getLegacySupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(it.surfaceConfigList)).isTrue()
}
}
@Test
fun checkLegacySurfaceCombinationSubListSupportedInLegacyDevice() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getLegacySupportedCombinationList().also {
assertThat(isAllSubConfigListSupported(supportedSurfaceCombination, it)).isTrue()
}
}
@Test
fun checkLimitedSurfaceCombinationNotSupportedInLegacyDevice() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getLimitedSupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(it.surfaceConfigList)).isFalse()
}
}
@Test
fun checkFullSurfaceCombinationNotSupportedInLegacyDevice() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getFullSupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(it.surfaceConfigList)).isFalse()
}
}
@Test
fun checkLevel3SurfaceCombinationNotSupportedInLegacyDevice() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
GuaranteedConfigurationsUtil.getLevel3SupportedCombinationList().forEach {
assertThat(supportedSurfaceCombination.checkSupported(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(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(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(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(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(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(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(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(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(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(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(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(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(supportedSurfaceCombination, it)).isTrue()
}
}
@Test
fun checkTargetAspectRatioInLegacyDevice_LegacyApi() {
checkTargetAspectRatioInLegacyDevice(legacyUseCaseCreator)
}
@Test
fun checkTargetAspectRatioInLegacyDevice_ResolutionSelector() {
checkTargetAspectRatioInLegacyDevice(resolutionSelectorUseCaseCreator)
}
private fun checkTargetAspectRatioInLegacyDevice(useCaseCreator: UseCaseCreator) {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val targetAspectRatio = ASPECT_RATIO_16_9
val useCase = useCaseCreator.createUseCase(
FAKE_USE_CASE,
preferredAspectRatio = AspectRatio.RATIO_16_9
)
val maxJpegSize = supportedSurfaceCombination.getMaxOutputSizeByFormat(ImageFormat.JPEG)
val maxJpegAspectRatio = Rational(maxJpegSize.width, maxJpegSize.height)
val suggestedResolutionMap = getSuggestedResolutionMap(supportedSurfaceCombination, useCase)
val selectedSize = suggestedResolutionMap[useCase]
val resultAspectRatio = Rational(selectedSize!!.width, selectedSize.height)
// The targetAspectRatio value will only be set to the same aspect ratio as maximum
// supported jpeg size in Legacy + API 21 combination. For other combinations, it should
// keep the original targetAspectRatio set for the use case.
if (Build.VERSION.SDK_INT == 21) {
// Checks targetAspectRatio and maxJpegAspectRatio, which is the ratio of maximum size
// in the mSupportedSizes, are not equal to make sure this test case is valid.
assertThat(targetAspectRatio).isNotEqualTo(maxJpegAspectRatio)
assertThat(resultAspectRatio).isEqualTo(maxJpegAspectRatio)
} else {
// Checks no correction is needed.
assertThat(resultAspectRatio).isEqualTo(targetAspectRatio)
}
}
@Test
fun checkResolutionForMixedUseCase_AfterBindToLifecycle_InLegacyDevice_LegacyApi() {
checkResolutionForMixedUseCase_AfterBindToLifecycle_InLegacyDevice(legacyUseCaseCreator)
}
@Test
fun checkResolutionForMixedUseCase_AfterBindToLifecycle_InLegacyDevice_ResolutionSelector() {
checkResolutionForMixedUseCase_AfterBindToLifecycle_InLegacyDevice(
resolutionSelectorUseCaseCreator
)
}
private fun checkResolutionForMixedUseCase_AfterBindToLifecycle_InLegacyDevice(
useCaseCreator: UseCaseCreator
) {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
// The test case make sure the selected result is expected after the regular flow.
val targetAspectRatio = ASPECT_RATIO_16_9
val preview = useCaseCreator.createUseCase(
PREVIEW_USE_CASE,
preferredAspectRatio = AspectRatio.RATIO_16_9
) as Preview
preview.setSurfaceProvider(
CameraXExecutors.directExecutor(),
SurfaceTextureProvider.createSurfaceTextureProvider(
Mockito.mock(
SurfaceTextureCallback::class.java
)
)
)
val imageCapture = useCaseCreator.createUseCase(
IMAGE_CAPTURE_USE_CASE,
preferredAspectRatio = AspectRatio.RATIO_16_9
)
val imageAnalysis = useCaseCreator.createUseCase(
IMAGE_ANALYSIS_USE_CASE,
preferredAspectRatio = AspectRatio.RATIO_16_9
)
val maxJpegSize = supportedSurfaceCombination.getMaxOutputSizeByFormat(ImageFormat.JPEG)
val maxJpegAspectRatio = Rational(maxJpegSize.width, maxJpegSize.height)
val suggestedResolutionMap = getSuggestedResolutionMap(
supportedSurfaceCombination, preview,
imageCapture, imageAnalysis
)
val previewResolution = suggestedResolutionMap[preview]
val imageCaptureResolution = suggestedResolutionMap[imageCapture]
val imageAnalysisResolution = suggestedResolutionMap[imageAnalysis]
// The targetAspectRatio value will only be set to the same aspect ratio as maximum
// supported jpeg size in Legacy + API 21 combination. For other combinations, it should
// keep the original targetAspectRatio set for the use case.
if (Build.VERSION.SDK_INT == 21) {
// Checks targetAspectRatio and maxJpegAspectRatio, which is the ratio of maximum size
// in the mSupportedSizes, are not equal to make sure this test case is valid.
assertThat(targetAspectRatio).isNotEqualTo(maxJpegAspectRatio)
assertThat(hasMatchingAspectRatio(previewResolution!!, maxJpegAspectRatio)).isTrue()
assertThat(
hasMatchingAspectRatio(
imageCaptureResolution!!,
maxJpegAspectRatio
)
).isTrue()
assertThat(
hasMatchingAspectRatio(
imageAnalysisResolution!!,
maxJpegAspectRatio
)
).isTrue()
} else {
// Checks no correction is needed.
assertThat(
hasMatchingAspectRatio(
previewResolution!!,
targetAspectRatio
)
).isTrue()
assertThat(
hasMatchingAspectRatio(
imageCaptureResolution!!,
targetAspectRatio
)
).isTrue()
assertThat(
hasMatchingAspectRatio(
imageAnalysisResolution!!,
targetAspectRatio
)
).isTrue()
}
}
@Test
fun checkDefaultAspectRatioAndResolutionForMixedUseCase_LegacyApi() {
checkDefaultAspectRatioAndResolutionForMixedUseCase(legacyUseCaseCreator)
}
@Test
fun checkDefaultAspectRatioAndResolutionForMixedUseCase_ResolutionSelector() {
checkDefaultAspectRatioAndResolutionForMixedUseCase(resolutionSelectorUseCaseCreator)
}
private fun checkDefaultAspectRatioAndResolutionForMixedUseCase(
useCaseCreator: UseCaseCreator
) {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val preview = useCaseCreator.createUseCase(PREVIEW_USE_CASE) as Preview
preview.setSurfaceProvider(
CameraXExecutors.directExecutor(),
SurfaceTextureProvider.createSurfaceTextureProvider(
Mockito.mock(
SurfaceTextureCallback::class.java
)
)
)
val imageCapture = useCaseCreator.createUseCase(IMAGE_CAPTURE_USE_CASE)
val imageAnalysis = useCaseCreator.createUseCase(IMAGE_ANALYSIS_USE_CASE)
// Preview/ImageCapture/ImageAnalysis' default config settings that will be applied after
// bound to lifecycle. Calling bindToLifecycle here to make sure sizes matching to
// default aspect ratio will be selected.
val suggestedResolutionMap = getSuggestedResolutionMap(
supportedSurfaceCombination, preview,
imageCapture, imageAnalysis
)
val previewSize = suggestedResolutionMap[preview]!!
val imageCaptureSize = suggestedResolutionMap[imageCapture]!!
val imageAnalysisSize = suggestedResolutionMap[imageAnalysis]!!
val previewAspectRatio = Rational(previewSize.width, previewSize.height)
val imageCaptureAspectRatio = Rational(imageCaptureSize.width, imageCaptureSize.height)
val imageAnalysisAspectRatio = Rational(imageAnalysisSize.width, imageAnalysisSize.height)
// Checks the default aspect ratio.
assertThat(previewAspectRatio).isEqualTo(ASPECT_RATIO_4_3)
assertThat(imageCaptureAspectRatio).isEqualTo(ASPECT_RATIO_4_3)
assertThat(imageAnalysisAspectRatio).isEqualTo(ASPECT_RATIO_4_3)
// Checks the default resolution.
assertThat(imageAnalysisSize).isEqualTo(RESOLUTION_VGA)
}
@Test
fun checkSmallSizesAreFilteredOutByDefaultSize480p() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
/* This test case is for b/139018208 that get small resolution 144x256 with below
conditions:
1. The target aspect ratio is set to the screen size 1080 x 2220 (9:18.5).
2. The camera doesn't provide any 9:18.5 resolution and the size 144x256(9:16)
is considered the 9:18.5 mod16 version.
3. There is no other bigger resolution matched the target aspect ratio.
*/
val displayWidth = 1080
val displayHeight = 2220
val preview = createUseCaseByLegacyApi(
PREVIEW_USE_CASE,
targetResolution = Size(displayHeight, displayWidth)
)
val suggestedResolutionMap = getSuggestedResolutionMap(supportedSurfaceCombination, preview)
// Checks the preconditions.
val preconditionSize = Size(256, 144)
val targetRatio = Rational(displayHeight, displayWidth)
assertThat(listOf(*DEFAULT_SUPPORTED_SIZES)).contains(preconditionSize)
DEFAULT_SUPPORTED_SIZES.forEach {
assertThat(Rational(it.width, it.height)).isNotEqualTo(targetRatio)
}
// Checks the mechanism has filtered out the sizes which are smaller than default size 480p.
val previewSize = suggestedResolutionMap[preview]
assertThat(previewSize).isNotEqualTo(preconditionSize)
}
@Test
fun checkAllSupportedSizesCanBeSelected_LegacyApi() {
checkAllSupportedSizesCanBeSelected(legacyUseCaseCreator)
}
@Test
fun checkAllSupportedSizesCanBeSelected_ResolutionSelector_SensorSize() {
checkAllSupportedSizesCanBeSelected(resolutionSelectorUseCaseCreator)
}
@Test
fun checkAllSupportedSizesCanBeSelected_ResolutionSelector_ViewSize() {
checkAllSupportedSizesCanBeSelected(viewSizeResolutionSelectorUseCaseCreator)
}
private fun checkAllSupportedSizesCanBeSelected(useCaseCreator: UseCaseCreator) {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
// Sets each of mSupportedSizes as target resolution and also sets target rotation as
// Surface.ROTATION to make it aligns the sensor direction and then exactly the same size
// will be selected as the result. This test can also verify that size smaller than
// 640x480 can be selected after set as target resolution.
DEFAULT_SUPPORTED_SIZES.forEach {
val imageCapture = useCaseCreator.createUseCase(
IMAGE_CAPTURE_USE_CASE,
Surface.ROTATION_90,
preferredResolution = it
)
val suggestedResolutionMap =
getSuggestedResolutionMap(supportedSurfaceCombination, imageCapture)
assertThat(it).isEqualTo(suggestedResolutionMap[imageCapture])
}
}
@Test
fun checkCorrectAspectRatioNotMatchedSizeCanBeSelected_LegacyApi() {
// Sets target resolution as 1280x640, all supported resolutions will be put into
// aspect ratio not matched list. Then, 1280x720 will be the nearest matched one.
// Finally, checks whether 1280x720 is selected or not.
checkCorrectAspectRatioNotMatchedSizeCanBeSelected(legacyUseCaseCreator, Size(1280, 720))
}
// 1280x640 is not included in the supported sizes list. So, the smallest size of the
// default aspect ratio 4:3 which is 1280x960 will be finally selected.
@Test
fun checkCorrectAspectRatioNotMatchedSizeCanBeSelected_ResolutionSelector_SensorSize() {
checkCorrectAspectRatioNotMatchedSizeCanBeSelected(
resolutionSelectorUseCaseCreator,
Size(1280, 960)
)
}
@Test
fun checkCorrectAspectRatioNotMatchedSizeCanBeSelected_ResolutionSelector_ViewSize() {
checkCorrectAspectRatioNotMatchedSizeCanBeSelected(
viewSizeResolutionSelectorUseCaseCreator,
Size(1280, 960)
)
}
private fun checkCorrectAspectRatioNotMatchedSizeCanBeSelected(
useCaseCreator: UseCaseCreator,
expectedResult: Size
) {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
// Sets target resolution as 1280x640, all supported resolutions will be put into aspect
// ratio not matched list. Then, 1280x720 will be the nearest matched one. Finally,
// checks whether 1280x720 is selected or not.
val resolution = Size(1280, 640)
val useCase = useCaseCreator.createUseCase(
FAKE_USE_CASE,
Surface.ROTATION_90,
preferredResolution = resolution
)
val suggestedResolutionMap = getSuggestedResolutionMap(supportedSurfaceCombination, useCase)
assertThat(suggestedResolutionMap[useCase]).isEqualTo(expectedResult)
}
@Test
fun suggestedResolutionsForMixedUseCaseNotSupportedInLegacyDevice_LegacyApi() {
suggestedResolutionsForMixedUseCaseNotSupportedInLegacyDevice(legacyUseCaseCreator)
}
@Test
fun suggestedResolutionsForMixedUseCaseNotSupportedInLegacyDevice_ResolutionSelector() {
suggestedResolutionsForMixedUseCaseNotSupportedInLegacyDevice(
resolutionSelectorUseCaseCreator
)
}
private fun suggestedResolutionsForMixedUseCaseNotSupportedInLegacyDevice(
useCaseCreator: UseCaseCreator
) {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val imageCapture = useCaseCreator.createUseCase(
IMAGE_CAPTURE_USE_CASE,
preferredAspectRatio = AspectRatio.RATIO_16_9
)
val videoCapture = createVideoCapture()
val preview = useCaseCreator.createUseCase(
PREVIEW_USE_CASE,
preferredAspectRatio = AspectRatio.RATIO_16_9
)
// An IllegalArgumentException will be thrown because a LEGACY level device can't support
// ImageCapture + VideoCapture + Preview
assertThrows(IllegalArgumentException::class.java) {
getSuggestedResolutionMap(
supportedSurfaceCombination,
imageCapture,
videoCapture,
preview
)
}
}
@Test
fun suggestedResolutionsForCustomizeResolutionsNotSupportedInLegacyDevice_LegacyApi() {
suggestedResolutionsForCustomizeResolutionsNotSupportedInLegacyDevice(legacyUseCaseCreator)
}
@Test
fun suggestedResolutionsForCustomizeResolutionsNotSupportedInLegacyDevice_ResolutionSelector() {
suggestedResolutionsForCustomizeResolutionsNotSupportedInLegacyDevice(
resolutionSelectorUseCaseCreator
)
}
private fun suggestedResolutionsForCustomizeResolutionsNotSupportedInLegacyDevice(
useCaseCreator: UseCaseCreator
) {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
// Legacy camera only support (PRIV, PREVIEW) + (PRIV, PREVIEW)
val previewResolutionsPairs = listOf(
Pair.create(ImageFormat.PRIVATE, arrayOf(PREVIEW_SIZE))
)
val videoCapture: VideoCapture<TestVideoOutput> = createVideoCapture(Quality.UHD)
val preview = useCaseCreator.createUseCase(
PREVIEW_USE_CASE,
supportedResolutions = previewResolutionsPairs
)
// An IllegalArgumentException will be thrown because the VideoCapture requests to only
// support a RECORD size but the configuration can't be supported on a LEGACY level device.
assertThrows(IllegalArgumentException::class.java) {
getSuggestedResolutionMap(supportedSurfaceCombination, videoCapture, preview)
}
}
@Test
fun getSuggestedResolutionsForMixedUseCaseInLimitedDevice_LegacyApi() {
getSuggestedResolutionsForMixedUseCaseInLimitedDevice(legacyUseCaseCreator)
}
@Test
fun getSuggestedResolutionsForMixedUseCaseInLimitedDevice_ResolutionSelector() {
getSuggestedResolutionsForMixedUseCaseInLimitedDevice(resolutionSelectorUseCaseCreator)
}
private fun getSuggestedResolutionsForMixedUseCaseInLimitedDevice(
useCaseCreator: UseCaseCreator
) {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val imageCapture = useCaseCreator.createUseCase(
IMAGE_CAPTURE_USE_CASE,
preferredAspectRatio = AspectRatio.RATIO_16_9
)
val videoCapture = createVideoCapture(Quality.HIGHEST)
val preview = useCaseCreator.createUseCase(
PREVIEW_USE_CASE,
preferredAspectRatio = AspectRatio.RATIO_16_9
)
val suggestedResolutionMap = getSuggestedResolutionMap(
supportedSurfaceCombination,
imageCapture,
videoCapture,
preview
)
// (PRIV, PREVIEW) + (PRIV, RECORD) + (JPEG, RECORD)
assertThat(suggestedResolutionMap[imageCapture]).isEqualTo(RECORD_SIZE)
assertThat(suggestedResolutionMap[videoCapture]).isEqualTo(RECORD_SIZE)
assertThat(suggestedResolutionMap[preview]).isEqualTo(PREVIEW_SIZE)
}
// For the use case in b/230651237,
// QualitySelector.from(Quality.UHD, FallbackStrategy.lowerQualityOrHigherThan(Quality.UHD).
// VideoCapture should have higher priority to choose size than ImageCapture.
@Test
@Throws(CameraUnavailableException::class)
fun getSuggestedResolutionsInFullDevice_videoHasHigherPriorityThanImage_LegacyApi() {
getSuggestedResolutionsInFullDevice_videoHasHigherPriorityThanImage(legacyUseCaseCreator)
}
@Test
@Throws(CameraUnavailableException::class)
fun getSuggestedResolutionsInFullDevice_videoHasHigherPriorityThanImage_ResolutionSelector() {
getSuggestedResolutionsInFullDevice_videoHasHigherPriorityThanImage(
resolutionSelectorUseCaseCreator
)
}
private fun getSuggestedResolutionsInFullDevice_videoHasHigherPriorityThanImage(
useCaseCreator: UseCaseCreator
) {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val imageCapture = useCaseCreator.createUseCase(
IMAGE_CAPTURE_USE_CASE,
preferredAspectRatio = AspectRatio.RATIO_16_9
)
val videoCapture = createVideoCapture(QualitySelector.from(
Quality.UHD,
FallbackStrategy.lowerQualityOrHigherThan(Quality.UHD)
))
val preview = useCaseCreator.createUseCase(
PREVIEW_USE_CASE,
preferredAspectRatio = AspectRatio.RATIO_16_9
)
val suggestedResolutionMap = getSuggestedResolutionMap(
supportedSurfaceCombination,
imageCapture,
videoCapture,
preview
)
// There are two possible combinations in Full level device
// (PRIV, PREVIEW) + (PRIV, RECORD) + (JPEG, RECORD) => should be applied
// (PRIV, PREVIEW) + (PRIV, PREVIEW) + (JPEG, MAXIMUM)
assertThat(suggestedResolutionMap[imageCapture]).isEqualTo(RECORD_SIZE)
assertThat(suggestedResolutionMap[videoCapture]).isEqualTo(RECORD_SIZE)
assertThat(suggestedResolutionMap[preview]).isEqualTo(PREVIEW_SIZE)
}
@Test
fun imageCaptureCanGetMaxSizeInFullDevice_videoRecordSizeLowPriority_LegacyApi() {
imageCaptureCanGetMaxSizeInFullDevice_videoRecordSizeLowPriority(legacyUseCaseCreator)
}
@Test
fun imageCaptureCanGetMaxSizeInFullDevice_videoRecordSizeLowPriority_ResolutionSelector() {
imageCaptureCanGetMaxSizeInFullDevice_videoRecordSizeLowPriority(
resolutionSelectorUseCaseCreator
)
}
private fun imageCaptureCanGetMaxSizeInFullDevice_videoRecordSizeLowPriority(
useCaseCreator: UseCaseCreator
) {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val imageCapture = useCaseCreator.createUseCase(
IMAGE_CAPTURE_USE_CASE,
preferredAspectRatio = AspectRatio.RATIO_4_3 // mMaximumSize(4032x3024) is 4:3
)
val videoCapture = createVideoCapture(
QualitySelector.fromOrderedList(
listOf<Quality>(Quality.HD, Quality.FHD, Quality.UHD)
)
)
val preview = useCaseCreator.createUseCase(
PREVIEW_USE_CASE,
preferredAspectRatio = AspectRatio.RATIO_16_9
)
val suggestedResolutionMap = getSuggestedResolutionMap(
supportedSurfaceCombination,
imageCapture,
videoCapture,
preview
)
// There are two possible combinations in Full level device
// (PRIV, PREVIEW) + (PRIV, RECORD) + (JPEG, RECORD)
// (PRIV, PREVIEW) + (PRIV, PREVIEW) + (JPEG, MAXIMUM) => should be applied
assertThat(suggestedResolutionMap[imageCapture]).isEqualTo(MAXIMUM_SIZE)
assertThat(suggestedResolutionMap[videoCapture]).isEqualTo(PREVIEW_SIZE) // Quality.HD
assertThat(suggestedResolutionMap[preview]).isEqualTo(PREVIEW_SIZE)
}
@Test
fun getSuggestedResolutionsWithSameSupportedListForDifferentUseCases_LegacyApi() {
getSuggestedResolutionsWithSameSupportedListForDifferentUseCases(
legacyUseCaseCreator,
DISPLAY_SIZE
)
}
@Test
fun getSuggestedResolutionsWithSameSupportedListForDifferentUseCases_RS_SensorSize() {
getSuggestedResolutionsWithSameSupportedListForDifferentUseCases(
resolutionSelectorUseCaseCreator,
PREVIEW_SIZE
)
}
@Test
fun getSuggestedResolutionsWithSameSupportedListForDifferentUseCases_RS_ViewSize() {
getSuggestedResolutionsWithSameSupportedListForDifferentUseCases(
viewSizeResolutionSelectorUseCaseCreator,
PREVIEW_SIZE
)
}
private fun getSuggestedResolutionsWithSameSupportedListForDifferentUseCases(
useCaseCreator: UseCaseCreator,
preferredResolution: Size
) {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
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 case.
Size(960, 544), // a mod16 version of resolution with 16:9 aspect ratio.
Size(800, 450), // 16:9
Size(640, 480), // 4:3
Size(320, 240), // 4:3
Size(320, 180), // 16:9
Size(256, 144) // 16:9 For checkSmallSizesAreFilteredOut test.
)
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
/* This test case is for b/132603284 that divide by zero issue crash happened in below
conditions:
1. There are duplicated two 1280x720 supported sizes for ImageCapture and Preview.
2. supportedOutputSizes for ImageCapture and Preview in
SupportedSurfaceCombination#getAllPossibleSizeArrangements are the same.
*/
val imageCapture = useCaseCreator.createUseCase(
IMAGE_CAPTURE_USE_CASE,
preferredResolution = preferredResolution
)
val preview = useCaseCreator.createUseCase(
PREVIEW_USE_CASE,
preferredResolution = preferredResolution
)
val imageAnalysis = useCaseCreator.createUseCase(
IMAGE_ANALYSIS_USE_CASE,
preferredResolution = preferredResolution
)
val suggestedResolutionMap = getSuggestedResolutionMap(
supportedSurfaceCombination,
imageCapture,
imageAnalysis,
preview
)
assertThat(suggestedResolutionMap[imageCapture]).isEqualTo(PREVIEW_SIZE)
assertThat(suggestedResolutionMap[imageAnalysis]).isEqualTo(PREVIEW_SIZE)
assertThat(suggestedResolutionMap[preview]).isEqualTo(PREVIEW_SIZE)
}
@Test
fun setTargetAspectRatioForMixedUseCases_LegacyApi() {
setTargetAspectRatioForMixedUseCases(legacyUseCaseCreator)
}
@Test
fun setTargetAspectRatioForMixedUseCases_ResolutionSelector() {
setTargetAspectRatioForMixedUseCases(resolutionSelectorUseCaseCreator)
}
private fun setTargetAspectRatioForMixedUseCases(useCaseCreator: UseCaseCreator) {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val preview = useCaseCreator.createUseCase(
PREVIEW_USE_CASE,
preferredAspectRatio = AspectRatio.RATIO_16_9
)
val imageCapture = useCaseCreator.createUseCase(
IMAGE_CAPTURE_USE_CASE,
preferredAspectRatio = AspectRatio.RATIO_16_9
)
val imageAnalysis = useCaseCreator.createUseCase(
IMAGE_ANALYSIS_USE_CASE,
preferredAspectRatio = AspectRatio.RATIO_16_9
)
val suggestedResolutionMap = getSuggestedResolutionMap(
supportedSurfaceCombination,
preview,
imageCapture,
imageAnalysis
)
assertThat(
hasMatchingAspectRatio(
suggestedResolutionMap[preview]!!,
ASPECT_RATIO_16_9
)
).isTrue()
assertThat(
hasMatchingAspectRatio(
suggestedResolutionMap[imageCapture]!!,
ASPECT_RATIO_16_9
)
).isTrue()
assertThat(
hasMatchingAspectRatio(
suggestedResolutionMap[imageAnalysis]!!,
ASPECT_RATIO_16_9
)
).isTrue()
}
@Test
fun getSuggestedResolutionsForCustomizedSupportedResolutions_LegacyApi() {
getSuggestedResolutionsForCustomizedSupportedResolutions(legacyUseCaseCreator)
}
@Test
fun getSuggestedResolutionsForCustomizedSupportedResolutions_ResolutionSelector() {
getSuggestedResolutionsForCustomizedSupportedResolutions(resolutionSelectorUseCaseCreator)
}
private fun getSuggestedResolutionsForCustomizedSupportedResolutions(
useCaseCreator: UseCaseCreator
) {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val formatResolutionsPairList = arrayListOf<Pair<Int, Array<Size>>>().apply {
add(Pair.create(ImageFormat.JPEG, arrayOf(RESOLUTION_VGA)))
add(Pair.create(ImageFormat.YUV_420_888, arrayOf(RESOLUTION_VGA)))
add(Pair.create(ImageFormat.PRIVATE, arrayOf(RESOLUTION_VGA)))
}
// Sets use cases customized supported resolutions to 640x480 only.
val imageCapture = useCaseCreator.createUseCase(
IMAGE_CAPTURE_USE_CASE,
supportedResolutions = formatResolutionsPairList
)
val videoCapture = createVideoCapture(Quality.SD)
val preview = useCaseCreator.createUseCase(
PREVIEW_USE_CASE,
supportedResolutions = formatResolutionsPairList
)
val suggestedResolutionMap = getSuggestedResolutionMap(
supportedSurfaceCombination,
imageCapture,
videoCapture,
preview
)
// Checks all suggested resolutions will become 640x480.
assertThat(suggestedResolutionMap[imageCapture]).isEqualTo(RESOLUTION_VGA)
assertThat(suggestedResolutionMap[videoCapture]).isEqualTo(RESOLUTION_VGA)
assertThat(suggestedResolutionMap[preview]).isEqualTo(RESOLUTION_VGA)
}
@Test
fun transformSurfaceConfigWithYUVAnalysisSize() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
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(
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(
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(
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(
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(
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(
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(
ImageFormat.JPEG, MAXIMUM_SIZE
)
val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM)
assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
}
@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 isAspectRatioMatchWithSupportedMod16Resolution_LegacyApi() {
isAspectRatioMatchWithSupportedMod16Resolution(legacyUseCaseCreator)
}
@Test
fun isAspectRatioMatchWithSupportedMod16Resolution_ResolutionSelector() {
isAspectRatioMatchWithSupportedMod16Resolution(resolutionSelectorUseCaseCreator)
}
private fun isAspectRatioMatchWithSupportedMod16Resolution(useCaseCreator: UseCaseCreator) {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = useCaseCreator.createUseCase(
FAKE_USE_CASE,
Surface.ROTATION_90,
preferredAspectRatio = AspectRatio.RATIO_16_9,
preferredResolution = MOD16_SIZE
)
val suggestedResolutionMap = getSuggestedResolutionMap(supportedSurfaceCombination, useCase)
assertThat(suggestedResolutionMap[useCase]).isEqualTo(MOD16_SIZE)
}
@Test
fun sortByCompareSizesByArea_canSortSizesCorrectly() {
val sizes = arrayOfNulls<Size>(DEFAULT_SUPPORTED_SIZES.size)
// Generates a unsorted array from mSupportedSizes.
val centerIndex = DEFAULT_SUPPORTED_SIZES.size / 2
// Puts 2nd half sizes in the front
for (i in centerIndex until DEFAULT_SUPPORTED_SIZES.size) {
sizes[i - centerIndex] = DEFAULT_SUPPORTED_SIZES[i]
}
// Puts 1st half sizes inversely in the tail
for (j in centerIndex - 1 downTo 0) {
sizes[DEFAULT_SUPPORTED_SIZES.size - j - 1] = DEFAULT_SUPPORTED_SIZES[j]
}
// The testing sizes array will be equal to mSupportedSizes after sorting.
Arrays.sort(sizes, CompareSizesByArea(true))
assertThat(listOf(*sizes)).isEqualTo(listOf(*DEFAULT_SUPPORTED_SIZES))
}
@Test
fun getSupportedOutputSizes_noConfigSettings() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(FAKE_USE_CASE)
// There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
// removed. No any aspect ratio related setting. The returned sizes list will be sorted in
// descending order.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(
Size(4032, 3024),
Size(3840, 2160),
Size(1920, 1440),
Size(1920, 1080),
Size(1280, 960),
Size(1280, 720),
Size(960, 544),
Size(800, 450),
Size(640, 480)
)
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_aspectRatio4x3() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetAspectRatio = AspectRatio.RATIO_4_3
)
// There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
// removed. Sizes of aspect ratio 4/3 will be in front of the returned sizes list and the
// list is sorted in descending order. Other items will be put in the following that are
// sorted by aspect ratio delta and then area size.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(
// Matched AspectRatio items, sorted by area size.
Size(4032, 3024),
Size(1920, 1440),
Size(1280, 960),
Size(640, 480),
// Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
Size(3840, 2160),
Size(1920, 1080),
Size(1280, 720),
Size(960, 544),
Size(800, 450)
)
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_aspectRatio16x9_InLimitedDevice() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetAspectRatio = AspectRatio.RATIO_16_9
)
// There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
// removed. Sizes of aspect ratio 16/9 will be in front of the returned sizes list and the
// list is sorted in descending order. Other items will be put in the following that are
// sorted by aspect ratio delta and then area size.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(
// Matched AspectRatio items, sorted by area size.
Size(3840, 2160),
Size(1920, 1080),
Size(1280, 720),
Size(960, 544),
Size(800, 450),
// Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
Size(4032, 3024),
Size(1920, 1440),
Size(1280, 960),
Size(640, 480)
)
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_aspectRatio16x9_inLegacyDevice() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetAspectRatio = AspectRatio.RATIO_16_9
)
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
// There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
// removed.
val expectedList: List<Size> = if (Build.VERSION.SDK_INT == 21) {
// Sizes with the same aspect ratio as maximum JPEG resolution will be in front of
// the returned sizes list and the list is sorted in descending order. Other items
// will be put in the following that are sorted by aspect ratio delta and then area
// size.
listOf(
// Matched the same AspectRatio as maximum JPEG items, sorted by aspect ratio
// delta then area size.
Size(4032, 3024),
Size(1920, 1440),
Size(1280, 960),
Size(640, 480),
// Mismatched the same AspectRatio as maximum JPEG items, sorted by area size.
Size(3840, 2160),
Size(1920, 1080),
Size(1280, 720),
Size(960, 544),
Size(800, 450)
)
} else {
// Sizes of aspect ratio 16/9 will be in front of the returned sizes list and the
// list is sorted in descending order. Other items will be put in the following that
// are sorted by aspect ratio delta and then area size.
listOf(
// Matched AspectRatio items, sorted by area size.
Size(3840, 2160),
Size(1920, 1080),
Size(1280, 720),
Size(960, 544),
Size(800, 450),
// Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
Size(4032, 3024),
Size(1920, 1440),
Size(1280, 960),
Size(640, 480)
)
}
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_targetResolution1080x1920InRotation0_InLimitedDevice() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetResolution = Size(1080, 1920)
)
// Unnecessary big enough sizes will be removed from the result list. There is default
// minimum size 640x480 setting. Sizes smaller than 640x480 will also be removed. The
// target resolution will be calibrated by default target rotation 0 degree. The
// auto-resolution mechanism will try to select the sizes which aspect ratio is nearest
// to the aspect ratio of target resolution in priority. Therefore, sizes of aspect ratio
// 16/9 will be in front of the returned sizes list and the list is sorted in descending
// order. Other items will be put in the following that are sorted by aspect ratio delta
// and then area size.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(
// Matched AspectRatio items, sorted by area size.
Size(1920, 1080),
Size(1280, 720),
Size(960, 544),
Size(800, 450),
// Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
Size(1920, 1440),
Size(1280, 960),
Size(640, 480)
)
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_targetResolution1080x1920InRotation0_InLegacyDevice() {
setupCameraAndInitCameraX()
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetResolution = Size(1080, 1920)
)
// Unnecessary big enough sizes will be removed from the result list. There is default
// minimum size 640x480 setting. Sizes smaller than 640x480 will also be removed.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList: List<Size> = if (Build.VERSION.SDK_INT == 21) {
// Sizes with the same aspect ratio as maximum JPEG resolution will be in front of
// the returned sizes list and the list is sorted in descending order. Other items
// will be put in the following that are sorted by aspect ratio delta and then area
// size.
listOf(
// Matched the same AspectRatio as maximum JPEG items, sorted by aspect ratio
// delta then area size.
Size(1920, 1440),
Size(1280, 960),
Size(640, 480),
// Mismatched the same AspectRatio as maximum JPEG items, sorted by area size.
Size(1920, 1080),
Size(1280, 720),
Size(960, 544),
Size(800, 450)
)
} else {
// The target resolution will be calibrated by default target rotation 0 degree. The
// auto-resolution mechanism will try to select the sizes which aspect ratio is
// nearest to the aspect ratio of target resolution in priority. Therefore, sizes of
// aspect ratio 16/9 will be in front of the returned sizes list and the list is
// sorted in descending order. Other items will be put in the following that are
// sorted by aspect ratio delta and then area size.
listOf(
// Matched AspectRatio items, sorted by area size.
Size(1920, 1080),
Size(1280, 720),
Size(960, 544),
Size(800, 450),
// Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
Size(1920, 1440),
Size(1280, 960),
Size(640, 480)
)
}
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_targetResolutionLargerThan640x480() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetRotation = Surface.ROTATION_90,
targetResolution = Size(1280, 960)
)
// Unnecessary big enough sizes will be removed from the result list. There is default
// minimum size 640x480 setting. Target resolution larger than 640x480 won't overwrite
// minimum size setting. Sizes smaller than 640x480 will be removed. The auto-resolution
// mechanism will try to select the sizes which aspect ratio is nearest to the aspect
// ratio of target resolution in priority. Therefore, sizes of aspect ratio 4/3 will be
// in front of the returned sizes list and the list is sorted in descending order. Other
// items will be put in the following that are sorted by aspect ratio delta and then area
// size.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(
// Matched AspectRatio items, sorted by area size.
Size(1280, 960),
Size(640, 480),
// Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
Size(1920, 1080),
Size(1280, 720),
Size(960, 544),
Size(800, 450)
)
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_targetResolutionSmallerThan640x480() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetRotation = Surface.ROTATION_90,
targetResolution = Size(320, 240)
)
// Unnecessary big enough sizes will be removed from the result list. Minimum size will
// be overwritten as 320x240. Sizes smaller than 320x240 will also be removed. The
// auto-resolution mechanism will try to select the sizes which aspect ratio is nearest
// to the aspect ratio of target resolution in priority. Therefore, sizes of aspect ratio
// 4/3 will be in front of the returned sizes list and the list is sorted in descending
// order. Other items will be put in the following that are sorted by aspect ratio delta
// and then area size.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(
// Matched AspectRatio items, sorted by area size.
Size(320, 240),
// Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
Size(800, 450)
)
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_maxResolutionSmallerThan640x480() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
maxResolution = Size(320, 240)
)
// Minimum size bound will be removed due to small max resolution setting.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = Arrays.asList(
*arrayOf(
Size(320, 240),
Size(320, 180),
Size(256, 144)
)
)
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_targetResolution1800x1440NearTo4x3() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetRotation = Surface.ROTATION_90,
targetResolution = Size(1800, 1440)
)
// Unnecessary big enough sizes will be removed from the result list. There is default
// minimum size 640x480 setting. Sizes smaller than 640x480 will also be removed. The
// auto-resolution mechanism will try to select the sizes which aspect ratio is nearest
// to the aspect ratio of target resolution in priority. Size 1800x1440 is near to 4/3
// therefore, sizes of aspect ratio 4/3 will be in front of the returned sizes list and
// the list is sorted in descending order.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(
// Sizes of 4/3 are near to aspect ratio of 1800/1440
Size(1920, 1440),
Size(1280, 960),
Size(640, 480),
// Sizes of 16/9 are far to aspect ratio of 1800/1440
Size(3840, 2160),
Size(1920, 1080),
Size(1280, 720),
Size(960, 544),
Size(800, 450)
)
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_targetResolution1280x600NearTo16x9() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetRotation = Surface.ROTATION_90,
targetResolution = Size(1280, 600)
)
// Unnecessary big enough sizes will be removed from the result list. There is default
// minimum size 640x480 setting. Sizes smaller than 640x480 will also be removed. The
// auto-resolution mechanism will try to select the sizes which aspect ratio is nearest
// to the aspect ratio of target resolution in priority. Size 1280x600 is near to 16/9,
// therefore, sizes of aspect ratio 16/9 will be in front of the returned sizes list and
// the list is sorted in descending order.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(
// Sizes of 16/9 are near to aspect ratio of 1280/600
Size(1280, 720),
Size(960, 544),
Size(800, 450),
// Sizes of 4/3 are far to aspect ratio of 1280/600
Size(1280, 960),
Size(640, 480)
)
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_maxResolution1280x720() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
maxResolution = Size(1280, 720)
)
// There is default minimum size 640x480 setting. Sizes smaller than 640x480 or
// larger than 1280x720 will be removed. The returned sizes list will be sorted in
// descending order.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(Size(1280, 720), Size(960, 544), Size(800, 450), Size(640, 480))
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun previewCanSelectResolutionLargerThanDisplay_withMaxResolution_LegacyApi() {
previewCanSelectResolutionLargerThanDisplay_withMaxResolution(legacyUseCaseCreator)
}
@Test
fun previewCanSelectResolutionLargerThanDisplay_withMaxResolution_ResolutionSelector() {
previewCanSelectResolutionLargerThanDisplay_withMaxResolution(
resolutionSelectorUseCaseCreator
)
}
private fun previewCanSelectResolutionLargerThanDisplay_withMaxResolution(
useCaseCreator: UseCaseCreator
) {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
// The max resolution is expressed in the sensor coordinate.
val useCase = useCaseCreator.createUseCase(
PREVIEW_USE_CASE,
maxResolution = MAXIMUM_SIZE
)
val suggestedResolutionMap = getSuggestedResolutionMap(supportedSurfaceCombination, useCase)
// Checks mMaximumSize is final selected for the use case.
assertThat(suggestedResolutionMap[useCase]).isEqualTo(MAXIMUM_SIZE)
}
@Test
fun getSupportedOutputSizes_defaultResolution1280x720_noTargetResolution() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
defaultResolution = Size(1280, 720)
)
// There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
// removed. If there is no target resolution setting, it will be overwritten by default
// resolution as 1280x720. Unnecessary big enough sizes will also be removed. The
// returned sizes list will be sorted in descending order.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(Size(1280, 720), Size(960, 544), Size(800, 450), Size(640, 480))
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_defaultResolution1280x720_targetResolution1920x1080() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetRotation = Surface.ROTATION_90,
defaultResolution = Size(1280, 720),
targetResolution = Size(1920, 1080)
)
// There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
// removed. There is target resolution 1920x1080, it won't be overwritten by default
// resolution 1280x720. Unnecessary big enough sizes will also be removed. Sizes of
// aspect ratio 16/9 will be in front of the returned sizes list and the list is sorted
// in descending order. Other items will be put in the following that are sorted by
// aspect ratio delta and then area size.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(
// Matched AspectRatio items, sorted by area size.
Size(1920, 1080),
Size(1280, 720),
Size(960, 544),
Size(800, 450),
// Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
Size(1920, 1440),
Size(1280, 960),
Size(640, 480)
)
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_fallbackToGuaranteedResolution_whenNotFulfillConditions() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
supportedSizes = arrayOf(
Size(640, 480),
Size(320, 240),
Size(320, 180),
Size(256, 144)
)
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetRotation = Surface.ROTATION_90,
targetResolution = Size(1920, 1080)
)
// There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
// removed. There is target resolution 1920x1080 (16:9). Even 640x480 does not match 16:9
// requirement, it will still be returned to use.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(Size(640, 480))
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_whenMaxSizeSmallerThanDefaultMiniSize() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
supportedSizes = arrayOf(
Size(640, 480),
Size(320, 240),
Size(320, 180),
Size(256, 144)
)
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
maxResolution = Size(320, 240)
)
// There is default minimum size 640x480 setting. Originally, sizes smaller than 640x480
// will be removed. Due to maximal size bound is smaller than the default minimum size
// bound and it is also smaller than 640x480, the default minimum size bound will be
// ignored. Then, sizes equal to or smaller than 320x240 will be kept in the result list.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(Size(320, 240), Size(320, 180), Size(256, 144))
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_whenMaxSizeSmallerThanSmallTargetResolution() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
supportedSizes = arrayOf(
Size(640, 480),
Size(320, 240),
Size(320, 180),
Size(256, 144)
)
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetRotation = Surface.ROTATION_90,
targetResolution = Size(320, 240),
maxResolution = Size(320, 180)
)
// The default minimum size 640x480 will be overwritten by the target resolution 320x240.
// Originally, sizes smaller than 320x240 will be removed. Due to maximal size bound is
// smaller than the minimum size bound and it is also smaller than 640x480, the minimum
// size bound will be ignored. Then, sizes equal to or smaller than 320x180 will be kept
// in the result list.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(Size(320, 180), Size(256, 144))
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_whenBothMaxAndTargetResolutionsSmallerThan640x480() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
supportedSizes = arrayOf(
Size(640, 480),
Size(320, 240),
Size(320, 180),
Size(256, 144)
)
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetRotation = Surface.ROTATION_90,
targetResolution = Size(320, 180),
maxResolution = Size(320, 240)
)
// The default minimum size 640x480 will be overwritten by the target resolution 320x180.
// Originally, sizes smaller than 320x180 will be removed. Due to maximal size bound is
// smaller than the minimum size bound and it is also smaller than 640x480, the minimum
// size bound will be ignored. Then, all sizes equal to or smaller than 320x320 will be
// kept in the result list.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(Size(320, 180), Size(256, 144), Size(320, 240))
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_whenMaxSizeSmallerThanBigTargetResolution() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetRotation = Surface.ROTATION_90,
targetResolution = Size(3840, 2160),
maxResolution = Size(1920, 1080)
)
// Because the target size 3840x2160 is larger than 640x480, it won't overwrite the
// default minimum size 640x480. Sizes smaller than 640x480 will be removed. The
// auto-resolution mechanism will try to select the sizes which aspect ratio is nearest
// to the aspect ratio of target resolution in priority. Therefore, sizes of aspect ratio
// 16/9 will be in front of the returned sizes list and the list is sorted in descending
// order. Other items will be put in the following that are sorted by aspect ratio delta
// and then area size.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(
// Matched AspectRatio items, sorted by area size.
Size(1920, 1080),
Size(1280, 720),
Size(960, 544),
Size(800, 450),
// Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
Size(1280, 960),
Size(640, 480)
)
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_whenNoSizeBetweenMaxSizeAndTargetResolution() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
supportedSizes = arrayOf(
Size(640, 480),
Size(320, 240),
Size(320, 180),
Size(256, 144)
)
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetRotation = Surface.ROTATION_90,
targetResolution = Size(320, 190),
maxResolution = Size(320, 200)
)
// The default minimum size 640x480 will be overwritten by the target resolution 320x190.
// Originally, sizes smaller than 320x190 will be removed. Due to there is no available
// size between the maximal size and the minimum size bound and the maximal size is
// smaller than 640x480, the default minimum size bound will be ignored. Then, sizes
// equal to or smaller than 320x200 will be kept in the result list.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(Size(320, 180), Size(256, 144))
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_whenTargetResolutionSmallerThanAnySize() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
supportedSizes = arrayOf(
Size(640, 480),
Size(320, 240),
Size(320, 180),
Size(256, 144)
)
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetRotation = Surface.ROTATION_90,
targetResolution = Size(192, 144)
)
// The default minimum size 640x480 will be overwritten by the target resolution 192x144.
// Because 192x144 is smaller than any size in the supported list, no one will be
// filtered out by it. The result list will only keep one big enough size of aspect ratio
// 4:3 and 16:9.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(Size(320, 240), Size(256, 144))
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_whenMaxResolutionSmallerThanAnySize() {
setupCameraAndInitCameraX(
supportedSizes = arrayOf(
Size(640, 480),
Size(320, 240),
Size(320, 180),
Size(256, 144)
)
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
maxResolution = Size(192, 144)
)
// All sizes will be filtered out by the max resolution 192x144 setting and an
// IllegalArgumentException will be thrown.
assertThrows(IllegalArgumentException::class.java) {
getSupportedOutputSizes(supportedSurfaceCombination, useCase)
}
}
@Test
fun getSupportedOutputSizes_whenMod16IsIgnoredForSmallSizes() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
supportedSizes = arrayOf(
Size(640, 480),
Size(320, 240),
Size(320, 180),
Size(296, 144),
Size(256, 144)
)
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetRotation = Surface.ROTATION_90,
targetResolution = Size(185, 90)
)
// The default minimum size 640x480 will be overwritten by the target resolution 185x90
// (18.5:9). If mod 16 calculation is not ignored for the sizes smaller than 640x480, the
// size 256x144 will be considered to match 18.5:9 and then become the first item in the
// result list. After ignoring mod 16 calculation for small sizes, 256x144 will still be
// kept as a 16:9 resolution as the result.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(Size(296, 144), Size(256, 144), Size(320, 240))
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizes_whenOneMod16SizeClosestToTargetResolution() {
setupCameraAndInitCameraX(
supportedSizes = arrayOf(
Size(1920, 1080),
Size(1440, 1080),
Size(1280, 960),
Size(1280, 720),
Size(864, 480), // This is a 16:9 mod16 size that is closest to 2016x1080
Size(768, 432),
Size(640, 480),
Size(640, 360),
Size(480, 360),
Size(384, 288)
)
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetResolution = Size(1080, 2016)
)
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(
Size(1920, 1080),
Size(1280, 720),
Size(864, 480),
Size(768, 432),
Size(1440, 1080),
Size(1280, 960),
Size(640, 480)
)
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizesWithPortraitPixelArraySize_aspectRatio16x9() {
// Sets the sensor orientation as 0 and pixel array size as a portrait size to simulate a
// phone device which majorly supports portrait output sizes.
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
sensorOrientation = SENSOR_ORIENTATION_0,
pixelArraySize = PORTRAIT_PIXEL_ARRAY_SIZE,
supportedSizes = arrayOf(
Size(1080, 1920),
Size(1080, 1440),
Size(960, 1280),
Size(720, 1280),
Size(1280, 720),
Size(480, 640),
Size(640, 480),
Size(360, 480)
)
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetAspectRatio = AspectRatio.RATIO_16_9
)
// There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
// removed. Due to the pixel array size is portrait, sizes of aspect ratio 9/16 will be in
// front of the returned sizes list and the list is sorted in descending order. Other
// items will be put in the following that are sorted by aspect ratio delta and then area
// size.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(
// Matched AspectRatio items, sorted by area size.
Size(1080, 1920),
Size(720, 1280),
// Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
Size(1080, 1440),
Size(960, 1280),
Size(480, 640),
Size(640, 480),
Size(1280, 720)
)
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizesOnTabletWithPortraitPixelArraySize_aspectRatio16x9() {
// Sets the sensor orientation as 90 and pixel array size as a portrait size to simulate a
// tablet device which majorly supports portrait output sizes.
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
sensorOrientation = SENSOR_ORIENTATION_90,
pixelArraySize = PORTRAIT_PIXEL_ARRAY_SIZE,
supportedSizes = arrayOf(
Size(1080, 1920),
Size(1080, 1440),
Size(960, 1280),
Size(720, 1280),
Size(1280, 720),
Size(480, 640),
Size(640, 480),
Size(360, 480)
)
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetAspectRatio = AspectRatio.RATIO_16_9
)
// There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
// removed. Due to the pixel array size is portrait, sizes of aspect ratio 9/16 will be in
// front of the returned sizes list and the list is sorted in descending order. Other
// items will be put in the following that are sorted by aspect ratio delta and then area
// size.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(
// Matched AspectRatio items, sorted by area size.
Size(1080, 1920),
Size(720, 1280),
// Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
Size(1080, 1440),
Size(960, 1280),
Size(480, 640),
Size(640, 480),
Size(1280, 720)
)
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizesOnTablet_aspectRatio16x9() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
sensorOrientation = SENSOR_ORIENTATION_0,
pixelArraySize = LANDSCAPE_PIXEL_ARRAY_SIZE
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetAspectRatio = AspectRatio.RATIO_16_9
)
// There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
// removed. Sizes of aspect ratio 16/9 will be in front of the returned sizes list and the
// list is sorted in descending order. Other items will be put in the following that are
// sorted by aspect ratio delta and then area size.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(
// Matched AspectRatio items, sorted by area size.
Size(3840, 2160),
Size(1920, 1080),
Size(1280, 720),
Size(960, 544),
Size(800, 450),
// Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
Size(4032, 3024),
Size(1920, 1440),
Size(1280, 960),
Size(640, 480)
)
assertThat(resultList).isEqualTo(expectedList)
}
@Test
fun getSupportedOutputSizesOnTabletWithPortraitSizes_aspectRatio16x9() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
sensorOrientation = SENSOR_ORIENTATION_0, supportedSizes = arrayOf(
Size(1920, 1080),
Size(1440, 1080),
Size(1280, 960),
Size(1280, 720),
Size(720, 1280),
Size(640, 480),
Size(480, 640),
Size(480, 360)
)
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByLegacyApi(
FAKE_USE_CASE,
targetAspectRatio = AspectRatio.RATIO_16_9
)
// There is default minimum size 640x480 setting. Sizes smaller than 640x480 will be
// removed. Sizes of aspect ratio 16/9 will be in front of the returned sizes list and the
// list is sorted in descending order. Other items will be put in the following that are
// sorted by aspect ratio delta and then area size.
val resultList = getSupportedOutputSizes(supportedSurfaceCombination, useCase)
val expectedList = listOf(
// Matched AspectRatio items, sorted by area size.
Size(1920, 1080),
Size(1280, 720),
// Mismatched AspectRatio items, sorted by aspect ratio delta then area size.
Size(1440, 1080),
Size(1280, 960),
Size(640, 480),
Size(480, 640),
Size(720, 1280)
)
assertThat(resultList).isEqualTo(expectedList)
}
@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
)
}
@Test
fun canGet640x480_whenAnotherGroupMatchedInMod16Exists_LegacyApi() {
canGet640x480_whenAnotherGroupMatchedInMod16Exists(legacyUseCaseCreator)
}
@Test
fun canGet640x480_whenAnotherGroupMatchedInMod16Exists_RS_SensorSize() {
canGet640x480_whenAnotherGroupMatchedInMod16Exists(resolutionSelectorUseCaseCreator)
}
@Test
fun canGet640x480_whenAnotherGroupMatchedInMod16Exists_RS_ViewSize() {
canGet640x480_whenAnotherGroupMatchedInMod16Exists(viewSizeResolutionSelectorUseCaseCreator)
}
private fun canGet640x480_whenAnotherGroupMatchedInMod16Exists(useCaseCreator: UseCaseCreator) {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
supportedSizes = arrayOf(
Size(4000, 3000),
Size(3840, 2160),
Size(1920, 1080),
Size(1024, 738), // This will create a 512/269 aspect ratio group that
// 640x480 will be considered to match in mod16 condition.
Size(800, 600),
Size(640, 480),
Size(320, 240)
)
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
// Sets the target resolution as 640x480 with target rotation as ROTATION_90 because the
// sensor orientation is 90.
val useCase = useCaseCreator.createUseCase(
FAKE_USE_CASE,
targetRotation = Surface.ROTATION_90,
preferredResolution = RESOLUTION_VGA
)
val suggestedResolutionMap = getSuggestedResolutionMap(supportedSurfaceCombination, useCase)
// Checks 640x480 is final selected for the use case.
assertThat(suggestedResolutionMap[useCase]).isEqualTo(RESOLUTION_VGA)
}
@Test
fun canGetSupportedSizeSmallerThan640x480_whenLargerMaxResolutionIsSet_LegacyApi() {
canGetSupportedSizeSmallerThan640x480_whenLargerMaxResolutionIsSet(legacyUseCaseCreator)
}
@Test
fun canGetSupportedSizeSmallerThan640x480_whenLargerMaxResolutionIsSet_ResolutionSelector() {
canGetSupportedSizeSmallerThan640x480_whenLargerMaxResolutionIsSet(
resolutionSelectorUseCaseCreator
)
}
private fun canGetSupportedSizeSmallerThan640x480_whenLargerMaxResolutionIsSet(
useCaseCreator: UseCaseCreator
) {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
supportedSizes = arrayOf(Size(480, 480))
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
// Sets the max resolution as 720x1280
val useCase = useCaseCreator.createUseCase(
FAKE_USE_CASE,
maxResolution = DISPLAY_SIZE
)
val suggestedResolutionMap = getSuggestedResolutionMap(supportedSurfaceCombination, useCase)
// Checks 480x480 is final selected for the use case.
assertThat(suggestedResolutionMap[useCase]).isEqualTo(Size(480, 480))
}
@Test
fun previewSizeIsSelectedForImageAnalysis_withImageCaptureInLimitedDevice_LegacyApi() {
previewSizeIsSelectedForImageAnalysis_withImageCaptureInLimitedDevice(
legacyUseCaseCreator, PREVIEW_SIZE
)
}
// For the ResolutionSelector API, RECORD_SIZE can't be used because it exceeds
// PREVIEW_SIZE. Therefore, the logic will fallback to select a 4:3 PREVIEW_SIZE. Then,
// 640x480 will be selected.
@Test
fun previewSizeIsSelectedForImageAnalysis_withImageCaptureInLimitedDevice_RS_SensorSize() {
previewSizeIsSelectedForImageAnalysis_withImageCaptureInLimitedDevice(
resolutionSelectorUseCaseCreator, RESOLUTION_VGA
)
}
@Test
fun previewSizeIsSelectedForImageAnalysis_withImageCaptureInLimitedDevice_RS_ViewSize() {
previewSizeIsSelectedForImageAnalysis_withImageCaptureInLimitedDevice(
viewSizeResolutionSelectorUseCaseCreator, RESOLUTION_VGA
)
}
private fun previewSizeIsSelectedForImageAnalysis_withImageCaptureInLimitedDevice(
useCaseCreator: UseCaseCreator,
expectedResult: Size
) {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val preview = useCaseCreator.createUseCase(PREVIEW_USE_CASE) as Preview
preview.setSurfaceProvider(
CameraXExecutors.directExecutor(),
SurfaceTextureProvider.createSurfaceTextureProvider(
Mockito.mock(
SurfaceTextureCallback::class.java
)
)
)
// ImageCapture has no explicit target resolution setting
val imageCapture = useCaseCreator.createUseCase(IMAGE_CAPTURE_USE_CASE)
// A LEGACY-level above device supports the following configuration.
// PRIV/PREVIEW + YUV/PREVIEW + JPEG/MAXIMUM
//
// A LIMITED-level above device supports the following configuration.
// PRIV/PREVIEW + YUV/RECORD + JPEG/RECORD
//
// Even there is a RECORD size target resolution setting for ImageAnalysis, ImageCapture
// will still have higher priority to have a MAXIMUM size resolution if the app doesn't
// explicitly specify a RECORD size target resolution to ImageCapture.
val imageAnalysis = useCaseCreator.createUseCase(
IMAGE_ANALYSIS_USE_CASE,
targetRotation = Surface.ROTATION_90,
preferredResolution = RECORD_SIZE
)
val suggestedResolutionMap = getSuggestedResolutionMap(
supportedSurfaceCombination,
preview,
imageCapture,
imageAnalysis
)
assertThat(suggestedResolutionMap[imageAnalysis]).isEqualTo(expectedResult)
}
@Test
fun imageAnalysisSelectRecordSize_imageCaptureHasExplicitSizeInLimitedDevice_LegacyApi() {
imageAnalysisSelectRecordSize_imageCaptureHasExplicitSizeInLimitedDevice(
legacyUseCaseCreator
)
}
@Test
fun imageAnalysisSelectRecordSize_imageCaptureHasExplicitSizeInLimitedDevice_RS_SensorSize() {
imageAnalysisSelectRecordSize_imageCaptureHasExplicitSizeInLimitedDevice(
resolutionSelectorUseCaseCreator
)
}
@Test
fun imageAnalysisSelectRecordSize_imageCaptureHasExplicitSizeInLimitedDevice_RS_ViewSize() {
imageAnalysisSelectRecordSize_imageCaptureHasExplicitSizeInLimitedDevice(
viewSizeResolutionSelectorUseCaseCreator
)
}
private fun imageAnalysisSelectRecordSize_imageCaptureHasExplicitSizeInLimitedDevice(
useCaseCreator: UseCaseCreator
) {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val preview = useCaseCreator.createUseCase(PREVIEW_USE_CASE) as Preview
preview.setSurfaceProvider(
CameraXExecutors.directExecutor(),
SurfaceTextureProvider.createSurfaceTextureProvider(
Mockito.mock(
SurfaceTextureCallback::class.java
)
)
)
// ImageCapture has no explicit RECORD size target resolution setting
val imageCapture = useCaseCreator.createUseCase(
IMAGE_CAPTURE_USE_CASE,
targetRotation = Surface.ROTATION_90,
preferredResolution = RECORD_SIZE
)
// A LEGACY-level above device supports the following configuration.
// PRIV/PREVIEW + YUV/PREVIEW + JPEG/MAXIMUM
//
// A LIMITED-level above device supports the following configuration.
// PRIV/PREVIEW + YUV/RECORD + JPEG/RECORD
//
// A RECORD can be selected for ImageAnalysis if the ImageCapture has a explicit RECORD
// size target resolution setting. It means that the application know the trade-off and
// the ImageAnalysis has higher priority to get a larger resolution than ImageCapture.
val imageAnalysis = useCaseCreator.createUseCase(
IMAGE_ANALYSIS_USE_CASE,
targetRotation = Surface.ROTATION_90,
preferredResolution = RECORD_SIZE
)
val suggestedResolutionMap = getSuggestedResolutionMap(
supportedSurfaceCombination,
preview,
imageCapture,
imageAnalysis
)
assertThat(suggestedResolutionMap[imageAnalysis]).isEqualTo(RECORD_SIZE)
}
@Config(minSdk = Build.VERSION_CODES.M)
@Test
fun highResolutionIsSelected_whenHighResolutionIsEnabled() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
capabilities = intArrayOf(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
),
supportedHighResolutionSizes = arrayOf(Size(8000, 6000), Size(8000, 4500))
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByResolutionSelector(FAKE_USE_CASE, highResolutionEnabled = true)
val suggestedResolutionMap = getSuggestedResolutionMap(supportedSurfaceCombination, useCase)
// Checks 8000x6000 is final selected for the use case.
assertThat(suggestedResolutionMap[useCase]).isEqualTo(Size(8000, 6000))
}
@Config(minSdk = Build.VERSION_CODES.M)
@Test
fun highResolutionIsNotSelected_whenHighResolutionIsEnabled_withoutBurstCaptureCapability() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
supportedHighResolutionSizes = arrayOf(Size(8000, 6000), Size(8000, 4500))
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByResolutionSelector(FAKE_USE_CASE, highResolutionEnabled = true)
val suggestedResolutionMap = getSuggestedResolutionMap(supportedSurfaceCombination, useCase)
// Checks 8000x6000 is final selected for the use case.
assertThat(suggestedResolutionMap[useCase]).isEqualTo(Size(4032, 3024))
}
@Config(minSdk = Build.VERSION_CODES.M)
@Test
fun highResolutionIsNotSelected_whenHighResolutionIsNotEnabled_targetResolution8000x6000() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
capabilities = intArrayOf(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
),
supportedHighResolutionSizes = arrayOf(Size(8000, 6000), Size(8000, 4500))
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase =
createUseCaseByResolutionSelector(FAKE_USE_CASE, preferredResolution = Size(8000, 6000))
val suggestedResolutionMap = getSuggestedResolutionMap(supportedSurfaceCombination, useCase)
// Checks 8000x6000 is final selected for the use case.
assertThat(suggestedResolutionMap[useCase]).isEqualTo(Size(4032, 3024))
}
@Config(minSdk = Build.VERSION_CODES.M)
@Test
fun highResolutionIsSelected_whenHighResolutionIsEnabled_aspectRatio16x9() {
setupCameraAndInitCameraX(
hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
capabilities = intArrayOf(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
),
supportedHighResolutionSizes = arrayOf(Size(8000, 6000), Size(8000, 4500))
)
val supportedSurfaceCombination = SupportedSurfaceCombination(
context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
)
val useCase = createUseCaseByResolutionSelector(
FAKE_USE_CASE,
preferredAspectRatio = AspectRatio.RATIO_16_9,
highResolutionEnabled = true
)
val suggestedResolutionMap = getSuggestedResolutionMap(supportedSurfaceCombination, useCase)
// Checks 8000x6000 is final selected for the use case.
assertThat(suggestedResolutionMap[useCase]).isEqualTo(Size(8000, 4500))
}
/**
* 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 {
camcorderProfileProvider = FakeCamcorderProfileProvider.Builder()
.addProfile(
CamcorderProfileUtil.asHighQuality(profileUhd),
profileUhd,
profileFhd,
profileHd,
profileSd,
CamcorderProfileUtil.asLowQuality(profileSd)
).build()
}
cameraFactory = FakeCameraFactory().apply {
insertCamera(lensFacingEnum, cameraId) {
FakeCamera(cameraId, null, cameraInfo)
}
}
initCameraX()
}
/**
* Initializes the [CameraX].
*/
private fun initCameraX() {
val surfaceManagerProvider =
CameraDeviceSurfaceManager.Provider { context, _, availableCameraIds ->
Camera2DeviceSurfaceManager(
context,
mockCamcorderProfileHelper,
CameraManagerCompat.from(this@SupportedSurfaceCombinationTest.context),
availableCameraIds
)
}
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(
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(subConfigurationList)) {
return false
}
}
}
return true
}
/**
* Gets the suggested resolution map by the converted ResolutionSelector use case config which
* will also be converted when a use case is bound to the lifecycle.
*/
private fun getSuggestedResolutionMap(
supportedSurfaceCombination: SupportedSurfaceCombination,
vararg useCases: UseCase,
cameraFactory: CameraFactory = this.cameraFactory!!,
cameraId: String = DEFAULT_CAMERA_ID,
useCaseConfigFactory: UseCaseConfigFactory = this.useCaseConfigFactory!!
): Map<UseCase, Size?> {
// Generates the use case to new ResolutionSelector use case config map
val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
cameraFactory.getCamera(cameraId).cameraInfoInternal,
listOf(*useCases),
useCaseConfigFactory
)
// Uses the use case config list to get suggested resolutions
val useCaseConfigResolutionMap = supportedSurfaceCombination.getSuggestedResolutions(
emptyList(),
mutableListOf<UseCaseConfig<*>?>().apply { addAll(useCaseToConfigMap.values) }
)
val useCaseResolutionMap = mutableMapOf<UseCase, Size?>()
// Maps the use cases to the suggestion resolutions
for (useCase in useCases) {
useCaseResolutionMap[useCase] = useCaseConfigResolutionMap[useCaseToConfigMap[useCase]]
}
return useCaseResolutionMap
}
/**
* Gets the supported output sizes by the converted ResolutionSelector use case config which
* will also be converted when a use case is bound to the lifecycle.
*/
private fun getSupportedOutputSizes(
supportedSurfaceCombination: SupportedSurfaceCombination,
useCase: UseCase,
cameraId: String = DEFAULT_CAMERA_ID,
useCaseConfigFactory: UseCaseConfigFactory = this.useCaseConfigFactory!!
): List<Size?> {
// Converts the use case config to new ResolutionSelector config
val useCaseToConfigMap = Configs.useCaseConfigMapWithDefaultSettingsFromUseCaseList(
cameraFactory!!.getCamera(cameraId).cameraInfoInternal,
listOf(useCase),
useCaseConfigFactory
)
return supportedSurfaceCombination.getSupportedOutputSizes(useCaseToConfigMap[useCase]!!)
}
/**
* Creates [Preview], [ImageCapture], [ImageAnalysis], [androidx.camera.core.VideoCapture] or
* FakeUseCase according to the specified settings.
*
* @param useCaseType Which of [Preview], [ImageCapture], [ImageAnalysis],
* [androidx.camera.core.VideoCapture] and FakeUseCase should be created.
* @param targetRotation the target rotation setting. Default is UNKNOWN_ROTATION and no target
* rotation will be set to the created use case.
* @param targetAspectRatio the target aspect ratio setting. Default is UNKNOWN_ASPECT_RATIO
* and no target aspect ratio will be set to the created use case.
* @param targetResolution the target resolution setting which should still be specified in the
* legacy API approach. The size should be expressed in the coordinate frame after rotating the
* supported sizes by the target rotation. Default is null.
* @param maxResolution the max resolution setting. Default is null.
* @param defaultResolution the default resolution setting. Default is null.
* @param supportedResolutions the customized supported resolutions. Default is null.
*/
@Suppress("DEPRECATION")
private fun createUseCaseByLegacyApi(
useCaseType: Int,
targetRotation: Int = UNKNOWN_ROTATION,
targetAspectRatio: Int = UNKNOWN_ASPECT_RATIO,
targetResolution: Size? = null,
maxResolution: Size? = null,
defaultResolution: Size? = null,
supportedResolutions: List<Pair<Int, Array<Size>>>? = null,
): UseCase {
val builder = when (useCaseType) {
PREVIEW_USE_CASE -> Preview.Builder()
IMAGE_CAPTURE_USE_CASE -> ImageCapture.Builder()
IMAGE_ANALYSIS_USE_CASE -> ImageAnalysis.Builder()
else -> FakeUseCaseConfig.Builder(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE)
}
if (targetRotation != UNKNOWN_ROTATION) {
builder.setTargetRotation(targetRotation)
}
if (targetAspectRatio != UNKNOWN_ASPECT_RATIO) {
builder.setTargetAspectRatio(targetAspectRatio)
}
targetResolution?.let { builder.setTargetResolution(it) }
maxResolution?.let { builder.setMaxResolution(it) }
defaultResolution?.let { builder.setDefaultResolution(it) }
supportedResolutions?.let { builder.setSupportedResolutions(it) }
return builder.build()
}
/** Creates a VideoCapture with a default QualitySelector */
private fun createVideoCapture(): VideoCapture<TestVideoOutput> {
return createVideoCapture(VideoSpec.QUALITY_SELECTOR_AUTO)
}
/** Creates a VideoCapture with a specific Quality */
private fun createVideoCapture(quality: Quality): VideoCapture<TestVideoOutput> {
return createVideoCapture(QualitySelector.from(quality))
}
/** Creates a VideoCapture with a customized QualitySelector */
private fun createVideoCapture(qualitySelector: QualitySelector):
VideoCapture<TestVideoOutput> {
val mediaSpec = MediaSpec.builder().configureVideo {
it.setQualitySelector(
qualitySelector
)
}.build()
val videoOutput = TestVideoOutput()
videoOutput.mediaSpecObservable.setState(mediaSpec)
return VideoCapture.withOutput(videoOutput)
}
/** A fake implementation of VideoOutput */
private class TestVideoOutput : VideoOutput {
var mediaSpecObservable: MutableStateObservable<MediaSpec> =
MutableStateObservable.withInitialState(MediaSpec.builder().build())
var surfaceRequest: SurfaceRequest? = null
var sourceState: SourceState? = null
override fun onSurfaceRequested(@NonNull request: SurfaceRequest) {
surfaceRequest = request
}
override fun getMediaSpec() = mediaSpecObservable
override fun onSourceStateChanged(@NonNull sourceState: SourceState) {
this.sourceState = sourceState
}
}
private interface UseCaseCreator {
fun createUseCase(
useCaseType: Int,
targetRotation: Int = UNKNOWN_ROTATION,
preferredAspectRatio: Int = UNKNOWN_ASPECT_RATIO,
preferredResolution: Size? = null,
maxResolution: Size? = null,
highResolutionEnabled: Boolean = false,
defaultResolution: Size? = null,
supportedResolutions: List<Pair<Int, Array<Size>>>? = null,
): UseCase
}
}